ts-gem-plugin 0.0.1 → 0.0.6
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 +103 -34
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -299,9 +299,13 @@ var HTMLDataProvider = class {
|
|
|
299
299
|
const ts = this.#ts;
|
|
300
300
|
const typeChecker = this.#getProgram().getTypeChecker();
|
|
301
301
|
const node = this.#elements.get(tag);
|
|
302
|
-
|
|
302
|
+
const result = [
|
|
303
|
+
{ name: "v-if", description: "Similar to vue `v-if`" },
|
|
304
|
+
{ name: "v-else-if", description: "Similar to vue `v-else-if`" },
|
|
305
|
+
{ name: "v-else", description: "Similar to vue `v-else`", valueSet: "v" }
|
|
306
|
+
];
|
|
307
|
+
if (!node) return result;
|
|
303
308
|
const isDep = isDepElement(node);
|
|
304
|
-
const result = [];
|
|
305
309
|
const props = typeChecker.getTypeAtLocation(node).getApparentProperties();
|
|
306
310
|
props.forEach((e) => {
|
|
307
311
|
const declaration = e.getDeclarations()?.at(0);
|
|
@@ -322,7 +326,7 @@ var HTMLDataProvider = class {
|
|
|
322
326
|
result.push({ name: `?${e.name}`, description });
|
|
323
327
|
break;
|
|
324
328
|
}
|
|
325
|
-
if (
|
|
329
|
+
if (type && getUnionValues(type)) {
|
|
326
330
|
result.push({ name: e.name, description });
|
|
327
331
|
}
|
|
328
332
|
if (typeText?.startsWith("Emitter")) {
|
|
@@ -345,31 +349,18 @@ var HTMLDataProvider = class {
|
|
|
345
349
|
const node = this.#elements.get(tag);
|
|
346
350
|
if (!node) return [];
|
|
347
351
|
const prop = typeChecker.getTypeAtLocation(node).getProperty(getAttrName(attr).attr);
|
|
348
|
-
const
|
|
349
|
-
const result = getBasicUnionValues(declaration);
|
|
352
|
+
const result = prop && getUnionValues(typeChecker.getTypeOfSymbol(prop));
|
|
350
353
|
return result?.map((name) => ({ name })) || [];
|
|
351
354
|
}
|
|
352
355
|
};
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
const typeText = node?.type?.getText();
|
|
356
|
-
if (!typeText) return;
|
|
356
|
+
function getUnionValues(type) {
|
|
357
|
+
if (!type.isUnion()) return;
|
|
357
358
|
const result = [];
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
result.push(t);
|
|
364
|
-
continue;
|
|
365
|
-
}
|
|
366
|
-
const match = t.match(STRING_REG);
|
|
367
|
-
if (!match) {
|
|
368
|
-
return;
|
|
369
|
-
}
|
|
370
|
-
result.push(match.groups.str);
|
|
371
|
-
}
|
|
372
|
-
if (result.length) return result;
|
|
359
|
+
type.types.forEach((e) => {
|
|
360
|
+
if (!e.isLiteral()) return;
|
|
361
|
+
result.push(String(e.value));
|
|
362
|
+
});
|
|
363
|
+
return result;
|
|
373
364
|
}
|
|
374
365
|
var COMMENT_LINE_CONTENT = /^(\/?[ *\t]*)?(?<str>.*?)(\**\/)?$/gm;
|
|
375
366
|
function getDocComment(typescript, declaration) {
|
|
@@ -438,7 +429,7 @@ var Cache = class {
|
|
|
438
429
|
// src/cache.ts
|
|
439
430
|
var LRUCache = class {
|
|
440
431
|
#bucket;
|
|
441
|
-
constructor(
|
|
432
|
+
constructor(args) {
|
|
442
433
|
this.#bucket = new Cache({ max: 25, renewal: true, ...args });
|
|
443
434
|
}
|
|
444
435
|
#genKey(context, position) {
|
|
@@ -508,6 +499,11 @@ var Context = class {
|
|
|
508
499
|
return this.#virtualHtmlCache.get({ text, fileName: "" }, void 0, () => {
|
|
509
500
|
const vDoc = createVirtualDocument("html", text);
|
|
510
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
|
+
});
|
|
511
507
|
return { vDoc, vHtml };
|
|
512
508
|
});
|
|
513
509
|
}
|
|
@@ -549,12 +545,34 @@ var Context = class {
|
|
|
549
545
|
const name = symbol.escapedName.toString();
|
|
550
546
|
const match = name.match(/^(SVG|HTML)(\w*)Element$/);
|
|
551
547
|
const declaration = symbol.declarations?.find((e) => this.ts.isInterfaceDeclaration(e));
|
|
552
|
-
if (match
|
|
553
|
-
|
|
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);
|
|
554
553
|
}
|
|
555
554
|
});
|
|
556
555
|
}
|
|
557
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
|
+
};
|
|
558
576
|
function createVirtualDocument(languageId, content) {
|
|
559
577
|
return import_vscode_html_languageservice2.TextDocument.create(`embedded://document.${languageId}`, languageId, 1, content);
|
|
560
578
|
}
|
|
@@ -755,6 +773,21 @@ function genAttrDefinitionInfo(context, { start, length }, propDeclaration) {
|
|
|
755
773
|
]
|
|
756
774
|
};
|
|
757
775
|
}
|
|
776
|
+
function genCurrentCtxDefinitionInfo(context, { start, length }, definitionTextSpan) {
|
|
777
|
+
return {
|
|
778
|
+
textSpan: { start, length },
|
|
779
|
+
definitions: [
|
|
780
|
+
{
|
|
781
|
+
containerName: "Attribute",
|
|
782
|
+
containerKind: context.typescript.ScriptElementKind.unknown,
|
|
783
|
+
name: context.text.slice(start, start + length),
|
|
784
|
+
kind: context.typescript.ScriptElementKind.memberVariableElement,
|
|
785
|
+
fileName: context.fileName,
|
|
786
|
+
textSpan: definitionTextSpan
|
|
787
|
+
}
|
|
788
|
+
]
|
|
789
|
+
};
|
|
790
|
+
}
|
|
758
791
|
|
|
759
792
|
// src/decorate-css.ts
|
|
760
793
|
var CSSLanguageService = class {
|
|
@@ -992,7 +1025,9 @@ var HTMLLanguageService = class {
|
|
|
992
1025
|
if (!tagDeclaration) return;
|
|
993
1026
|
for (const [attributeName, { value, start, end }] of node.attributesMap) {
|
|
994
1027
|
if (attributeName.startsWith("_")) continue;
|
|
1028
|
+
const hasValueSpan = value?.startsWith("_");
|
|
995
1029
|
const attrInfo = getAttrName(attributeName);
|
|
1030
|
+
const propType = getPropType(typeChecker, tagDeclaration, attrInfo);
|
|
996
1031
|
const diagnostic = {
|
|
997
1032
|
category: context.typescript.DiagnosticCategory.Warning,
|
|
998
1033
|
file,
|
|
@@ -1000,9 +1035,28 @@ var HTMLLanguageService = class {
|
|
|
1000
1035
|
length: end - start,
|
|
1001
1036
|
source: NAME,
|
|
1002
1037
|
code: 103 /* PropTypeError */,
|
|
1003
|
-
messageText: `'${attributeName}' type error`
|
|
1038
|
+
messageText: !propType ? `'${attributeName}' type error` : `'${attributeName}' not satisfied '${typeChecker.typeToString(propType)}'`
|
|
1004
1039
|
};
|
|
1005
|
-
|
|
1040
|
+
if ((attributeName === "v-else-if" || attributeName === "v-else") && !node.prev?.attributesMap.has("v-if") && !node.prev?.attributesMap.has("v-else-if")) {
|
|
1041
|
+
diagnostics.push({
|
|
1042
|
+
...diagnostic,
|
|
1043
|
+
code: 104 /* PropSyntaxError */,
|
|
1044
|
+
messageText: `'${attrInfo.attr}' syntax error`
|
|
1045
|
+
});
|
|
1046
|
+
}
|
|
1047
|
+
if (attributeName === "v-if" || attributeName === "v-else-if") {
|
|
1048
|
+
const spanType = getSpanType(this.#ctx.ts, typeChecker, file, offset, end);
|
|
1049
|
+
if (!hasValueSpan || !typeChecker.isTypeAssignableTo(spanType, typeChecker.getBooleanType())) {
|
|
1050
|
+
diagnostics.push(diagnostic);
|
|
1051
|
+
}
|
|
1052
|
+
continue;
|
|
1053
|
+
}
|
|
1054
|
+
if (attributeName === "v-else") {
|
|
1055
|
+
if (value !== null) {
|
|
1056
|
+
diagnostics.push(diagnostic);
|
|
1057
|
+
}
|
|
1058
|
+
continue;
|
|
1059
|
+
}
|
|
1006
1060
|
if (!propType) {
|
|
1007
1061
|
if (attrInfo.decorate !== "@") {
|
|
1008
1062
|
diagnostics.push({
|
|
@@ -1025,10 +1079,8 @@ var HTMLLanguageService = class {
|
|
|
1025
1079
|
}
|
|
1026
1080
|
continue;
|
|
1027
1081
|
}
|
|
1028
|
-
if (
|
|
1029
|
-
const
|
|
1030
|
-
const spanExp = getSpanExpression(this.#ctx.ts, file, valueOffset);
|
|
1031
|
-
const spanType = typeChecker.getTypeAtLocation(spanExp);
|
|
1082
|
+
if (hasValueSpan) {
|
|
1083
|
+
const spanType = getSpanType(this.#ctx.ts, typeChecker, file, offset, end);
|
|
1032
1084
|
switch (attrInfo.decorate) {
|
|
1033
1085
|
case "?":
|
|
1034
1086
|
const boolType = getUnionType(typeChecker, [
|
|
@@ -1085,7 +1137,7 @@ var HTMLLanguageService = class {
|
|
|
1085
1137
|
const node = vHtml.findNodeAt(currentOffset);
|
|
1086
1138
|
const { text, start, length, before } = getHTMLTextAtPosition(context.text, currentOffset);
|
|
1087
1139
|
const empty = { textSpan: { start, length } };
|
|
1088
|
-
if (node.tag === "style") {
|
|
1140
|
+
if (node.tag === "style" && currentOffset > node.startTagEnd) {
|
|
1089
1141
|
const { style, vDoc, position: pos } = this.#getEmbeddedCss(context, position, vHtml);
|
|
1090
1142
|
const cssNode = style.findChildAtOffset(vDoc.offsetAt(pos), true);
|
|
1091
1143
|
if (!cssNode) return empty;
|
|
@@ -1105,6 +1157,15 @@ var HTMLLanguageService = class {
|
|
|
1105
1157
|
}
|
|
1106
1158
|
const { attr, offset } = getAttrName(text);
|
|
1107
1159
|
if (before.length > attr.length) return empty;
|
|
1160
|
+
if (attr === "v-else" || attr === "v-else-if") {
|
|
1161
|
+
const ifAttr = node.prev?.attributesMap.get("v-if") || node.prev?.attributesMap.get("v-else-if");
|
|
1162
|
+
if (!ifAttr) return empty;
|
|
1163
|
+
return genCurrentCtxDefinitionInfo(
|
|
1164
|
+
context,
|
|
1165
|
+
{ start: start + offset, length: attr.length },
|
|
1166
|
+
{ start: ifAttr.start, length: ifAttr.end - ifAttr.start }
|
|
1167
|
+
);
|
|
1168
|
+
}
|
|
1108
1169
|
const typeChecker = this.#ctx.getProgram().getTypeChecker();
|
|
1109
1170
|
const propSymbol = typeChecker.getTypeAtLocation(definitionNode).getProperty(kebabToCamelCase(attr));
|
|
1110
1171
|
const propDeclaration = propSymbol?.getDeclarations()?.at(0);
|
|
@@ -1120,8 +1181,16 @@ function getSpanExpression(typescript, file, pos) {
|
|
|
1120
1181
|
}
|
|
1121
1182
|
return node.expression;
|
|
1122
1183
|
}
|
|
1184
|
+
function getSpanType(typescript, typeChecker, file, htmlOffset, attrNameEnd) {
|
|
1185
|
+
const valueOffset = attrNameEnd + htmlOffset + 3;
|
|
1186
|
+
const spanExp = getSpanExpression(typescript, file, valueOffset);
|
|
1187
|
+
return typeChecker.getTypeAtLocation(spanExp);
|
|
1188
|
+
}
|
|
1123
1189
|
function getPropType(typeChecker, tagClassDeclaration, attrInfo) {
|
|
1124
1190
|
const classType = typeChecker.getTypeAtLocation(tagClassDeclaration);
|
|
1191
|
+
if (attrInfo.attr.startsWith("data-")) {
|
|
1192
|
+
return typeChecker.getStringType();
|
|
1193
|
+
}
|
|
1125
1194
|
const propName = kebabToCamelCase(attrInfo.attr);
|
|
1126
1195
|
switch (propName) {
|
|
1127
1196
|
case "class":
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ts-gem-plugin",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.6",
|
|
4
4
|
"description": "Typescript language service plugin for Gem",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"gem",
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
"@mantou/typescript-template-language-service-decorator": "^2.3.4",
|
|
19
19
|
"@mantou/vscode-css-languageservice": "^6.3.3",
|
|
20
20
|
"@mantou/vscode-emmet-helper": "^2.9.3",
|
|
21
|
-
"@mantou/vscode-html-languageservice": "^5.3.
|
|
21
|
+
"@mantou/vscode-html-languageservice": "^5.3.4",
|
|
22
22
|
"duoyun-ui": "^2.2.0",
|
|
23
23
|
"vscode-languageserver-textdocument": "^1.0.12",
|
|
24
24
|
"vscode-languageserver-types": "^3.17.5"
|