ts-gem-plugin 0.0.5
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 +970 -0
- package/package.json +41 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,970 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __copyProps = (to, from, except, desc) => {
|
|
9
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
10
|
+
for (let key of __getOwnPropNames(from))
|
|
11
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
12
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
13
|
+
}
|
|
14
|
+
return to;
|
|
15
|
+
};
|
|
16
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
17
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
18
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
19
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
20
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
21
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
22
|
+
mod
|
|
23
|
+
));
|
|
24
|
+
|
|
25
|
+
// src/index.ts
|
|
26
|
+
var import_typescript_template_language_service_decorator = require("@mantou/typescript-template-language-service-decorator");
|
|
27
|
+
|
|
28
|
+
// src/decorate-html.ts
|
|
29
|
+
var import_vscode_html_languageservice = require("vscode-html-languageservice");
|
|
30
|
+
var import_emmet_helper = require("@vscode/emmet-helper");
|
|
31
|
+
var import_vscode_css_languageservice = require("vscode-css-languageservice");
|
|
32
|
+
|
|
33
|
+
// ../duoyun-ui/lib/types.js
|
|
34
|
+
function isNullish(v) {
|
|
35
|
+
return v === null || v === void 0;
|
|
36
|
+
}
|
|
37
|
+
function isNotNullish(v) {
|
|
38
|
+
return !isNullish(v);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// ../gem/lib/utils.js
|
|
42
|
+
var { assign, fromEntries, entries, keys } = Object;
|
|
43
|
+
var LinkedList = class extends EventTarget {
|
|
44
|
+
#map = /* @__PURE__ */ new Map();
|
|
45
|
+
#firstItem;
|
|
46
|
+
#lastItem;
|
|
47
|
+
#delete(value) {
|
|
48
|
+
const existItem = this.#map.get(value);
|
|
49
|
+
if (existItem) {
|
|
50
|
+
if (existItem.prev) {
|
|
51
|
+
existItem.prev.next = existItem.next;
|
|
52
|
+
} else {
|
|
53
|
+
this.#firstItem = existItem.next;
|
|
54
|
+
}
|
|
55
|
+
if (existItem.next) {
|
|
56
|
+
existItem.next.prev = existItem.prev;
|
|
57
|
+
} else {
|
|
58
|
+
this.#lastItem = existItem.prev;
|
|
59
|
+
}
|
|
60
|
+
this.#map.delete(value);
|
|
61
|
+
}
|
|
62
|
+
return existItem;
|
|
63
|
+
}
|
|
64
|
+
get size() {
|
|
65
|
+
return this.#map.size;
|
|
66
|
+
}
|
|
67
|
+
get first() {
|
|
68
|
+
return this.#firstItem;
|
|
69
|
+
}
|
|
70
|
+
get last() {
|
|
71
|
+
return this.#lastItem;
|
|
72
|
+
}
|
|
73
|
+
isSuperLinkOf(subLink) {
|
|
74
|
+
let subItem = subLink.first;
|
|
75
|
+
if (!subItem)
|
|
76
|
+
return true;
|
|
77
|
+
let item = this.find(subItem.value);
|
|
78
|
+
while (item && item.value === subItem.value) {
|
|
79
|
+
subItem = subItem.next;
|
|
80
|
+
if (!subItem)
|
|
81
|
+
return true;
|
|
82
|
+
item = item.next;
|
|
83
|
+
}
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
86
|
+
find(value) {
|
|
87
|
+
return this.#map.get(value);
|
|
88
|
+
}
|
|
89
|
+
// 添加到尾部,已存在时会删除老的项目
|
|
90
|
+
// 如果是添加第一个,start 事件会在添加前触发,避免处理事件重复的逻辑
|
|
91
|
+
add(value) {
|
|
92
|
+
if (!this.#lastItem) {
|
|
93
|
+
this.dispatchEvent(new CustomEvent("start"));
|
|
94
|
+
}
|
|
95
|
+
const item = this.#delete(value) || { value };
|
|
96
|
+
item.prev = this.#lastItem;
|
|
97
|
+
if (item.prev) {
|
|
98
|
+
item.prev.next = item;
|
|
99
|
+
}
|
|
100
|
+
item.next = void 0;
|
|
101
|
+
this.#lastItem = item;
|
|
102
|
+
if (!this.#firstItem) {
|
|
103
|
+
this.#firstItem = item;
|
|
104
|
+
}
|
|
105
|
+
this.#map.set(value, item);
|
|
106
|
+
}
|
|
107
|
+
// 删除这个元素后没有其他元素时立即出发 end 事件
|
|
108
|
+
delete(value) {
|
|
109
|
+
const deleteItem = this.#delete(value);
|
|
110
|
+
if (!this.#firstItem) {
|
|
111
|
+
this.dispatchEvent(new CustomEvent("end"));
|
|
112
|
+
}
|
|
113
|
+
return deleteItem;
|
|
114
|
+
}
|
|
115
|
+
// 获取头部元素
|
|
116
|
+
// 会从链表删除
|
|
117
|
+
get() {
|
|
118
|
+
const firstItem = this.#firstItem;
|
|
119
|
+
if (!firstItem)
|
|
120
|
+
return;
|
|
121
|
+
this.delete(firstItem.value);
|
|
122
|
+
return firstItem.value;
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
function kebabToCamelCase(str) {
|
|
126
|
+
return str.replace(/-(.)/g, (_substr, $1) => $1.toUpperCase());
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// src/utils.ts
|
|
130
|
+
var vscode = __toESM(require("vscode-languageserver-types"));
|
|
131
|
+
var import_vscode_languageserver_textdocument = require("vscode-languageserver-textdocument");
|
|
132
|
+
function isCustomElement(tag) {
|
|
133
|
+
return tag.includes("-");
|
|
134
|
+
}
|
|
135
|
+
function isDepElement(node) {
|
|
136
|
+
return node.getSourceFile().fileName.includes("/node_modules/");
|
|
137
|
+
}
|
|
138
|
+
var openTagReg = /(?<prefix><)(?<tag>\w+-\w+)\s+/g;
|
|
139
|
+
function forEachTag(typescript, containerNode, fn) {
|
|
140
|
+
const list = [containerNode];
|
|
141
|
+
while (true) {
|
|
142
|
+
const currentNode = list.pop();
|
|
143
|
+
if (!currentNode) return;
|
|
144
|
+
typescript.forEachChild(currentNode, (node) => {
|
|
145
|
+
list.push(node);
|
|
146
|
+
if (typescript.isTemplateHead(node) || typescript.isTemplateMiddle(node) || typescript.isTemplateTail(node) || typescript.isNoSubstitutionTemplateLiteral(node)) {
|
|
147
|
+
[...node.text.matchAll(openTagReg)].forEach((e) => {
|
|
148
|
+
const { prefix = "", tag } = e.groups;
|
|
149
|
+
const start = node.getStart() + 1 + prefix.length + e.index;
|
|
150
|
+
fn({ tag, length: tag.length, start });
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
function getHTMLTextAtPosition(text, offset) {
|
|
157
|
+
const before = text.slice(0, offset).match(/[^ \n<>]+$/)?.at(0) || "";
|
|
158
|
+
const after = text.slice(offset).match(/^[^ \n<>]+/)?.at(0) || "";
|
|
159
|
+
const str = before + after;
|
|
160
|
+
return {
|
|
161
|
+
text: str,
|
|
162
|
+
start: offset - before.length,
|
|
163
|
+
length: str.length
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
var attrMap = {
|
|
167
|
+
".": "property",
|
|
168
|
+
"?": "boolean",
|
|
169
|
+
"@": "event"
|
|
170
|
+
};
|
|
171
|
+
function getAttrName(text) {
|
|
172
|
+
const attr = text.split("=").at(0);
|
|
173
|
+
const char = attr.at(0);
|
|
174
|
+
if (char in attrMap) {
|
|
175
|
+
return { attr: attr.slice(1), type: attrMap[char] };
|
|
176
|
+
}
|
|
177
|
+
return { attr };
|
|
178
|
+
}
|
|
179
|
+
function getCustomElementTag(typescript, node, isDep = isDepElement(node)) {
|
|
180
|
+
if (!typescript.isClassDeclaration(node)) return;
|
|
181
|
+
for (const modifier of node.modifiers || []) {
|
|
182
|
+
if (typescript.isDecorator(modifier) && typescript.isCallExpression(modifier.expression) && modifier.expression.expression.getText() === "customElement") {
|
|
183
|
+
const arg = modifier.expression.arguments.at(0);
|
|
184
|
+
if (arg && typescript.isStringLiteral(arg)) {
|
|
185
|
+
return arg.text;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
if (isDep && node.name && typescript.isIdentifier(node.name)) {
|
|
190
|
+
const name = node.name.text;
|
|
191
|
+
if (name.endsWith("Element")) {
|
|
192
|
+
return name;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
function getDocComment(typescript, declaration) {
|
|
197
|
+
const fullText = declaration.getSourceFile().getFullText();
|
|
198
|
+
const commentRanges = typescript.getLeadingCommentRanges(fullText, declaration.getFullStart());
|
|
199
|
+
const commentStrings = commentRanges?.filter(({ kind }) => kind === typescript.SyntaxKind.MultiLineCommentTrivia).map(({ pos, end }) => fullText.slice(pos, end));
|
|
200
|
+
return commentStrings?.join("\n");
|
|
201
|
+
}
|
|
202
|
+
function getAstNodeAtPosition(typescript, node, pos) {
|
|
203
|
+
if (node.pos > pos || node.end <= pos) return;
|
|
204
|
+
while (node.kind >= typescript.SyntaxKind.FirstNode) {
|
|
205
|
+
const nested = typescript.forEachChild(node, (child) => child.pos <= pos && child.end > pos ? child : void 0);
|
|
206
|
+
if (nested === void 0) break;
|
|
207
|
+
node = nested;
|
|
208
|
+
}
|
|
209
|
+
return node;
|
|
210
|
+
}
|
|
211
|
+
var marker = Symbol();
|
|
212
|
+
function decorate(origin, cb) {
|
|
213
|
+
if (origin[marker]) return origin;
|
|
214
|
+
const result = cb(origin);
|
|
215
|
+
result[marker] = true;
|
|
216
|
+
return result;
|
|
217
|
+
}
|
|
218
|
+
function createVirtualDocument(languageId, content) {
|
|
219
|
+
return import_vscode_languageserver_textdocument.TextDocument.create(`embedded://document.${languageId}`, languageId, 1, content);
|
|
220
|
+
}
|
|
221
|
+
function getSubstitution(templateString, start, end) {
|
|
222
|
+
return templateString.slice(start, end).replaceAll(/[^\n]/g, "_");
|
|
223
|
+
}
|
|
224
|
+
function isValidCSSTemplate(typescript, node, callName) {
|
|
225
|
+
switch (node.kind) {
|
|
226
|
+
case typescript.SyntaxKind.NoSubstitutionTemplateLiteral:
|
|
227
|
+
case typescript.SyntaxKind.TemplateExpression:
|
|
228
|
+
const parent = node.parent;
|
|
229
|
+
if (typescript.isCallExpression(parent) && parent.expression.getText() === callName) {
|
|
230
|
+
return true;
|
|
231
|
+
}
|
|
232
|
+
if (typescript.isPropertyAssignment(parent)) {
|
|
233
|
+
const call = parent.parent.parent;
|
|
234
|
+
if (typescript.isCallExpression(call) && call.expression.getText() === callName) {
|
|
235
|
+
return true;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
return false;
|
|
239
|
+
default:
|
|
240
|
+
return false;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
function translateCompletionItemsToCompletionInfo(context, items) {
|
|
244
|
+
return {
|
|
245
|
+
defaultCommitCharacters: [],
|
|
246
|
+
isGlobalCompletion: false,
|
|
247
|
+
isMemberCompletion: false,
|
|
248
|
+
isNewIdentifierLocation: false,
|
|
249
|
+
entries: items.items.map((x) => translateCompletionEntry(context, x))
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
function translateCompletionEntry(context, vsItem) {
|
|
253
|
+
const entry = {
|
|
254
|
+
name: vsItem.label,
|
|
255
|
+
kind: translationCompletionItemKind(context, vsItem.kind),
|
|
256
|
+
sortText: "0",
|
|
257
|
+
filterText: vsItem.label,
|
|
258
|
+
labelDetails: { description: vsItem.detail }
|
|
259
|
+
};
|
|
260
|
+
if (vsItem.textEdit) {
|
|
261
|
+
entry.isSnippet = vsItem.insertTextFormat === vscode.InsertTextFormat.Snippet || void 0;
|
|
262
|
+
entry.insertText = vsItem.textEdit.newText;
|
|
263
|
+
entry.replacementSpan = "range" in vsItem.textEdit ? toTsSpan(context, vsItem.textEdit.range) : void 0;
|
|
264
|
+
}
|
|
265
|
+
return entry;
|
|
266
|
+
}
|
|
267
|
+
function translationCompletionItemKind(context, kind) {
|
|
268
|
+
const typescript = context.typescript;
|
|
269
|
+
switch (kind) {
|
|
270
|
+
case vscode.CompletionItemKind.Method:
|
|
271
|
+
return typescript.ScriptElementKind.memberFunctionElement;
|
|
272
|
+
case vscode.CompletionItemKind.Function:
|
|
273
|
+
return typescript.ScriptElementKind.functionElement;
|
|
274
|
+
case vscode.CompletionItemKind.Constructor:
|
|
275
|
+
return typescript.ScriptElementKind.constructorImplementationElement;
|
|
276
|
+
case vscode.CompletionItemKind.Field:
|
|
277
|
+
case vscode.CompletionItemKind.Variable:
|
|
278
|
+
return typescript.ScriptElementKind.variableElement;
|
|
279
|
+
case vscode.CompletionItemKind.Class:
|
|
280
|
+
return typescript.ScriptElementKind.classElement;
|
|
281
|
+
case vscode.CompletionItemKind.Interface:
|
|
282
|
+
return typescript.ScriptElementKind.interfaceElement;
|
|
283
|
+
case vscode.CompletionItemKind.Module:
|
|
284
|
+
return typescript.ScriptElementKind.moduleElement;
|
|
285
|
+
case vscode.CompletionItemKind.Property:
|
|
286
|
+
return typescript.ScriptElementKind.memberVariableElement;
|
|
287
|
+
case vscode.CompletionItemKind.Unit:
|
|
288
|
+
case vscode.CompletionItemKind.Value:
|
|
289
|
+
return typescript.ScriptElementKind.constElement;
|
|
290
|
+
case vscode.CompletionItemKind.Enum:
|
|
291
|
+
return typescript.ScriptElementKind.enumElement;
|
|
292
|
+
case vscode.CompletionItemKind.Keyword:
|
|
293
|
+
return typescript.ScriptElementKind.keyword;
|
|
294
|
+
case vscode.CompletionItemKind.Color:
|
|
295
|
+
return typescript.ScriptElementKind.constElement;
|
|
296
|
+
case vscode.CompletionItemKind.Reference:
|
|
297
|
+
return typescript.ScriptElementKind.alias;
|
|
298
|
+
case vscode.CompletionItemKind.File:
|
|
299
|
+
return typescript.ScriptElementKind.moduleElement;
|
|
300
|
+
case vscode.CompletionItemKind.Snippet:
|
|
301
|
+
case vscode.CompletionItemKind.Text:
|
|
302
|
+
default:
|
|
303
|
+
return typescript.ScriptElementKind.unknown;
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
function toTsSpan(context, range) {
|
|
307
|
+
const editStart = context.toOffset(range.start);
|
|
308
|
+
const editEnd = context.toOffset(range.end);
|
|
309
|
+
return {
|
|
310
|
+
start: editStart,
|
|
311
|
+
length: editEnd - editStart
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
function translateHover(context, hover, position, offset = 0) {
|
|
315
|
+
const typescript = context.typescript;
|
|
316
|
+
const header = [];
|
|
317
|
+
const docs = [];
|
|
318
|
+
const convertPart = (hoverContents) => {
|
|
319
|
+
if (typeof hoverContents === "string") {
|
|
320
|
+
docs.push({ kind: "unknown", text: hoverContents });
|
|
321
|
+
} else if (Array.isArray(hoverContents)) {
|
|
322
|
+
hoverContents.forEach(convertPart);
|
|
323
|
+
} else if ("language" in hoverContents && hoverContents.language === "html") {
|
|
324
|
+
header.push({ kind: "unknown", text: hoverContents.value });
|
|
325
|
+
} else {
|
|
326
|
+
docs.push({ kind: "unknown", text: hoverContents.value });
|
|
327
|
+
}
|
|
328
|
+
};
|
|
329
|
+
convertPart(hover.contents);
|
|
330
|
+
const start = context.toOffset(hover.range ? hover.range.start : position);
|
|
331
|
+
return {
|
|
332
|
+
kind: typescript.ScriptElementKind.string,
|
|
333
|
+
kindModifiers: "",
|
|
334
|
+
textSpan: {
|
|
335
|
+
start: start - offset,
|
|
336
|
+
length: hover.range ? context.toOffset(hover.range.end) - start : 1
|
|
337
|
+
},
|
|
338
|
+
displayParts: header,
|
|
339
|
+
documentation: docs,
|
|
340
|
+
tags: []
|
|
341
|
+
};
|
|
342
|
+
}
|
|
343
|
+
function translateCompletionItemsToCompletionEntryDetails(context, item) {
|
|
344
|
+
return {
|
|
345
|
+
name: item.label,
|
|
346
|
+
kindModifiers: "declare",
|
|
347
|
+
kind: item.kind ? translationCompletionItemKind(context, item.kind) : context.typescript.ScriptElementKind.unknown,
|
|
348
|
+
displayParts: toDisplayParts(item.detail),
|
|
349
|
+
documentation: toDisplayParts(item.documentation, true),
|
|
350
|
+
tags: []
|
|
351
|
+
};
|
|
352
|
+
}
|
|
353
|
+
function genDefaultCompletionEntryDetails(context, name) {
|
|
354
|
+
return {
|
|
355
|
+
name,
|
|
356
|
+
kindModifiers: "",
|
|
357
|
+
kind: context.typescript.ScriptElementKind.unknown,
|
|
358
|
+
displayParts: toDisplayParts(name),
|
|
359
|
+
documentation: [],
|
|
360
|
+
tags: []
|
|
361
|
+
};
|
|
362
|
+
}
|
|
363
|
+
function toDisplayParts(text, isDoc = false) {
|
|
364
|
+
if (!text) return [];
|
|
365
|
+
const escape = (unsafe) => unsafe.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'").replaceAll(" ", " ").replaceAll("\n", " \n").replaceAll(" ", " ");
|
|
366
|
+
return [
|
|
367
|
+
{
|
|
368
|
+
kind: "unknown",
|
|
369
|
+
text: typeof text !== "string" ? text.value : isDoc ? escape(text) : text
|
|
370
|
+
}
|
|
371
|
+
];
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
// ../duoyun-ui/lib/cache.js
|
|
375
|
+
var Cache = class {
|
|
376
|
+
#max;
|
|
377
|
+
#maxAge;
|
|
378
|
+
#renewal;
|
|
379
|
+
#map = /* @__PURE__ */ new Map();
|
|
380
|
+
#reverseMap = /* @__PURE__ */ new Map();
|
|
381
|
+
#linkedList = new LinkedList();
|
|
382
|
+
constructor({ max = Infinity, maxAge = Infinity, renewal = false } = {}) {
|
|
383
|
+
this.#max = max;
|
|
384
|
+
this.#maxAge = maxAge;
|
|
385
|
+
this.#renewal = renewal;
|
|
386
|
+
}
|
|
387
|
+
#trim() {
|
|
388
|
+
for (let i = this.#linkedList.size - this.#max; i > 0; i--) {
|
|
389
|
+
const value = this.#linkedList.get();
|
|
390
|
+
const key = this.#reverseMap.get(value);
|
|
391
|
+
this.#reverseMap.delete(value);
|
|
392
|
+
this.#map.delete(key);
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
set(key, value) {
|
|
396
|
+
this.#linkedList.add(value);
|
|
397
|
+
this.#reverseMap.set(value, key);
|
|
398
|
+
this.#map.set(key, { value, timestamp: Date.now() });
|
|
399
|
+
this.#trim();
|
|
400
|
+
return value;
|
|
401
|
+
}
|
|
402
|
+
get(key, init) {
|
|
403
|
+
const cache = this.#map.get(key);
|
|
404
|
+
if (!cache) {
|
|
405
|
+
return init && this.set(key, init(key));
|
|
406
|
+
}
|
|
407
|
+
const { timestamp, value } = cache;
|
|
408
|
+
if (Date.now() - timestamp > this.#maxAge) {
|
|
409
|
+
this.#linkedList.delete(value);
|
|
410
|
+
this.#reverseMap.delete(value);
|
|
411
|
+
this.#map.delete(key);
|
|
412
|
+
return init && this.set(key, init(key));
|
|
413
|
+
}
|
|
414
|
+
if (this.#renewal) {
|
|
415
|
+
cache.timestamp = Date.now();
|
|
416
|
+
}
|
|
417
|
+
this.#linkedList.get();
|
|
418
|
+
this.#linkedList.add(value);
|
|
419
|
+
return value;
|
|
420
|
+
}
|
|
421
|
+
};
|
|
422
|
+
|
|
423
|
+
// src/cache.ts
|
|
424
|
+
var LRUCache = class {
|
|
425
|
+
#bucket = new Cache({ max: 100, renewal: true });
|
|
426
|
+
#genKey(context, position) {
|
|
427
|
+
return [context.fileName, position?.line, position?.character, context.text].join(";");
|
|
428
|
+
}
|
|
429
|
+
getCached(context, position) {
|
|
430
|
+
return this.#bucket.get(this.#genKey(context, position));
|
|
431
|
+
}
|
|
432
|
+
updateCached(context, posOrContent, contentOrUndefined) {
|
|
433
|
+
let position;
|
|
434
|
+
let content;
|
|
435
|
+
if ("line" in posOrContent && "character" in posOrContent) {
|
|
436
|
+
position = posOrContent;
|
|
437
|
+
content = contentOrUndefined;
|
|
438
|
+
} else {
|
|
439
|
+
position = void 0;
|
|
440
|
+
content = posOrContent;
|
|
441
|
+
}
|
|
442
|
+
return this.#bucket.set(this.#genKey(context, position), content);
|
|
443
|
+
}
|
|
444
|
+
};
|
|
445
|
+
|
|
446
|
+
// src/decorate-html.ts
|
|
447
|
+
var HTMLLanguageService = class {
|
|
448
|
+
#completionsCache = new LRUCache();
|
|
449
|
+
#diagnosticsCache = new LRUCache();
|
|
450
|
+
#cssLanguageService;
|
|
451
|
+
#htmlLanguageService;
|
|
452
|
+
#ctx;
|
|
453
|
+
constructor(ctx) {
|
|
454
|
+
this.#ctx = ctx;
|
|
455
|
+
const ts = ctx.ts;
|
|
456
|
+
const htmlDataProvider = (0, import_vscode_html_languageservice.getDefaultHTMLDataProvider)();
|
|
457
|
+
const provideTags = htmlDataProvider.provideTags.bind(htmlDataProvider);
|
|
458
|
+
htmlDataProvider.provideTags = () => {
|
|
459
|
+
const result = [...ctx.elements].map(([tag, node]) => ({
|
|
460
|
+
name: tag,
|
|
461
|
+
attributes: [],
|
|
462
|
+
description: getDocComment(ts, node)
|
|
463
|
+
})).filter(isNotNullish);
|
|
464
|
+
return [...result, ...provideTags()];
|
|
465
|
+
};
|
|
466
|
+
const provideAttributes = htmlDataProvider.provideAttributes.bind(htmlDataProvider);
|
|
467
|
+
htmlDataProvider.provideAttributes = (tag) => {
|
|
468
|
+
const typeChecker = ctx.getProgram().getTypeChecker();
|
|
469
|
+
const node = ctx.elements.get(tag);
|
|
470
|
+
const isDep = node && isDepElement(node);
|
|
471
|
+
const props = node && typeChecker.getTypeAtLocation(node).getApparentProperties();
|
|
472
|
+
const result = (props || []).map((e) => {
|
|
473
|
+
const declaration = e.getDeclarations()?.at(0);
|
|
474
|
+
const prop = declaration && ts.isPropertyDeclaration(declaration);
|
|
475
|
+
if (!prop) return;
|
|
476
|
+
const hasPropDecorator = declaration.modifiers?.some(
|
|
477
|
+
(m) => ts.isDecorator(m) && ts.isIdentifier(m.expression)
|
|
478
|
+
);
|
|
479
|
+
if (!hasPropDecorator && !isDep) return;
|
|
480
|
+
const type = declaration.type && typeChecker.getTypeFromTypeNode(declaration.type);
|
|
481
|
+
return {
|
|
482
|
+
name: type === typeChecker.getBooleanType() ? e.name : `.${e.name}`,
|
|
483
|
+
description: getDocComment(ts, declaration),
|
|
484
|
+
valueSet: "v"
|
|
485
|
+
};
|
|
486
|
+
}).filter(isNotNullish);
|
|
487
|
+
return [...result, ...provideAttributes(isCustomElement(tag) ? "div" : tag)];
|
|
488
|
+
};
|
|
489
|
+
this.#htmlLanguageService = (0, import_vscode_html_languageservice.getLanguageService)({ customDataProviders: [htmlDataProvider] });
|
|
490
|
+
this.#cssLanguageService = (0, import_vscode_css_languageservice.getCSSLanguageService)({ useDefaultDataProvider: true });
|
|
491
|
+
}
|
|
492
|
+
#getAllCssDoc(doc) {
|
|
493
|
+
const styles = [];
|
|
494
|
+
const nodes = [...doc.roots];
|
|
495
|
+
while (true) {
|
|
496
|
+
const node = nodes.pop();
|
|
497
|
+
if (!node) break;
|
|
498
|
+
if (node.tag === "style") styles.push(node);
|
|
499
|
+
nodes.push(...node.children);
|
|
500
|
+
}
|
|
501
|
+
return styles;
|
|
502
|
+
}
|
|
503
|
+
#getCssDoc(context, position, doc) {
|
|
504
|
+
const node = doc.findNodeAt(context.toOffset(position));
|
|
505
|
+
if (node.tag !== "style") return;
|
|
506
|
+
const virtualDocument = createVirtualDocument("css", context.text.slice(node.startTagEnd, node.endTagStart));
|
|
507
|
+
const style = this.#cssLanguageService.parseStylesheet(virtualDocument);
|
|
508
|
+
const offset = context.toOffset(position) - node.startTagEnd;
|
|
509
|
+
const toPosition = (pos) => context.toPosition(virtualDocument.offsetAt(pos) + node.startTagEnd);
|
|
510
|
+
return {
|
|
511
|
+
style,
|
|
512
|
+
virtualDocument,
|
|
513
|
+
position: virtualDocument.positionAt(offset),
|
|
514
|
+
updateRange: (range) => ({
|
|
515
|
+
start: toPosition(range.start),
|
|
516
|
+
end: toPosition(range.end)
|
|
517
|
+
})
|
|
518
|
+
};
|
|
519
|
+
}
|
|
520
|
+
#getCSSCompletionsAtPosition(context, position, doc) {
|
|
521
|
+
const css = this.#getCssDoc(context, position, doc);
|
|
522
|
+
if (!css) return [];
|
|
523
|
+
let emmetResults;
|
|
524
|
+
this.#cssLanguageService.setCompletionParticipants([
|
|
525
|
+
{
|
|
526
|
+
onCssProperty: () => {
|
|
527
|
+
emmetResults = (0, import_emmet_helper.doComplete)(css.virtualDocument, css.position, "css", this.#ctx.config.emmet);
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
]);
|
|
531
|
+
const completions = this.#cssLanguageService.doComplete(css.virtualDocument, css.position, css.style);
|
|
532
|
+
completions.items.push(...emmetResults?.items || []);
|
|
533
|
+
return completions.items.map((e) => ({
|
|
534
|
+
...e,
|
|
535
|
+
textEdit: e.textEdit && "range" in e.textEdit ? {
|
|
536
|
+
newText: e.textEdit.newText,
|
|
537
|
+
range: css.updateRange(e.textEdit.range)
|
|
538
|
+
} : e.textEdit
|
|
539
|
+
}));
|
|
540
|
+
}
|
|
541
|
+
#getCompletionsAtPosition(context, position) {
|
|
542
|
+
const cached = this.#completionsCache.getCached(context, position);
|
|
543
|
+
if (cached) return cached;
|
|
544
|
+
const virtualDocument = createVirtualDocument("html", context.text);
|
|
545
|
+
const vHtml = this.#htmlLanguageService.parseHTMLDocument(virtualDocument);
|
|
546
|
+
let emmetResults;
|
|
547
|
+
this.#htmlLanguageService.setCompletionParticipants([
|
|
548
|
+
{
|
|
549
|
+
onHtmlContent: () => {
|
|
550
|
+
emmetResults = (0, import_emmet_helper.doComplete)(virtualDocument, position, "html", this.#ctx.config.emmet);
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
]);
|
|
554
|
+
const completions = this.#htmlLanguageService.doComplete(virtualDocument, position, vHtml);
|
|
555
|
+
completions.items.push(...emmetResults?.items || []);
|
|
556
|
+
completions.items.push(...this.#getCSSCompletionsAtPosition(context, position, vHtml));
|
|
557
|
+
return this.#completionsCache.updateCached(context, position, completions);
|
|
558
|
+
}
|
|
559
|
+
getCompletionsAtPosition(context, position) {
|
|
560
|
+
return translateCompletionItemsToCompletionInfo(context, this.#getCompletionsAtPosition(context, position));
|
|
561
|
+
}
|
|
562
|
+
getCompletionEntryDetails(context, position, name) {
|
|
563
|
+
const completions = this.#getCompletionsAtPosition(context, position);
|
|
564
|
+
const item = completions.items.find((e) => e.label === name);
|
|
565
|
+
if (!item) return genDefaultCompletionEntryDetails(context, name);
|
|
566
|
+
return translateCompletionItemsToCompletionEntryDetails(context, item);
|
|
567
|
+
}
|
|
568
|
+
#getCSSQuickInfoAtPosition(context, position, doc) {
|
|
569
|
+
const css = this.#getCssDoc(context, position, doc);
|
|
570
|
+
const hover = css && this.#cssLanguageService.doHover(css.virtualDocument, css.position, css.style, {
|
|
571
|
+
documentation: true,
|
|
572
|
+
references: true
|
|
573
|
+
});
|
|
574
|
+
return hover && {
|
|
575
|
+
...hover,
|
|
576
|
+
range: hover.range && css.updateRange(hover.range)
|
|
577
|
+
};
|
|
578
|
+
}
|
|
579
|
+
getQuickInfoAtPosition(context, position) {
|
|
580
|
+
const virtualDocument = createVirtualDocument("html", context.text);
|
|
581
|
+
const vHtml = this.#htmlLanguageService.parseHTMLDocument(virtualDocument);
|
|
582
|
+
const hover = this.#htmlLanguageService.doHover(virtualDocument, position, vHtml, {
|
|
583
|
+
documentation: true,
|
|
584
|
+
references: true
|
|
585
|
+
}) || this.#getCSSQuickInfoAtPosition(context, position, vHtml);
|
|
586
|
+
if (!hover) return;
|
|
587
|
+
return translateHover(context, hover, position);
|
|
588
|
+
}
|
|
589
|
+
getSyntacticDiagnostics(context) {
|
|
590
|
+
const cached = this.#diagnosticsCache.getCached(context);
|
|
591
|
+
if (cached) return cached;
|
|
592
|
+
const virtualDocument = createVirtualDocument("html", context.text);
|
|
593
|
+
const vHtml = this.#htmlLanguageService.parseHTMLDocument(virtualDocument);
|
|
594
|
+
const styles = this.#getAllCssDoc(vHtml);
|
|
595
|
+
const file = this.#ctx.getProgram().getSourceFile(context.fileName);
|
|
596
|
+
return this.#diagnosticsCache.updateCached(
|
|
597
|
+
context,
|
|
598
|
+
styles.map((node) => {
|
|
599
|
+
const textDocument = createVirtualDocument("css", context.text.slice(node.startTagEnd, node.endTagStart));
|
|
600
|
+
const vCss = this.#cssLanguageService.parseStylesheet(textDocument);
|
|
601
|
+
const oDiagnostics = this.#cssLanguageService.doValidation(textDocument, vCss);
|
|
602
|
+
return oDiagnostics.map(({ message, range }) => {
|
|
603
|
+
const start = node.startTagEnd + textDocument.offsetAt(range.start);
|
|
604
|
+
const end = node.startTagEnd + textDocument.offsetAt(range.end);
|
|
605
|
+
return {
|
|
606
|
+
category: context.typescript.DiagnosticCategory.Warning,
|
|
607
|
+
code: 0,
|
|
608
|
+
file,
|
|
609
|
+
start,
|
|
610
|
+
length: end - start,
|
|
611
|
+
messageText: message
|
|
612
|
+
};
|
|
613
|
+
});
|
|
614
|
+
}).flat()
|
|
615
|
+
);
|
|
616
|
+
}
|
|
617
|
+
getDefinitionAndBoundSpan(context, position) {
|
|
618
|
+
const htmlOffset = context.node.pos + 1;
|
|
619
|
+
const virtualDocument = createVirtualDocument("html", context.text);
|
|
620
|
+
const vHtml = this.#htmlLanguageService.parseHTMLDocument(virtualDocument);
|
|
621
|
+
const offset = context.toOffset(position);
|
|
622
|
+
const node = vHtml.findNodeAt(offset);
|
|
623
|
+
const { text, start, length } = getHTMLTextAtPosition(context.text, offset);
|
|
624
|
+
const definitionNode = this.#ctx.elements.get(node.tag);
|
|
625
|
+
if (!definitionNode || node.tag === "style" || offset > node.startTagEnd || !text) {
|
|
626
|
+
return { textSpan: { start, length } };
|
|
627
|
+
}
|
|
628
|
+
if (text === node.tag) {
|
|
629
|
+
return {
|
|
630
|
+
textSpan: { start, length },
|
|
631
|
+
definitions: [
|
|
632
|
+
{
|
|
633
|
+
containerName: "Custom Element",
|
|
634
|
+
containerKind: context.typescript.ScriptElementKind.unknown,
|
|
635
|
+
name: definitionNode.name.text,
|
|
636
|
+
kind: context.typescript.ScriptElementKind.classElement,
|
|
637
|
+
fileName: definitionNode.getSourceFile().fileName,
|
|
638
|
+
textSpan: {
|
|
639
|
+
start: definitionNode.name.getStart() - htmlOffset,
|
|
640
|
+
length: definitionNode.name.text.length
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
]
|
|
644
|
+
};
|
|
645
|
+
}
|
|
646
|
+
const { attr, type } = getAttrName(text);
|
|
647
|
+
const typeChecker = this.#ctx.getProgram().getTypeChecker();
|
|
648
|
+
const propSymbol = typeChecker.getTypeAtLocation(definitionNode).getProperty(kebabToCamelCase(attr));
|
|
649
|
+
const propDeclaration = propSymbol?.getDeclarations()?.at(0);
|
|
650
|
+
return {
|
|
651
|
+
textSpan: { start: type ? start + 1 : start, length: attr.length },
|
|
652
|
+
definitions: !propDeclaration ? void 0 : [
|
|
653
|
+
{
|
|
654
|
+
containerName: "Attribute",
|
|
655
|
+
containerKind: context.typescript.ScriptElementKind.unknown,
|
|
656
|
+
name: propDeclaration.getText(),
|
|
657
|
+
kind: context.typescript.ScriptElementKind.memberVariableElement,
|
|
658
|
+
fileName: propDeclaration.getSourceFile().fileName,
|
|
659
|
+
textSpan: {
|
|
660
|
+
start: propDeclaration.getStart() - htmlOffset,
|
|
661
|
+
length: propDeclaration.getText().length
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
]
|
|
665
|
+
};
|
|
666
|
+
}
|
|
667
|
+
};
|
|
668
|
+
|
|
669
|
+
// src/decorate-ts.ts
|
|
670
|
+
function decorateTypeChecker(ctx, typeChecker) {
|
|
671
|
+
const fn = typeChecker.isValidPropertyAccessForCompletions.bind(typeChecker);
|
|
672
|
+
typeChecker.isValidPropertyAccessForCompletions = (...args) => {
|
|
673
|
+
const result = fn(...args);
|
|
674
|
+
if (!result) return false;
|
|
675
|
+
try {
|
|
676
|
+
const { declarations } = args.at(2);
|
|
677
|
+
if (!declarations) return true;
|
|
678
|
+
const isNever = declarations.every(
|
|
679
|
+
(node) => ctx.ts.isPropertySignature(node) && node.type?.getText() === "never"
|
|
680
|
+
);
|
|
681
|
+
return !isNever;
|
|
682
|
+
} catch {
|
|
683
|
+
return true;
|
|
684
|
+
}
|
|
685
|
+
};
|
|
686
|
+
return typeChecker;
|
|
687
|
+
}
|
|
688
|
+
function updateElement(ctx, file) {
|
|
689
|
+
const isDep = isDepElement(file);
|
|
690
|
+
ctx.ts.forEachChild(file, (node) => {
|
|
691
|
+
const tag = getCustomElementTag(ctx.ts, node, isDep);
|
|
692
|
+
if (tag && ctx.ts.isClassDeclaration(node)) {
|
|
693
|
+
ctx.elements.set(tag, node);
|
|
694
|
+
}
|
|
695
|
+
});
|
|
696
|
+
}
|
|
697
|
+
function decorateLanguageService(ctx, languageService) {
|
|
698
|
+
const { ts, getProgram } = ctx;
|
|
699
|
+
const ls = Object.fromEntries(
|
|
700
|
+
Object.entries(languageService).map(([key, value]) => [key, value.bind(languageService)])
|
|
701
|
+
);
|
|
702
|
+
languageService.getCompletionsAtPosition = (...args) => {
|
|
703
|
+
const program = getProgram();
|
|
704
|
+
const typeChecker = program.getTypeChecker();
|
|
705
|
+
decorate(typeChecker, () => decorateTypeChecker(ctx, typeChecker));
|
|
706
|
+
return ls.getCompletionsAtPosition(...args);
|
|
707
|
+
};
|
|
708
|
+
languageService.findReferences = (...args) => {
|
|
709
|
+
const program = getProgram();
|
|
710
|
+
const result = [];
|
|
711
|
+
const currentNode = getAstNodeAtPosition(ts, program.getSourceFile(args[0]), args[1]);
|
|
712
|
+
const currentTag = currentNode && getCustomElementTag(ts, currentNode.parent);
|
|
713
|
+
for (const file of program.getSourceFiles()) {
|
|
714
|
+
const references = [];
|
|
715
|
+
forEachTag(ts, file, ({ tag, length, start }) => {
|
|
716
|
+
if (tag !== currentTag) return;
|
|
717
|
+
references.push({
|
|
718
|
+
fileName: file.fileName,
|
|
719
|
+
isWriteAccess: true,
|
|
720
|
+
textSpan: { start, length }
|
|
721
|
+
});
|
|
722
|
+
});
|
|
723
|
+
if (!references.length) continue;
|
|
724
|
+
result.push({
|
|
725
|
+
references,
|
|
726
|
+
definition: {
|
|
727
|
+
containerKind: ctx.ts.ScriptElementKind.unknown,
|
|
728
|
+
containerName: "",
|
|
729
|
+
displayParts: [],
|
|
730
|
+
fileName: file.fileName,
|
|
731
|
+
textSpan: { start: 0, length: 0 },
|
|
732
|
+
name: "test",
|
|
733
|
+
kind: ctx.ts.ScriptElementKind.unknown
|
|
734
|
+
}
|
|
735
|
+
});
|
|
736
|
+
}
|
|
737
|
+
return [...result, ...ls.findReferences(...args) || []];
|
|
738
|
+
};
|
|
739
|
+
languageService.getSuggestionDiagnostics = (...args) => {
|
|
740
|
+
const program = getProgram();
|
|
741
|
+
decorate(program, () => {
|
|
742
|
+
program.getSourceFiles().forEach((file2) => updateElement(ctx, file2));
|
|
743
|
+
return program;
|
|
744
|
+
});
|
|
745
|
+
const file = program.getSourceFile(args[0]);
|
|
746
|
+
const result = ls.getSuggestionDiagnostics(...args);
|
|
747
|
+
updateElement(ctx, file);
|
|
748
|
+
return result.filter(({ start, reportsUnnecessary, category }) => {
|
|
749
|
+
if (!reportsUnnecessary || category !== ts.DiagnosticCategory.Suggestion) return true;
|
|
750
|
+
const node = getAstNodeAtPosition(ts, file, start);
|
|
751
|
+
if (!node || !ts.isPrivateIdentifier(node)) return true;
|
|
752
|
+
const declaration = node.parent;
|
|
753
|
+
if (!ts.isMethodDeclaration(declaration) && !ts.isPropertyDeclaration(declaration)) return true;
|
|
754
|
+
return !declaration.modifiers?.some((e) => e?.kind === ts.SyntaxKind.Decorator);
|
|
755
|
+
});
|
|
756
|
+
};
|
|
757
|
+
return languageService;
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
// src/configuration.ts
|
|
761
|
+
var defaultConfiguration = {
|
|
762
|
+
emmet: {},
|
|
763
|
+
tags: ["html", "raw"],
|
|
764
|
+
format: {
|
|
765
|
+
enabled: true
|
|
766
|
+
}
|
|
767
|
+
};
|
|
768
|
+
var Configuration = class {
|
|
769
|
+
#emmet = defaultConfiguration.emmet;
|
|
770
|
+
#format = defaultConfiguration.format;
|
|
771
|
+
#tags = defaultConfiguration.tags;
|
|
772
|
+
update(config) {
|
|
773
|
+
this.#format = Object.assign({}, defaultConfiguration.format, config.format || {});
|
|
774
|
+
this.#tags = config.tags || defaultConfiguration.tags;
|
|
775
|
+
}
|
|
776
|
+
get emmet() {
|
|
777
|
+
return this.#emmet;
|
|
778
|
+
}
|
|
779
|
+
get format() {
|
|
780
|
+
return this.#format;
|
|
781
|
+
}
|
|
782
|
+
get tags() {
|
|
783
|
+
return this.#tags;
|
|
784
|
+
}
|
|
785
|
+
};
|
|
786
|
+
var Store = class {
|
|
787
|
+
#map = /* @__PURE__ */ new Map();
|
|
788
|
+
#weakMap = /* @__PURE__ */ new WeakMap();
|
|
789
|
+
#registry = new FinalizationRegistry((key) => this.#map.delete(key));
|
|
790
|
+
set(key, val) {
|
|
791
|
+
this.#map.set(key, new WeakRef(val));
|
|
792
|
+
this.#weakMap.set(val, key);
|
|
793
|
+
this.#registry.register(val, key);
|
|
794
|
+
}
|
|
795
|
+
get(key) {
|
|
796
|
+
return this.#map.get(key)?.deref();
|
|
797
|
+
}
|
|
798
|
+
findKey(val) {
|
|
799
|
+
return this.#weakMap.get(val);
|
|
800
|
+
}
|
|
801
|
+
*[Symbol.iterator]() {
|
|
802
|
+
const entries2 = this.#map.entries();
|
|
803
|
+
for (const [tag, ref] of entries2) {
|
|
804
|
+
yield [tag, ref.deref()];
|
|
805
|
+
}
|
|
806
|
+
}
|
|
807
|
+
};
|
|
808
|
+
|
|
809
|
+
// src/decorate-css.ts
|
|
810
|
+
var import_vscode_css_languageservice2 = require("vscode-css-languageservice");
|
|
811
|
+
var import_emmet_helper2 = require("@vscode/emmet-helper");
|
|
812
|
+
var CSSLanguageService = class {
|
|
813
|
+
#completionsCache = new LRUCache();
|
|
814
|
+
#diagnosticsCache = new LRUCache();
|
|
815
|
+
#cssLanguageService = (0, import_vscode_css_languageservice2.getCSSLanguageService)();
|
|
816
|
+
#ctx;
|
|
817
|
+
constructor(ctx) {
|
|
818
|
+
this.#ctx = ctx;
|
|
819
|
+
}
|
|
820
|
+
#normalize = (context, position) => {
|
|
821
|
+
const parent = context.node.parent;
|
|
822
|
+
const tag = context.typescript.isTaggedTemplateExpression(parent) && parent.tag.getText();
|
|
823
|
+
if (context.typescript.isPropertyAssignment(parent) || tag === "styled") {
|
|
824
|
+
const appendBefore = ".parent { ";
|
|
825
|
+
const appendAfter = " }";
|
|
826
|
+
return {
|
|
827
|
+
offset: appendBefore.length,
|
|
828
|
+
text: `${appendBefore}${context.text}${appendAfter}`,
|
|
829
|
+
pos: {
|
|
830
|
+
line: position.line,
|
|
831
|
+
character: position.line === 0 ? position.character + appendBefore.length : position.character
|
|
832
|
+
}
|
|
833
|
+
};
|
|
834
|
+
}
|
|
835
|
+
return {
|
|
836
|
+
offset: 0,
|
|
837
|
+
text: context.text,
|
|
838
|
+
pos: { ...position }
|
|
839
|
+
};
|
|
840
|
+
};
|
|
841
|
+
#getCompletionsAtPosition(context, position) {
|
|
842
|
+
const cached = this.#completionsCache.getCached(context, position);
|
|
843
|
+
if (cached) return cached;
|
|
844
|
+
const { text, pos } = this.#normalize(context, position);
|
|
845
|
+
const virtualDocument = createVirtualDocument("css", text);
|
|
846
|
+
const vCss = this.#cssLanguageService.parseStylesheet(virtualDocument);
|
|
847
|
+
let emmetResults;
|
|
848
|
+
this.#cssLanguageService.setCompletionParticipants([
|
|
849
|
+
{
|
|
850
|
+
onCssProperty: () => {
|
|
851
|
+
emmetResults = (0, import_emmet_helper2.doComplete)(virtualDocument, pos, "css", this.#ctx.config.emmet);
|
|
852
|
+
}
|
|
853
|
+
}
|
|
854
|
+
]);
|
|
855
|
+
const completions = this.#cssLanguageService.doComplete(virtualDocument, pos, vCss);
|
|
856
|
+
completions.items.push(...emmetResults?.items || []);
|
|
857
|
+
return this.#completionsCache.updateCached(context, position, completions);
|
|
858
|
+
}
|
|
859
|
+
getCompletionsAtPosition(context, position) {
|
|
860
|
+
return translateCompletionItemsToCompletionInfo(context, this.#getCompletionsAtPosition(context, position));
|
|
861
|
+
}
|
|
862
|
+
getCompletionEntryDetails(context, position, name) {
|
|
863
|
+
const completions = this.#getCompletionsAtPosition(context, position);
|
|
864
|
+
const item = completions.items.find((e) => e.label === name);
|
|
865
|
+
if (!item) return genDefaultCompletionEntryDetails(context, name);
|
|
866
|
+
return translateCompletionItemsToCompletionEntryDetails(context, item);
|
|
867
|
+
}
|
|
868
|
+
getQuickInfoAtPosition(context, position) {
|
|
869
|
+
const { text, pos } = this.#normalize(context, position);
|
|
870
|
+
const virtualDocument = createVirtualDocument("css", text);
|
|
871
|
+
const vCss = this.#cssLanguageService.parseStylesheet(virtualDocument);
|
|
872
|
+
const hover = this.#cssLanguageService.doHover(virtualDocument, pos, vCss, {
|
|
873
|
+
documentation: true,
|
|
874
|
+
references: true
|
|
875
|
+
});
|
|
876
|
+
if (!hover) return;
|
|
877
|
+
return translateHover(context, hover, position, pos.character - position.character);
|
|
878
|
+
}
|
|
879
|
+
getSyntacticDiagnostics(context) {
|
|
880
|
+
const cached = this.#diagnosticsCache.getCached(context);
|
|
881
|
+
if (cached) return cached;
|
|
882
|
+
const { text, offset } = this.#normalize(context, { line: 0, character: 0 });
|
|
883
|
+
const virtualDocument = createVirtualDocument("css", text);
|
|
884
|
+
const vCss = this.#cssLanguageService.parseStylesheet(virtualDocument);
|
|
885
|
+
const oDiagnostics = this.#cssLanguageService.doValidation(virtualDocument, vCss);
|
|
886
|
+
const file = this.#ctx.getProgram().getSourceFile(context.fileName);
|
|
887
|
+
return this.#diagnosticsCache.updateCached(
|
|
888
|
+
context,
|
|
889
|
+
oDiagnostics.map(({ message, range }) => {
|
|
890
|
+
const start = context.toOffset(range.start);
|
|
891
|
+
return {
|
|
892
|
+
category: context.typescript.DiagnosticCategory.Warning,
|
|
893
|
+
code: 0,
|
|
894
|
+
file,
|
|
895
|
+
start: range.start.line === 0 ? start - offset : start,
|
|
896
|
+
length: context.toOffset(range.end) - start,
|
|
897
|
+
messageText: message
|
|
898
|
+
};
|
|
899
|
+
})
|
|
900
|
+
);
|
|
901
|
+
}
|
|
902
|
+
};
|
|
903
|
+
|
|
904
|
+
// src/index.ts
|
|
905
|
+
var LanguageServiceLogger = class {
|
|
906
|
+
#info;
|
|
907
|
+
constructor(info) {
|
|
908
|
+
this.#info = info;
|
|
909
|
+
}
|
|
910
|
+
log(msg) {
|
|
911
|
+
this.#info.project.projectService.logger.info(`[gem] ${msg}`);
|
|
912
|
+
}
|
|
913
|
+
};
|
|
914
|
+
var HtmlPlugin = class {
|
|
915
|
+
#ts;
|
|
916
|
+
#config = new Configuration();
|
|
917
|
+
constructor(typescript) {
|
|
918
|
+
this.#ts = typescript;
|
|
919
|
+
}
|
|
920
|
+
create(info) {
|
|
921
|
+
return decorate(info.languageService, () => {
|
|
922
|
+
const logger = new LanguageServiceLogger(info);
|
|
923
|
+
logger.log("Starting ts-gem-plugin...");
|
|
924
|
+
this.#config.update(info.config);
|
|
925
|
+
const context = {
|
|
926
|
+
config: this.#config,
|
|
927
|
+
ts: this.#ts,
|
|
928
|
+
logger,
|
|
929
|
+
elements: new Store(),
|
|
930
|
+
getProgram: () => {
|
|
931
|
+
return info.languageService.getProgram();
|
|
932
|
+
},
|
|
933
|
+
getProject: () => {
|
|
934
|
+
return info.project;
|
|
935
|
+
}
|
|
936
|
+
};
|
|
937
|
+
const decoratedService = decorateLanguageService(context, info.languageService);
|
|
938
|
+
const decoratedService1 = (0, import_typescript_template_language_service_decorator.decorateWithTemplateLanguageService)(
|
|
939
|
+
this.#ts,
|
|
940
|
+
decoratedService,
|
|
941
|
+
info.project,
|
|
942
|
+
new CSSLanguageService(context),
|
|
943
|
+
{
|
|
944
|
+
tags: ["styled", "css"],
|
|
945
|
+
enableForStringWithSubstitutions: true,
|
|
946
|
+
getSubstitution,
|
|
947
|
+
isValidTemplate: (node) => isValidCSSTemplate(this.#ts, node, "css")
|
|
948
|
+
},
|
|
949
|
+
{ logger }
|
|
950
|
+
);
|
|
951
|
+
return (0, import_typescript_template_language_service_decorator.decorateWithTemplateLanguageService)(
|
|
952
|
+
this.#ts,
|
|
953
|
+
decoratedService1,
|
|
954
|
+
info.project,
|
|
955
|
+
new HTMLLanguageService(context),
|
|
956
|
+
{
|
|
957
|
+
tags: ["html", "raw", "h"],
|
|
958
|
+
enableForStringWithSubstitutions: true,
|
|
959
|
+
getSubstitution
|
|
960
|
+
},
|
|
961
|
+
{ logger }
|
|
962
|
+
);
|
|
963
|
+
});
|
|
964
|
+
}
|
|
965
|
+
onConfigurationChanged(config) {
|
|
966
|
+
this.#config.update(config);
|
|
967
|
+
}
|
|
968
|
+
};
|
|
969
|
+
module.exports = (mod) => new HtmlPlugin(mod.typescript);
|
|
970
|
+
//# sourceMappingURL=index.js.map
|
package/package.json
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "ts-gem-plugin",
|
|
3
|
+
"version": "0.0.5",
|
|
4
|
+
"description": "Typescript language service plugin for Gem",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"gem",
|
|
7
|
+
"typescript-language-service"
|
|
8
|
+
],
|
|
9
|
+
"main": "dist/index.js",
|
|
10
|
+
"files": [],
|
|
11
|
+
"scripts": {
|
|
12
|
+
"build": "esbuild ./src/index.ts --outdir=./dist --platform=node --sourcemap --bundle --external:@vscode/* --external:@mantou/typescript-* --external:vscode-*",
|
|
13
|
+
"start": "pnpm run build --watch",
|
|
14
|
+
"prepublishOnly": "pnpm run build"
|
|
15
|
+
},
|
|
16
|
+
"dependencies": {
|
|
17
|
+
"@mantou/gem": "^2.2.0",
|
|
18
|
+
"@mantou/typescript-template-language-service-decorator": "^2.3.2",
|
|
19
|
+
"@vscode/emmet-helper": "^2.9.3",
|
|
20
|
+
"duoyun-ui": "^2.2.0",
|
|
21
|
+
"vscode-css-languageservice": "^6.3.1",
|
|
22
|
+
"vscode-html-languageservice": "^5.3.1",
|
|
23
|
+
"vscode-languageserver-textdocument": "^1.0.12",
|
|
24
|
+
"vscode-languageserver-types": "^3.17.5"
|
|
25
|
+
},
|
|
26
|
+
"devDependencies": {
|
|
27
|
+
"@gemjs/config": "^2.1.0",
|
|
28
|
+
"typescript": "^5.6.2"
|
|
29
|
+
},
|
|
30
|
+
"author": "mantou132",
|
|
31
|
+
"license": "MIT",
|
|
32
|
+
"repository": {
|
|
33
|
+
"type": "git",
|
|
34
|
+
"url": "git+https://github.com/mantou132/gem.git",
|
|
35
|
+
"directory": "packages/ts-gem-plugin"
|
|
36
|
+
},
|
|
37
|
+
"bugs": {
|
|
38
|
+
"url": "https://github.com/mantou132/gem/issues"
|
|
39
|
+
},
|
|
40
|
+
"homepage": "https://github.com/mantou132/gem#readme"
|
|
41
|
+
}
|