vscode-gem-languageservice 0.0.2 → 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +479 -0
- package/dist/index.js.map +7 -0
- package/package.json +7 -5
- package/src/index.ts +2 -1
- package/src/cache.ts +0 -34
- package/src/color.ts +0 -31
- package/src/constants.ts +0 -10
- package/src/css.ts +0 -122
- package/src/diagnostic.ts +0 -45
- package/src/hover.ts +0 -73
- package/src/html.ts +0 -85
- package/src/style.ts +0 -36
- package/src/util.ts +0 -59
package/dist/index.js
ADDED
|
@@ -0,0 +1,479 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
|
|
4
|
+
// src/index.ts
|
|
5
|
+
var import_node4 = require("vscode-languageserver/node");
|
|
6
|
+
var import_vscode_languageserver_textdocument = require("vscode-languageserver-textdocument");
|
|
7
|
+
var import_timer = require("duoyun-ui/lib/timer");
|
|
8
|
+
|
|
9
|
+
// src/color.ts
|
|
10
|
+
var import_color = require("duoyun-ui/lib/color");
|
|
11
|
+
var import_node = require("vscode-languageserver/node");
|
|
12
|
+
|
|
13
|
+
// src/constants.ts
|
|
14
|
+
var COLOR_REG = /(?<start>'|")?(?<content>#([0-9a-fA-F]{8}|[0-9a-fA-F]{6}|[0-9a-fA-F]{3,4}))($1|\s*;|\s*\))/g;
|
|
15
|
+
var CSS_REG = /(?<start>\/\*\s*css\s*\*\/\s*`|(?<!`)(?:css|less|scss)\s*`)(?<content>.*?)(`(?=;|,?\s*\)))/gs;
|
|
16
|
+
var STYLE_REG = /(?<start>\/\*\s*style\s*\*\/\s*`|(?<!`)styled?\s*`)(?<content>.*?)(`(?=,|\s*}\s*\)))/gs;
|
|
17
|
+
var HTML_REG = /(?<start>\/\*\s*html\s*\*\/\s*`|(?<!`)(?:html|raw)\s*`)(?<content>[^`]*)(`)/g;
|
|
18
|
+
|
|
19
|
+
// src/color.ts
|
|
20
|
+
var ColorProvider = class {
|
|
21
|
+
provideDocumentColors(document) {
|
|
22
|
+
COLOR_REG.exec("null");
|
|
23
|
+
const documentText = document.getText();
|
|
24
|
+
const colors = [];
|
|
25
|
+
let match;
|
|
26
|
+
while ((match = COLOR_REG.exec(documentText)) !== null) {
|
|
27
|
+
const hex = match.groups.content;
|
|
28
|
+
const [red, green, blue, alpha] = (0, import_color.parseHexColor)(hex);
|
|
29
|
+
const offset = match.index + (match.groups.start?.length || 0);
|
|
30
|
+
const range = import_node.Range.create(document.positionAt(offset), document.positionAt(offset + hex.length));
|
|
31
|
+
const color = import_node.Color.create(red / 255, green / 255, blue / 255, alpha);
|
|
32
|
+
colors.push({ range, color });
|
|
33
|
+
}
|
|
34
|
+
return colors;
|
|
35
|
+
}
|
|
36
|
+
provideColorPresentations({ red, green, blue, alpha }) {
|
|
37
|
+
return [{ label: (0, import_color.rgbToHexColor)([red * 255, green * 255, blue * 255, alpha]) }];
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
// src/diagnostic.ts
|
|
42
|
+
var import_vscode_css_languageservice = require("vscode-css-languageservice");
|
|
43
|
+
var import_node3 = require("vscode-languageserver/node");
|
|
44
|
+
|
|
45
|
+
// src/util.ts
|
|
46
|
+
var import_vscode_html_languageservice = require("vscode-html-languageservice");
|
|
47
|
+
var import_node2 = require("vscode-languageserver/node");
|
|
48
|
+
function removeSlot(text) {
|
|
49
|
+
const v = text.replace(/\$\{[^${]*?\}/g, (str) => str.replaceAll(/[^\n]/g, "x"));
|
|
50
|
+
if (v === text) return v;
|
|
51
|
+
return removeSlot(v);
|
|
52
|
+
}
|
|
53
|
+
function removeHTMLSlot(text, position) {
|
|
54
|
+
const left = text.slice(0, position);
|
|
55
|
+
const right = text.slice(position);
|
|
56
|
+
const left1 = removeSlot(left);
|
|
57
|
+
const left2 = left1.replace(/(.*(?=html`))/s, (str) => str.replaceAll(/[^\n]/g, " "));
|
|
58
|
+
return left2 + removeSlot(right);
|
|
59
|
+
}
|
|
60
|
+
function translateCompletionList(result, position) {
|
|
61
|
+
const getRange = (item) => {
|
|
62
|
+
if (item.textEdit && "range" in item.textEdit) {
|
|
63
|
+
const { start, end } = item.textEdit.range;
|
|
64
|
+
return import_node2.Range.create(
|
|
65
|
+
import_node2.Position.create(position.line, start.character),
|
|
66
|
+
import_node2.Position.create(position.line, end.character)
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
return {
|
|
71
|
+
...result,
|
|
72
|
+
items: result?.items.map((item) => ({
|
|
73
|
+
...item,
|
|
74
|
+
textEdit: item.textEdit && {
|
|
75
|
+
...item.textEdit,
|
|
76
|
+
range: getRange(item)
|
|
77
|
+
}
|
|
78
|
+
}))
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
function matchOffset(regex, docText, offset) {
|
|
82
|
+
regex.exec("null");
|
|
83
|
+
let match;
|
|
84
|
+
while ((match = regex.exec(docText)) !== null) {
|
|
85
|
+
const [fullStr, startStr] = match;
|
|
86
|
+
const start = match.index + startStr.length;
|
|
87
|
+
const end = match.index + fullStr.length;
|
|
88
|
+
if (offset > start && offset < end) {
|
|
89
|
+
return match;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
function createVirtualDocument(languageId, content) {
|
|
95
|
+
return import_vscode_html_languageservice.TextDocument.create(`embedded://document.${languageId}`, languageId, 1, content);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// src/diagnostic.ts
|
|
99
|
+
var cssLanguageService = (0, import_vscode_css_languageservice.getCSSLanguageService)();
|
|
100
|
+
function getDiagnostics(document, _relatedInformation) {
|
|
101
|
+
const diagnostics = [];
|
|
102
|
+
const text = document.getText();
|
|
103
|
+
const matchFragments = (regexp, appendBefore, appendAfter) => {
|
|
104
|
+
regexp.exec("null");
|
|
105
|
+
let match;
|
|
106
|
+
while (match = regexp.exec(text)) {
|
|
107
|
+
const matchContent = match.groups.content;
|
|
108
|
+
const offset = match.index + match.groups.start.length;
|
|
109
|
+
const virtualDocument = createVirtualDocument("css", `${appendBefore}${removeSlot(matchContent)}${appendAfter}`);
|
|
110
|
+
const vCss = cssLanguageService.parseStylesheet(virtualDocument);
|
|
111
|
+
const oDiagnostics = cssLanguageService.doValidation(virtualDocument, vCss);
|
|
112
|
+
for (const { message, range } of oDiagnostics) {
|
|
113
|
+
const { start, end } = range;
|
|
114
|
+
const startOffset = virtualDocument.offsetAt(start) - appendBefore.length + offset;
|
|
115
|
+
const endOffset = virtualDocument.offsetAt(end) - appendBefore.length + offset;
|
|
116
|
+
diagnostics.push({
|
|
117
|
+
range: import_node3.Range.create(document.positionAt(startOffset), document.positionAt(endOffset)),
|
|
118
|
+
message
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
matchFragments(CSS_REG, "", "");
|
|
124
|
+
matchFragments(STYLE_REG, ":host { ", " }");
|
|
125
|
+
return diagnostics;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// src/hover.ts
|
|
129
|
+
var import_vscode_html_languageservice2 = require("vscode-html-languageservice");
|
|
130
|
+
var import_vscode_css_languageservice2 = require("vscode-css-languageservice");
|
|
131
|
+
var HTMLHoverProvider = class {
|
|
132
|
+
#htmlLanguageService = (0, import_vscode_html_languageservice2.getLanguageService)();
|
|
133
|
+
provideHover(document, position) {
|
|
134
|
+
const currentOffset = document.offsetAt(position);
|
|
135
|
+
const documentText = removeHTMLSlot(document.getText(), currentOffset);
|
|
136
|
+
const match = matchOffset(HTML_REG, documentText, currentOffset);
|
|
137
|
+
if (!match) return null;
|
|
138
|
+
const matchContent = match.groups.content;
|
|
139
|
+
const matchStartOffset = match.index + match.groups.start.length;
|
|
140
|
+
const virtualOffset = currentOffset - matchStartOffset;
|
|
141
|
+
const virtualDocument = createVirtualDocument("html", matchContent);
|
|
142
|
+
const html = this.#htmlLanguageService.parseHTMLDocument(virtualDocument);
|
|
143
|
+
return this.#htmlLanguageService.doHover(virtualDocument, virtualDocument.positionAt(virtualOffset), html, {
|
|
144
|
+
documentation: true,
|
|
145
|
+
references: true
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
};
|
|
149
|
+
var CSSHoverProvider = class {
|
|
150
|
+
#cssLanguageService = (0, import_vscode_css_languageservice2.getCSSLanguageService)();
|
|
151
|
+
provideHover(document, position) {
|
|
152
|
+
const currentOffset = document.offsetAt(position);
|
|
153
|
+
const documentText = document.getText();
|
|
154
|
+
const match = matchOffset(CSS_REG, documentText, currentOffset);
|
|
155
|
+
if (!match) return null;
|
|
156
|
+
const matchContent = match.groups.content;
|
|
157
|
+
const matchStartOffset = match.index + match.groups.start.length;
|
|
158
|
+
const virtualOffset = currentOffset - matchStartOffset;
|
|
159
|
+
const virtualDocument = createVirtualDocument("css", removeSlot(matchContent));
|
|
160
|
+
const stylesheet = this.#cssLanguageService.parseStylesheet(virtualDocument);
|
|
161
|
+
return this.#cssLanguageService.doHover(virtualDocument, virtualDocument.positionAt(virtualOffset), stylesheet, {
|
|
162
|
+
documentation: true,
|
|
163
|
+
references: true
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
};
|
|
167
|
+
var StyleHoverProvider = class {
|
|
168
|
+
#cssLanguageService = (0, import_vscode_css_languageservice2.getCSSLanguageService)();
|
|
169
|
+
provideHover(document, position) {
|
|
170
|
+
const currentOffset = document.offsetAt(position);
|
|
171
|
+
const documentText = document.getText();
|
|
172
|
+
const match = matchOffset(STYLE_REG, documentText, currentOffset);
|
|
173
|
+
if (!match) return null;
|
|
174
|
+
const matchContent = match.groups.content;
|
|
175
|
+
const matchStartOffset = match.index + match.groups.start.length;
|
|
176
|
+
const virtualOffset = currentOffset - matchStartOffset + 8;
|
|
177
|
+
const virtualDocument = createVirtualDocument("css", `:host { ${removeSlot(matchContent)} }`);
|
|
178
|
+
const stylesheet = this.#cssLanguageService.parseStylesheet(virtualDocument);
|
|
179
|
+
return this.#cssLanguageService.doHover(virtualDocument, virtualDocument.positionAt(virtualOffset), stylesheet, {
|
|
180
|
+
documentation: true,
|
|
181
|
+
references: true
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
// src/css.ts
|
|
187
|
+
var import_vscode_html_languageservice3 = require("vscode-html-languageservice");
|
|
188
|
+
var import_vscode_css_languageservice3 = require("vscode-css-languageservice");
|
|
189
|
+
|
|
190
|
+
// src/cache.ts
|
|
191
|
+
var CompletionsCache = class {
|
|
192
|
+
#cachedCompletionsFile;
|
|
193
|
+
#cachedCompletionsPosition;
|
|
194
|
+
#cachedCompletionsContent;
|
|
195
|
+
#completions;
|
|
196
|
+
#equalPositions(left, right) {
|
|
197
|
+
return !!right && left.line === right.line && left.character === right.character;
|
|
198
|
+
}
|
|
199
|
+
getCached(doc, position) {
|
|
200
|
+
if (this.#completions && doc.uri === this.#cachedCompletionsFile && this.#equalPositions(position, this.#cachedCompletionsPosition) && doc.getText() === this.#cachedCompletionsContent) {
|
|
201
|
+
return this.#completions;
|
|
202
|
+
}
|
|
203
|
+
return void 0;
|
|
204
|
+
}
|
|
205
|
+
updateCached(context, position, completions) {
|
|
206
|
+
this.#cachedCompletionsFile = context.uri;
|
|
207
|
+
this.#cachedCompletionsPosition = position;
|
|
208
|
+
this.#cachedCompletionsContent = context.getText();
|
|
209
|
+
this.#completions = completions;
|
|
210
|
+
return completions;
|
|
211
|
+
}
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
// src/css.ts
|
|
215
|
+
function getRegionAtOffset(regions, offset) {
|
|
216
|
+
for (const region of regions) {
|
|
217
|
+
if (region.start <= offset) {
|
|
218
|
+
if (offset <= region.end) {
|
|
219
|
+
return region;
|
|
220
|
+
}
|
|
221
|
+
} else {
|
|
222
|
+
break;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
return null;
|
|
226
|
+
}
|
|
227
|
+
function getLanguageRegions(service, data) {
|
|
228
|
+
const scanner = service.createScanner(data);
|
|
229
|
+
const regions = [];
|
|
230
|
+
let tokenType;
|
|
231
|
+
while ((tokenType = scanner.scan()) !== import_vscode_html_languageservice3.TokenType.EOS) {
|
|
232
|
+
switch (tokenType) {
|
|
233
|
+
case import_vscode_html_languageservice3.TokenType.Styles:
|
|
234
|
+
regions.push({
|
|
235
|
+
languageId: "css",
|
|
236
|
+
start: scanner.getTokenOffset(),
|
|
237
|
+
end: scanner.getTokenEnd(),
|
|
238
|
+
length: scanner.getTokenLength(),
|
|
239
|
+
content: scanner.getTokenText()
|
|
240
|
+
});
|
|
241
|
+
break;
|
|
242
|
+
default:
|
|
243
|
+
break;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
return regions;
|
|
247
|
+
}
|
|
248
|
+
var HTMLStyleCompletionItemProvider = class {
|
|
249
|
+
#cssLanguageService = (0, import_vscode_css_languageservice3.getCSSLanguageService)();
|
|
250
|
+
#htmlLanguageService = (0, import_vscode_html_languageservice3.getLanguageService)();
|
|
251
|
+
#cache = new CompletionsCache();
|
|
252
|
+
provideCompletionItems(document, position) {
|
|
253
|
+
const cached = this.#cache.getCached(document, position);
|
|
254
|
+
if (cached) return cached;
|
|
255
|
+
const currentOffset = document.offsetAt(position);
|
|
256
|
+
const documentText = document.getText();
|
|
257
|
+
const match = matchOffset(HTML_REG, documentText, currentOffset);
|
|
258
|
+
if (!match) return;
|
|
259
|
+
const matchContent = match.groups.content;
|
|
260
|
+
const matchStartOffset = match.index + match.groups.start.length;
|
|
261
|
+
const regions = getLanguageRegions(this.#htmlLanguageService, matchContent);
|
|
262
|
+
if (regions.length <= 0) return;
|
|
263
|
+
const region = getRegionAtOffset(regions, currentOffset - matchStartOffset);
|
|
264
|
+
if (!region) return;
|
|
265
|
+
const virtualOffset = currentOffset - (matchStartOffset + region.start);
|
|
266
|
+
const virtualDocument = createVirtualDocument("css", removeSlot(region.content));
|
|
267
|
+
const stylesheet = this.#cssLanguageService.parseStylesheet(virtualDocument);
|
|
268
|
+
const completions = this.#cssLanguageService.doComplete(
|
|
269
|
+
virtualDocument,
|
|
270
|
+
virtualDocument.positionAt(virtualOffset),
|
|
271
|
+
stylesheet
|
|
272
|
+
);
|
|
273
|
+
return this.#cache.updateCached(document, position, translateCompletionList(completions, position));
|
|
274
|
+
}
|
|
275
|
+
};
|
|
276
|
+
var CSSCompletionItemProvider = class {
|
|
277
|
+
#cssLanguageService = (0, import_vscode_css_languageservice3.getCSSLanguageService)();
|
|
278
|
+
#cache = new CompletionsCache();
|
|
279
|
+
provideCompletionItems(document, position) {
|
|
280
|
+
const cached = this.#cache.getCached(document, position);
|
|
281
|
+
if (cached) return cached;
|
|
282
|
+
const currentOffset = document.offsetAt(position);
|
|
283
|
+
const documentText = document.getText();
|
|
284
|
+
const match = matchOffset(CSS_REG, documentText, currentOffset);
|
|
285
|
+
if (!match) return;
|
|
286
|
+
const matchContent = match.groups.content;
|
|
287
|
+
const matchStartOffset = match.index + match.groups.start.length;
|
|
288
|
+
const virtualOffset = currentOffset - matchStartOffset;
|
|
289
|
+
const virtualDocument = createVirtualDocument("css", removeSlot(matchContent));
|
|
290
|
+
const vCss = this.#cssLanguageService.parseStylesheet(virtualDocument);
|
|
291
|
+
const completions = this.#cssLanguageService.doComplete(
|
|
292
|
+
virtualDocument,
|
|
293
|
+
virtualDocument.positionAt(virtualOffset),
|
|
294
|
+
vCss
|
|
295
|
+
);
|
|
296
|
+
return this.#cache.updateCached(document, position, translateCompletionList(completions, position));
|
|
297
|
+
}
|
|
298
|
+
};
|
|
299
|
+
|
|
300
|
+
// src/html.ts
|
|
301
|
+
var import_vscode_html_languageservice4 = require("vscode-html-languageservice");
|
|
302
|
+
var import_emmet_helper = require("@vscode/emmet-helper");
|
|
303
|
+
var HTMLCompletionItemProvider = class {
|
|
304
|
+
#htmlLanguageService = (0, import_vscode_html_languageservice4.getLanguageService)();
|
|
305
|
+
#cache = new CompletionsCache();
|
|
306
|
+
#connection;
|
|
307
|
+
#emmetConfig;
|
|
308
|
+
constructor(connection2) {
|
|
309
|
+
this.#connection = connection2;
|
|
310
|
+
}
|
|
311
|
+
async #getEmmetConfig() {
|
|
312
|
+
if (!this.#emmetConfig) {
|
|
313
|
+
const emmetConfig = await this.#connection.workspace.getConfiguration("emmet");
|
|
314
|
+
this.#emmetConfig = {
|
|
315
|
+
showExpandedAbbreviation: emmetConfig.showExpandedAbbreviation,
|
|
316
|
+
showAbbreviationSuggestions: emmetConfig.showAbbreviationSuggestions,
|
|
317
|
+
syntaxProfiles: emmetConfig.syntaxProfiles,
|
|
318
|
+
variables: emmetConfig.variables
|
|
319
|
+
};
|
|
320
|
+
}
|
|
321
|
+
return this.#emmetConfig;
|
|
322
|
+
}
|
|
323
|
+
async provideCompletionItems(document, position) {
|
|
324
|
+
const emmetConfig = await this.#getEmmetConfig();
|
|
325
|
+
const cached = this.#cache.getCached(document, position);
|
|
326
|
+
if (cached) return cached;
|
|
327
|
+
const currentOffset = document.offsetAt(position);
|
|
328
|
+
const documentText = removeHTMLSlot(document.getText(), currentOffset);
|
|
329
|
+
const match = matchOffset(HTML_REG, documentText, currentOffset);
|
|
330
|
+
if (!match) return;
|
|
331
|
+
const matchContent = match.groups.content;
|
|
332
|
+
const matchStartOffset = match.index + match.groups.start.length;
|
|
333
|
+
const virtualOffset = currentOffset - matchStartOffset;
|
|
334
|
+
const virtualDocument = createVirtualDocument("html", matchContent);
|
|
335
|
+
const vHtml = this.#htmlLanguageService.parseHTMLDocument(virtualDocument);
|
|
336
|
+
let emmetResults = { isIncomplete: true, items: [] };
|
|
337
|
+
this.#htmlLanguageService.setCompletionParticipants([
|
|
338
|
+
{
|
|
339
|
+
onHtmlContent: async () => {
|
|
340
|
+
const pos = virtualDocument.positionAt(virtualOffset);
|
|
341
|
+
const result = (0, import_emmet_helper.doComplete)(virtualDocument, pos, "html", emmetConfig);
|
|
342
|
+
if (result) {
|
|
343
|
+
emmetResults = {
|
|
344
|
+
...result,
|
|
345
|
+
items: result.items.map((item) => ({
|
|
346
|
+
...item,
|
|
347
|
+
command: {
|
|
348
|
+
title: "Emmet Expand Abbreviation",
|
|
349
|
+
command: "editor.emmet.action.expandAbbreviation"
|
|
350
|
+
}
|
|
351
|
+
}))
|
|
352
|
+
};
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
]);
|
|
357
|
+
const completions = this.#htmlLanguageService.doComplete(
|
|
358
|
+
virtualDocument,
|
|
359
|
+
virtualDocument.positionAt(virtualOffset),
|
|
360
|
+
vHtml
|
|
361
|
+
);
|
|
362
|
+
if (emmetResults.items.length) {
|
|
363
|
+
completions.items.push(...emmetResults.items);
|
|
364
|
+
}
|
|
365
|
+
return this.#cache.updateCached(document, position, translateCompletionList(completions, position));
|
|
366
|
+
}
|
|
367
|
+
};
|
|
368
|
+
|
|
369
|
+
// src/style.ts
|
|
370
|
+
var import_vscode_css_languageservice4 = require("vscode-css-languageservice");
|
|
371
|
+
var StyleCompletionItemProvider = class {
|
|
372
|
+
#cssLanguageService = (0, import_vscode_css_languageservice4.getCSSLanguageService)();
|
|
373
|
+
#cache = new CompletionsCache();
|
|
374
|
+
provideCompletionItems(document, position) {
|
|
375
|
+
const cached = this.#cache.getCached(document, position);
|
|
376
|
+
if (cached) return cached;
|
|
377
|
+
const currentOffset = document.offsetAt(position);
|
|
378
|
+
const documentText = document.getText();
|
|
379
|
+
const match = matchOffset(STYLE_REG, documentText, currentOffset);
|
|
380
|
+
if (!match) return;
|
|
381
|
+
const matchContent = match.groups.content;
|
|
382
|
+
const matchStartOffset = match.index + match.groups.start.length;
|
|
383
|
+
const virtualOffset = currentOffset - matchStartOffset + 8;
|
|
384
|
+
const virtualDocument = createVirtualDocument("css", `:host { ${removeSlot(matchContent)} }`);
|
|
385
|
+
const vCss = this.#cssLanguageService.parseStylesheet(virtualDocument);
|
|
386
|
+
const completions = this.#cssLanguageService.doComplete(
|
|
387
|
+
virtualDocument,
|
|
388
|
+
virtualDocument.positionAt(virtualOffset),
|
|
389
|
+
vCss
|
|
390
|
+
);
|
|
391
|
+
return this.#cache.updateCached(document, position, translateCompletionList(completions, position));
|
|
392
|
+
}
|
|
393
|
+
};
|
|
394
|
+
|
|
395
|
+
// src/index.ts
|
|
396
|
+
var connection = (0, import_node4.createConnection)(import_node4.ProposedFeatures.all);
|
|
397
|
+
var documents = new import_node4.TextDocuments(import_vscode_languageserver_textdocument.TextDocument);
|
|
398
|
+
var hasConfigurationCapability = false;
|
|
399
|
+
var hasWorkspaceFolderCapability = false;
|
|
400
|
+
var hasDiagnosticRelatedInformationCapability = false;
|
|
401
|
+
connection.onInitialize(({ capabilities }) => {
|
|
402
|
+
hasConfigurationCapability = !!capabilities.workspace?.configuration;
|
|
403
|
+
hasWorkspaceFolderCapability = !!capabilities.workspace?.workspaceFolders;
|
|
404
|
+
hasDiagnosticRelatedInformationCapability = !!capabilities.textDocument?.publishDiagnostics?.relatedInformation;
|
|
405
|
+
return {
|
|
406
|
+
capabilities: {
|
|
407
|
+
completionProvider: {
|
|
408
|
+
resolveProvider: true,
|
|
409
|
+
triggerCharacters: ["!", ".", "}", ":", "*", "$", "]", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "<"]
|
|
410
|
+
},
|
|
411
|
+
hoverProvider: true,
|
|
412
|
+
colorProvider: true,
|
|
413
|
+
textDocumentSync: import_node4.TextDocumentSyncKind.Incremental,
|
|
414
|
+
workspace: !hasWorkspaceFolderCapability ? void 0 : { workspaceFolders: { supported: true } }
|
|
415
|
+
}
|
|
416
|
+
};
|
|
417
|
+
});
|
|
418
|
+
connection.onInitialized(() => {
|
|
419
|
+
if (hasConfigurationCapability) {
|
|
420
|
+
connection.client.register(import_node4.DidChangeConfigurationNotification.type, void 0);
|
|
421
|
+
}
|
|
422
|
+
if (hasWorkspaceFolderCapability) {
|
|
423
|
+
connection.workspace.onDidChangeWorkspaceFolders((_event) => {
|
|
424
|
+
connection.console.log("Workspace folder change event received.");
|
|
425
|
+
});
|
|
426
|
+
}
|
|
427
|
+
});
|
|
428
|
+
var defaultSettings = { maxNumberOfProblems: 1e3 };
|
|
429
|
+
var globalSettings = defaultSettings;
|
|
430
|
+
var documentSettings = /* @__PURE__ */ new Map();
|
|
431
|
+
connection.onDidChangeConfiguration((change) => {
|
|
432
|
+
if (hasConfigurationCapability) {
|
|
433
|
+
documentSettings.clear();
|
|
434
|
+
} else {
|
|
435
|
+
globalSettings = change.settings.languageServerGem || defaultSettings;
|
|
436
|
+
}
|
|
437
|
+
documents.all().forEach(validateTextDocument);
|
|
438
|
+
});
|
|
439
|
+
documents.onDidClose((e) => documentSettings.delete(e.document.uri));
|
|
440
|
+
documents.onDidChangeContent((change) => validateTextDocument(change.document));
|
|
441
|
+
var validateTextDocument = (0, import_timer.debounce)((textDocument) => {
|
|
442
|
+
connection.sendDiagnostics({
|
|
443
|
+
uri: textDocument.uri,
|
|
444
|
+
diagnostics: getDiagnostics(textDocument, hasDiagnosticRelatedInformationCapability)
|
|
445
|
+
});
|
|
446
|
+
});
|
|
447
|
+
connection.onDidChangeWatchedFiles((_change) => {
|
|
448
|
+
connection.console.log("We received a file change event");
|
|
449
|
+
});
|
|
450
|
+
var completionItemProviders = [
|
|
451
|
+
new CSSCompletionItemProvider(),
|
|
452
|
+
new HTMLStyleCompletionItemProvider(),
|
|
453
|
+
new HTMLCompletionItemProvider(connection),
|
|
454
|
+
new StyleCompletionItemProvider()
|
|
455
|
+
];
|
|
456
|
+
connection.onCompletion(async ({ textDocument, position }) => {
|
|
457
|
+
for (const provider of completionItemProviders) {
|
|
458
|
+
const result = await provider.provideCompletionItems(documents.get(textDocument.uri), position);
|
|
459
|
+
if (result) return { isIncomplete: true, items: result.items };
|
|
460
|
+
}
|
|
461
|
+
});
|
|
462
|
+
connection.onCompletionResolve((item) => item);
|
|
463
|
+
var colorProvider = new ColorProvider();
|
|
464
|
+
connection.onColorPresentation(({ color }) => {
|
|
465
|
+
return colorProvider.provideColorPresentations(color);
|
|
466
|
+
});
|
|
467
|
+
connection.onDocumentColor(({ textDocument }) => {
|
|
468
|
+
return colorProvider.provideDocumentColors(documents.get(textDocument.uri));
|
|
469
|
+
});
|
|
470
|
+
var hoverProviders = [new CSSHoverProvider(), new StyleHoverProvider(), new HTMLHoverProvider()];
|
|
471
|
+
connection.onHover(({ textDocument, position }) => {
|
|
472
|
+
for (const provider of hoverProviders) {
|
|
473
|
+
const result = provider.provideHover(documents.get(textDocument.uri), position);
|
|
474
|
+
if (result) return result;
|
|
475
|
+
}
|
|
476
|
+
});
|
|
477
|
+
documents.listen(connection);
|
|
478
|
+
connection.listen();
|
|
479
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/index.ts", "../src/color.ts", "../src/constants.ts", "../src/diagnostic.ts", "../src/util.ts", "../src/hover.ts", "../src/css.ts", "../src/cache.ts", "../src/html.ts", "../src/style.ts"],
|
|
4
|
+
"sourcesContent": ["#!/usr/bin/env node\n\nimport type { InitializeParams } from 'vscode-languageserver/node';\nimport {\n createConnection,\n TextDocuments,\n ProposedFeatures,\n DidChangeConfigurationNotification,\n TextDocumentSyncKind,\n} from 'vscode-languageserver/node';\nimport { TextDocument } from 'vscode-languageserver-textdocument';\nimport { debounce } from 'duoyun-ui/lib/timer';\n\nimport { ColorProvider } from './color';\nimport { getDiagnostics } from './diagnostic';\nimport { CSSHoverProvider, HTMLHoverProvider, StyleHoverProvider } from './hover';\nimport { CSSCompletionItemProvider, HTMLStyleCompletionItemProvider } from './css';\nimport { HTMLCompletionItemProvider } from './html';\nimport { StyleCompletionItemProvider } from './style';\n\nconst connection = createConnection(ProposedFeatures.all);\nconst documents = new TextDocuments(TextDocument);\n\nlet hasConfigurationCapability = false;\nlet hasWorkspaceFolderCapability = false;\nlet hasDiagnosticRelatedInformationCapability = false;\n\nconnection.onInitialize(({ capabilities }: InitializeParams) => {\n hasConfigurationCapability = !!capabilities.workspace?.configuration;\n hasWorkspaceFolderCapability = !!capabilities.workspace?.workspaceFolders;\n hasDiagnosticRelatedInformationCapability = !!capabilities.textDocument?.publishDiagnostics?.relatedInformation;\n return {\n capabilities: {\n completionProvider: {\n resolveProvider: true,\n triggerCharacters: ['!', '.', '}', ':', '*', '$', ']', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '<'],\n },\n hoverProvider: true,\n colorProvider: true,\n textDocumentSync: TextDocumentSyncKind.Incremental,\n workspace: !hasWorkspaceFolderCapability ? undefined : { workspaceFolders: { supported: true } },\n },\n };\n});\n\nconnection.onInitialized(() => {\n if (hasConfigurationCapability) {\n connection.client.register(DidChangeConfigurationNotification.type, undefined);\n }\n if (hasWorkspaceFolderCapability) {\n connection.workspace.onDidChangeWorkspaceFolders((_event) => {\n connection.console.log('Workspace folder change event received.');\n });\n }\n});\n\ninterface ExampleSettings {\n maxNumberOfProblems: number;\n}\n\nconst defaultSettings: ExampleSettings = { maxNumberOfProblems: 1000 };\n\nlet globalSettings: ExampleSettings = defaultSettings;\nconst documentSettings: Map<string, Thenable<ExampleSettings>> = new Map();\n\nconnection.onDidChangeConfiguration((change) => {\n if (hasConfigurationCapability) {\n documentSettings.clear();\n } else {\n globalSettings = <ExampleSettings>(change.settings.languageServerGem || defaultSettings);\n }\n documents.all().forEach(validateTextDocument);\n});\n\nfunction _getDocumentSettings(resource: string) {\n if (!hasConfigurationCapability) return globalSettings;\n if (!documentSettings.has(resource)) {\n const settings = connection.workspace.getConfiguration({ scopeUri: resource, section: 'languageServerGem' });\n documentSettings.set(resource, settings);\n }\n return documentSettings.get(resource)!;\n}\n\ndocuments.onDidClose((e) => documentSettings.delete(e.document.uri));\n\ndocuments.onDidChangeContent((change) => validateTextDocument(change.document));\n\nconst validateTextDocument = debounce((textDocument: TextDocument) => {\n connection.sendDiagnostics({\n uri: textDocument.uri,\n diagnostics: getDiagnostics(textDocument, hasDiagnosticRelatedInformationCapability),\n });\n});\n\nconnection.onDidChangeWatchedFiles((_change) => {\n connection.console.log('We received a file change event');\n});\n\nconst completionItemProviders = [\n new CSSCompletionItemProvider(),\n new HTMLStyleCompletionItemProvider(),\n new HTMLCompletionItemProvider(connection),\n new StyleCompletionItemProvider(),\n];\nconnection.onCompletion(async ({ textDocument, position }) => {\n for (const provider of completionItemProviders) {\n const result = await provider.provideCompletionItems(documents.get(textDocument.uri)!, position);\n if (result) return { isIncomplete: true, items: result.items };\n }\n});\n\nconnection.onCompletionResolve((item) => item);\n\nconst colorProvider = new ColorProvider();\nconnection.onColorPresentation(({ color }) => {\n return colorProvider.provideColorPresentations(color);\n});\n\nconnection.onDocumentColor(({ textDocument }) => {\n return colorProvider.provideDocumentColors(documents.get(textDocument.uri)!);\n});\n\nconst hoverProviders = [new CSSHoverProvider(), new StyleHoverProvider(), new HTMLHoverProvider()];\nconnection.onHover(({ textDocument, position }) => {\n for (const provider of hoverProviders) {\n const result = provider.provideHover(documents.get(textDocument.uri)!, position);\n if (result) return result;\n }\n});\n\ndocuments.listen(connection);\nconnection.listen();\n", "import { rgbToHexColor, parseHexColor } from 'duoyun-ui/lib/color';\nimport { Range, Color } from 'vscode-languageserver/node';\nimport type { ColorInformation, ColorPresentation } from 'vscode-languageserver/node';\nimport type { HexColor } from 'duoyun-ui/lib/color';\nimport type { TextDocument } from 'vscode-languageserver-textdocument';\n\nimport { COLOR_REG } from './constants';\n\nexport class ColorProvider {\n provideDocumentColors(document: TextDocument) {\n COLOR_REG.exec('null');\n\n const documentText = document.getText();\n const colors: ColorInformation[] = [];\n\n let match: RegExpExecArray | null;\n while ((match = COLOR_REG.exec(documentText)) !== null) {\n const hex = match.groups!.content as HexColor;\n const [red, green, blue, alpha] = parseHexColor(hex);\n const offset = match.index + (match.groups!.start?.length || 0);\n const range = Range.create(document.positionAt(offset), document.positionAt(offset + hex.length));\n const color = Color.create(red / 255, green / 255, blue / 255, alpha);\n colors.push({ range, color });\n }\n return colors;\n }\n\n provideColorPresentations({ red, green, blue, alpha }: Color): ColorPresentation[] {\n return [{ label: rgbToHexColor([red * 255, green * 255, blue * 255, alpha]) }];\n }\n}\n", "export const COLOR_REG = /(?<start>'|\")?(?<content>#([0-9a-fA-F]{8}|[0-9a-fA-F]{6}|[0-9a-fA-F]{3,4}))($1|\\s*;|\\s*\\))/g;\n\n// \u76F4\u63A5\u901A\u8FC7\u6B63\u5219\u5339\u914D css \u7247\u6BB5\uFF0C\u901A\u8FC7\u6761\u4EF6\u7684\u7ED3\u675F ` \u53F7\u5339\u914D\nexport const CSS_REG = /(?<start>\\/\\*\\s*css\\s*\\*\\/\\s*`|(?<!`)(?:css|less|scss)\\s*`)(?<content>.*?)(`(?=;|,?\\s*\\)))/gs;\n// \u76F4\u63A5\u901A\u8FC7\u6B63\u5219\u5339\u914D style \u7247\u6BB5\uFF0C\u901A\u8FC7\u6761\u4EF6\u7684\u7ED3\u675F ` \u53F7\u5339\u914D\n// \u8BED\u8A00\u670D\u52A1\u548C\u9AD8\u4EAE\u90FD\u53EA\u652F\u6301 styled \u5199\u6CD5\nexport const STYLE_REG = /(?<start>\\/\\*\\s*style\\s*\\*\\/\\s*`|(?<!`)styled?\\s*`)(?<content>.*?)(`(?=,|\\s*}\\s*\\)))/gs;\n\n// \u5904\u7406\u540E\u8FDB\u884C\u6B63\u5219\u5339\u914D\uFF0C\u6240\u4EE5\u4E0D\u9700\u8981\u9A8C\u8BC1\u540E\u9762\u7684 ` \u53F7\nexport const HTML_REG = /(?<start>\\/\\*\\s*html\\s*\\*\\/\\s*`|(?<!`)(?:html|raw)\\s*`)(?<content>[^`]*)(`)/g;\n", "// \u53EA\u5BF9 CSS \u8BED\u6CD5\u548C\u5C5E\u6027\u505A\u4E86\u7B80\u5355\u7684\u68C0\u67E5\uFF0C\u4E0D\u505A\u503C\u68C0\u67E5\n// TODO: \u6FC0\u6D3B\u6269\u5C55\u3001\u6253\u5F00\u5DE5\u4F5C\u533A\u65F6\u9700\u8981\u81EA\u52A8\u8BCA\u65AD\u6240\u6709\u6587\u4EF6\n// TODO: \u4F7F\u7528 LRU \u7F13\u5B58\n\nimport { getCSSLanguageService } from 'vscode-css-languageservice';\nimport { Range } from 'vscode-languageserver/node';\nimport type { Diagnostic } from 'vscode-languageserver/node';\nimport type { TextDocument } from 'vscode-languageserver-textdocument';\n\nimport { CSS_REG, STYLE_REG } from './constants';\nimport { createVirtualDocument, removeSlot } from './util';\n\nconst cssLanguageService = getCSSLanguageService();\n\nexport function getDiagnostics(document: TextDocument, _relatedInformation: boolean) {\n const diagnostics: Diagnostic[] = [];\n const text = document.getText();\n\n const matchFragments = (regexp: RegExp, appendBefore: string, appendAfter: string) => {\n regexp.exec('null');\n\n let match;\n while ((match = regexp.exec(text))) {\n const matchContent = match.groups!.content;\n const offset = match.index + match.groups!.start.length;\n const virtualDocument = createVirtualDocument('css', `${appendBefore}${removeSlot(matchContent)}${appendAfter}`);\n const vCss = cssLanguageService.parseStylesheet(virtualDocument);\n const oDiagnostics = cssLanguageService.doValidation(virtualDocument, vCss) as Diagnostic[];\n for (const { message, range } of oDiagnostics) {\n const { start, end } = range;\n const startOffset = virtualDocument.offsetAt(start) - appendBefore.length + offset;\n const endOffset = virtualDocument.offsetAt(end) - appendBefore.length + offset;\n diagnostics.push({\n range: Range.create(document.positionAt(startOffset), document.positionAt(endOffset)),\n message,\n });\n }\n }\n };\n\n matchFragments(CSS_REG, '', '');\n matchFragments(STYLE_REG, ':host { ', ' }');\n\n return diagnostics;\n}\n", "import { TextDocument } from 'vscode-html-languageservice';\nimport { Position, Range } from 'vscode-languageserver/node';\nimport type { CompletionItem } from 'vscode-languageserver/node';\n\nexport function removeSlot(text: string) {\n const v = text.replace(/\\$\\{[^${]*?\\}/g, (str) => str.replaceAll(/[^\\n]/g, 'x'));\n if (v === text) return v;\n return removeSlot(v);\n}\n\nexport function removeHTMLSlot(text: string, position: number) {\n const left = text.slice(0, position);\n const right = text.slice(position);\n const left1 = removeSlot(left);\n // \u5904\u7406\u5728\u63D2\u69FD\u4E2D\u7684\u60C5\u51B5\uFF0C\u53EA\u4FDD\u7559\u5149\u6807\u9644\u4EF6\u7684 html \u6807\u7B7E\n const left2 = left1.replace(/(.*(?=html`))/s, (str) => str.replaceAll(/[^\\n]/g, ' '));\n return left2 + removeSlot(right);\n}\n\nexport function translateCompletionList(result: any, position: Position) {\n const getRange = (item: CompletionItem): Range | undefined => {\n if (item.textEdit && 'range' in item.textEdit) {\n const { start, end } = item.textEdit.range;\n return Range.create(\n Position.create(position.line, start.character),\n Position.create(position.line, end.character),\n );\n }\n };\n\n return {\n ...result,\n items: result?.items.map((item: CompletionItem) => ({\n ...item,\n textEdit: item.textEdit && {\n ...item.textEdit,\n range: getRange(item),\n },\n })),\n };\n}\nexport function matchOffset(regex: RegExp, docText: string, offset: number) {\n regex.exec('null');\n\n let match: RegExpExecArray | null;\n while ((match = regex.exec(docText)) !== null) {\n const [fullStr, startStr] = match;\n const start = match.index + startStr.length;\n const end = match.index + fullStr.length;\n if (offset > start && offset < end) {\n return match;\n }\n }\n return null;\n}\n\nexport function createVirtualDocument(languageId: string, content: string) {\n return TextDocument.create(`embedded://document.${languageId}`, languageId, 1, content);\n}\n", "import { getLanguageService as getHtmlLanguageService } from 'vscode-html-languageservice';\nimport { getCSSLanguageService } from 'vscode-css-languageservice';\nimport type { LanguageService as CssLanguageService } from 'vscode-css-languageservice';\nimport type { Position, TextDocument } from 'vscode-languageserver-textdocument';\n\nimport { createVirtualDocument, matchOffset, removeHTMLSlot, removeSlot } from './util';\nimport { CSS_REG, HTML_REG, STYLE_REG } from './constants';\n\nexport class HTMLHoverProvider {\n #htmlLanguageService = getHtmlLanguageService();\n\n provideHover(document: TextDocument, position: Position) {\n const currentOffset = document.offsetAt(position);\n const documentText = removeHTMLSlot(document.getText(), currentOffset);\n const match = matchOffset(HTML_REG, documentText, currentOffset);\n\n if (!match) return null;\n\n const matchContent = match.groups!.content;\n const matchStartOffset = match.index + match.groups!.start.length;\n const virtualOffset = currentOffset - matchStartOffset;\n const virtualDocument = createVirtualDocument('html', matchContent);\n const html = this.#htmlLanguageService.parseHTMLDocument(virtualDocument);\n return this.#htmlLanguageService.doHover(virtualDocument, virtualDocument.positionAt(virtualOffset), html, {\n documentation: true,\n references: true,\n });\n }\n}\n\nexport class CSSHoverProvider {\n #cssLanguageService: CssLanguageService = getCSSLanguageService();\n\n provideHover(document: TextDocument, position: Position) {\n const currentOffset = document.offsetAt(position);\n const documentText = document.getText();\n const match = matchOffset(CSS_REG, documentText, currentOffset);\n\n if (!match) return null;\n\n const matchContent = match.groups!.content;\n const matchStartOffset = match.index + match.groups!.start.length;\n const virtualOffset = currentOffset - matchStartOffset;\n const virtualDocument = createVirtualDocument('css', removeSlot(matchContent));\n const stylesheet = this.#cssLanguageService.parseStylesheet(virtualDocument);\n return this.#cssLanguageService.doHover(virtualDocument, virtualDocument.positionAt(virtualOffset), stylesheet, {\n documentation: true,\n references: true,\n });\n }\n}\n\nexport class StyleHoverProvider {\n #cssLanguageService: CssLanguageService = getCSSLanguageService();\n\n provideHover(document: TextDocument, position: Position) {\n const currentOffset = document.offsetAt(position);\n const documentText = document.getText();\n const match = matchOffset(STYLE_REG, documentText, currentOffset);\n\n if (!match) return null;\n\n const matchContent = match.groups!.content;\n const matchStartOffset = match.index + match.groups!.start.length;\n const virtualOffset = currentOffset - matchStartOffset + 8;\n const virtualDocument = createVirtualDocument('css', `:host { ${removeSlot(matchContent)} }`);\n const stylesheet = this.#cssLanguageService.parseStylesheet(virtualDocument);\n return this.#cssLanguageService.doHover(virtualDocument, virtualDocument.positionAt(virtualOffset), stylesheet, {\n documentation: true,\n references: true,\n });\n }\n}\n", "import type { LanguageService as HTMLanguageService } from 'vscode-html-languageservice';\nimport type { Position, TextDocument } from 'vscode-languageserver-textdocument';\nimport { getLanguageService as getHTMLanguageService, TokenType as HTMLTokenType } from 'vscode-html-languageservice';\nimport { getCSSLanguageService } from 'vscode-css-languageservice';\n\nimport { matchOffset, createVirtualDocument, removeSlot, translateCompletionList } from './util';\nimport { CSS_REG, HTML_REG } from './constants';\nimport { CompletionsCache } from './cache';\n\nfunction getRegionAtOffset(regions: IEmbeddedRegion[], offset: number) {\n for (const region of regions) {\n if (region.start <= offset) {\n if (offset <= region.end) {\n return region;\n }\n } else {\n break;\n }\n }\n return null;\n}\n\ninterface IEmbeddedRegion {\n languageId: string;\n start: number;\n end: number;\n length: number;\n content: string;\n}\n\nfunction getLanguageRegions(service: HTMLanguageService, data: string) {\n const scanner = service.createScanner(data);\n const regions: IEmbeddedRegion[] = [];\n let tokenType: HTMLTokenType;\n\n while ((tokenType = scanner.scan()) !== HTMLTokenType.EOS) {\n switch (tokenType) {\n case HTMLTokenType.Styles:\n regions.push({\n languageId: 'css',\n start: scanner.getTokenOffset(),\n end: scanner.getTokenEnd(),\n length: scanner.getTokenLength(),\n content: scanner.getTokenText(),\n });\n break;\n default:\n break;\n }\n }\n\n return regions;\n}\n\nexport class HTMLStyleCompletionItemProvider {\n #cssLanguageService = getCSSLanguageService();\n #htmlLanguageService = getHTMLanguageService();\n #cache = new CompletionsCache();\n\n provideCompletionItems(document: TextDocument, position: Position) {\n const cached = this.#cache.getCached(document, position);\n if (cached) return cached;\n\n const currentOffset = document.offsetAt(position);\n const documentText = document.getText();\n const match = matchOffset(HTML_REG, documentText, currentOffset);\n\n if (!match) return;\n\n const matchContent = match.groups!.content;\n const matchStartOffset = match.index + match.groups!.start.length;\n const regions = getLanguageRegions(this.#htmlLanguageService, matchContent);\n\n if (regions.length <= 0) return;\n\n const region = getRegionAtOffset(regions, currentOffset - matchStartOffset);\n\n if (!region) return;\n\n const virtualOffset = currentOffset - (matchStartOffset + region.start);\n const virtualDocument = createVirtualDocument('css', removeSlot(region.content));\n const stylesheet = this.#cssLanguageService.parseStylesheet(virtualDocument);\n\n const completions = this.#cssLanguageService.doComplete(\n virtualDocument,\n virtualDocument.positionAt(virtualOffset),\n stylesheet,\n );\n\n return this.#cache.updateCached(document, position, translateCompletionList(completions, position));\n }\n}\n\nexport class CSSCompletionItemProvider {\n #cssLanguageService = getCSSLanguageService();\n #cache = new CompletionsCache();\n\n provideCompletionItems(document: TextDocument, position: Position) {\n const cached = this.#cache.getCached(document, position);\n if (cached) return cached;\n\n const currentOffset = document.offsetAt(position);\n const documentText = document.getText();\n const match = matchOffset(CSS_REG, documentText, currentOffset);\n\n if (!match) return;\n\n const matchContent = match.groups!.content;\n const matchStartOffset = match.index + match.groups!.start.length;\n const virtualOffset = currentOffset - matchStartOffset;\n const virtualDocument = createVirtualDocument('css', removeSlot(matchContent));\n const vCss = this.#cssLanguageService.parseStylesheet(virtualDocument);\n\n const completions = this.#cssLanguageService.doComplete(\n virtualDocument,\n virtualDocument.positionAt(virtualOffset),\n vCss,\n );\n\n return this.#cache.updateCached(document, position, translateCompletionList(completions, position));\n }\n}\n", "import type { CompletionList } from 'vscode-languageserver';\nimport type { Position, TextDocument } from 'vscode-languageserver-textdocument';\n\nexport class CompletionsCache {\n #cachedCompletionsFile?: string;\n #cachedCompletionsPosition?: Position;\n #cachedCompletionsContent?: string;\n #completions?: CompletionList;\n\n #equalPositions(left: Position, right?: Position) {\n return !!right && left.line === right.line && left.character === right.character;\n }\n\n getCached(doc: TextDocument, position: Position) {\n if (\n this.#completions &&\n doc.uri === this.#cachedCompletionsFile &&\n this.#equalPositions(position, this.#cachedCompletionsPosition) &&\n doc.getText() === this.#cachedCompletionsContent\n ) {\n return this.#completions;\n }\n\n return undefined;\n }\n\n updateCached(context: TextDocument, position: Position, completions: CompletionList) {\n this.#cachedCompletionsFile = context.uri;\n this.#cachedCompletionsPosition = position;\n this.#cachedCompletionsContent = context.getText();\n this.#completions = completions;\n return completions;\n }\n}\n", "import type { CompletionList as HTMLCompletionList } from 'vscode-html-languageservice';\nimport { getLanguageService as getHTMLanguageService } from 'vscode-html-languageservice';\nimport { doComplete as doEmmetComplete, type VSCodeEmmetConfig } from '@vscode/emmet-helper';\nimport type { Position, TextDocument } from 'vscode-languageserver-textdocument';\nimport type { Connection } from 'vscode-languageserver';\n\nimport { matchOffset, createVirtualDocument, removeHTMLSlot, translateCompletionList } from './util';\nimport { HTML_REG } from './constants';\nimport { CompletionsCache } from './cache';\n\nexport class HTMLCompletionItemProvider {\n #htmlLanguageService = getHTMLanguageService();\n #cache = new CompletionsCache();\n #connection: Connection;\n #emmetConfig: VSCodeEmmetConfig;\n\n constructor(connection: Connection) {\n this.#connection = connection;\n }\n\n async #getEmmetConfig() {\n if (!this.#emmetConfig) {\n const emmetConfig = await this.#connection.workspace.getConfiguration('emmet');\n this.#emmetConfig = {\n showExpandedAbbreviation: emmetConfig.showExpandedAbbreviation,\n showAbbreviationSuggestions: emmetConfig.showAbbreviationSuggestions,\n syntaxProfiles: emmetConfig.syntaxProfiles,\n variables: emmetConfig.variables,\n };\n }\n return this.#emmetConfig;\n }\n\n async provideCompletionItems(document: TextDocument, position: Position) {\n const emmetConfig = await this.#getEmmetConfig();\n const cached = this.#cache.getCached(document, position);\n if (cached) return cached;\n\n const currentOffset = document.offsetAt(position);\n const documentText = removeHTMLSlot(document.getText(), currentOffset);\n const match = matchOffset(HTML_REG, documentText, currentOffset);\n\n if (!match) return;\n\n const matchContent = match.groups!.content;\n const matchStartOffset = match.index + match.groups!.start.length;\n const virtualOffset = currentOffset - matchStartOffset;\n const virtualDocument = createVirtualDocument('html', matchContent);\n const vHtml = this.#htmlLanguageService.parseHTMLDocument(virtualDocument);\n\n let emmetResults: HTMLCompletionList = { isIncomplete: true, items: [] };\n this.#htmlLanguageService.setCompletionParticipants([\n {\n onHtmlContent: async () => {\n const pos = virtualDocument.positionAt(virtualOffset);\n const result = doEmmetComplete(virtualDocument, pos, 'html', emmetConfig);\n if (result) {\n emmetResults = {\n ...result,\n items: result.items.map((item) => ({\n ...item,\n command: {\n title: 'Emmet Expand Abbreviation',\n command: 'editor.emmet.action.expandAbbreviation',\n },\n })),\n };\n }\n },\n },\n ]);\n\n const completions = this.#htmlLanguageService.doComplete(\n virtualDocument,\n virtualDocument.positionAt(virtualOffset),\n vHtml,\n );\n\n if (emmetResults.items.length) {\n completions.items.push(...emmetResults.items);\n }\n\n return this.#cache.updateCached(document, position, translateCompletionList(completions, position));\n }\n}\n", "import { getCSSLanguageService as getCSSLanguageService } from 'vscode-css-languageservice';\nimport type { Position, TextDocument } from 'vscode-languageserver-textdocument';\n\nimport { matchOffset, createVirtualDocument, removeSlot, translateCompletionList } from './util';\nimport { STYLE_REG } from './constants';\nimport { CompletionsCache } from './cache';\n\nexport class StyleCompletionItemProvider {\n #cssLanguageService = getCSSLanguageService();\n #cache = new CompletionsCache();\n\n provideCompletionItems(document: TextDocument, position: Position) {\n const cached = this.#cache.getCached(document, position);\n if (cached) return cached;\n\n const currentOffset = document.offsetAt(position);\n const documentText = document.getText();\n const match = matchOffset(STYLE_REG, documentText, currentOffset);\n\n if (!match) return;\n\n const matchContent = match.groups!.content;\n const matchStartOffset = match.index + match.groups!.start.length;\n const virtualOffset = currentOffset - matchStartOffset + 8; // accounting for :host { }\n const virtualDocument = createVirtualDocument('css', `:host { ${removeSlot(matchContent)} }`);\n const vCss = this.#cssLanguageService.parseStylesheet(virtualDocument);\n\n const completions = this.#cssLanguageService.doComplete(\n virtualDocument,\n virtualDocument.positionAt(virtualOffset),\n vCss,\n );\n\n return this.#cache.updateCached(document, position, translateCompletionList(completions, position));\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["import_node", "import_node", "import_node", "import_vscode_html_languageservice", "import_vscode_css_languageservice", "getHtmlLanguageService", "import_vscode_html_languageservice", "import_vscode_css_languageservice", "HTMLTokenType", "getHTMLanguageService", "import_vscode_html_languageservice", "getHTMLanguageService", "connection", "doEmmetComplete", "import_vscode_css_languageservice"]
|
|
7
|
+
}
|
package/package.json
CHANGED
|
@@ -1,18 +1,20 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vscode-gem-languageservice",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.3",
|
|
4
4
|
"description": "Language service for Gem",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"gem",
|
|
7
7
|
"language-service"
|
|
8
8
|
],
|
|
9
|
-
"type": "module",
|
|
10
9
|
"module": "src/index.ts",
|
|
11
|
-
"
|
|
10
|
+
"main": "src/index.ts",
|
|
11
|
+
"bin": "src/index.js",
|
|
12
12
|
"files": [
|
|
13
|
-
"/
|
|
13
|
+
"/dist/"
|
|
14
14
|
],
|
|
15
|
-
"scripts": {
|
|
15
|
+
"scripts": {
|
|
16
|
+
"prepublishOnly": "esbuild ./src/index.ts --outdir=./dist --platform=node --sourcemap --bundle --packages=external"
|
|
17
|
+
},
|
|
16
18
|
"dependencies": {
|
|
17
19
|
"@vscode/emmet-helper": "^2.9.3",
|
|
18
20
|
"duoyun-ui": "^2.2.0",
|
package/src/index.ts
CHANGED
package/src/cache.ts
DELETED
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
import type { CompletionList } from 'vscode-languageserver';
|
|
2
|
-
import type { Position, TextDocument } from 'vscode-languageserver-textdocument';
|
|
3
|
-
|
|
4
|
-
export class CompletionsCache {
|
|
5
|
-
#cachedCompletionsFile?: string;
|
|
6
|
-
#cachedCompletionsPosition?: Position;
|
|
7
|
-
#cachedCompletionsContent?: string;
|
|
8
|
-
#completions?: CompletionList;
|
|
9
|
-
|
|
10
|
-
#equalPositions(left: Position, right?: Position) {
|
|
11
|
-
return !!right && left.line === right.line && left.character === right.character;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
getCached(doc: TextDocument, position: Position) {
|
|
15
|
-
if (
|
|
16
|
-
this.#completions &&
|
|
17
|
-
doc.uri === this.#cachedCompletionsFile &&
|
|
18
|
-
this.#equalPositions(position, this.#cachedCompletionsPosition) &&
|
|
19
|
-
doc.getText() === this.#cachedCompletionsContent
|
|
20
|
-
) {
|
|
21
|
-
return this.#completions;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
return undefined;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
updateCached(context: TextDocument, position: Position, completions: CompletionList) {
|
|
28
|
-
this.#cachedCompletionsFile = context.uri;
|
|
29
|
-
this.#cachedCompletionsPosition = position;
|
|
30
|
-
this.#cachedCompletionsContent = context.getText();
|
|
31
|
-
this.#completions = completions;
|
|
32
|
-
return completions;
|
|
33
|
-
}
|
|
34
|
-
}
|
package/src/color.ts
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import { rgbToHexColor, parseHexColor } from 'duoyun-ui/lib/color';
|
|
2
|
-
import { Range, Color } from 'vscode-languageserver/node';
|
|
3
|
-
import type { ColorInformation, ColorPresentation } from 'vscode-languageserver/node';
|
|
4
|
-
import type { HexColor } from 'duoyun-ui/lib/color';
|
|
5
|
-
import type { TextDocument } from 'vscode-languageserver-textdocument';
|
|
6
|
-
|
|
7
|
-
import { COLOR_REG } from './constants';
|
|
8
|
-
|
|
9
|
-
export class ColorProvider {
|
|
10
|
-
provideDocumentColors(document: TextDocument) {
|
|
11
|
-
COLOR_REG.exec('null');
|
|
12
|
-
|
|
13
|
-
const documentText = document.getText();
|
|
14
|
-
const colors: ColorInformation[] = [];
|
|
15
|
-
|
|
16
|
-
let match: RegExpExecArray | null;
|
|
17
|
-
while ((match = COLOR_REG.exec(documentText)) !== null) {
|
|
18
|
-
const hex = match.groups!.content as HexColor;
|
|
19
|
-
const [red, green, blue, alpha] = parseHexColor(hex);
|
|
20
|
-
const offset = match.index + (match.groups!.start?.length || 0);
|
|
21
|
-
const range = Range.create(document.positionAt(offset), document.positionAt(offset + hex.length));
|
|
22
|
-
const color = Color.create(red / 255, green / 255, blue / 255, alpha);
|
|
23
|
-
colors.push({ range, color });
|
|
24
|
-
}
|
|
25
|
-
return colors;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
provideColorPresentations({ red, green, blue, alpha }: Color): ColorPresentation[] {
|
|
29
|
-
return [{ label: rgbToHexColor([red * 255, green * 255, blue * 255, alpha]) }];
|
|
30
|
-
}
|
|
31
|
-
}
|
package/src/constants.ts
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
export const COLOR_REG = /(?<start>'|")?(?<content>#([0-9a-fA-F]{8}|[0-9a-fA-F]{6}|[0-9a-fA-F]{3,4}))($1|\s*;|\s*\))/g;
|
|
2
|
-
|
|
3
|
-
// 直接通过正则匹配 css 片段,通过条件的结束 ` 号匹配
|
|
4
|
-
export const CSS_REG = /(?<start>\/\*\s*css\s*\*\/\s*`|(?<!`)(?:css|less|scss)\s*`)(?<content>.*?)(`(?=;|,?\s*\)))/gs;
|
|
5
|
-
// 直接通过正则匹配 style 片段,通过条件的结束 ` 号匹配
|
|
6
|
-
// 语言服务和高亮都只支持 styled 写法
|
|
7
|
-
export const STYLE_REG = /(?<start>\/\*\s*style\s*\*\/\s*`|(?<!`)styled?\s*`)(?<content>.*?)(`(?=,|\s*}\s*\)))/gs;
|
|
8
|
-
|
|
9
|
-
// 处理后进行正则匹配,所以不需要验证后面的 ` 号
|
|
10
|
-
export const HTML_REG = /(?<start>\/\*\s*html\s*\*\/\s*`|(?<!`)(?:html|raw)\s*`)(?<content>[^`]*)(`)/g;
|
package/src/css.ts
DELETED
|
@@ -1,122 +0,0 @@
|
|
|
1
|
-
import type { LanguageService as HTMLanguageService } from 'vscode-html-languageservice';
|
|
2
|
-
import type { Position, TextDocument } from 'vscode-languageserver-textdocument';
|
|
3
|
-
import { getLanguageService as getHTMLanguageService, TokenType as HTMLTokenType } from 'vscode-html-languageservice';
|
|
4
|
-
import { getCSSLanguageService } from 'vscode-css-languageservice';
|
|
5
|
-
|
|
6
|
-
import { matchOffset, createVirtualDocument, removeSlot, translateCompletionList } from './util';
|
|
7
|
-
import { CSS_REG, HTML_REG } from './constants';
|
|
8
|
-
import { CompletionsCache } from './cache';
|
|
9
|
-
|
|
10
|
-
function getRegionAtOffset(regions: IEmbeddedRegion[], offset: number) {
|
|
11
|
-
for (const region of regions) {
|
|
12
|
-
if (region.start <= offset) {
|
|
13
|
-
if (offset <= region.end) {
|
|
14
|
-
return region;
|
|
15
|
-
}
|
|
16
|
-
} else {
|
|
17
|
-
break;
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
return null;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
interface IEmbeddedRegion {
|
|
24
|
-
languageId: string;
|
|
25
|
-
start: number;
|
|
26
|
-
end: number;
|
|
27
|
-
length: number;
|
|
28
|
-
content: string;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
function getLanguageRegions(service: HTMLanguageService, data: string) {
|
|
32
|
-
const scanner = service.createScanner(data);
|
|
33
|
-
const regions: IEmbeddedRegion[] = [];
|
|
34
|
-
let tokenType: HTMLTokenType;
|
|
35
|
-
|
|
36
|
-
while ((tokenType = scanner.scan()) !== HTMLTokenType.EOS) {
|
|
37
|
-
switch (tokenType) {
|
|
38
|
-
case HTMLTokenType.Styles:
|
|
39
|
-
regions.push({
|
|
40
|
-
languageId: 'css',
|
|
41
|
-
start: scanner.getTokenOffset(),
|
|
42
|
-
end: scanner.getTokenEnd(),
|
|
43
|
-
length: scanner.getTokenLength(),
|
|
44
|
-
content: scanner.getTokenText(),
|
|
45
|
-
});
|
|
46
|
-
break;
|
|
47
|
-
default:
|
|
48
|
-
break;
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
return regions;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
export class HTMLStyleCompletionItemProvider {
|
|
56
|
-
#cssLanguageService = getCSSLanguageService();
|
|
57
|
-
#htmlLanguageService = getHTMLanguageService();
|
|
58
|
-
#cache = new CompletionsCache();
|
|
59
|
-
|
|
60
|
-
provideCompletionItems(document: TextDocument, position: Position) {
|
|
61
|
-
const cached = this.#cache.getCached(document, position);
|
|
62
|
-
if (cached) return cached;
|
|
63
|
-
|
|
64
|
-
const currentOffset = document.offsetAt(position);
|
|
65
|
-
const documentText = document.getText();
|
|
66
|
-
const match = matchOffset(HTML_REG, documentText, currentOffset);
|
|
67
|
-
|
|
68
|
-
if (!match) return;
|
|
69
|
-
|
|
70
|
-
const matchContent = match.groups!.content;
|
|
71
|
-
const matchStartOffset = match.index + match.groups!.start.length;
|
|
72
|
-
const regions = getLanguageRegions(this.#htmlLanguageService, matchContent);
|
|
73
|
-
|
|
74
|
-
if (regions.length <= 0) return;
|
|
75
|
-
|
|
76
|
-
const region = getRegionAtOffset(regions, currentOffset - matchStartOffset);
|
|
77
|
-
|
|
78
|
-
if (!region) return;
|
|
79
|
-
|
|
80
|
-
const virtualOffset = currentOffset - (matchStartOffset + region.start);
|
|
81
|
-
const virtualDocument = createVirtualDocument('css', removeSlot(region.content));
|
|
82
|
-
const stylesheet = this.#cssLanguageService.parseStylesheet(virtualDocument);
|
|
83
|
-
|
|
84
|
-
const completions = this.#cssLanguageService.doComplete(
|
|
85
|
-
virtualDocument,
|
|
86
|
-
virtualDocument.positionAt(virtualOffset),
|
|
87
|
-
stylesheet,
|
|
88
|
-
);
|
|
89
|
-
|
|
90
|
-
return this.#cache.updateCached(document, position, translateCompletionList(completions, position));
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
export class CSSCompletionItemProvider {
|
|
95
|
-
#cssLanguageService = getCSSLanguageService();
|
|
96
|
-
#cache = new CompletionsCache();
|
|
97
|
-
|
|
98
|
-
provideCompletionItems(document: TextDocument, position: Position) {
|
|
99
|
-
const cached = this.#cache.getCached(document, position);
|
|
100
|
-
if (cached) return cached;
|
|
101
|
-
|
|
102
|
-
const currentOffset = document.offsetAt(position);
|
|
103
|
-
const documentText = document.getText();
|
|
104
|
-
const match = matchOffset(CSS_REG, documentText, currentOffset);
|
|
105
|
-
|
|
106
|
-
if (!match) return;
|
|
107
|
-
|
|
108
|
-
const matchContent = match.groups!.content;
|
|
109
|
-
const matchStartOffset = match.index + match.groups!.start.length;
|
|
110
|
-
const virtualOffset = currentOffset - matchStartOffset;
|
|
111
|
-
const virtualDocument = createVirtualDocument('css', removeSlot(matchContent));
|
|
112
|
-
const vCss = this.#cssLanguageService.parseStylesheet(virtualDocument);
|
|
113
|
-
|
|
114
|
-
const completions = this.#cssLanguageService.doComplete(
|
|
115
|
-
virtualDocument,
|
|
116
|
-
virtualDocument.positionAt(virtualOffset),
|
|
117
|
-
vCss,
|
|
118
|
-
);
|
|
119
|
-
|
|
120
|
-
return this.#cache.updateCached(document, position, translateCompletionList(completions, position));
|
|
121
|
-
}
|
|
122
|
-
}
|
package/src/diagnostic.ts
DELETED
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
// 只对 CSS 语法和属性做了简单的检查,不做值检查
|
|
2
|
-
// TODO: 激活扩展、打开工作区时需要自动诊断所有文件
|
|
3
|
-
// TODO: 使用 LRU 缓存
|
|
4
|
-
|
|
5
|
-
import { getCSSLanguageService } from 'vscode-css-languageservice';
|
|
6
|
-
import { Range } from 'vscode-languageserver/node';
|
|
7
|
-
import type { Diagnostic } from 'vscode-languageserver/node';
|
|
8
|
-
import type { TextDocument } from 'vscode-languageserver-textdocument';
|
|
9
|
-
|
|
10
|
-
import { CSS_REG, STYLE_REG } from './constants';
|
|
11
|
-
import { createVirtualDocument, removeSlot } from './util';
|
|
12
|
-
|
|
13
|
-
const cssLanguageService = getCSSLanguageService();
|
|
14
|
-
|
|
15
|
-
export function getDiagnostics(document: TextDocument, _relatedInformation: boolean) {
|
|
16
|
-
const diagnostics: Diagnostic[] = [];
|
|
17
|
-
const text = document.getText();
|
|
18
|
-
|
|
19
|
-
const matchFragments = (regexp: RegExp, appendBefore: string, appendAfter: string) => {
|
|
20
|
-
regexp.exec('null');
|
|
21
|
-
|
|
22
|
-
let match;
|
|
23
|
-
while ((match = regexp.exec(text))) {
|
|
24
|
-
const matchContent = match.groups!.content;
|
|
25
|
-
const offset = match.index + match.groups!.start.length;
|
|
26
|
-
const virtualDocument = createVirtualDocument('css', `${appendBefore}${removeSlot(matchContent)}${appendAfter}`);
|
|
27
|
-
const vCss = cssLanguageService.parseStylesheet(virtualDocument);
|
|
28
|
-
const oDiagnostics = cssLanguageService.doValidation(virtualDocument, vCss) as Diagnostic[];
|
|
29
|
-
for (const { message, range } of oDiagnostics) {
|
|
30
|
-
const { start, end } = range;
|
|
31
|
-
const startOffset = virtualDocument.offsetAt(start) - appendBefore.length + offset;
|
|
32
|
-
const endOffset = virtualDocument.offsetAt(end) - appendBefore.length + offset;
|
|
33
|
-
diagnostics.push({
|
|
34
|
-
range: Range.create(document.positionAt(startOffset), document.positionAt(endOffset)),
|
|
35
|
-
message,
|
|
36
|
-
});
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
};
|
|
40
|
-
|
|
41
|
-
matchFragments(CSS_REG, '', '');
|
|
42
|
-
matchFragments(STYLE_REG, ':host { ', ' }');
|
|
43
|
-
|
|
44
|
-
return diagnostics;
|
|
45
|
-
}
|
package/src/hover.ts
DELETED
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
import { getLanguageService as getHtmlLanguageService } from 'vscode-html-languageservice';
|
|
2
|
-
import { getCSSLanguageService } from 'vscode-css-languageservice';
|
|
3
|
-
import type { LanguageService as CssLanguageService } from 'vscode-css-languageservice';
|
|
4
|
-
import type { Position, TextDocument } from 'vscode-languageserver-textdocument';
|
|
5
|
-
|
|
6
|
-
import { createVirtualDocument, matchOffset, removeHTMLSlot, removeSlot } from './util';
|
|
7
|
-
import { CSS_REG, HTML_REG, STYLE_REG } from './constants';
|
|
8
|
-
|
|
9
|
-
export class HTMLHoverProvider {
|
|
10
|
-
#htmlLanguageService = getHtmlLanguageService();
|
|
11
|
-
|
|
12
|
-
provideHover(document: TextDocument, position: Position) {
|
|
13
|
-
const currentOffset = document.offsetAt(position);
|
|
14
|
-
const documentText = removeHTMLSlot(document.getText(), currentOffset);
|
|
15
|
-
const match = matchOffset(HTML_REG, documentText, currentOffset);
|
|
16
|
-
|
|
17
|
-
if (!match) return null;
|
|
18
|
-
|
|
19
|
-
const matchContent = match.groups!.content;
|
|
20
|
-
const matchStartOffset = match.index + match.groups!.start.length;
|
|
21
|
-
const virtualOffset = currentOffset - matchStartOffset;
|
|
22
|
-
const virtualDocument = createVirtualDocument('html', matchContent);
|
|
23
|
-
const html = this.#htmlLanguageService.parseHTMLDocument(virtualDocument);
|
|
24
|
-
return this.#htmlLanguageService.doHover(virtualDocument, virtualDocument.positionAt(virtualOffset), html, {
|
|
25
|
-
documentation: true,
|
|
26
|
-
references: true,
|
|
27
|
-
});
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
export class CSSHoverProvider {
|
|
32
|
-
#cssLanguageService: CssLanguageService = getCSSLanguageService();
|
|
33
|
-
|
|
34
|
-
provideHover(document: TextDocument, position: Position) {
|
|
35
|
-
const currentOffset = document.offsetAt(position);
|
|
36
|
-
const documentText = document.getText();
|
|
37
|
-
const match = matchOffset(CSS_REG, documentText, currentOffset);
|
|
38
|
-
|
|
39
|
-
if (!match) return null;
|
|
40
|
-
|
|
41
|
-
const matchContent = match.groups!.content;
|
|
42
|
-
const matchStartOffset = match.index + match.groups!.start.length;
|
|
43
|
-
const virtualOffset = currentOffset - matchStartOffset;
|
|
44
|
-
const virtualDocument = createVirtualDocument('css', removeSlot(matchContent));
|
|
45
|
-
const stylesheet = this.#cssLanguageService.parseStylesheet(virtualDocument);
|
|
46
|
-
return this.#cssLanguageService.doHover(virtualDocument, virtualDocument.positionAt(virtualOffset), stylesheet, {
|
|
47
|
-
documentation: true,
|
|
48
|
-
references: true,
|
|
49
|
-
});
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
export class StyleHoverProvider {
|
|
54
|
-
#cssLanguageService: CssLanguageService = getCSSLanguageService();
|
|
55
|
-
|
|
56
|
-
provideHover(document: TextDocument, position: Position) {
|
|
57
|
-
const currentOffset = document.offsetAt(position);
|
|
58
|
-
const documentText = document.getText();
|
|
59
|
-
const match = matchOffset(STYLE_REG, documentText, currentOffset);
|
|
60
|
-
|
|
61
|
-
if (!match) return null;
|
|
62
|
-
|
|
63
|
-
const matchContent = match.groups!.content;
|
|
64
|
-
const matchStartOffset = match.index + match.groups!.start.length;
|
|
65
|
-
const virtualOffset = currentOffset - matchStartOffset + 8;
|
|
66
|
-
const virtualDocument = createVirtualDocument('css', `:host { ${removeSlot(matchContent)} }`);
|
|
67
|
-
const stylesheet = this.#cssLanguageService.parseStylesheet(virtualDocument);
|
|
68
|
-
return this.#cssLanguageService.doHover(virtualDocument, virtualDocument.positionAt(virtualOffset), stylesheet, {
|
|
69
|
-
documentation: true,
|
|
70
|
-
references: true,
|
|
71
|
-
});
|
|
72
|
-
}
|
|
73
|
-
}
|
package/src/html.ts
DELETED
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
import type { CompletionList as HTMLCompletionList } from 'vscode-html-languageservice';
|
|
2
|
-
import { getLanguageService as getHTMLanguageService } from 'vscode-html-languageservice';
|
|
3
|
-
import { doComplete as doEmmetComplete, type VSCodeEmmetConfig } from '@vscode/emmet-helper';
|
|
4
|
-
import type { Position, TextDocument } from 'vscode-languageserver-textdocument';
|
|
5
|
-
import type { Connection } from 'vscode-languageserver';
|
|
6
|
-
|
|
7
|
-
import { matchOffset, createVirtualDocument, removeHTMLSlot, translateCompletionList } from './util';
|
|
8
|
-
import { HTML_REG } from './constants';
|
|
9
|
-
import { CompletionsCache } from './cache';
|
|
10
|
-
|
|
11
|
-
export class HTMLCompletionItemProvider {
|
|
12
|
-
#htmlLanguageService = getHTMLanguageService();
|
|
13
|
-
#cache = new CompletionsCache();
|
|
14
|
-
#connection: Connection;
|
|
15
|
-
#emmetConfig: VSCodeEmmetConfig;
|
|
16
|
-
|
|
17
|
-
constructor(connection: Connection) {
|
|
18
|
-
this.#connection = connection;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
async #getEmmetConfig() {
|
|
22
|
-
if (!this.#emmetConfig) {
|
|
23
|
-
const emmetConfig = await this.#connection.workspace.getConfiguration('emmet');
|
|
24
|
-
this.#emmetConfig = {
|
|
25
|
-
showExpandedAbbreviation: emmetConfig.showExpandedAbbreviation,
|
|
26
|
-
showAbbreviationSuggestions: emmetConfig.showAbbreviationSuggestions,
|
|
27
|
-
syntaxProfiles: emmetConfig.syntaxProfiles,
|
|
28
|
-
variables: emmetConfig.variables,
|
|
29
|
-
};
|
|
30
|
-
}
|
|
31
|
-
return this.#emmetConfig;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
async provideCompletionItems(document: TextDocument, position: Position) {
|
|
35
|
-
const emmetConfig = await this.#getEmmetConfig();
|
|
36
|
-
const cached = this.#cache.getCached(document, position);
|
|
37
|
-
if (cached) return cached;
|
|
38
|
-
|
|
39
|
-
const currentOffset = document.offsetAt(position);
|
|
40
|
-
const documentText = removeHTMLSlot(document.getText(), currentOffset);
|
|
41
|
-
const match = matchOffset(HTML_REG, documentText, currentOffset);
|
|
42
|
-
|
|
43
|
-
if (!match) return;
|
|
44
|
-
|
|
45
|
-
const matchContent = match.groups!.content;
|
|
46
|
-
const matchStartOffset = match.index + match.groups!.start.length;
|
|
47
|
-
const virtualOffset = currentOffset - matchStartOffset;
|
|
48
|
-
const virtualDocument = createVirtualDocument('html', matchContent);
|
|
49
|
-
const vHtml = this.#htmlLanguageService.parseHTMLDocument(virtualDocument);
|
|
50
|
-
|
|
51
|
-
let emmetResults: HTMLCompletionList = { isIncomplete: true, items: [] };
|
|
52
|
-
this.#htmlLanguageService.setCompletionParticipants([
|
|
53
|
-
{
|
|
54
|
-
onHtmlContent: async () => {
|
|
55
|
-
const pos = virtualDocument.positionAt(virtualOffset);
|
|
56
|
-
const result = doEmmetComplete(virtualDocument, pos, 'html', emmetConfig);
|
|
57
|
-
if (result) {
|
|
58
|
-
emmetResults = {
|
|
59
|
-
...result,
|
|
60
|
-
items: result.items.map((item) => ({
|
|
61
|
-
...item,
|
|
62
|
-
command: {
|
|
63
|
-
title: 'Emmet Expand Abbreviation',
|
|
64
|
-
command: 'editor.emmet.action.expandAbbreviation',
|
|
65
|
-
},
|
|
66
|
-
})),
|
|
67
|
-
};
|
|
68
|
-
}
|
|
69
|
-
},
|
|
70
|
-
},
|
|
71
|
-
]);
|
|
72
|
-
|
|
73
|
-
const completions = this.#htmlLanguageService.doComplete(
|
|
74
|
-
virtualDocument,
|
|
75
|
-
virtualDocument.positionAt(virtualOffset),
|
|
76
|
-
vHtml,
|
|
77
|
-
);
|
|
78
|
-
|
|
79
|
-
if (emmetResults.items.length) {
|
|
80
|
-
completions.items.push(...emmetResults.items);
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
return this.#cache.updateCached(document, position, translateCompletionList(completions, position));
|
|
84
|
-
}
|
|
85
|
-
}
|
package/src/style.ts
DELETED
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
import { getCSSLanguageService as getCSSLanguageService } from 'vscode-css-languageservice';
|
|
2
|
-
import type { Position, TextDocument } from 'vscode-languageserver-textdocument';
|
|
3
|
-
|
|
4
|
-
import { matchOffset, createVirtualDocument, removeSlot, translateCompletionList } from './util';
|
|
5
|
-
import { STYLE_REG } from './constants';
|
|
6
|
-
import { CompletionsCache } from './cache';
|
|
7
|
-
|
|
8
|
-
export class StyleCompletionItemProvider {
|
|
9
|
-
#cssLanguageService = getCSSLanguageService();
|
|
10
|
-
#cache = new CompletionsCache();
|
|
11
|
-
|
|
12
|
-
provideCompletionItems(document: TextDocument, position: Position) {
|
|
13
|
-
const cached = this.#cache.getCached(document, position);
|
|
14
|
-
if (cached) return cached;
|
|
15
|
-
|
|
16
|
-
const currentOffset = document.offsetAt(position);
|
|
17
|
-
const documentText = document.getText();
|
|
18
|
-
const match = matchOffset(STYLE_REG, documentText, currentOffset);
|
|
19
|
-
|
|
20
|
-
if (!match) return;
|
|
21
|
-
|
|
22
|
-
const matchContent = match.groups!.content;
|
|
23
|
-
const matchStartOffset = match.index + match.groups!.start.length;
|
|
24
|
-
const virtualOffset = currentOffset - matchStartOffset + 8; // accounting for :host { }
|
|
25
|
-
const virtualDocument = createVirtualDocument('css', `:host { ${removeSlot(matchContent)} }`);
|
|
26
|
-
const vCss = this.#cssLanguageService.parseStylesheet(virtualDocument);
|
|
27
|
-
|
|
28
|
-
const completions = this.#cssLanguageService.doComplete(
|
|
29
|
-
virtualDocument,
|
|
30
|
-
virtualDocument.positionAt(virtualOffset),
|
|
31
|
-
vCss,
|
|
32
|
-
);
|
|
33
|
-
|
|
34
|
-
return this.#cache.updateCached(document, position, translateCompletionList(completions, position));
|
|
35
|
-
}
|
|
36
|
-
}
|
package/src/util.ts
DELETED
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
import { TextDocument } from 'vscode-html-languageservice';
|
|
2
|
-
import { Position, Range } from 'vscode-languageserver/node';
|
|
3
|
-
import type { CompletionItem } from 'vscode-languageserver/node';
|
|
4
|
-
|
|
5
|
-
export function removeSlot(text: string) {
|
|
6
|
-
const v = text.replace(/\$\{[^${]*?\}/g, (str) => str.replaceAll(/[^\n]/g, 'x'));
|
|
7
|
-
if (v === text) return v;
|
|
8
|
-
return removeSlot(v);
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export function removeHTMLSlot(text: string, position: number) {
|
|
12
|
-
const left = text.slice(0, position);
|
|
13
|
-
const right = text.slice(position);
|
|
14
|
-
const left1 = removeSlot(left);
|
|
15
|
-
// 处理在插槽中的情况,只保留光标附件的 html 标签
|
|
16
|
-
const left2 = left1.replace(/(.*(?=html`))/s, (str) => str.replaceAll(/[^\n]/g, ' '));
|
|
17
|
-
return left2 + removeSlot(right);
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export function translateCompletionList(result: any, position: Position) {
|
|
21
|
-
const getRange = (item: CompletionItem): Range | undefined => {
|
|
22
|
-
if (item.textEdit && 'range' in item.textEdit) {
|
|
23
|
-
const { start, end } = item.textEdit.range;
|
|
24
|
-
return Range.create(
|
|
25
|
-
Position.create(position.line, start.character),
|
|
26
|
-
Position.create(position.line, end.character),
|
|
27
|
-
);
|
|
28
|
-
}
|
|
29
|
-
};
|
|
30
|
-
|
|
31
|
-
return {
|
|
32
|
-
...result,
|
|
33
|
-
items: result?.items.map((item: CompletionItem) => ({
|
|
34
|
-
...item,
|
|
35
|
-
textEdit: item.textEdit && {
|
|
36
|
-
...item.textEdit,
|
|
37
|
-
range: getRange(item),
|
|
38
|
-
},
|
|
39
|
-
})),
|
|
40
|
-
};
|
|
41
|
-
}
|
|
42
|
-
export function matchOffset(regex: RegExp, docText: string, offset: number) {
|
|
43
|
-
regex.exec('null');
|
|
44
|
-
|
|
45
|
-
let match: RegExpExecArray | null;
|
|
46
|
-
while ((match = regex.exec(docText)) !== null) {
|
|
47
|
-
const [fullStr, startStr] = match;
|
|
48
|
-
const start = match.index + startStr.length;
|
|
49
|
-
const end = match.index + fullStr.length;
|
|
50
|
-
if (offset > start && offset < end) {
|
|
51
|
-
return match;
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
return null;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
export function createVirtualDocument(languageId: string, content: string) {
|
|
58
|
-
return TextDocument.create(`embedded://document.${languageId}`, languageId, 1, content);
|
|
59
|
-
}
|