@ktjs/ts-plugin 0.1.7 → 0.1.9
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/README.md +6 -4
- package/dist/config.js +5 -4
- package/dist/kfor-highlighting.js +55 -6
- package/dist/scope-analysis.js +12 -23
- package/dist/types.d.ts +10 -8
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -10,6 +10,7 @@ TypeScript language service plugin for KT.js `k-for` scope variables in TSX.
|
|
|
10
10
|
- Provides hover type info and member completions for inferred aliases.
|
|
11
11
|
- Supports hover type info inside `k-for` expression strings (`item in users`) for alias/source identifiers.
|
|
12
12
|
- Adds `k-for` inline semantic highlighting in string expressions (for example alias/keyword/source in `k-for="item in list"`).
|
|
13
|
+
- Highlights directive attribute names (`k-for`, `k-if`, `k-else`, `k-else-if`, `k-model`, `k-html`) with semantic token colors.
|
|
13
14
|
- Supports Vue-like syntax:
|
|
14
15
|
- `k-for="item in list"`
|
|
15
16
|
- `k-for="(item, i) in list"`
|
|
@@ -54,10 +55,11 @@ If VS Code still does not load the plugin, make sure it uses your workspace Type
|
|
|
54
55
|
{
|
|
55
56
|
"name": "@ktjs/ts-plugin",
|
|
56
57
|
"forAttr": "k-for",
|
|
57
|
-
"
|
|
58
|
-
"
|
|
59
|
-
"
|
|
60
|
-
"
|
|
58
|
+
"ifAttr": "k-if",
|
|
59
|
+
"elseAttr": "k-else",
|
|
60
|
+
"elseIfAttr": "k-else-if",
|
|
61
|
+
"modelAttr": "k-model",
|
|
62
|
+
"htmlAttr": "k-html",
|
|
61
63
|
"allowOfKeyword": true
|
|
62
64
|
}
|
|
63
65
|
]
|
package/dist/config.js
CHANGED
|
@@ -9,10 +9,11 @@ const node_path_1 = __importDefault(require("node:path"));
|
|
|
9
9
|
function resolveConfig(config) {
|
|
10
10
|
return {
|
|
11
11
|
forAttr: config?.forAttr || 'k-for',
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
12
|
+
ifAttr: config?.ifAttr || 'k-if',
|
|
13
|
+
elseAttr: config?.elseAttr || 'k-else',
|
|
14
|
+
elseIfAttr: config?.elseIfAttr || 'k-else-if',
|
|
15
|
+
modelAttr: config?.modelAttr || 'k-model',
|
|
16
|
+
htmlAttr: config?.htmlAttr || 'k-html',
|
|
16
17
|
allowOfKeyword: config?.allowOfKeyword !== false,
|
|
17
18
|
};
|
|
18
19
|
}
|
|
@@ -5,10 +5,9 @@ exports.addKForSyntacticClassifications = addKForSyntacticClassifications;
|
|
|
5
5
|
const jsx_attributes_1 = require("./jsx-attributes");
|
|
6
6
|
const kfor_parser_1 = require("./kfor-parser");
|
|
7
7
|
const identifiers_1 = require("./identifiers");
|
|
8
|
-
const TOKEN_TYPE_VARIABLE = 7;
|
|
9
8
|
const TOKEN_TYPE_TYPE = 5;
|
|
9
|
+
const TOKEN_TYPE_VARIABLE = 7;
|
|
10
10
|
const TOKEN_MODIFIER_READONLY = 1 << 3;
|
|
11
|
-
const TOKEN_MODIFIER_LOCAL = 1 << 5;
|
|
12
11
|
const TOKEN_ENCODING_TYPE_OFFSET = 8;
|
|
13
12
|
const IDENTIFIER_PATTERN = /[A-Za-z_$][A-Za-z0-9_$]*/g;
|
|
14
13
|
const KEYWORD_DELIMITER_PATTERN = /\s+(in|of)\s+/;
|
|
@@ -47,6 +46,24 @@ function collectHighlightTokens(sourceFile, ts, config, span) {
|
|
|
47
46
|
opening = node;
|
|
48
47
|
}
|
|
49
48
|
if (opening) {
|
|
49
|
+
const directiveTokens = collectDirectiveAttributeTokens(opening, sourceFile, ts, config);
|
|
50
|
+
for (let i = 0; i < directiveTokens.length; i++) {
|
|
51
|
+
const token = directiveTokens[i];
|
|
52
|
+
const tokenStart = token.start;
|
|
53
|
+
const tokenEnd = token.start + token.length;
|
|
54
|
+
if (tokenEnd <= spanStart || tokenStart >= spanEnd) {
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
const clippedStart = Math.max(tokenStart, spanStart);
|
|
58
|
+
const clippedEnd = Math.min(tokenEnd, spanEnd);
|
|
59
|
+
if (clippedEnd > clippedStart) {
|
|
60
|
+
tokens.push({
|
|
61
|
+
start: clippedStart,
|
|
62
|
+
length: clippedEnd - clippedStart,
|
|
63
|
+
kind: token.kind,
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
}
|
|
50
67
|
const attr = (0, jsx_attributes_1.getJsxAttribute)(opening, config.forAttr, ts);
|
|
51
68
|
if (attr) {
|
|
52
69
|
const parsed = parseKForAttributeTokens(attr, sourceFile, ts, config.allowOfKeyword);
|
|
@@ -134,6 +151,34 @@ function parseKForAttributeTokens(attr, sourceFile, ts, allowOfKeyword) {
|
|
|
134
151
|
}
|
|
135
152
|
return tokens;
|
|
136
153
|
}
|
|
154
|
+
function collectDirectiveAttributeTokens(opening, sourceFile, ts, config) {
|
|
155
|
+
const directiveNames = new Set([
|
|
156
|
+
config.forAttr,
|
|
157
|
+
config.ifAttr,
|
|
158
|
+
config.elseAttr,
|
|
159
|
+
config.elseIfAttr,
|
|
160
|
+
config.modelAttr,
|
|
161
|
+
config.htmlAttr,
|
|
162
|
+
]);
|
|
163
|
+
const tokens = [];
|
|
164
|
+
for (const directiveName of directiveNames) {
|
|
165
|
+
const attr = (0, jsx_attributes_1.getJsxAttribute)(opening, directiveName, ts);
|
|
166
|
+
if (!attr) {
|
|
167
|
+
continue;
|
|
168
|
+
}
|
|
169
|
+
const start = attr.name.getStart(sourceFile);
|
|
170
|
+
const length = attr.name.getWidth(sourceFile);
|
|
171
|
+
if (length <= 0) {
|
|
172
|
+
continue;
|
|
173
|
+
}
|
|
174
|
+
tokens.push({
|
|
175
|
+
start,
|
|
176
|
+
length,
|
|
177
|
+
kind: 'directive',
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
return tokens;
|
|
181
|
+
}
|
|
137
182
|
function collectAliasTokens(aliasText, baseStart, aliases) {
|
|
138
183
|
const result = [];
|
|
139
184
|
const allowed = new Set(aliases);
|
|
@@ -237,10 +282,10 @@ function buildSemanticSpans(tokens, format, ts) {
|
|
|
237
282
|
spans.push({
|
|
238
283
|
start: token.start,
|
|
239
284
|
length: token.length,
|
|
240
|
-
classification: token.kind === '
|
|
241
|
-
? encodeSemantic2020(
|
|
285
|
+
classification: token.kind === 'directive'
|
|
286
|
+
? encodeSemantic2020(TOKEN_TYPE_TYPE, 0)
|
|
242
287
|
: token.kind === 'keyword'
|
|
243
|
-
? encodeSemantic2020(
|
|
288
|
+
? encodeSemantic2020(TOKEN_TYPE_VARIABLE, TOKEN_MODIFIER_READONLY)
|
|
244
289
|
: encodeSemantic2020(TOKEN_TYPE_VARIABLE, 0),
|
|
245
290
|
});
|
|
246
291
|
}
|
|
@@ -251,7 +296,11 @@ function buildSemanticSpans(tokens, format, ts) {
|
|
|
251
296
|
spans.push({
|
|
252
297
|
start: token.start,
|
|
253
298
|
length: token.length,
|
|
254
|
-
classification: token.kind === '
|
|
299
|
+
classification: token.kind === 'directive'
|
|
300
|
+
? ts.ClassificationType.className
|
|
301
|
+
: token.kind === 'keyword'
|
|
302
|
+
? ts.ClassificationType.keyword
|
|
303
|
+
: ts.ClassificationType.identifier,
|
|
255
304
|
});
|
|
256
305
|
}
|
|
257
306
|
return spans;
|
package/dist/scope-analysis.js
CHANGED
|
@@ -6,7 +6,6 @@ exports.collectBindingsAtPosition = collectBindingsAtPosition;
|
|
|
6
6
|
exports.resolveBindingsForForAttribute = resolveBindingsForForAttribute;
|
|
7
7
|
const jsx_attributes_1 = require("./jsx-attributes");
|
|
8
8
|
const kfor_parser_1 = require("./kfor-parser");
|
|
9
|
-
const identifiers_1 = require("./identifiers");
|
|
10
9
|
const type_resolution_1 = require("./type-resolution");
|
|
11
10
|
function getFileAnalysis(fileName, languageService, ts, config) {
|
|
12
11
|
const program = languageService.getProgram();
|
|
@@ -78,22 +77,19 @@ function collectKForScopes(sourceFile, checker, ts, config) {
|
|
|
78
77
|
}
|
|
79
78
|
function resolveScopeBindings(opening, forAttr, checker, config, ts) {
|
|
80
79
|
const forExpression = (0, jsx_attributes_1.getAttributeText)(forAttr, ts);
|
|
81
|
-
if (forExpression
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
scopeNode: opening,
|
|
88
|
-
});
|
|
89
|
-
return createBindings(parsed.aliases, sourceTypes, checker, opening, ts);
|
|
90
|
-
}
|
|
80
|
+
if (forExpression === undefined) {
|
|
81
|
+
return [];
|
|
82
|
+
}
|
|
83
|
+
const parsed = (0, kfor_parser_1.parseKForExpression)(forExpression, config.allowOfKeyword);
|
|
84
|
+
if (!parsed) {
|
|
85
|
+
return [];
|
|
91
86
|
}
|
|
92
|
-
const
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
87
|
+
const sourceTypes = (0, type_resolution_1.resolveExpressionTypesFromText)(parsed.source, {
|
|
88
|
+
checker,
|
|
89
|
+
ts,
|
|
90
|
+
scopeNode: opening,
|
|
91
|
+
});
|
|
92
|
+
return createBindings(parsed.aliases, sourceTypes, checker, opening, ts);
|
|
97
93
|
}
|
|
98
94
|
function createBindings(names, sourceTypes, checker, scopeNode, ts) {
|
|
99
95
|
if (names.length === 0) {
|
|
@@ -146,10 +142,3 @@ function expandUnionTypes(types, ts) {
|
|
|
146
142
|
}
|
|
147
143
|
return result;
|
|
148
144
|
}
|
|
149
|
-
function getLegacyForSourceTypes(forAttr, checker, ts) {
|
|
150
|
-
const expression = (0, jsx_attributes_1.getAttributeExpression)(forAttr, ts);
|
|
151
|
-
if (!expression || ts.isStringLiteralLike(expression)) {
|
|
152
|
-
return [];
|
|
153
|
-
}
|
|
154
|
-
return [checker.getTypeAtLocation(expression)];
|
|
155
|
-
}
|
package/dist/types.d.ts
CHANGED
|
@@ -1,18 +1,20 @@
|
|
|
1
1
|
import type tsModule from 'typescript/lib/tsserverlibrary';
|
|
2
2
|
export interface KForPluginConfig {
|
|
3
3
|
forAttr?: string;
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
4
|
+
ifAttr?: string;
|
|
5
|
+
elseAttr?: string;
|
|
6
|
+
elseIfAttr?: string;
|
|
7
|
+
modelAttr?: string;
|
|
8
|
+
htmlAttr?: string;
|
|
8
9
|
allowOfKeyword?: boolean;
|
|
9
10
|
}
|
|
10
11
|
export interface ResolvedConfig {
|
|
11
12
|
forAttr: string;
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
ifAttr: string;
|
|
14
|
+
elseAttr: string;
|
|
15
|
+
elseIfAttr: string;
|
|
16
|
+
modelAttr: string;
|
|
17
|
+
htmlAttr: string;
|
|
16
18
|
allowOfKeyword: boolean;
|
|
17
19
|
}
|
|
18
20
|
export interface KForBinding {
|