ts-gem-plugin 0.0.5 → 0.0.7
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 +1044 -523
- package/package.json +6 -6
package/dist/index.js
CHANGED
|
@@ -25,19 +25,6 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
25
25
|
// src/index.ts
|
|
26
26
|
var import_typescript_template_language_service_decorator = require("@mantou/typescript-template-language-service-decorator");
|
|
27
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
28
|
// ../gem/lib/utils.js
|
|
42
29
|
var { assign, fromEntries, entries, keys } = Object;
|
|
43
30
|
var LinkedList = class extends EventTarget {
|
|
@@ -86,8 +73,10 @@ var LinkedList = class extends EventTarget {
|
|
|
86
73
|
find(value) {
|
|
87
74
|
return this.#map.get(value);
|
|
88
75
|
}
|
|
89
|
-
|
|
90
|
-
|
|
76
|
+
/**
|
|
77
|
+
* 添加到尾部,已存在时会删除老的项目
|
|
78
|
+
* 如果是添加第一个,start 事件会在添加前触发,避免处理事件重复的逻辑
|
|
79
|
+
*/
|
|
91
80
|
add(value) {
|
|
92
81
|
if (!this.#lastItem) {
|
|
93
82
|
this.dispatchEvent(new CustomEvent("start"));
|
|
@@ -104,7 +93,7 @@ var LinkedList = class extends EventTarget {
|
|
|
104
93
|
}
|
|
105
94
|
this.#map.set(value, item);
|
|
106
95
|
}
|
|
107
|
-
|
|
96
|
+
/** 删除这个元素后没有其他元素时立即出发 end 事件 */
|
|
108
97
|
delete(value) {
|
|
109
98
|
const deleteItem = this.#delete(value);
|
|
110
99
|
if (!this.#firstItem) {
|
|
@@ -112,8 +101,7 @@ var LinkedList = class extends EventTarget {
|
|
|
112
101
|
}
|
|
113
102
|
return deleteItem;
|
|
114
103
|
}
|
|
115
|
-
|
|
116
|
-
// 会从链表删除
|
|
104
|
+
/** 获取头部元素,会从链表删除 */
|
|
117
105
|
get() {
|
|
118
106
|
const firstItem = this.#firstItem;
|
|
119
107
|
if (!firstItem)
|
|
@@ -122,91 +110,225 @@ var LinkedList = class extends EventTarget {
|
|
|
122
110
|
return firstItem.value;
|
|
123
111
|
}
|
|
124
112
|
};
|
|
113
|
+
function camelToKebabCase(str) {
|
|
114
|
+
return str.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase();
|
|
115
|
+
}
|
|
125
116
|
function kebabToCamelCase(str) {
|
|
126
117
|
return str.replace(/-(.)/g, (_substr, $1) => $1.toUpperCase());
|
|
127
118
|
}
|
|
128
119
|
|
|
120
|
+
// src/configuration.ts
|
|
121
|
+
var defaultConfiguration = {
|
|
122
|
+
emmet: {},
|
|
123
|
+
elementDefineRules: {
|
|
124
|
+
"Duoyun*Element": "dy-*",
|
|
125
|
+
"*Element": "*"
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
var Rules = class {
|
|
129
|
+
#map = /* @__PURE__ */ new Map();
|
|
130
|
+
constructor(data) {
|
|
131
|
+
Object.entries(data).forEach(([classNamePattern, tagPattern]) => {
|
|
132
|
+
this.#map.set(new RegExp(classNamePattern.replace("*", "(.*)")), tagPattern.replace("*", "$1"));
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
findTag(className) {
|
|
136
|
+
for (const [reg, replaceStr] of this.#map) {
|
|
137
|
+
if (reg.exec(className)) {
|
|
138
|
+
return camelToKebabCase(className.replace(reg, replaceStr));
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
var Configuration = class {
|
|
144
|
+
#emmet = defaultConfiguration.emmet;
|
|
145
|
+
#elementDefineRules = new Rules(defaultConfiguration.elementDefineRules);
|
|
146
|
+
update(config) {
|
|
147
|
+
this.#emmet = config.emmet || defaultConfiguration.emmet;
|
|
148
|
+
this.#elementDefineRules = new Rules({
|
|
149
|
+
...defaultConfiguration.elementDefineRules,
|
|
150
|
+
...config.elementDefineRules
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
get emmet() {
|
|
154
|
+
return this.#emmet;
|
|
155
|
+
}
|
|
156
|
+
get elementDefineRules() {
|
|
157
|
+
return this.#elementDefineRules;
|
|
158
|
+
}
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
// src/constants.ts
|
|
162
|
+
var NAME = "gem-plugin";
|
|
163
|
+
|
|
164
|
+
// src/context.ts
|
|
165
|
+
var import_standard_script_source_helper = __toESM(require("@mantou/typescript-template-language-service-decorator/lib/standard-script-source-helper"));
|
|
166
|
+
var import_standard_template_source_helper = __toESM(require("@mantou/typescript-template-language-service-decorator/lib/standard-template-source-helper"));
|
|
167
|
+
var import_vscode_css_languageservice = require("@mantou/vscode-css-languageservice");
|
|
168
|
+
var import_vscode_html_languageservice2 = require("@mantou/vscode-html-languageservice");
|
|
169
|
+
|
|
170
|
+
// ../duoyun-ui/lib/map.js
|
|
171
|
+
var StringWeakMap = class {
|
|
172
|
+
#map = /* @__PURE__ */ new Map();
|
|
173
|
+
#weakMap = /* @__PURE__ */ new WeakMap();
|
|
174
|
+
#registry = new FinalizationRegistry((key) => this.#map.delete(key));
|
|
175
|
+
set(key, val) {
|
|
176
|
+
this.#map.set(key, new WeakRef(val));
|
|
177
|
+
this.#weakMap.set(val, key);
|
|
178
|
+
this.#registry.register(val, key);
|
|
179
|
+
}
|
|
180
|
+
get(key) {
|
|
181
|
+
return this.#map.get(key)?.deref();
|
|
182
|
+
}
|
|
183
|
+
findKey(val) {
|
|
184
|
+
return this.#weakMap.get(val);
|
|
185
|
+
}
|
|
186
|
+
*[Symbol.iterator]() {
|
|
187
|
+
const entries2 = this.#map.entries();
|
|
188
|
+
for (const [tag, ref] of entries2) {
|
|
189
|
+
yield [tag, ref.deref()];
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
// ../duoyun-ui/lib/cache.js
|
|
195
|
+
var Cache = class {
|
|
196
|
+
#max;
|
|
197
|
+
#maxAge;
|
|
198
|
+
#renewal;
|
|
199
|
+
#map = /* @__PURE__ */ new Map();
|
|
200
|
+
#reverseMap = /* @__PURE__ */ new Map();
|
|
201
|
+
#addedLinked = new LinkedList();
|
|
202
|
+
constructor({ max = Infinity, maxAge = Infinity, renewal = false } = {}) {
|
|
203
|
+
this.#max = max;
|
|
204
|
+
this.#maxAge = maxAge;
|
|
205
|
+
this.#renewal = renewal;
|
|
206
|
+
}
|
|
207
|
+
setOptions(options) {
|
|
208
|
+
this.#max = options.max ?? this.#max;
|
|
209
|
+
this.#maxAge = options.maxAge ?? this.#maxAge;
|
|
210
|
+
this.#renewal = options.renewal ?? this.#renewal;
|
|
211
|
+
}
|
|
212
|
+
#trim() {
|
|
213
|
+
for (let i = this.#addedLinked.size - this.#max; i > 0; i--) {
|
|
214
|
+
const value = this.#addedLinked.get();
|
|
215
|
+
const key = this.#reverseMap.get(value);
|
|
216
|
+
this.#reverseMap.delete(value);
|
|
217
|
+
this.#map.delete(key);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
set(key, value) {
|
|
221
|
+
this.#addedLinked.add(value);
|
|
222
|
+
this.#reverseMap.set(value, key);
|
|
223
|
+
this.#map.set(key, { value, timestamp: Date.now() });
|
|
224
|
+
this.#trim();
|
|
225
|
+
return value;
|
|
226
|
+
}
|
|
227
|
+
get(key, init) {
|
|
228
|
+
const cache = this.#map.get(key);
|
|
229
|
+
if (!cache) {
|
|
230
|
+
return init && this.set(key, init(key));
|
|
231
|
+
}
|
|
232
|
+
const { timestamp, value } = cache;
|
|
233
|
+
if (Date.now() - timestamp > this.#maxAge) {
|
|
234
|
+
this.#addedLinked.delete(value);
|
|
235
|
+
this.#reverseMap.delete(value);
|
|
236
|
+
this.#map.delete(key);
|
|
237
|
+
return init && this.set(key, init(key));
|
|
238
|
+
}
|
|
239
|
+
if (this.#renewal) {
|
|
240
|
+
cache.timestamp = Date.now();
|
|
241
|
+
}
|
|
242
|
+
this.#addedLinked.add(value);
|
|
243
|
+
return value;
|
|
244
|
+
}
|
|
245
|
+
};
|
|
246
|
+
|
|
247
|
+
// src/cache.ts
|
|
248
|
+
var LRUCache = class {
|
|
249
|
+
#bucket;
|
|
250
|
+
constructor(args) {
|
|
251
|
+
this.#bucket = new Cache({ max: 25, renewal: true, ...args });
|
|
252
|
+
}
|
|
253
|
+
#genKey(context, position) {
|
|
254
|
+
return [context.fileName, position?.line, position?.character, context.text].join(";");
|
|
255
|
+
}
|
|
256
|
+
get(context, position, init) {
|
|
257
|
+
return this.#bucket.get(this.#genKey(context, position), init);
|
|
258
|
+
}
|
|
259
|
+
};
|
|
260
|
+
|
|
261
|
+
// src/data-provider.ts
|
|
262
|
+
var import_vscode_html_languageservice = require("@mantou/vscode-html-languageservice");
|
|
263
|
+
|
|
129
264
|
// src/utils.ts
|
|
130
|
-
|
|
131
|
-
var import_vscode_languageserver_textdocument = require("vscode-languageserver-textdocument");
|
|
132
|
-
function isCustomElement(tag) {
|
|
265
|
+
function isCustomElementTag(tag) {
|
|
133
266
|
return tag.includes("-");
|
|
134
267
|
}
|
|
135
268
|
function isDepElement(node) {
|
|
136
269
|
return node.getSourceFile().fileName.includes("/node_modules/");
|
|
137
270
|
}
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
271
|
+
function bindMemberFunction(o, keys2 = Object.keys(o)) {
|
|
272
|
+
return Object.fromEntries(keys2.map((key) => [key, o[key].bind?.(o)]));
|
|
273
|
+
}
|
|
274
|
+
function forEachNode(roots, fn) {
|
|
275
|
+
const list = [...roots];
|
|
141
276
|
while (true) {
|
|
142
277
|
const currentNode = list.pop();
|
|
143
278
|
if (!currentNode) return;
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
279
|
+
fn(currentNode);
|
|
280
|
+
list.push(...currentNode.children);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
function getAstNodeAtPosition(typescript, node, pos) {
|
|
284
|
+
if (node.pos > pos || node.end <= pos) return;
|
|
285
|
+
while (node.kind >= typescript.SyntaxKind.FirstNode) {
|
|
286
|
+
const nested = typescript.forEachChild(node, (child) => child.pos <= pos && child.end > pos ? child : void 0);
|
|
287
|
+
if (nested === void 0) break;
|
|
288
|
+
node = nested;
|
|
154
289
|
}
|
|
290
|
+
return node;
|
|
155
291
|
}
|
|
292
|
+
var BEFORE_REG = /[^\s</>]+$/;
|
|
293
|
+
var AFTER_REG = /^[^\s</>]+/;
|
|
156
294
|
function getHTMLTextAtPosition(text, offset) {
|
|
157
|
-
const before = text.slice(0, offset).match(
|
|
158
|
-
const after = text.slice(offset).match(
|
|
295
|
+
const before = text.slice(0, offset).match(BEFORE_REG)?.at(0) || "";
|
|
296
|
+
const after = text.slice(offset).match(AFTER_REG)?.at(0) || "";
|
|
159
297
|
const str = before + after;
|
|
160
298
|
return {
|
|
299
|
+
before,
|
|
300
|
+
after,
|
|
161
301
|
text: str,
|
|
162
302
|
start: offset - before.length,
|
|
163
303
|
length: str.length
|
|
164
304
|
};
|
|
165
305
|
}
|
|
166
|
-
var attrMap = {
|
|
167
|
-
".": "property",
|
|
168
|
-
"?": "boolean",
|
|
169
|
-
"@": "event"
|
|
170
|
-
};
|
|
171
306
|
function getAttrName(text) {
|
|
172
307
|
const attr = text.split("=").at(0);
|
|
173
|
-
const
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
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
|
-
}
|
|
308
|
+
const isNotLetter = hasDecoratorAttr(attr);
|
|
309
|
+
const offset = isNotLetter ? 1 : 0;
|
|
310
|
+
return {
|
|
311
|
+
attr: attr.slice(offset),
|
|
312
|
+
offset,
|
|
313
|
+
decorate: isNotLetter ? attr.at(0) : ""
|
|
314
|
+
};
|
|
195
315
|
}
|
|
196
|
-
function
|
|
197
|
-
|
|
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");
|
|
316
|
+
function hasDecoratorAttr(str) {
|
|
317
|
+
return str.charCodeAt(0) < 65;
|
|
201
318
|
}
|
|
202
|
-
function
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
319
|
+
function getTagInfo(node, offset) {
|
|
320
|
+
const tag = node.tag;
|
|
321
|
+
const openStart = node.start + 1 + offset;
|
|
322
|
+
return {
|
|
323
|
+
node,
|
|
324
|
+
tag,
|
|
325
|
+
offset,
|
|
326
|
+
open: { start: openStart, length: tag.length },
|
|
327
|
+
end: node.endTagStart && {
|
|
328
|
+
start: node.endTagStart + 2 + offset,
|
|
329
|
+
length: node.end - node.endTagStart - 3
|
|
330
|
+
}
|
|
331
|
+
};
|
|
210
332
|
}
|
|
211
333
|
var marker = Symbol();
|
|
212
334
|
function decorate(origin, cb) {
|
|
@@ -215,8 +337,244 @@ function decorate(origin, cb) {
|
|
|
215
337
|
result[marker] = true;
|
|
216
338
|
return result;
|
|
217
339
|
}
|
|
340
|
+
|
|
341
|
+
// src/data-provider.ts
|
|
342
|
+
var dataProvider = (0, import_vscode_html_languageservice.getDefaultHTMLDataProvider)();
|
|
343
|
+
var HTMLDataProvider = class {
|
|
344
|
+
#ts;
|
|
345
|
+
#elements;
|
|
346
|
+
#getProgram;
|
|
347
|
+
constructor(typescript, elements, getProgram) {
|
|
348
|
+
this.#ts = typescript;
|
|
349
|
+
this.#elements = elements;
|
|
350
|
+
this.#getProgram = getProgram;
|
|
351
|
+
}
|
|
352
|
+
getId() {
|
|
353
|
+
return NAME;
|
|
354
|
+
}
|
|
355
|
+
isApplicable() {
|
|
356
|
+
return true;
|
|
357
|
+
}
|
|
358
|
+
provideTags() {
|
|
359
|
+
return [...this.#elements].map(([tag, node]) => ({
|
|
360
|
+
name: tag,
|
|
361
|
+
attributes: [],
|
|
362
|
+
description: getDocComment(this.#ts, node)
|
|
363
|
+
}));
|
|
364
|
+
}
|
|
365
|
+
provideAttributes(tag) {
|
|
366
|
+
const ts = this.#ts;
|
|
367
|
+
const typeChecker = this.#getProgram().getTypeChecker();
|
|
368
|
+
const node = this.#elements.get(tag);
|
|
369
|
+
const result = [
|
|
370
|
+
{ name: "v-if", description: "Similar to vue `v-if`" },
|
|
371
|
+
{ name: "v-else-if", description: "Similar to vue `v-else-if`" },
|
|
372
|
+
{ name: "v-else", description: "Similar to vue `v-else`", valueSet: "v" }
|
|
373
|
+
];
|
|
374
|
+
if (!node) return result;
|
|
375
|
+
const isDep = isDepElement(node);
|
|
376
|
+
const props = typeChecker.getTypeAtLocation(node).getApparentProperties();
|
|
377
|
+
props.forEach((e) => {
|
|
378
|
+
const declaration = e.getDeclarations()?.at(0);
|
|
379
|
+
const prop = declaration && ts.isPropertyDeclaration(declaration);
|
|
380
|
+
if (!prop) return;
|
|
381
|
+
const hasPropDecorator = declaration.modifiers?.some((m) => ts.isDecorator(m) && ts.isIdentifier(m.expression));
|
|
382
|
+
if (!hasPropDecorator && !isDep) return;
|
|
383
|
+
const type = declaration.type && typeChecker.getTypeFromTypeNode(declaration.type);
|
|
384
|
+
const typeText = declaration.type?.getText();
|
|
385
|
+
const description = getDocComment(ts, declaration);
|
|
386
|
+
switch (type) {
|
|
387
|
+
case typeChecker.getStringType():
|
|
388
|
+
case typeChecker.getNumberType():
|
|
389
|
+
result.push({ name: e.name, description });
|
|
390
|
+
break;
|
|
391
|
+
case typeChecker.getBooleanType():
|
|
392
|
+
result.push({ name: e.name, description, valueSet: "v" });
|
|
393
|
+
result.push({ name: `?${e.name}`, description });
|
|
394
|
+
break;
|
|
395
|
+
}
|
|
396
|
+
if (type && getUnionValues(type)) {
|
|
397
|
+
result.push({ name: e.name, description });
|
|
398
|
+
}
|
|
399
|
+
if (typeText?.startsWith("Emitter")) {
|
|
400
|
+
result.push({ name: `@${e.name}`, description });
|
|
401
|
+
} else {
|
|
402
|
+
result.push({ name: `.${e.name}`, description });
|
|
403
|
+
}
|
|
404
|
+
});
|
|
405
|
+
const oResult = dataProvider.provideAttributes(isCustomElementTag(tag) ? "div" : tag);
|
|
406
|
+
oResult.forEach((data) => {
|
|
407
|
+
const tryEvtName = data.name.replace(/^on/, "@");
|
|
408
|
+
if (tryEvtName !== data.name) {
|
|
409
|
+
result.push({ ...data, name: tryEvtName });
|
|
410
|
+
}
|
|
411
|
+
});
|
|
412
|
+
return result;
|
|
413
|
+
}
|
|
414
|
+
provideValues(tag, attr) {
|
|
415
|
+
const typeChecker = this.#getProgram().getTypeChecker();
|
|
416
|
+
const node = this.#elements.get(tag);
|
|
417
|
+
if (!node) return [];
|
|
418
|
+
const prop = typeChecker.getTypeAtLocation(node).getProperty(getAttrName(attr).attr);
|
|
419
|
+
const result = prop && getUnionValues(typeChecker.getTypeOfSymbol(prop));
|
|
420
|
+
return result?.map((name) => ({ name })) || [];
|
|
421
|
+
}
|
|
422
|
+
};
|
|
423
|
+
function getUnionValues(type) {
|
|
424
|
+
if (!type.isUnion()) return;
|
|
425
|
+
const result = [];
|
|
426
|
+
type.types.forEach((e) => {
|
|
427
|
+
if (!e.isLiteral()) return;
|
|
428
|
+
result.push(String(e.value));
|
|
429
|
+
});
|
|
430
|
+
return result;
|
|
431
|
+
}
|
|
432
|
+
var COMMENT_LINE_CONTENT = /^(\/?[ *\t]*)?(?<str>.*?)(\**\/)?$/gm;
|
|
433
|
+
function getDocComment(typescript, declaration) {
|
|
434
|
+
const fullText = declaration.getSourceFile().getFullText();
|
|
435
|
+
const commentRanges = typescript.getLeadingCommentRanges(fullText, declaration.getFullStart());
|
|
436
|
+
const commentStrings = commentRanges?.filter(({ kind }) => kind === typescript.SyntaxKind.MultiLineCommentTrivia).map(({ pos, end }) => {
|
|
437
|
+
const fullComment = [...fullText.slice(pos, end).matchAll(COMMENT_LINE_CONTENT)];
|
|
438
|
+
return fullComment.map((m) => m.groups.str).join("\n");
|
|
439
|
+
});
|
|
440
|
+
return commentStrings?.join("\n\n");
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
// src/context.ts
|
|
444
|
+
var Context = class {
|
|
445
|
+
elements;
|
|
446
|
+
builtInElements;
|
|
447
|
+
ts;
|
|
448
|
+
config;
|
|
449
|
+
project;
|
|
450
|
+
logger;
|
|
451
|
+
dataProvider;
|
|
452
|
+
cssLanguageService;
|
|
453
|
+
htmlLanguageService;
|
|
454
|
+
htmlSourceHelper;
|
|
455
|
+
htmlTemplateStringSettings;
|
|
456
|
+
cssTemplateStringSettings;
|
|
457
|
+
getProgram;
|
|
458
|
+
constructor(typescript, config, info, logger) {
|
|
459
|
+
this.ts = typescript;
|
|
460
|
+
this.config = config;
|
|
461
|
+
this.getProgram = () => info.languageService.getProgram();
|
|
462
|
+
this.project = info.project;
|
|
463
|
+
this.logger = logger;
|
|
464
|
+
this.dataProvider = dataProvider;
|
|
465
|
+
this.elements = new StringWeakMap();
|
|
466
|
+
this.builtInElements = new StringWeakMap();
|
|
467
|
+
this.cssLanguageService = (0, import_vscode_css_languageservice.getCSSLanguageService)({});
|
|
468
|
+
this.htmlLanguageService = (0, import_vscode_html_languageservice2.getLanguageService)({
|
|
469
|
+
customDataProviders: [dataProvider, new HTMLDataProvider(typescript, this.elements, this.getProgram)]
|
|
470
|
+
});
|
|
471
|
+
this.htmlTemplateStringSettings = {
|
|
472
|
+
tags: ["html", "raw", "h"],
|
|
473
|
+
enableForStringWithSubstitutions: true,
|
|
474
|
+
getSubstitution
|
|
475
|
+
};
|
|
476
|
+
this.cssTemplateStringSettings = {
|
|
477
|
+
tags: ["styled", "css"],
|
|
478
|
+
enableForStringWithSubstitutions: true,
|
|
479
|
+
getSubstitution,
|
|
480
|
+
isValidTemplate: (node) => isValidCSSTemplate(typescript, node, "css")
|
|
481
|
+
};
|
|
482
|
+
this.htmlSourceHelper = new import_standard_template_source_helper.default(
|
|
483
|
+
typescript,
|
|
484
|
+
this.htmlTemplateStringSettings,
|
|
485
|
+
new import_standard_script_source_helper.default(typescript, info.project),
|
|
486
|
+
logger
|
|
487
|
+
);
|
|
488
|
+
}
|
|
489
|
+
#virtualHtmlCache = new LRUCache({ max: 1e3 });
|
|
490
|
+
#virtualCssCache = new LRUCache({ max: 1e3 });
|
|
491
|
+
getCssDoc(text) {
|
|
492
|
+
return this.#virtualCssCache.get({ text, fileName: "" }, void 0, () => {
|
|
493
|
+
const vDoc = createVirtualDocument("css", text);
|
|
494
|
+
const vCss = this.cssLanguageService.parseStylesheet(vDoc);
|
|
495
|
+
return { vDoc, vCss };
|
|
496
|
+
});
|
|
497
|
+
}
|
|
498
|
+
getHtmlDoc(text) {
|
|
499
|
+
return this.#virtualHtmlCache.get({ text, fileName: "" }, void 0, () => {
|
|
500
|
+
const vDoc = createVirtualDocument("html", text);
|
|
501
|
+
const vHtml = this.htmlLanguageService.parseHTMLDocument(vDoc);
|
|
502
|
+
vHtml.roots.forEach(function transform(e, index, arr) {
|
|
503
|
+
e.prev = arr[index - 1];
|
|
504
|
+
e.next = arr[index + 1];
|
|
505
|
+
e.children.forEach(transform);
|
|
506
|
+
});
|
|
507
|
+
return { vDoc, vHtml };
|
|
508
|
+
});
|
|
509
|
+
}
|
|
510
|
+
getTagFromNode(node, supportClassName = isDepElement(node)) {
|
|
511
|
+
if (!this.ts.isClassDeclaration(node)) return;
|
|
512
|
+
for (const modifier of node.modifiers || []) {
|
|
513
|
+
if (this.ts.isDecorator(modifier) && this.ts.isCallExpression(modifier.expression) && modifier.expression.expression.getText() === "customElement") {
|
|
514
|
+
const arg = modifier.expression.arguments.at(0);
|
|
515
|
+
if (arg && this.ts.isStringLiteral(arg)) {
|
|
516
|
+
return arg.text;
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
if (supportClassName && node.name && this.ts.isIdentifier(node.name)) {
|
|
521
|
+
return this.config.elementDefineRules.findTag(node.name.text);
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
updateElement(file) {
|
|
525
|
+
const isDep = isDepElement(file);
|
|
526
|
+
this.ts.forEachChild(file, (node) => {
|
|
527
|
+
const tag = this.getTagFromNode(node, isDep);
|
|
528
|
+
if (tag && this.ts.isClassDeclaration(node)) {
|
|
529
|
+
this.elements.set(tag, node);
|
|
530
|
+
}
|
|
531
|
+
});
|
|
532
|
+
}
|
|
533
|
+
#initElementsCache = /* @__PURE__ */ new WeakSet();
|
|
534
|
+
/**
|
|
535
|
+
* 当 project 准备好了执行
|
|
536
|
+
*/
|
|
537
|
+
initElements() {
|
|
538
|
+
const program = this.getProgram();
|
|
539
|
+
if (this.#initElementsCache.has(program)) return;
|
|
540
|
+
const files = program.getSourceFiles();
|
|
541
|
+
files.forEach((file) => this.updateElement(file));
|
|
542
|
+
const typeChecker = program.getTypeChecker();
|
|
543
|
+
const symbols = typeChecker.getSymbolsInScope(files.at(0), this.ts.SymbolFlags.Interface);
|
|
544
|
+
symbols.forEach((symbol) => {
|
|
545
|
+
const name = symbol.escapedName.toString();
|
|
546
|
+
const match = name.match(/^(SVG|HTML)(\w*)Element$/);
|
|
547
|
+
const declaration = symbol.declarations?.find((e) => this.ts.isInterfaceDeclaration(e));
|
|
548
|
+
if (!match || !declaration) return;
|
|
549
|
+
if (name in partialBuiltInElementMap) {
|
|
550
|
+
partialBuiltInElementMap[name].forEach((e) => this.builtInElements.set(e, declaration));
|
|
551
|
+
} else {
|
|
552
|
+
this.builtInElements.set(match[2].toLowerCase(), declaration);
|
|
553
|
+
}
|
|
554
|
+
});
|
|
555
|
+
}
|
|
556
|
+
};
|
|
557
|
+
var partialBuiltInElementMap = {
|
|
558
|
+
SVGAElement: [],
|
|
559
|
+
HTMLAnchorElement: ["a"],
|
|
560
|
+
SVGImageElement: [],
|
|
561
|
+
HTMLImageElement: ["img"],
|
|
562
|
+
SVGStyleElement: [],
|
|
563
|
+
HTMLStyleElement: ["style"],
|
|
564
|
+
HTMLDListElement: ["dl"],
|
|
565
|
+
HTMLOListElement: ["ol"],
|
|
566
|
+
HTMLUListElement: ["ul"],
|
|
567
|
+
HTMLHeadingElement: ["h1", "h2", "h3", "h4", "h5", "h6"],
|
|
568
|
+
HTMLModElement: ["del", "ins"],
|
|
569
|
+
HTMLQuoteElement: ["blockquote", "q", "cite"],
|
|
570
|
+
HTMLTableCaptionElement: ["caption"],
|
|
571
|
+
HTMLTableCellElement: ["th", "td"],
|
|
572
|
+
HTMLTableColElement: ["col"],
|
|
573
|
+
HTMLTableRowElement: ["tr"],
|
|
574
|
+
HTMLTableSectionElement: ["thead", "tfoot", "tbody"]
|
|
575
|
+
};
|
|
218
576
|
function createVirtualDocument(languageId, content) {
|
|
219
|
-
return
|
|
577
|
+
return import_vscode_html_languageservice2.TextDocument.create(`embedded://document.${languageId}`, languageId, 1, content);
|
|
220
578
|
}
|
|
221
579
|
function getSubstitution(templateString, start, end) {
|
|
222
580
|
return templateString.slice(start, end).replaceAll(/[^\n]/g, "_");
|
|
@@ -224,7 +582,7 @@ function getSubstitution(templateString, start, end) {
|
|
|
224
582
|
function isValidCSSTemplate(typescript, node, callName) {
|
|
225
583
|
switch (node.kind) {
|
|
226
584
|
case typescript.SyntaxKind.NoSubstitutionTemplateLiteral:
|
|
227
|
-
case typescript.SyntaxKind.TemplateExpression:
|
|
585
|
+
case typescript.SyntaxKind.TemplateExpression: {
|
|
228
586
|
const parent = node.parent;
|
|
229
587
|
if (typescript.isCallExpression(parent) && parent.expression.getText() === callName) {
|
|
230
588
|
return true;
|
|
@@ -236,10 +594,18 @@ function isValidCSSTemplate(typescript, node, callName) {
|
|
|
236
594
|
}
|
|
237
595
|
}
|
|
238
596
|
return false;
|
|
597
|
+
}
|
|
239
598
|
default:
|
|
240
599
|
return false;
|
|
241
600
|
}
|
|
242
601
|
}
|
|
602
|
+
|
|
603
|
+
// src/decorate-css.ts
|
|
604
|
+
var import_vscode_css_languageservice2 = require("@mantou/vscode-css-languageservice");
|
|
605
|
+
var import_vscode_emmet_helper = require("@mantou/vscode-emmet-helper");
|
|
606
|
+
|
|
607
|
+
// src/translates.ts
|
|
608
|
+
var vscode = __toESM(require("vscode-languageserver-types"));
|
|
243
609
|
function translateCompletionItemsToCompletionInfo(context, items) {
|
|
244
610
|
return {
|
|
245
611
|
defaultCommitCharacters: [],
|
|
@@ -297,8 +663,6 @@ function translationCompletionItemKind(context, kind) {
|
|
|
297
663
|
return typescript.ScriptElementKind.alias;
|
|
298
664
|
case vscode.CompletionItemKind.File:
|
|
299
665
|
return typescript.ScriptElementKind.moduleElement;
|
|
300
|
-
case vscode.CompletionItemKind.Snippet:
|
|
301
|
-
case vscode.CompletionItemKind.Text:
|
|
302
666
|
default:
|
|
303
667
|
return typescript.ScriptElementKind.unknown;
|
|
304
668
|
}
|
|
@@ -362,134 +726,168 @@ function genDefaultCompletionEntryDetails(context, name) {
|
|
|
362
726
|
}
|
|
363
727
|
function toDisplayParts(text, isDoc = false) {
|
|
364
728
|
if (!text) return [];
|
|
365
|
-
const
|
|
729
|
+
const escapeText = (unsafe) => unsafe.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'").replaceAll(" ", " ").replaceAll("\n", " \n").replaceAll(" ", " ");
|
|
366
730
|
return [
|
|
367
731
|
{
|
|
368
732
|
kind: "unknown",
|
|
369
|
-
text: typeof text !== "string" ? text.value : isDoc ?
|
|
733
|
+
text: typeof text !== "string" ? text.value : isDoc ? escapeText(text) : text
|
|
370
734
|
}
|
|
371
735
|
];
|
|
372
736
|
}
|
|
737
|
+
function genElementDefinitionInfo(context, { start, length }, definitionNode) {
|
|
738
|
+
const htmlOffset = context.node.pos + 1;
|
|
739
|
+
return {
|
|
740
|
+
textSpan: { start, length },
|
|
741
|
+
definitions: [
|
|
742
|
+
{
|
|
743
|
+
containerName: "Custom Element",
|
|
744
|
+
containerKind: context.typescript.ScriptElementKind.unknown,
|
|
745
|
+
name: definitionNode.name.text,
|
|
746
|
+
kind: context.typescript.ScriptElementKind.classElement,
|
|
747
|
+
fileName: definitionNode.getSourceFile().fileName,
|
|
748
|
+
textSpan: {
|
|
749
|
+
start: definitionNode.name.getStart() - htmlOffset,
|
|
750
|
+
length: definitionNode.name.text.length
|
|
751
|
+
}
|
|
752
|
+
}
|
|
753
|
+
]
|
|
754
|
+
};
|
|
755
|
+
}
|
|
756
|
+
function genAttrDefinitionInfo(context, { start, length }, propDeclaration) {
|
|
757
|
+
const htmlOffset = context.node.pos + 1;
|
|
758
|
+
return {
|
|
759
|
+
textSpan: { start, length },
|
|
760
|
+
definitions: [
|
|
761
|
+
{
|
|
762
|
+
containerName: "Attribute",
|
|
763
|
+
containerKind: context.typescript.ScriptElementKind.unknown,
|
|
764
|
+
name: propDeclaration.getText(),
|
|
765
|
+
kind: context.typescript.ScriptElementKind.memberVariableElement,
|
|
766
|
+
fileName: propDeclaration.getSourceFile().fileName,
|
|
767
|
+
textSpan: {
|
|
768
|
+
start: propDeclaration.getStart() - htmlOffset,
|
|
769
|
+
length: propDeclaration.getText().length
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
]
|
|
773
|
+
};
|
|
774
|
+
}
|
|
775
|
+
function genCurrentCtxDefinitionInfo(context, { start, length }, definitionTextSpan) {
|
|
776
|
+
return {
|
|
777
|
+
textSpan: { start, length },
|
|
778
|
+
definitions: [
|
|
779
|
+
{
|
|
780
|
+
containerName: "Attribute",
|
|
781
|
+
containerKind: context.typescript.ScriptElementKind.unknown,
|
|
782
|
+
name: context.text.slice(start, start + length),
|
|
783
|
+
kind: context.typescript.ScriptElementKind.memberVariableElement,
|
|
784
|
+
fileName: context.fileName,
|
|
785
|
+
textSpan: definitionTextSpan
|
|
786
|
+
}
|
|
787
|
+
]
|
|
788
|
+
};
|
|
789
|
+
}
|
|
373
790
|
|
|
374
|
-
//
|
|
375
|
-
var
|
|
376
|
-
#max;
|
|
377
|
-
#
|
|
378
|
-
#
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
#linkedList = new LinkedList();
|
|
382
|
-
constructor({ max = Infinity, maxAge = Infinity, renewal = false } = {}) {
|
|
383
|
-
this.#max = max;
|
|
384
|
-
this.#maxAge = maxAge;
|
|
385
|
-
this.#renewal = renewal;
|
|
791
|
+
// src/decorate-css.ts
|
|
792
|
+
var CSSLanguageService = class {
|
|
793
|
+
#completionsCache = new LRUCache({ max: 1 });
|
|
794
|
+
#diagnosticsCache = new LRUCache();
|
|
795
|
+
#ctx;
|
|
796
|
+
constructor(ctx) {
|
|
797
|
+
this.#ctx = ctx;
|
|
386
798
|
}
|
|
387
|
-
#
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
}
|
|
799
|
+
#normalize(context, position) {
|
|
800
|
+
const parent = context.node.parent;
|
|
801
|
+
const tag = context.typescript.isTaggedTemplateExpression(parent) && parent.tag.getText();
|
|
802
|
+
const isStyle = context.typescript.isPropertyAssignment(parent) || tag === "styled";
|
|
803
|
+
if (!isStyle) return { offset: 0, text: context.text, pos: { ...position } };
|
|
804
|
+
const appendBefore = ".parent { ";
|
|
805
|
+
const appendAfter = " }";
|
|
806
|
+
const character = position.line === 0 ? position.character + appendBefore.length : position.character;
|
|
807
|
+
return {
|
|
808
|
+
offset: appendBefore.length,
|
|
809
|
+
text: `${appendBefore}${context.text}${appendAfter}`,
|
|
810
|
+
pos: { line: position.line, character }
|
|
811
|
+
};
|
|
394
812
|
}
|
|
395
|
-
|
|
396
|
-
this.#
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
813
|
+
#getCompletionsAtPosition(context, position) {
|
|
814
|
+
return this.#completionsCache.get(context, position, () => {
|
|
815
|
+
const { text, pos } = this.#normalize(context, position);
|
|
816
|
+
const { vDoc, vCss } = this.#ctx.getCssDoc(text);
|
|
817
|
+
let emmetResults;
|
|
818
|
+
const onCssProperty = () => emmetResults = (0, import_vscode_emmet_helper.doComplete)(vDoc, pos, "css", this.#ctx.config.emmet);
|
|
819
|
+
this.#ctx.cssLanguageService.setCompletionParticipants([{ onCssProperty }]);
|
|
820
|
+
(0, import_vscode_css_languageservice2.updateTags)([...this.#ctx.elements].map(([tag]) => tag));
|
|
821
|
+
const completions = this.#ctx.cssLanguageService.doComplete(vDoc, pos, vCss);
|
|
822
|
+
completions.items.push(...emmetResults?.items || []);
|
|
823
|
+
return completions;
|
|
824
|
+
});
|
|
401
825
|
}
|
|
402
|
-
|
|
403
|
-
|
|
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;
|
|
826
|
+
getCompletionsAtPosition(context, position) {
|
|
827
|
+
return translateCompletionItemsToCompletionInfo(context, this.#getCompletionsAtPosition(context, position));
|
|
420
828
|
}
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
#genKey(context, position) {
|
|
427
|
-
return [context.fileName, position?.line, position?.character, context.text].join(";");
|
|
829
|
+
getCompletionEntryDetails(context, position, name) {
|
|
830
|
+
const completions = this.#getCompletionsAtPosition(context, position);
|
|
831
|
+
const item = completions.items.find((e) => e.label === name);
|
|
832
|
+
if (!item) return genDefaultCompletionEntryDetails(context, name);
|
|
833
|
+
return translateCompletionItemsToCompletionEntryDetails(context, item);
|
|
428
834
|
}
|
|
429
|
-
|
|
430
|
-
|
|
835
|
+
getQuickInfoAtPosition(context, position) {
|
|
836
|
+
const { text, pos } = this.#normalize(context, position);
|
|
837
|
+
const { vDoc, vCss } = this.#ctx.getCssDoc(text);
|
|
838
|
+
const hover = this.#ctx.cssLanguageService.doHover(vDoc, pos, vCss, {
|
|
839
|
+
documentation: true,
|
|
840
|
+
references: true
|
|
841
|
+
});
|
|
842
|
+
if (!hover) return;
|
|
843
|
+
return translateHover(context, hover, position, pos.character - position.character);
|
|
431
844
|
}
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
845
|
+
#getSyntacticDiagnostics(context) {
|
|
846
|
+
const { text, offset } = this.#normalize(context, { line: 0, character: 0 });
|
|
847
|
+
const { vDoc, vCss } = this.#ctx.getCssDoc(text);
|
|
848
|
+
const oDiagnostics = this.#ctx.cssLanguageService.doValidation(vDoc, vCss);
|
|
849
|
+
const file = this.#ctx.getProgram().getSourceFile(context.fileName);
|
|
850
|
+
return oDiagnostics.map(({ message, range }) => {
|
|
851
|
+
const start = context.toOffset(range.start);
|
|
852
|
+
return {
|
|
853
|
+
category: context.typescript.DiagnosticCategory.Warning,
|
|
854
|
+
code: 0,
|
|
855
|
+
file,
|
|
856
|
+
start: range.start.line === 0 ? start - offset : start,
|
|
857
|
+
length: context.toOffset(range.end) - start,
|
|
858
|
+
messageText: message,
|
|
859
|
+
source: NAME
|
|
860
|
+
};
|
|
861
|
+
});
|
|
862
|
+
}
|
|
863
|
+
getSyntacticDiagnostics(context) {
|
|
864
|
+
this.#ctx.initElements();
|
|
865
|
+
return this.#diagnosticsCache.get(context, void 0, () => this.#getSyntacticDiagnostics(context));
|
|
866
|
+
}
|
|
867
|
+
getDefinitionAndBoundSpan(context, position) {
|
|
868
|
+
const { text, offset } = this.#normalize(context, position);
|
|
869
|
+
const { vDoc, vCss } = this.#ctx.getCssDoc(text);
|
|
870
|
+
const empty = { textSpan: { start: 0, length: 0 } };
|
|
871
|
+
const node = vCss.findChildAtOffset(context.toOffset(position), true);
|
|
872
|
+
if (!node) return empty;
|
|
873
|
+
const ident = vDoc.getText().slice(node.offset, node.end);
|
|
874
|
+
const definitionNode = this.#ctx.elements.get(ident);
|
|
875
|
+
if (!definitionNode) return empty;
|
|
876
|
+
return genElementDefinitionInfo(context, { start: node.offset - offset, length: node.length }, definitionNode);
|
|
443
877
|
}
|
|
444
878
|
};
|
|
445
879
|
|
|
446
880
|
// src/decorate-html.ts
|
|
881
|
+
var import_vscode_css_languageservice3 = require("@mantou/vscode-css-languageservice");
|
|
882
|
+
var import_vscode_emmet_helper2 = require("@mantou/vscode-emmet-helper");
|
|
447
883
|
var HTMLLanguageService = class {
|
|
448
|
-
#completionsCache = new LRUCache();
|
|
884
|
+
#completionsCache = new LRUCache({ max: 1 });
|
|
449
885
|
#diagnosticsCache = new LRUCache();
|
|
450
|
-
#cssLanguageService;
|
|
451
|
-
#htmlLanguageService;
|
|
452
886
|
#ctx;
|
|
453
887
|
constructor(ctx) {
|
|
454
888
|
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
889
|
}
|
|
492
|
-
#
|
|
890
|
+
#getAllStyleSheet(doc) {
|
|
493
891
|
const styles = [];
|
|
494
892
|
const nodes = [...doc.roots];
|
|
495
893
|
while (true) {
|
|
@@ -500,17 +898,17 @@ var HTMLLanguageService = class {
|
|
|
500
898
|
}
|
|
501
899
|
return styles;
|
|
502
900
|
}
|
|
503
|
-
#
|
|
901
|
+
#getEmbeddedCss(context, position, doc) {
|
|
504
902
|
const node = doc.findNodeAt(context.toOffset(position));
|
|
505
903
|
if (node.tag !== "style") return;
|
|
506
|
-
const
|
|
507
|
-
const
|
|
904
|
+
const text = context.text.slice(node.startTagEnd, node.endTagStart);
|
|
905
|
+
const { vDoc, vCss } = this.#ctx.getCssDoc(text);
|
|
508
906
|
const offset = context.toOffset(position) - node.startTagEnd;
|
|
509
|
-
const toPosition = (pos) => context.toPosition(
|
|
907
|
+
const toPosition = (pos) => context.toPosition(vDoc.offsetAt(pos) + node.startTagEnd);
|
|
510
908
|
return {
|
|
511
|
-
style,
|
|
512
|
-
|
|
513
|
-
position:
|
|
909
|
+
style: vCss,
|
|
910
|
+
vDoc,
|
|
911
|
+
position: vDoc.positionAt(offset),
|
|
514
912
|
updateRange: (range) => ({
|
|
515
913
|
start: toPosition(range.start),
|
|
516
914
|
end: toPosition(range.end)
|
|
@@ -518,43 +916,34 @@ var HTMLLanguageService = class {
|
|
|
518
916
|
};
|
|
519
917
|
}
|
|
520
918
|
#getCSSCompletionsAtPosition(context, position, doc) {
|
|
521
|
-
const css = this.#
|
|
919
|
+
const css = this.#getEmbeddedCss(context, position, doc);
|
|
522
920
|
if (!css) return [];
|
|
523
921
|
let emmetResults;
|
|
524
|
-
this.#
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
}
|
|
529
|
-
}
|
|
530
|
-
]);
|
|
531
|
-
const completions = this.#cssLanguageService.doComplete(css.virtualDocument, css.position, css.style);
|
|
922
|
+
const onCssProperty = () => emmetResults = (0, import_vscode_emmet_helper2.doComplete)(css.vDoc, css.position, "css", this.#ctx.config.emmet);
|
|
923
|
+
this.#ctx.cssLanguageService.setCompletionParticipants([{ onCssProperty }]);
|
|
924
|
+
(0, import_vscode_css_languageservice3.updateTags)([...this.#ctx.elements].map(([tag]) => tag));
|
|
925
|
+
const completions = this.#ctx.cssLanguageService.doComplete(css.vDoc, css.position, css.style);
|
|
532
926
|
completions.items.push(...emmetResults?.items || []);
|
|
533
|
-
return completions.items.map((e) =>
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
} : e.textEdit
|
|
539
|
-
}));
|
|
927
|
+
return completions.items.map((e) => {
|
|
928
|
+
const textEdit = e.textEdit && "range" in e.textEdit && e.textEdit;
|
|
929
|
+
const newTextEdit = textEdit && { newText: textEdit.newText, range: css.updateRange(textEdit.range) };
|
|
930
|
+
return { ...e, textEdit: newTextEdit || e.textEdit };
|
|
931
|
+
});
|
|
540
932
|
}
|
|
541
933
|
#getCompletionsAtPosition(context, position) {
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
completions.items.push(...emmetResults?.items || []);
|
|
556
|
-
completions.items.push(...this.#getCSSCompletionsAtPosition(context, position, vHtml));
|
|
557
|
-
return this.#completionsCache.updateCached(context, position, completions);
|
|
934
|
+
return this.#completionsCache.get(context, position, () => {
|
|
935
|
+
const { vDoc, vHtml } = this.#ctx.getHtmlDoc(context.text);
|
|
936
|
+
let emmetResults;
|
|
937
|
+
const onHtmlContent = () => {
|
|
938
|
+
(0, import_vscode_emmet_helper2.updateTags)([...this.#ctx.elements].map(([tag]) => tag));
|
|
939
|
+
emmetResults = (0, import_vscode_emmet_helper2.doComplete)(vDoc, position, "html", this.#ctx.config.emmet);
|
|
940
|
+
};
|
|
941
|
+
this.#ctx.htmlLanguageService.setCompletionParticipants([{ onHtmlContent }]);
|
|
942
|
+
const completions = this.#ctx.htmlLanguageService.doComplete(vDoc, position, vHtml);
|
|
943
|
+
completions.items.push(...emmetResults?.items || []);
|
|
944
|
+
completions.items.push(...this.#getCSSCompletionsAtPosition(context, position, vHtml));
|
|
945
|
+
return completions;
|
|
946
|
+
});
|
|
558
947
|
}
|
|
559
948
|
getCompletionsAtPosition(context, position) {
|
|
560
949
|
return translateCompletionItemsToCompletionInfo(context, this.#getCompletionsAtPosition(context, position));
|
|
@@ -566,163 +955,326 @@ var HTMLLanguageService = class {
|
|
|
566
955
|
return translateCompletionItemsToCompletionEntryDetails(context, item);
|
|
567
956
|
}
|
|
568
957
|
#getCSSQuickInfoAtPosition(context, position, doc) {
|
|
569
|
-
const css = this.#
|
|
570
|
-
|
|
958
|
+
const css = this.#getEmbeddedCss(context, position, doc);
|
|
959
|
+
if (!css) return;
|
|
960
|
+
const hover = this.#ctx.cssLanguageService.doHover(css.vDoc, css.position, css.style, {
|
|
571
961
|
documentation: true,
|
|
572
962
|
references: true
|
|
573
963
|
});
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
range: hover.range && css.updateRange(hover.range)
|
|
577
|
-
};
|
|
964
|
+
if (!hover) return;
|
|
965
|
+
return { ...hover, range: hover.range && css.updateRange(hover.range) };
|
|
578
966
|
}
|
|
579
967
|
getQuickInfoAtPosition(context, position) {
|
|
580
|
-
const
|
|
581
|
-
const
|
|
582
|
-
const hover = this.#htmlLanguageService.doHover(virtualDocument, position, vHtml, {
|
|
968
|
+
const { vDoc, vHtml } = this.#ctx.getHtmlDoc(context.text);
|
|
969
|
+
const htmlHover = this.#ctx.htmlLanguageService.doHover(vDoc, position, vHtml, {
|
|
583
970
|
documentation: true,
|
|
584
971
|
references: true
|
|
585
|
-
})
|
|
972
|
+
});
|
|
973
|
+
const hover = htmlHover || this.#getCSSQuickInfoAtPosition(context, position, vHtml);
|
|
586
974
|
if (!hover) return;
|
|
587
975
|
return translateHover(context, hover, position);
|
|
588
976
|
}
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
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 {
|
|
977
|
+
#getCssSyntacticDiagnostics(context) {
|
|
978
|
+
return this.#diagnosticsCache.get(context, void 0, () => {
|
|
979
|
+
const { vHtml } = this.#ctx.getHtmlDoc(context.text);
|
|
980
|
+
const file = this.#ctx.getProgram().getSourceFile(context.fileName);
|
|
981
|
+
const styles = this.#getAllStyleSheet(vHtml);
|
|
982
|
+
const diagnostics = [];
|
|
983
|
+
styles.forEach((node) => {
|
|
984
|
+
const text = context.text.slice(node.startTagEnd, node.endTagStart);
|
|
985
|
+
const { vDoc, vCss } = this.#ctx.getCssDoc(text);
|
|
986
|
+
this.#ctx.cssLanguageService.doValidation(vDoc, vCss).forEach(({ message, range }) => {
|
|
987
|
+
const start = node.startTagEnd + vDoc.offsetAt(range.start);
|
|
988
|
+
const end = node.startTagEnd + vDoc.offsetAt(range.end);
|
|
989
|
+
diagnostics.push({
|
|
606
990
|
category: context.typescript.DiagnosticCategory.Warning,
|
|
607
991
|
code: 0,
|
|
608
992
|
file,
|
|
609
993
|
start,
|
|
610
994
|
length: end - start,
|
|
611
|
-
messageText: message
|
|
612
|
-
|
|
995
|
+
messageText: message,
|
|
996
|
+
source: NAME
|
|
997
|
+
});
|
|
613
998
|
});
|
|
614
|
-
})
|
|
615
|
-
|
|
999
|
+
});
|
|
1000
|
+
return diagnostics;
|
|
1001
|
+
});
|
|
1002
|
+
}
|
|
1003
|
+
#getHtmlSyntacticDiagnostics(context) {
|
|
1004
|
+
const offset = context.node.getStart() + 1;
|
|
1005
|
+
const { vHtml } = this.#ctx.getHtmlDoc(context.text);
|
|
1006
|
+
const program = this.#ctx.getProgram();
|
|
1007
|
+
const file = program.getSourceFile(context.fileName);
|
|
1008
|
+
const typeChecker = program.getTypeChecker();
|
|
1009
|
+
const diagnostics = [];
|
|
1010
|
+
forEachNode(vHtml.roots, (node) => {
|
|
1011
|
+
if (!node.tag) return;
|
|
1012
|
+
if (isCustomElementTag(node.tag) && !this.#ctx.elements.get(node.tag)) {
|
|
1013
|
+
diagnostics.push({
|
|
1014
|
+
category: context.typescript.DiagnosticCategory.Warning,
|
|
1015
|
+
code: 101 /* UnknownTag */,
|
|
1016
|
+
file,
|
|
1017
|
+
start: node.start + 1,
|
|
1018
|
+
length: node.tag.length,
|
|
1019
|
+
messageText: `Unknown element tag '${node.tag}'`,
|
|
1020
|
+
source: NAME
|
|
1021
|
+
});
|
|
1022
|
+
}
|
|
1023
|
+
const tagDeclaration = this.#ctx.elements.get(node.tag) || this.#ctx.builtInElements.get(node.tag);
|
|
1024
|
+
if (!tagDeclaration) return;
|
|
1025
|
+
for (const [attributeName, { value, start, end }] of node.attributesMap) {
|
|
1026
|
+
if (attributeName.startsWith("_")) continue;
|
|
1027
|
+
const hasValueSpan = value?.startsWith("_");
|
|
1028
|
+
const attrInfo = getAttrName(attributeName);
|
|
1029
|
+
const propType = getPropType(typeChecker, tagDeclaration, attrInfo);
|
|
1030
|
+
const diagnostic = {
|
|
1031
|
+
category: context.typescript.DiagnosticCategory.Warning,
|
|
1032
|
+
file,
|
|
1033
|
+
start,
|
|
1034
|
+
length: end - start,
|
|
1035
|
+
source: NAME,
|
|
1036
|
+
code: 103 /* PropTypeError */,
|
|
1037
|
+
messageText: !propType ? `'${attributeName}' type error` : `'${attributeName}' not satisfied '${typeChecker.typeToString(propType)}'`
|
|
1038
|
+
};
|
|
1039
|
+
if ((attributeName === "v-else-if" || attributeName === "v-else") && !node.prev?.attributesMap.has("v-if") && !node.prev?.attributesMap.has("v-else-if")) {
|
|
1040
|
+
diagnostics.push({
|
|
1041
|
+
...diagnostic,
|
|
1042
|
+
code: 104 /* PropSyntaxError */,
|
|
1043
|
+
messageText: `'${attrInfo.attr}' syntax error`
|
|
1044
|
+
});
|
|
1045
|
+
}
|
|
1046
|
+
if (attributeName === "v-if" || attributeName === "v-else-if") {
|
|
1047
|
+
const spanType = getSpanType(this.#ctx.ts, typeChecker, file, offset, end);
|
|
1048
|
+
if (!hasValueSpan || !typeChecker.isTypeAssignableTo(spanType, typeChecker.getBooleanType())) {
|
|
1049
|
+
diagnostics.push(diagnostic);
|
|
1050
|
+
}
|
|
1051
|
+
continue;
|
|
1052
|
+
}
|
|
1053
|
+
if (attributeName === "v-else") {
|
|
1054
|
+
if (value !== null) {
|
|
1055
|
+
diagnostics.push(diagnostic);
|
|
1056
|
+
}
|
|
1057
|
+
continue;
|
|
1058
|
+
}
|
|
1059
|
+
if (!propType) {
|
|
1060
|
+
if (attrInfo.decorate !== "@") {
|
|
1061
|
+
diagnostics.push({
|
|
1062
|
+
...diagnostic,
|
|
1063
|
+
code: 102 /* UnknownProp */,
|
|
1064
|
+
messageText: `Unknown property '${attrInfo.attr}'`
|
|
1065
|
+
});
|
|
1066
|
+
}
|
|
1067
|
+
continue;
|
|
1068
|
+
}
|
|
1069
|
+
if (value === null) {
|
|
1070
|
+
if (attrInfo.decorate) {
|
|
1071
|
+
diagnostics.push({
|
|
1072
|
+
...diagnostic,
|
|
1073
|
+
code: 104 /* PropSyntaxError */,
|
|
1074
|
+
messageText: `Consider using '${camelToKebabCase(attrInfo.attr)}'`
|
|
1075
|
+
});
|
|
1076
|
+
} else if (propType !== typeChecker.getBooleanType()) {
|
|
1077
|
+
diagnostics.push(diagnostic);
|
|
1078
|
+
}
|
|
1079
|
+
continue;
|
|
1080
|
+
}
|
|
1081
|
+
if (hasValueSpan) {
|
|
1082
|
+
const spanType = getSpanType(this.#ctx.ts, typeChecker, file, offset, end);
|
|
1083
|
+
switch (attrInfo.decorate) {
|
|
1084
|
+
case "?": {
|
|
1085
|
+
const boolType = getUnionType(typeChecker, [
|
|
1086
|
+
typeChecker.getBooleanType(),
|
|
1087
|
+
typeChecker.getUndefinedType(),
|
|
1088
|
+
typeChecker.getNullType()
|
|
1089
|
+
]);
|
|
1090
|
+
if (!typeChecker.isTypeAssignableTo(spanType, boolType)) {
|
|
1091
|
+
diagnostics.push(diagnostic);
|
|
1092
|
+
}
|
|
1093
|
+
continue;
|
|
1094
|
+
}
|
|
1095
|
+
case ".":
|
|
1096
|
+
case "@":
|
|
1097
|
+
if (!typeChecker.isTypeAssignableTo(spanType, propType)) {
|
|
1098
|
+
diagnostics.push(diagnostic);
|
|
1099
|
+
}
|
|
1100
|
+
continue;
|
|
1101
|
+
default:
|
|
1102
|
+
if (!typeChecker.isTypeAssignableTo(spanType, propType) && (!typeChecker.isTypeAssignableTo(propType, typeChecker.getStringType()) || !typeChecker.isTypeAssignableTo(spanType, typeChecker.getStringType()))) {
|
|
1103
|
+
diagnostics.push(diagnostic);
|
|
1104
|
+
}
|
|
1105
|
+
continue;
|
|
1106
|
+
}
|
|
1107
|
+
} else {
|
|
1108
|
+
const types = [typeChecker.getStringType(), typeChecker.getNumberType()];
|
|
1109
|
+
const valueLetter = value.startsWith('"') ? value.slice(1, -1) : value;
|
|
1110
|
+
types.push(typeChecker.getStringLiteralType(valueLetter));
|
|
1111
|
+
const numberValue = Number(valueLetter);
|
|
1112
|
+
if (!Number.isNaN(numberValue)) {
|
|
1113
|
+
types.push(typeChecker.getNumberLiteralType(numberValue));
|
|
1114
|
+
}
|
|
1115
|
+
if (attrInfo.decorate) {
|
|
1116
|
+
diagnostics.push({
|
|
1117
|
+
...diagnostic,
|
|
1118
|
+
code: 104 /* PropSyntaxError */,
|
|
1119
|
+
messageText: `Consider using '${camelToKebabCase(attrInfo.attr)}'`
|
|
1120
|
+
});
|
|
1121
|
+
} else if (types.every((t) => !typeChecker.isTypeAssignableTo(t, propType))) {
|
|
1122
|
+
diagnostics.push(diagnostic);
|
|
1123
|
+
}
|
|
1124
|
+
}
|
|
1125
|
+
}
|
|
1126
|
+
});
|
|
1127
|
+
return diagnostics;
|
|
1128
|
+
}
|
|
1129
|
+
getSyntacticDiagnostics(context) {
|
|
1130
|
+
this.#ctx.initElements();
|
|
1131
|
+
return [...this.#getCssSyntacticDiagnostics(context), ...this.#getHtmlSyntacticDiagnostics(context)];
|
|
616
1132
|
}
|
|
617
1133
|
getDefinitionAndBoundSpan(context, position) {
|
|
618
|
-
const
|
|
619
|
-
const
|
|
620
|
-
const
|
|
621
|
-
const
|
|
622
|
-
const
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
1134
|
+
const currentOffset = context.toOffset(position);
|
|
1135
|
+
const { vHtml } = this.#ctx.getHtmlDoc(context.text);
|
|
1136
|
+
const node = vHtml.findNodeAt(currentOffset);
|
|
1137
|
+
const { text, start, length, before } = getHTMLTextAtPosition(context.text, currentOffset);
|
|
1138
|
+
const empty = { textSpan: { start, length } };
|
|
1139
|
+
if (node.tag === "style" && currentOffset > node.startTagEnd) {
|
|
1140
|
+
const { style, vDoc, position: pos } = this.#getEmbeddedCss(context, position, vHtml);
|
|
1141
|
+
const cssNode = style.findChildAtOffset(vDoc.offsetAt(pos), true);
|
|
1142
|
+
if (!cssNode) return empty;
|
|
1143
|
+
const ident = vDoc.getText().slice(cssNode.offset, cssNode.end);
|
|
1144
|
+
const definitionNode2 = this.#ctx.elements.get(ident);
|
|
1145
|
+
if (!definitionNode2) return empty;
|
|
1146
|
+
return genElementDefinitionInfo(
|
|
1147
|
+
context,
|
|
1148
|
+
{ start: cssNode.offset + node.startTagEnd, length: cssNode.length },
|
|
1149
|
+
definitionNode2
|
|
1150
|
+
);
|
|
627
1151
|
}
|
|
1152
|
+
const definitionNode = this.#ctx.elements.get(node.tag) || this.#ctx.builtInElements.get(node.tag);
|
|
1153
|
+
if (!definitionNode || currentOffset > node.startTagEnd || !text) return empty;
|
|
628
1154
|
if (text === node.tag) {
|
|
629
|
-
return {
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
}
|
|
642
|
-
}
|
|
643
|
-
]
|
|
644
|
-
};
|
|
1155
|
+
return genElementDefinitionInfo(context, { start, length }, definitionNode);
|
|
1156
|
+
}
|
|
1157
|
+
const { attr, offset } = getAttrName(text);
|
|
1158
|
+
if (before.length > attr.length) return empty;
|
|
1159
|
+
if (attr === "v-else" || attr === "v-else-if") {
|
|
1160
|
+
const ifAttr = node.prev?.attributesMap.get("v-if") || node.prev?.attributesMap.get("v-else-if");
|
|
1161
|
+
if (!ifAttr) return empty;
|
|
1162
|
+
return genCurrentCtxDefinitionInfo(
|
|
1163
|
+
context,
|
|
1164
|
+
{ start: start + offset, length: attr.length },
|
|
1165
|
+
{ start: ifAttr.start, length: ifAttr.end - ifAttr.start }
|
|
1166
|
+
);
|
|
645
1167
|
}
|
|
646
|
-
const { attr, type } = getAttrName(text);
|
|
647
1168
|
const typeChecker = this.#ctx.getProgram().getTypeChecker();
|
|
648
1169
|
const propSymbol = typeChecker.getTypeAtLocation(definitionNode).getProperty(kebabToCamelCase(attr));
|
|
649
1170
|
const propDeclaration = propSymbol?.getDeclarations()?.at(0);
|
|
650
|
-
return
|
|
651
|
-
|
|
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
|
-
};
|
|
1171
|
+
if (!propDeclaration) return empty;
|
|
1172
|
+
return genAttrDefinitionInfo(context, { start: start + offset, length: attr.length }, propDeclaration);
|
|
666
1173
|
}
|
|
667
1174
|
};
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
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;
|
|
1175
|
+
function getSpanExpression(typescript, file, pos) {
|
|
1176
|
+
let node = getAstNodeAtPosition(typescript, file, pos);
|
|
1177
|
+
while (!typescript.isTemplateSpan(node)) {
|
|
1178
|
+
node = node.parent;
|
|
1179
|
+
if (!node) return;
|
|
1180
|
+
}
|
|
1181
|
+
return node.expression;
|
|
687
1182
|
}
|
|
688
|
-
function
|
|
689
|
-
const
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
1183
|
+
function getSpanType(typescript, typeChecker, file, htmlOffset, attrNameEnd) {
|
|
1184
|
+
const valueOffset = attrNameEnd + htmlOffset + 3;
|
|
1185
|
+
const spanExp = getSpanExpression(typescript, file, valueOffset);
|
|
1186
|
+
return typeChecker.getTypeAtLocation(spanExp);
|
|
1187
|
+
}
|
|
1188
|
+
function getPropType(typeChecker, tagClassDeclaration, attrInfo) {
|
|
1189
|
+
const classType = typeChecker.getTypeAtLocation(tagClassDeclaration);
|
|
1190
|
+
if (attrInfo.attr.startsWith("data-")) {
|
|
1191
|
+
return typeChecker.getStringType();
|
|
1192
|
+
}
|
|
1193
|
+
const propName = kebabToCamelCase(attrInfo.attr);
|
|
1194
|
+
switch (propName) {
|
|
1195
|
+
case "class":
|
|
1196
|
+
case "style":
|
|
1197
|
+
case "part":
|
|
1198
|
+
case "exportparts":
|
|
1199
|
+
case "xmlns":
|
|
1200
|
+
case "viewBox":
|
|
1201
|
+
return typeChecker.getStringType();
|
|
1202
|
+
case "tabindex":
|
|
1203
|
+
return typeChecker.getNumberType();
|
|
1204
|
+
case "ariaDisabled":
|
|
1205
|
+
case "ariaChecked":
|
|
1206
|
+
case "ariaHidden":
|
|
1207
|
+
return getUnionType(typeChecker, [
|
|
1208
|
+
typeChecker.getStringType(),
|
|
1209
|
+
typeChecker.getBooleanType(),
|
|
1210
|
+
typeChecker.getUndefinedType()
|
|
1211
|
+
]);
|
|
1212
|
+
default: {
|
|
1213
|
+
const isEvent = attrInfo.decorate === "@";
|
|
1214
|
+
const propSymbol = classType.getProperty(propName);
|
|
1215
|
+
const propType = propSymbol && typeChecker.getTypeOfSymbol(propSymbol);
|
|
1216
|
+
if (!isEvent) return propType;
|
|
1217
|
+
const eventHandleType = getEmitterHandleType(typeChecker, classType, propType);
|
|
1218
|
+
return getUnionType(typeChecker, [eventHandleType, typeChecker.getUndefinedType()]);
|
|
694
1219
|
}
|
|
695
|
-
}
|
|
1220
|
+
}
|
|
1221
|
+
}
|
|
1222
|
+
function getEmitterHandleType(typeChecker, classType, propType) {
|
|
1223
|
+
const handleSymbol = propType?.getProperty("handler");
|
|
1224
|
+
if (handleSymbol) return typeChecker.getTypeOfSymbol(handleSymbol);
|
|
1225
|
+
const addEventListenerSymbol = classType.getProperty("addEventListener");
|
|
1226
|
+
if (!addEventListenerSymbol) return typeChecker.getAnyType();
|
|
1227
|
+
const addEventListenerDecls = addEventListenerSymbol.declarations;
|
|
1228
|
+
const addEventListenerDecl = addEventListenerDecls.find((e) => !e.typeParameters);
|
|
1229
|
+
const listenerDecl = addEventListenerDecl.parameters.at(1);
|
|
1230
|
+
if (!listenerDecl) return typeChecker.getAnyType();
|
|
1231
|
+
return typeChecker.getTypeAtLocation(listenerDecl);
|
|
1232
|
+
}
|
|
1233
|
+
function getUnionType(typeChecker, types) {
|
|
1234
|
+
if ("getUnionType" in typeChecker) {
|
|
1235
|
+
return typeChecker.getUnionType(types);
|
|
1236
|
+
}
|
|
1237
|
+
return types.at(0);
|
|
696
1238
|
}
|
|
1239
|
+
|
|
1240
|
+
// src/decorate-ts.ts
|
|
697
1241
|
function decorateLanguageService(ctx, languageService) {
|
|
698
1242
|
const { ts, getProgram } = ctx;
|
|
699
|
-
const ls =
|
|
700
|
-
Object.entries(languageService).map(([key, value]) => [key, value.bind(languageService)])
|
|
701
|
-
);
|
|
1243
|
+
const ls = bindMemberFunction(languageService);
|
|
702
1244
|
languageService.getCompletionsAtPosition = (...args) => {
|
|
703
1245
|
const program = getProgram();
|
|
704
1246
|
const typeChecker = program.getTypeChecker();
|
|
705
1247
|
decorate(typeChecker, () => decorateTypeChecker(ctx, typeChecker));
|
|
706
1248
|
return ls.getCompletionsAtPosition(...args);
|
|
707
1249
|
};
|
|
1250
|
+
languageService.getSuggestionDiagnostics = (...args) => {
|
|
1251
|
+
const program = getProgram();
|
|
1252
|
+
const file = program.getSourceFile(args[0]);
|
|
1253
|
+
const result = ls.getSuggestionDiagnostics(...args);
|
|
1254
|
+
ctx.updateElement(file);
|
|
1255
|
+
return result.filter(({ start, reportsUnnecessary, category }) => {
|
|
1256
|
+
if (!reportsUnnecessary || category !== ts.DiagnosticCategory.Suggestion) return true;
|
|
1257
|
+
const node = getAstNodeAtPosition(ts, file, start);
|
|
1258
|
+
if (!node || !ts.isPrivateIdentifier(node)) return true;
|
|
1259
|
+
const declaration = node.parent;
|
|
1260
|
+
if (!ts.isMethodDeclaration(declaration) && !ts.isPropertyDeclaration(declaration)) return true;
|
|
1261
|
+
return !declaration.modifiers?.some((e) => e?.kind === ts.SyntaxKind.Decorator);
|
|
1262
|
+
});
|
|
1263
|
+
};
|
|
708
1264
|
languageService.findReferences = (...args) => {
|
|
1265
|
+
const oResult = ls.findReferences(...args) || [];
|
|
709
1266
|
const program = getProgram();
|
|
710
|
-
const result = [];
|
|
711
1267
|
const currentNode = getAstNodeAtPosition(ts, program.getSourceFile(args[0]), args[1]);
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
});
|
|
723
|
-
if (!references.length) continue;
|
|
724
|
-
result.push({
|
|
725
|
-
references,
|
|
1268
|
+
if (!currentNode) return oResult;
|
|
1269
|
+
const isIdent = ts.isIdentifier(currentNode);
|
|
1270
|
+
if (!isIdent) return oResult;
|
|
1271
|
+
const currentTag = ctx.getTagFromNode(currentNode.parent) || ctx.getTagFromNode(currentNode.parent.parent);
|
|
1272
|
+
const prop = ts.isClassDeclaration(currentNode.parent.parent) && currentNode;
|
|
1273
|
+
if (!currentTag) return oResult;
|
|
1274
|
+
const map = /* @__PURE__ */ new Map();
|
|
1275
|
+
forEachAllHtmlTemplateNode(ctx, currentTag, (file, tagInfo) => {
|
|
1276
|
+
const symbol = map.get(file.fileName) || {
|
|
1277
|
+
references: [],
|
|
726
1278
|
definition: {
|
|
727
1279
|
containerKind: ctx.ts.ScriptElementKind.unknown,
|
|
728
1280
|
containerName: "",
|
|
@@ -732,174 +1284,163 @@ function decorateLanguageService(ctx, languageService) {
|
|
|
732
1284
|
name: "test",
|
|
733
1285
|
kind: ctx.ts.ScriptElementKind.unknown
|
|
734
1286
|
}
|
|
735
|
-
}
|
|
1287
|
+
};
|
|
1288
|
+
map.set(file.fileName, symbol);
|
|
1289
|
+
if (prop) {
|
|
1290
|
+
const propNames = /* @__PURE__ */ new Set([`.${prop.text}`]);
|
|
1291
|
+
const kebabCaseName = camelToKebabCase(prop.text);
|
|
1292
|
+
["", "?", "@"].forEach((c) => propNames.add(`${c}${kebabCaseName}`));
|
|
1293
|
+
for (const propName of propNames) {
|
|
1294
|
+
const info = tagInfo.node.attributesMap.get(propName);
|
|
1295
|
+
if (!info) continue;
|
|
1296
|
+
const textSpan = { start: info.start + tagInfo.offset, length: info.end - info.start };
|
|
1297
|
+
symbol.references.push({ fileName: file.fileName, isWriteAccess: true, textSpan });
|
|
1298
|
+
}
|
|
1299
|
+
} else {
|
|
1300
|
+
symbol.references.push({
|
|
1301
|
+
fileName: file.fileName,
|
|
1302
|
+
isWriteAccess: true,
|
|
1303
|
+
textSpan: tagInfo.open
|
|
1304
|
+
});
|
|
1305
|
+
}
|
|
1306
|
+
});
|
|
1307
|
+
return [...map.values(), ...oResult];
|
|
1308
|
+
};
|
|
1309
|
+
languageService.getRenameInfo = (fileName, position, ...args) => {
|
|
1310
|
+
const result = ls.getRenameInfo(fileName, position, ...args);
|
|
1311
|
+
const tagInfo = findCurrentTagInfo(ctx, fileName, position);
|
|
1312
|
+
if (tagInfo) {
|
|
1313
|
+
return {
|
|
1314
|
+
canRename: true,
|
|
1315
|
+
displayName: tagInfo.tag,
|
|
1316
|
+
fullDisplayName: tagInfo.tag,
|
|
1317
|
+
kind: ts.ScriptElementKind.alias,
|
|
1318
|
+
kindModifiers: "tag",
|
|
1319
|
+
triggerSpan: tagInfo.open
|
|
1320
|
+
};
|
|
736
1321
|
}
|
|
737
|
-
|
|
1322
|
+
const tagDefinedInfo = findDefinedTagInfo(ctx, fileName, position);
|
|
1323
|
+
if (tagDefinedInfo) {
|
|
1324
|
+
return {
|
|
1325
|
+
canRename: true,
|
|
1326
|
+
displayName: tagDefinedInfo.tag,
|
|
1327
|
+
fullDisplayName: tagDefinedInfo.tag,
|
|
1328
|
+
kind: ts.ScriptElementKind.alias,
|
|
1329
|
+
kindModifiers: "tag",
|
|
1330
|
+
triggerSpan: tagDefinedInfo.textSpan
|
|
1331
|
+
};
|
|
1332
|
+
}
|
|
1333
|
+
return result;
|
|
738
1334
|
};
|
|
739
|
-
languageService.
|
|
740
|
-
const
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
const
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
1335
|
+
languageService.findRenameLocations = (fileName, position, ...args) => {
|
|
1336
|
+
const tagPairInfo = findCurrentTagInfo(ctx, fileName, position);
|
|
1337
|
+
if (tagPairInfo) {
|
|
1338
|
+
const result = [{ fileName, textSpan: tagPairInfo.open }];
|
|
1339
|
+
if (tagPairInfo.end) result.push({ fileName, textSpan: tagPairInfo.end });
|
|
1340
|
+
return result;
|
|
1341
|
+
}
|
|
1342
|
+
const tagDefinedInfo = findDefinedTagInfo(ctx, fileName, position);
|
|
1343
|
+
if (tagDefinedInfo) {
|
|
1344
|
+
const result = [{ fileName, textSpan: tagDefinedInfo.textSpan }];
|
|
1345
|
+
forEachAllHtmlTemplateNode(ctx, tagDefinedInfo.tag, (f, info) => {
|
|
1346
|
+
result.push({ fileName: f.fileName, textSpan: info.open });
|
|
1347
|
+
if (info.end) result.push({ fileName: f.fileName, textSpan: info.end });
|
|
1348
|
+
});
|
|
1349
|
+
return result;
|
|
1350
|
+
}
|
|
1351
|
+
const oResult = [...ls.findRenameLocations(fileName, position, ...args) || []];
|
|
1352
|
+
const file = ctx.getProgram().getSourceFile(fileName);
|
|
1353
|
+
const node = getAstNodeAtPosition(ctx.ts, file, position);
|
|
1354
|
+
const tag = node && ts.isPropertyDeclaration(node.parent) && ctx.getTagFromNode(node.parent.parent);
|
|
1355
|
+
if (!tag) return oResult;
|
|
1356
|
+
const propText = node.getText();
|
|
1357
|
+
const kebabCaseName = camelToKebabCase(propText);
|
|
1358
|
+
if (isPropType(ctx.ts, node.parent, ["emitter", "globalemitter"])) {
|
|
1359
|
+
forEachAllHtmlTemplateNode(ctx, tag, (f, tagInfo) => {
|
|
1360
|
+
const info = tagInfo.node.attributesMap.get(`@${kebabCaseName}`);
|
|
1361
|
+
if (!info) return;
|
|
1362
|
+
const textSpan = { start: info.start + tagInfo.offset, length: info.end - info.start };
|
|
1363
|
+
oResult.push({ fileName: f.fileName, prefixText: "@", textSpan });
|
|
1364
|
+
});
|
|
1365
|
+
}
|
|
1366
|
+
if (isPropType(ctx.ts, node.parent, ["attribute", "numattribute", "boolattribute", "property"])) {
|
|
1367
|
+
forEachAllHtmlTemplateNode(ctx, tag, (f, tagInfo) => {
|
|
1368
|
+
const propNames = ["", ".", "?"].map((c) => `${c}${kebabCaseName}`);
|
|
1369
|
+
propNames.map((propName) => {
|
|
1370
|
+
const info = tagInfo.node.attributesMap.get(propName);
|
|
1371
|
+
if (!info) return;
|
|
1372
|
+
const textSpan = { start: info.start + tagInfo.offset, length: info.end - info.start };
|
|
1373
|
+
oResult.push({ fileName: f.fileName, prefixText: ".", textSpan });
|
|
1374
|
+
});
|
|
1375
|
+
});
|
|
1376
|
+
}
|
|
1377
|
+
return oResult;
|
|
756
1378
|
};
|
|
757
1379
|
return languageService;
|
|
758
1380
|
}
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
};
|
|
768
|
-
|
|
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();
|
|
1381
|
+
function forEachAllHtmlTemplateNode(ctx, tag, fn) {
|
|
1382
|
+
for (const file of ctx.getProgram().getSourceFiles()) {
|
|
1383
|
+
if (file.fileName.endsWith(".d.ts")) continue;
|
|
1384
|
+
for (const templateContext of ctx.htmlSourceHelper.getAllTemplates(file.fileName)) {
|
|
1385
|
+
const { vHtml } = ctx.getHtmlDoc(templateContext.text);
|
|
1386
|
+
forEachNode(vHtml.roots, (node) => {
|
|
1387
|
+
if (node.tag !== tag) return;
|
|
1388
|
+
fn(file, getTagInfo(node, templateContext.node.getStart() + 1));
|
|
1389
|
+
});
|
|
1390
|
+
}
|
|
797
1391
|
}
|
|
798
|
-
|
|
799
|
-
|
|
1392
|
+
}
|
|
1393
|
+
function findCurrentTagInfo(ctx, fileName, position) {
|
|
1394
|
+
const templateContext = ctx.htmlSourceHelper.getTemplate(fileName, position);
|
|
1395
|
+
if (!templateContext) return;
|
|
1396
|
+
const htmlOffset = templateContext.node.pos + 1;
|
|
1397
|
+
const { vHtml } = ctx.getHtmlDoc(templateContext.text);
|
|
1398
|
+
const relativePosition = ctx.htmlSourceHelper.getRelativePosition(templateContext, position);
|
|
1399
|
+
const offset = templateContext.toOffset(relativePosition);
|
|
1400
|
+
const node = vHtml.findNodeAt(offset);
|
|
1401
|
+
const { text } = getHTMLTextAtPosition(templateContext.text, offset);
|
|
1402
|
+
const onTag = offset < node.startTagEnd && text === node.tag;
|
|
1403
|
+
if (!onTag || !node.tag) return;
|
|
1404
|
+
return getTagInfo(node, htmlOffset);
|
|
1405
|
+
}
|
|
1406
|
+
function findDefinedTagInfo(ctx, fileName, position) {
|
|
1407
|
+
const file = ctx.getProgram().getSourceFile(fileName);
|
|
1408
|
+
const node = getAstNodeAtPosition(ctx.ts, file, position);
|
|
1409
|
+
if (!node || !ctx.ts.isStringLiteral(node) || !ctx.ts.isCallExpression(node.parent) || node.parent.expression.getText() !== "customElement") {
|
|
1410
|
+
return;
|
|
800
1411
|
}
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
1412
|
+
const tag = node.text;
|
|
1413
|
+
return { tag, textSpan: { start: node.getStart() + 1, length: tag.length } };
|
|
1414
|
+
}
|
|
1415
|
+
function isPropType(typescript, node, types) {
|
|
1416
|
+
if (!typescript.isPropertyDeclaration(node)) return;
|
|
1417
|
+
for (const modifier of node.modifiers || []) {
|
|
1418
|
+
if (!typescript.isDecorator(modifier)) continue;
|
|
1419
|
+
const { expression } = modifier;
|
|
1420
|
+
if (typescript.isIdentifier(expression) && types.includes(expression.text)) {
|
|
1421
|
+
return true;
|
|
805
1422
|
}
|
|
806
1423
|
}
|
|
807
|
-
}
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
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
|
-
};
|
|
1424
|
+
}
|
|
1425
|
+
function decorateTypeChecker(ctx, typeChecker) {
|
|
1426
|
+
const internal = typeChecker;
|
|
1427
|
+
const checker = bindMemberFunction(internal, ["isValidPropertyAccessForCompletions"]);
|
|
1428
|
+
internal.isValidPropertyAccessForCompletions = (...args) => {
|
|
1429
|
+
const result = checker.isValidPropertyAccessForCompletions(...args);
|
|
1430
|
+
if (!result) return false;
|
|
1431
|
+
try {
|
|
1432
|
+
const { declarations } = args.at(2);
|
|
1433
|
+
if (!declarations) return true;
|
|
1434
|
+
const isNever = declarations.every(
|
|
1435
|
+
(node) => ctx.ts.isPropertySignature(node) && node.type?.getText() === "never"
|
|
1436
|
+
);
|
|
1437
|
+
return !isNever;
|
|
1438
|
+
} catch {
|
|
1439
|
+
return true;
|
|
834
1440
|
}
|
|
835
|
-
return {
|
|
836
|
-
offset: 0,
|
|
837
|
-
text: context.text,
|
|
838
|
-
pos: { ...position }
|
|
839
|
-
};
|
|
840
1441
|
};
|
|
841
|
-
|
|
842
|
-
|
|
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
|
-
};
|
|
1442
|
+
return typeChecker;
|
|
1443
|
+
}
|
|
903
1444
|
|
|
904
1445
|
// src/index.ts
|
|
905
1446
|
var LanguageServiceLogger = class {
|
|
@@ -908,7 +1449,7 @@ var LanguageServiceLogger = class {
|
|
|
908
1449
|
this.#info = info;
|
|
909
1450
|
}
|
|
910
1451
|
log(msg) {
|
|
911
|
-
this.#info.project.projectService.logger.info(`[
|
|
1452
|
+
this.#info.project.projectService.logger.info(`[${NAME}] ${msg}`);
|
|
912
1453
|
}
|
|
913
1454
|
};
|
|
914
1455
|
var HtmlPlugin = class {
|
|
@@ -920,32 +1461,16 @@ var HtmlPlugin = class {
|
|
|
920
1461
|
create(info) {
|
|
921
1462
|
return decorate(info.languageService, () => {
|
|
922
1463
|
const logger = new LanguageServiceLogger(info);
|
|
923
|
-
logger.log("Starting
|
|
1464
|
+
logger.log("Starting...");
|
|
924
1465
|
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
|
-
};
|
|
1466
|
+
const context = new Context(this.#ts, this.#config, info, logger);
|
|
937
1467
|
const decoratedService = decorateLanguageService(context, info.languageService);
|
|
938
1468
|
const decoratedService1 = (0, import_typescript_template_language_service_decorator.decorateWithTemplateLanguageService)(
|
|
939
1469
|
this.#ts,
|
|
940
1470
|
decoratedService,
|
|
941
1471
|
info.project,
|
|
942
1472
|
new CSSLanguageService(context),
|
|
943
|
-
|
|
944
|
-
tags: ["styled", "css"],
|
|
945
|
-
enableForStringWithSubstitutions: true,
|
|
946
|
-
getSubstitution,
|
|
947
|
-
isValidTemplate: (node) => isValidCSSTemplate(this.#ts, node, "css")
|
|
948
|
-
},
|
|
1473
|
+
context.cssTemplateStringSettings,
|
|
949
1474
|
{ logger }
|
|
950
1475
|
);
|
|
951
1476
|
return (0, import_typescript_template_language_service_decorator.decorateWithTemplateLanguageService)(
|
|
@@ -953,11 +1478,7 @@ var HtmlPlugin = class {
|
|
|
953
1478
|
decoratedService1,
|
|
954
1479
|
info.project,
|
|
955
1480
|
new HTMLLanguageService(context),
|
|
956
|
-
|
|
957
|
-
tags: ["html", "raw", "h"],
|
|
958
|
-
enableForStringWithSubstitutions: true,
|
|
959
|
-
getSubstitution
|
|
960
|
-
},
|
|
1481
|
+
context.htmlTemplateStringSettings,
|
|
961
1482
|
{ logger }
|
|
962
1483
|
);
|
|
963
1484
|
});
|