Skip to content

Commit d63ec93

Browse files
Merge pull request #3 from shezhangzhang/develop
Feature: Support color and value decorations in editor document.
2 parents dcf862d + 8f0d559 commit d63ec93

File tree

12 files changed

+411
-40
lines changed

12 files changed

+411
-40
lines changed

.vscode/launch.json

Lines changed: 28 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -3,33 +3,32 @@
33
// Hover to view descriptions of existing attributes.
44
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
55
{
6-
"version": "0.2.0",
7-
"configurations": [
8-
{
9-
"name": "Run Extension",
10-
"type": "extensionHost",
11-
"request": "launch",
12-
"args": [
13-
"--extensionDevelopmentPath=${workspaceFolder}"
14-
],
15-
"outFiles": [
16-
"${workspaceFolder}/dist/**/*.js"
17-
],
18-
"preLaunchTask": "${defaultBuildTask}"
19-
},
20-
{
21-
"name": "Extension Tests",
22-
"type": "extensionHost",
23-
"request": "launch",
24-
"args": [
25-
"--extensionDevelopmentPath=${workspaceFolder}",
26-
"--extensionTestsPath=${workspaceFolder}/out/test/suite/index"
27-
],
28-
"outFiles": [
29-
"${workspaceFolder}/out/**/*.js",
30-
"${workspaceFolder}/dist/**/*.js"
31-
],
32-
"preLaunchTask": "tasks: watch-tests"
33-
}
34-
]
6+
"version": "0.2.0",
7+
"configurations": [
8+
{
9+
"name": "Run Extension",
10+
"type": "extensionHost",
11+
"request": "launch",
12+
"args": [
13+
"--extensionDevelopmentPath=${workspaceFolder}",
14+
"--disable-extensions"
15+
],
16+
"outFiles": ["${workspaceFolder}/dist/**/*.js"],
17+
"preLaunchTask": "${defaultBuildTask}"
18+
},
19+
{
20+
"name": "Extension Tests",
21+
"type": "extensionHost",
22+
"request": "launch",
23+
"args": [
24+
"--extensionDevelopmentPath=${workspaceFolder}",
25+
"--extensionTestsPath=${workspaceFolder}/out/test/suite/index"
26+
],
27+
"outFiles": [
28+
"${workspaceFolder}/out/**/*.js",
29+
"${workspaceFolder}/dist/**/*.js"
30+
],
31+
"preLaunchTask": "tasks: watch-tests"
32+
}
33+
]
3534
}

README.md

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,19 @@
11
# antd design token
22

3+
![logo](assets/logo-mini.png)
4+
35
VSCode extension for antd v5 design token.
46

57
## Features
68

7-
Provide the hover hint of antd v5 design token, include `color`, `value`.
9+
Provide the hover hint and editor decorations of antd v5 design token, include `color`, `value`:
810

9-
![hover](assets/hover.gif)
11+
![decorations](assets/decorations.gif)
1012

11-
Support completion for antd v5 design token value on typing. Note that for sorting, you can type an `a` before typing the token. For example, if you want to type `padding...`, you can type `apadding...`. Which will not affect the variable inserted after the enter keydown.
13+
Support completion for antd v5 design token value on typing. Note that for sorting, you can type an `a` before typing the token. For example, if you want to type `margin...`, you can type `amargin...`. Which will not affect the variable inserted after the enter keydown.
1214

13-
![typing](assets/type.gif)
15+
Sort by token value:
16+
![sort](assets/sort.png)
1417

1518
## Commands
1619

assets/decorations.gif

815 KB
Loading

assets/hover.gif

-5.27 MB
Binary file not shown.

assets/logo-mini.png

15.8 KB
Loading

assets/logo.png

1.68 KB
Loading

assets/type.gif

-4.51 MB
Binary file not shown.

package.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"name": "antd-design-token",
33
"displayName": "antd Design Token",
44
"description": "VSCode extension for antd v5 design token.",
5-
"version": "0.1.1",
5+
"version": "0.2.0",
66
"publisher": "shezhangzhang",
77
"engines": {
88
"vscode": "^1.68.0"
@@ -24,6 +24,10 @@
2424
}
2525
]
2626
},
27+
"galleryBanner": {
28+
"color": "#1890ff",
29+
"theme": "dark"
30+
},
2731
"main": "./dist/extension.js",
2832
"icon": "assets/logo.png",
2933
"scripts": {

src/decoration-manager.ts

Lines changed: 241 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,241 @@
1+
import * as vscode from "vscode";
2+
import { genMarkdownString, getColorTokenValue } from "./utils";
3+
4+
export default class DecorationManager {
5+
private activeEditor: vscode.TextEditor | undefined;
6+
private fullToken: any;
7+
private fileDecorationMap: Map<
8+
string,
9+
Map<number, vscode.TextEditorDecorationType[]>
10+
>;
11+
private timeout: NodeJS.Timer | undefined = undefined;
12+
private fileName: string = "";
13+
14+
constructor($activeEditor: vscode.TextEditor | undefined, $fullToken: any) {
15+
this.activeEditor = $activeEditor;
16+
this.fullToken = $fullToken;
17+
this.fileDecorationMap = new Map();
18+
}
19+
20+
setActiveEditor(editor: vscode.TextEditor) {
21+
this.activeEditor = editor;
22+
this.fileName = editor.document.fileName;
23+
}
24+
25+
triggerUpdateDecorations(
26+
throttle: boolean = false,
27+
isEdit: boolean = false,
28+
diffLine: number = 0,
29+
startLine?: number,
30+
endLine?: number,
31+
originalStartLine?: number,
32+
originalEndLine?: number
33+
) {
34+
/**
35+
* Active editor changed event
36+
*/
37+
if (!isEdit) {
38+
if (this.fileDecorationMap.has(this.fileName)) {
39+
this.clearCurrentFileDecoration();
40+
this.fileDecorationMap.set(this.fileName, new Map());
41+
}
42+
}
43+
44+
if (this.timeout) {
45+
clearTimeout(this.timeout);
46+
this.timeout = undefined;
47+
}
48+
49+
if (throttle) {
50+
this.timeout = setTimeout(() => {
51+
this.setupDecorations(
52+
diffLine,
53+
startLine,
54+
endLine,
55+
originalStartLine,
56+
originalEndLine
57+
);
58+
}, 500);
59+
} else {
60+
this.setupDecorations(
61+
diffLine,
62+
startLine,
63+
endLine,
64+
originalStartLine,
65+
originalEndLine
66+
);
67+
}
68+
}
69+
70+
setupDecorations(
71+
diffLine: number,
72+
startLine?: number,
73+
endLine?: number,
74+
originalStartLine?: number,
75+
originalEndLine?: number
76+
) {
77+
if (startLine && endLine) {
78+
const lines = this.getLines(startLine, endLine);
79+
80+
if (diffLine < 0) {
81+
this.clearCurrentFileDecoration(lines);
82+
this.updateFileDecorationMap(diffLine, startLine);
83+
}
84+
85+
if (diffLine === 0) {
86+
this.clearCurrentFileDecoration([startLine]);
87+
this.setDecorations([startLine]);
88+
}
89+
90+
if (diffLine > 0) {
91+
/**
92+
* paste override copyed lines
93+
*/
94+
if (originalStartLine && originalEndLine) {
95+
const originalLines = this.getLines(
96+
originalStartLine,
97+
originalEndLine
98+
);
99+
this.clearCurrentFileDecoration(originalLines);
100+
}
101+
102+
this.updateFileDecorationMap(diffLine, startLine);
103+
this.setDecorations(lines);
104+
}
105+
} else {
106+
this.setDecorations();
107+
}
108+
}
109+
110+
setDecorations(sepecificLines?: number[]) {
111+
const text = this.activeEditor!.document.getText();
112+
const fullTokenKeys = Object.keys(this.fullToken);
113+
const lineDecorationMap: Map<number, vscode.TextEditorDecorationType[]> =
114+
this.fileDecorationMap.get(this.fileName) || new Map();
115+
116+
if (!this.fileDecorationMap.has(this.fileName)) {
117+
this.fileDecorationMap.set(this.fileName, new Map());
118+
}
119+
120+
fullTokenKeys.forEach((key: string) => {
121+
const regEx = new RegExp(`\\b(${key})\\b(?!-)`, "g");
122+
let match;
123+
124+
while ((match = regEx.exec(text))) {
125+
const currentLine = this.activeEditor!.document.positionAt(
126+
match.index
127+
).line;
128+
129+
if (
130+
!sepecificLines ||
131+
(sepecificLines && sepecificLines.includes(currentLine))
132+
) {
133+
this.setDecoration(match, key, lineDecorationMap);
134+
}
135+
}
136+
});
137+
138+
this.fileDecorationMap.set(this.fileName, lineDecorationMap);
139+
}
140+
141+
clearCurrentFileDecoration(lines?: number[]) {
142+
const lineDecorationMapItem = this.fileDecorationMap.get(this.fileName);
143+
if (lineDecorationMapItem?.size) {
144+
if (lines) {
145+
lines.forEach((line) => {
146+
if (lineDecorationMapItem.has(line)) {
147+
lineDecorationMapItem.get(line)?.forEach(this.dispose);
148+
lineDecorationMapItem.delete(line);
149+
}
150+
});
151+
} else {
152+
lineDecorationMapItem.forEach((value) => {
153+
value.forEach(this.dispose);
154+
});
155+
}
156+
}
157+
}
158+
159+
dispose(disposable: vscode.TextEditorDecorationType) {
160+
disposable.dispose();
161+
}
162+
163+
getLines(start: number, end: number): number[] {
164+
const result: number[] = [];
165+
166+
if (start > end) {
167+
return [];
168+
}
169+
170+
for (let i = start; i <= end; i++) {
171+
result.push(i);
172+
}
173+
174+
return result;
175+
}
176+
177+
setDecoration(
178+
match: RegExpExecArray,
179+
key: string,
180+
lineDecorationMap: Map<number, vscode.TextEditorDecorationType[]>
181+
) {
182+
const valueDecorations: vscode.DecorationOptions[] = [];
183+
const startPos = this.activeEditor!.document.positionAt(match.index);
184+
const endPos = this.activeEditor!.document.positionAt(
185+
match.index + match[0].length
186+
);
187+
const currentLine = startPos.line;
188+
const value = String(this.fullToken[key]);
189+
const colorSpan = genMarkdownString(value);
190+
const markDownString = new vscode.MarkdownString(
191+
`<h3>antd design token: ${match[0]}</h3>${colorSpan}<code>${value}</code><br></br>`
192+
);
193+
markDownString.supportHtml = true;
194+
markDownString.isTrusted = true;
195+
196+
const decoration = {
197+
range: new vscode.Range(startPos, endPos),
198+
hoverMessage: markDownString,
199+
};
200+
201+
const colorValue = getColorTokenValue(this.fullToken[key]);
202+
valueDecorations.push(decoration);
203+
204+
const decorationType = vscode.window.createTextEditorDecorationType({
205+
after: {
206+
contentText: colorValue ? `**` : `${String(this.fullToken[key])}`,
207+
backgroundColor: colorValue || "",
208+
margin: "0 0 0 5px;",
209+
color: colorValue || "#b37feb",
210+
fontWeight: "bolder",
211+
},
212+
});
213+
214+
const lineValue = lineDecorationMap.get(currentLine);
215+
lineDecorationMap.set(
216+
currentLine,
217+
lineValue ? lineValue.concat([decorationType]) : [decorationType]
218+
);
219+
this.activeEditor!.setDecorations(decorationType, valueDecorations);
220+
}
221+
222+
updateFileDecorationMap(diffLine: number, startLine: number) {
223+
const lineDecorationMapItem = this.fileDecorationMap.get(this.fileName);
224+
const newMap: Map<number, vscode.TextEditorDecorationType[]> = new Map();
225+
226+
if (lineDecorationMapItem?.size) {
227+
lineDecorationMapItem.forEach((value, key) => {
228+
if (key >= startLine) {
229+
newMap.set(
230+
diffLine > 0 ? key + diffLine : key - Math.abs(diffLine),
231+
lineDecorationMapItem.get(key)!
232+
);
233+
} else {
234+
newMap.set(key, lineDecorationMapItem.get(key)!);
235+
}
236+
});
237+
}
238+
239+
this.fileDecorationMap.set(this.fileName, newMap);
240+
}
241+
}

src/extension.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import getDesignToken from "antd-token-previewer/es/utils/getDesignToken";
22
import * as vscode from "vscode";
3-
import setupChangeEvent from "./decorator";
4-
import setupAntdToken from "./typing";
3+
import setupEventListener from "./listener";
4+
import setupAntdTokenCompletion from "./typing";
55

66
export function activate(context: vscode.ExtensionContext) {
77
let isActive = true;
@@ -14,16 +14,17 @@ export function activate(context: vscode.ExtensionContext) {
1414
return;
1515
}
1616

17-
disposeTyping = setupAntdToken(fullToken);
18-
setupChangeEvent(context, fullToken);
17+
disposeTyping = setupAntdTokenCompletion(fullToken);
18+
19+
setupEventListener(context, fullToken);
1920

2021
const disposable = vscode.commands.registerCommand(
2122
"antd-design-token.toggle",
2223
() => {
2324
isActive = !isActive;
2425

2526
if (isActive) {
26-
disposeTyping = setupAntdToken(fullToken);
27+
disposeTyping = setupAntdTokenCompletion(fullToken);
2728
vscode.window.showInformationMessage(
2829
"antd design token is active now."
2930
);

0 commit comments

Comments
 (0)