@ktjs/ts-plugin 0.1.4 → 0.1.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/README.md +1 -0
- package/dist/ast.d.ts +4 -0
- package/dist/ast.js +41 -0
- package/dist/completion.d.ts +7 -0
- package/dist/completion.js +128 -0
- package/dist/config.d.ts +3 -0
- package/dist/config.js +22 -0
- package/dist/constants.d.ts +5 -0
- package/dist/constants.js +8 -0
- package/dist/identifiers.d.ts +3 -0
- package/dist/identifiers.js +29 -0
- package/dist/index.js +57 -637
- package/dist/jsx-attributes.d.ts +6 -0
- package/dist/jsx-attributes.js +59 -0
- package/dist/kfor-highlighting.d.ts +4 -0
- package/dist/kfor-highlighting.js +407 -0
- package/dist/kfor-parser.d.ts +2 -0
- package/dist/kfor-parser.js +36 -0
- package/dist/scope-analysis.d.ts +5 -0
- package/dist/scope-analysis.js +151 -0
- package/dist/type-resolution.d.ts +5 -0
- package/dist/type-resolution.js +193 -0
- package/dist/types.d.ts +46 -0
- package/dist/types.js +2 -0
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -1,18 +1,17 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
const
|
|
6
|
-
const
|
|
7
|
-
const
|
|
8
|
-
const
|
|
9
|
-
const
|
|
10
|
-
const MEMBER_COMPLETION_PATTERN = /([A-Za-z_$][A-Za-z0-9_$]*(?:\s*(?:\.\s*[A-Za-z_$][A-Za-z0-9_$]*|\[\s*(?:'[^'\\]*(?:\\.[^'\\]*)*'|"[^"\\]*(?:\\.[^"\\]*)*"|\d+)\s*\]))*)\s*\.\s*([A-Za-z_$][A-Za-z0-9_$]*)?$/;
|
|
2
|
+
const ast_1 = require("./ast");
|
|
3
|
+
const completion_1 = require("./completion");
|
|
4
|
+
const constants_1 = require("./constants");
|
|
5
|
+
const config_1 = require("./config");
|
|
6
|
+
const identifiers_1 = require("./identifiers");
|
|
7
|
+
const kfor_highlighting_1 = require("./kfor-highlighting");
|
|
8
|
+
const scope_analysis_1 = require("./scope-analysis");
|
|
9
|
+
const type_resolution_1 = require("./type-resolution");
|
|
11
10
|
function init(modules) {
|
|
12
11
|
const ts = modules.typescript;
|
|
13
12
|
function create(info) {
|
|
14
13
|
const languageService = info.languageService;
|
|
15
|
-
const config = resolveConfig(info.config);
|
|
14
|
+
const config = (0, config_1.resolveConfig)(info.config);
|
|
16
15
|
const proxy = Object.create(null);
|
|
17
16
|
for (const key of Object.keys(languageService)) {
|
|
18
17
|
const member = languageService[key];
|
|
@@ -20,43 +19,67 @@ function init(modules) {
|
|
|
20
19
|
}
|
|
21
20
|
proxy.getSemanticDiagnostics = (fileName) => {
|
|
22
21
|
const diagnostics = languageService.getSemanticDiagnostics(fileName);
|
|
23
|
-
if (!isJsxLikeFile(fileName)) {
|
|
22
|
+
if (!(0, config_1.isJsxLikeFile)(fileName)) {
|
|
24
23
|
return diagnostics;
|
|
25
24
|
}
|
|
26
|
-
const analysis = getFileAnalysis(fileName, languageService, ts, config);
|
|
25
|
+
const analysis = (0, scope_analysis_1.getFileAnalysis)(fileName, languageService, ts, config);
|
|
27
26
|
if (!analysis) {
|
|
28
27
|
return diagnostics;
|
|
29
28
|
}
|
|
30
|
-
return diagnostics.filter((
|
|
31
|
-
if (
|
|
29
|
+
return diagnostics.filter((diagnostic) => {
|
|
30
|
+
if (diagnostic.code !== constants_1.DIAGNOSTIC_CANNOT_FIND_NAME ||
|
|
31
|
+
diagnostic.start == null ||
|
|
32
|
+
diagnostic.length == null) {
|
|
32
33
|
return true;
|
|
33
34
|
}
|
|
34
|
-
const name = analysis.sourceFile.text.slice(
|
|
35
|
-
if (!isValidIdentifier(name)) {
|
|
35
|
+
const name = analysis.sourceFile.text.slice(diagnostic.start, diagnostic.start + diagnostic.length).trim();
|
|
36
|
+
if (!(0, identifiers_1.isValidIdentifier)(name)) {
|
|
36
37
|
return true;
|
|
37
38
|
}
|
|
38
|
-
return !isSuppressed(
|
|
39
|
+
return !(0, scope_analysis_1.isSuppressed)(diagnostic.start, name, analysis.scopes);
|
|
39
40
|
});
|
|
40
41
|
};
|
|
42
|
+
proxy.getEncodedSemanticClassifications = (fileName, span, format) => {
|
|
43
|
+
const classifications = languageService.getEncodedSemanticClassifications(fileName, span, format);
|
|
44
|
+
if (!(0, config_1.isJsxLikeFile)(fileName)) {
|
|
45
|
+
return classifications;
|
|
46
|
+
}
|
|
47
|
+
const sourceFile = languageService.getProgram()?.getSourceFile(fileName);
|
|
48
|
+
if (!sourceFile) {
|
|
49
|
+
return classifications;
|
|
50
|
+
}
|
|
51
|
+
return (0, kfor_highlighting_1.addKForSemanticClassifications)(classifications, sourceFile, span, format, ts, config);
|
|
52
|
+
};
|
|
53
|
+
proxy.getEncodedSyntacticClassifications = (fileName, span) => {
|
|
54
|
+
const classifications = languageService.getEncodedSyntacticClassifications(fileName, span);
|
|
55
|
+
if (!(0, config_1.isJsxLikeFile)(fileName)) {
|
|
56
|
+
return classifications;
|
|
57
|
+
}
|
|
58
|
+
const sourceFile = languageService.getProgram()?.getSourceFile(fileName);
|
|
59
|
+
if (!sourceFile) {
|
|
60
|
+
return classifications;
|
|
61
|
+
}
|
|
62
|
+
return (0, kfor_highlighting_1.addKForSyntacticClassifications)(classifications, sourceFile, span, ts, config);
|
|
63
|
+
};
|
|
41
64
|
proxy.getQuickInfoAtPosition = (fileName, position) => {
|
|
42
65
|
const quickInfo = languageService.getQuickInfoAtPosition(fileName, position);
|
|
43
|
-
if (!isJsxLikeFile(fileName)) {
|
|
66
|
+
if (!(0, config_1.isJsxLikeFile)(fileName)) {
|
|
44
67
|
return quickInfo;
|
|
45
68
|
}
|
|
46
|
-
const analysis = getFileAnalysis(fileName, languageService, ts, config);
|
|
69
|
+
const analysis = (0, scope_analysis_1.getFileAnalysis)(fileName, languageService, ts, config);
|
|
47
70
|
if (!analysis) {
|
|
48
71
|
return quickInfo;
|
|
49
72
|
}
|
|
50
|
-
const identifier = findIdentifierAtPosition(analysis.sourceFile, position, ts);
|
|
73
|
+
const identifier = (0, ast_1.findIdentifierAtPosition)(analysis.sourceFile, position, ts);
|
|
51
74
|
if (!identifier) {
|
|
52
75
|
return quickInfo;
|
|
53
76
|
}
|
|
54
|
-
const bindings = collectBindingsAtPosition(position, analysis.scopes);
|
|
77
|
+
const bindings = (0, scope_analysis_1.collectBindingsAtPosition)(position, analysis.scopes);
|
|
55
78
|
const binding = bindings.get(identifier.text);
|
|
56
79
|
if (!binding) {
|
|
57
80
|
return quickInfo;
|
|
58
81
|
}
|
|
59
|
-
const typeText = formatTypeList(binding.types, analysis.checker, identifier, ts);
|
|
82
|
+
const typeText = (0, type_resolution_1.formatTypeList)(binding.types, analysis.checker, identifier, ts);
|
|
60
83
|
return {
|
|
61
84
|
kind: ts.ScriptElementKind.localVariableElement,
|
|
62
85
|
kindModifiers: '',
|
|
@@ -69,35 +92,36 @@ function init(modules) {
|
|
|
69
92
|
};
|
|
70
93
|
proxy.getCompletionsAtPosition = (fileName, position, options, formattingSettings) => {
|
|
71
94
|
const completions = languageService.getCompletionsAtPosition(fileName, position, options, formattingSettings);
|
|
72
|
-
if (!isJsxLikeFile(fileName)) {
|
|
95
|
+
if (!(0, config_1.isJsxLikeFile)(fileName)) {
|
|
73
96
|
return completions;
|
|
74
97
|
}
|
|
75
|
-
const analysis = getFileAnalysis(fileName, languageService, ts, config);
|
|
98
|
+
const analysis = (0, scope_analysis_1.getFileAnalysis)(fileName, languageService, ts, config);
|
|
76
99
|
if (!analysis) {
|
|
77
100
|
return completions;
|
|
78
101
|
}
|
|
79
|
-
const bindings = collectBindingsAtPosition(position, analysis.scopes);
|
|
102
|
+
const bindings = (0, scope_analysis_1.collectBindingsAtPosition)(position, analysis.scopes);
|
|
80
103
|
if (bindings.size === 0) {
|
|
81
104
|
return completions;
|
|
82
105
|
}
|
|
83
|
-
const contextNode = findInnermostNode(analysis.sourceFile, normalizePosition(position, analysis.sourceFile), ts) ||
|
|
84
|
-
|
|
85
|
-
const
|
|
106
|
+
const contextNode = (0, ast_1.findInnermostNode)(analysis.sourceFile, (0, ast_1.normalizePosition)(position, analysis.sourceFile), ts) ||
|
|
107
|
+
analysis.sourceFile;
|
|
108
|
+
const localBindings = (0, completion_1.createBindingTypeMap)(bindings);
|
|
109
|
+
const memberContext = (0, completion_1.getMemberCompletionContext)(analysis.sourceFile.text, position);
|
|
86
110
|
if (memberContext) {
|
|
87
|
-
const receiverTypes = resolveExpressionTypesFromText(memberContext.receiver, {
|
|
111
|
+
const receiverTypes = (0, type_resolution_1.resolveExpressionTypesFromText)(memberContext.receiver, {
|
|
88
112
|
checker: analysis.checker,
|
|
89
113
|
ts,
|
|
90
114
|
scopeNode: contextNode,
|
|
91
115
|
localBindings,
|
|
92
116
|
});
|
|
93
|
-
const memberEntries = createMemberCompletionEntries(receiverTypes, memberContext.prefix, analysis.checker, contextNode, ts);
|
|
117
|
+
const memberEntries = (0, completion_1.createMemberCompletionEntries)(receiverTypes, memberContext.prefix, analysis.checker, contextNode, ts);
|
|
94
118
|
if (memberEntries.length > 0) {
|
|
95
|
-
return mergeCompletionInfo(completions, memberEntries, true);
|
|
119
|
+
return (0, completion_1.mergeCompletionInfo)(completions, memberEntries, true);
|
|
96
120
|
}
|
|
97
121
|
}
|
|
98
|
-
const aliasEntries = createAliasCompletionEntries(bindings, analysis.sourceFile.text, position, ts);
|
|
122
|
+
const aliasEntries = (0, completion_1.createAliasCompletionEntries)(bindings, analysis.sourceFile.text, position, ts);
|
|
99
123
|
if (aliasEntries.length > 0) {
|
|
100
|
-
return mergeCompletionInfo(completions, aliasEntries, false);
|
|
124
|
+
return (0, completion_1.mergeCompletionInfo)(completions, aliasEntries, false);
|
|
101
125
|
}
|
|
102
126
|
return completions;
|
|
103
127
|
};
|
|
@@ -105,608 +129,4 @@ function init(modules) {
|
|
|
105
129
|
}
|
|
106
130
|
return { create };
|
|
107
131
|
}
|
|
108
|
-
function resolveConfig(config) {
|
|
109
|
-
return {
|
|
110
|
-
forAttr: config?.forAttr || 'k-for',
|
|
111
|
-
itemAttr: config?.itemAttr || 'k-for-item',
|
|
112
|
-
indexAttr: config?.indexAttr || 'k-for-index',
|
|
113
|
-
itemName: config?.itemName || 'item',
|
|
114
|
-
indexName: config?.indexName || 'index',
|
|
115
|
-
allowOfKeyword: config?.allowOfKeyword !== false,
|
|
116
|
-
};
|
|
117
|
-
}
|
|
118
|
-
function isJsxLikeFile(fileName) {
|
|
119
|
-
const ext = node_path_1.default.extname(fileName).toLowerCase();
|
|
120
|
-
return ext === '.tsx' || ext === '.jsx';
|
|
121
|
-
}
|
|
122
|
-
function getFileAnalysis(fileName, languageService, ts, config) {
|
|
123
|
-
const program = languageService.getProgram();
|
|
124
|
-
if (!program) {
|
|
125
|
-
return undefined;
|
|
126
|
-
}
|
|
127
|
-
const sourceFile = program.getSourceFile(fileName);
|
|
128
|
-
if (!sourceFile) {
|
|
129
|
-
return undefined;
|
|
130
|
-
}
|
|
131
|
-
const checker = program.getTypeChecker();
|
|
132
|
-
const scopes = collectKForScopes(sourceFile, checker, ts, config);
|
|
133
|
-
if (scopes.length === 0) {
|
|
134
|
-
return undefined;
|
|
135
|
-
}
|
|
136
|
-
return { sourceFile, checker, scopes };
|
|
137
|
-
}
|
|
138
|
-
function collectKForScopes(sourceFile, checker, ts, config) {
|
|
139
|
-
const scopes = [];
|
|
140
|
-
const visit = (node) => {
|
|
141
|
-
if (ts.isJsxElement(node)) {
|
|
142
|
-
const forAttr = getJsxAttribute(node.openingElement, config.forAttr, ts);
|
|
143
|
-
if (forAttr) {
|
|
144
|
-
const bindings = resolveScopeBindings(node.openingElement, forAttr, checker, config, ts);
|
|
145
|
-
const start = node.openingElement.end;
|
|
146
|
-
const end = node.closingElement.getStart(sourceFile);
|
|
147
|
-
if (start < end && bindings.length > 0) {
|
|
148
|
-
scopes.push({ start, end, bindings });
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
ts.forEachChild(node, visit);
|
|
153
|
-
};
|
|
154
|
-
visit(sourceFile);
|
|
155
|
-
return scopes;
|
|
156
|
-
}
|
|
157
|
-
function resolveScopeBindings(opening, forAttr, checker, config, ts) {
|
|
158
|
-
const forExpression = getAttributeText(forAttr, ts);
|
|
159
|
-
if (forExpression !== undefined) {
|
|
160
|
-
const parsed = parseKForExpression(forExpression, config.allowOfKeyword);
|
|
161
|
-
if (parsed) {
|
|
162
|
-
const sourceTypes = resolveExpressionTypesFromText(parsed.source, {
|
|
163
|
-
checker,
|
|
164
|
-
ts,
|
|
165
|
-
scopeNode: opening,
|
|
166
|
-
});
|
|
167
|
-
return createBindings(parsed.aliases, sourceTypes, checker, opening, ts);
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
const itemName = getScopeName(opening, config.itemAttr, config.itemName, ts);
|
|
171
|
-
const indexName = getScopeName(opening, config.indexAttr, config.indexName, ts);
|
|
172
|
-
const aliases = uniqueIdentifiers([itemName, indexName]);
|
|
173
|
-
const sourceTypes = getLegacyForSourceTypes(forAttr, checker, ts);
|
|
174
|
-
return createBindings(aliases, sourceTypes, checker, opening, ts);
|
|
175
|
-
}
|
|
176
|
-
function createBindings(names, sourceTypes, checker, scopeNode, ts) {
|
|
177
|
-
if (names.length === 0) {
|
|
178
|
-
return [];
|
|
179
|
-
}
|
|
180
|
-
const inferred = inferBindingTypes(sourceTypes, names.length, checker, scopeNode, ts);
|
|
181
|
-
const bindings = [];
|
|
182
|
-
for (let i = 0; i < names.length; i++) {
|
|
183
|
-
bindings.push({
|
|
184
|
-
name: names[i],
|
|
185
|
-
types: inferred[i] || [],
|
|
186
|
-
});
|
|
187
|
-
}
|
|
188
|
-
return bindings;
|
|
189
|
-
}
|
|
190
|
-
function inferBindingTypes(sourceTypes, bindingCount, checker, scopeNode, ts) {
|
|
191
|
-
const slots = Array.from({ length: bindingCount }, () => []);
|
|
192
|
-
const candidates = expandUnionTypes(sourceTypes, ts);
|
|
193
|
-
for (let i = 0; i < candidates.length; i++) {
|
|
194
|
-
const sourceType = checker.getApparentType(candidates[i]);
|
|
195
|
-
const elementType = checker.getIndexTypeOfType(sourceType, ts.IndexKind.Number);
|
|
196
|
-
const stringValueType = elementType ? undefined : checker.getIndexTypeOfType(sourceType, ts.IndexKind.String);
|
|
197
|
-
const valueTypes = elementType ? [elementType] : stringValueType ? [stringValueType] : [];
|
|
198
|
-
if (valueTypes.length === 0) {
|
|
199
|
-
continue;
|
|
200
|
-
}
|
|
201
|
-
slots[0].push(...valueTypes);
|
|
202
|
-
if (bindingCount > 1) {
|
|
203
|
-
slots[1].push(elementType ? checker.getNumberType() : checker.getStringType());
|
|
204
|
-
}
|
|
205
|
-
if (bindingCount > 2) {
|
|
206
|
-
slots[2].push(checker.getNumberType());
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
for (let i = 0; i < slots.length; i++) {
|
|
210
|
-
slots[i] = uniqueTypes(slots[i], checker, scopeNode, ts);
|
|
211
|
-
}
|
|
212
|
-
return slots;
|
|
213
|
-
}
|
|
214
|
-
function expandUnionTypes(types, ts) {
|
|
215
|
-
const result = [];
|
|
216
|
-
for (let i = 0; i < types.length; i++) {
|
|
217
|
-
const type = types[i];
|
|
218
|
-
if (type.flags & ts.TypeFlags.Union) {
|
|
219
|
-
const union = type;
|
|
220
|
-
result.push(...union.types);
|
|
221
|
-
continue;
|
|
222
|
-
}
|
|
223
|
-
result.push(type);
|
|
224
|
-
}
|
|
225
|
-
return result;
|
|
226
|
-
}
|
|
227
|
-
function getLegacyForSourceTypes(forAttr, checker, ts) {
|
|
228
|
-
const expression = getAttributeExpression(forAttr, ts);
|
|
229
|
-
if (!expression || ts.isStringLiteralLike(expression)) {
|
|
230
|
-
return [];
|
|
231
|
-
}
|
|
232
|
-
return [checker.getTypeAtLocation(expression)];
|
|
233
|
-
}
|
|
234
|
-
function getAttributeExpression(attr, ts) {
|
|
235
|
-
if (!attr?.initializer || !ts.isJsxExpression(attr.initializer) || !attr.initializer.expression) {
|
|
236
|
-
return undefined;
|
|
237
|
-
}
|
|
238
|
-
return attr.initializer.expression;
|
|
239
|
-
}
|
|
240
|
-
function resolveExpressionTypesFromText(raw, context) {
|
|
241
|
-
const value = raw.trim();
|
|
242
|
-
if (!value) {
|
|
243
|
-
return [];
|
|
244
|
-
}
|
|
245
|
-
const sourceFile = context.ts.createSourceFile('__k_for_expression.ts', `(${value});`, context.ts.ScriptTarget.Latest, true, context.ts.ScriptKind.TS);
|
|
246
|
-
if (sourceFile.statements.length === 0) {
|
|
247
|
-
return [];
|
|
248
|
-
}
|
|
249
|
-
const statement = sourceFile.statements[0];
|
|
250
|
-
if (!context.ts.isExpressionStatement(statement)) {
|
|
251
|
-
return [];
|
|
252
|
-
}
|
|
253
|
-
const types = resolveExpressionTypes(statement.expression, context);
|
|
254
|
-
return uniqueTypes(types, context.checker, context.scopeNode, context.ts);
|
|
255
|
-
}
|
|
256
|
-
function resolveExpressionTypes(expr, context) {
|
|
257
|
-
const ts = context.ts;
|
|
258
|
-
const target = unwrapExpression(expr, ts);
|
|
259
|
-
if (ts.isIdentifier(target)) {
|
|
260
|
-
return resolveIdentifierTypes(target.text, context);
|
|
261
|
-
}
|
|
262
|
-
if (ts.isPropertyAccessExpression(target)) {
|
|
263
|
-
const objectTypes = resolveExpressionTypes(target.expression, context);
|
|
264
|
-
return resolvePropertyTypes(objectTypes, target.name.text, context, false);
|
|
265
|
-
}
|
|
266
|
-
if (ts.isElementAccessExpression(target)) {
|
|
267
|
-
const objectTypes = resolveExpressionTypes(target.expression, context);
|
|
268
|
-
const arg = target.argumentExpression;
|
|
269
|
-
if (!arg) {
|
|
270
|
-
return [];
|
|
271
|
-
}
|
|
272
|
-
if (ts.isStringLiteralLike(arg)) {
|
|
273
|
-
return resolvePropertyTypes(objectTypes, arg.text, context, true);
|
|
274
|
-
}
|
|
275
|
-
if (ts.isNumericLiteral(arg)) {
|
|
276
|
-
return resolveNumericElementTypes(objectTypes, Number(arg.text), context);
|
|
277
|
-
}
|
|
278
|
-
return resolveIndexedTypes(objectTypes, context);
|
|
279
|
-
}
|
|
280
|
-
if (ts.isCallExpression(target)) {
|
|
281
|
-
const calleeTypes = resolveExpressionTypes(target.expression, context);
|
|
282
|
-
const result = [];
|
|
283
|
-
for (let i = 0; i < calleeTypes.length; i++) {
|
|
284
|
-
const signatures = context.checker.getSignaturesOfType(calleeTypes[i], ts.SignatureKind.Call);
|
|
285
|
-
for (let j = 0; j < signatures.length; j++) {
|
|
286
|
-
result.push(context.checker.getReturnTypeOfSignature(signatures[j]));
|
|
287
|
-
}
|
|
288
|
-
}
|
|
289
|
-
return uniqueTypes(result, context.checker, context.scopeNode, ts);
|
|
290
|
-
}
|
|
291
|
-
if (ts.isConditionalExpression(target)) {
|
|
292
|
-
const left = resolveExpressionTypes(target.whenTrue, context);
|
|
293
|
-
const right = resolveExpressionTypes(target.whenFalse, context);
|
|
294
|
-
return uniqueTypes([...left, ...right], context.checker, context.scopeNode, ts);
|
|
295
|
-
}
|
|
296
|
-
if (ts.isBinaryExpression(target) &&
|
|
297
|
-
(target.operatorToken.kind === ts.SyntaxKind.BarBarToken ||
|
|
298
|
-
target.operatorToken.kind === ts.SyntaxKind.AmpersandAmpersandToken ||
|
|
299
|
-
target.operatorToken.kind === ts.SyntaxKind.QuestionQuestionToken)) {
|
|
300
|
-
const left = resolveExpressionTypes(target.left, context);
|
|
301
|
-
const right = resolveExpressionTypes(target.right, context);
|
|
302
|
-
return uniqueTypes([...left, ...right], context.checker, context.scopeNode, ts);
|
|
303
|
-
}
|
|
304
|
-
return [];
|
|
305
|
-
}
|
|
306
|
-
function unwrapExpression(expr, ts) {
|
|
307
|
-
let current = expr;
|
|
308
|
-
while (true) {
|
|
309
|
-
if (ts.isParenthesizedExpression(current)) {
|
|
310
|
-
current = current.expression;
|
|
311
|
-
continue;
|
|
312
|
-
}
|
|
313
|
-
if (ts.isAsExpression(current) || ts.isTypeAssertionExpression(current) || ts.isSatisfiesExpression(current)) {
|
|
314
|
-
current = current.expression;
|
|
315
|
-
continue;
|
|
316
|
-
}
|
|
317
|
-
if (ts.isNonNullExpression(current)) {
|
|
318
|
-
current = current.expression;
|
|
319
|
-
continue;
|
|
320
|
-
}
|
|
321
|
-
return current;
|
|
322
|
-
}
|
|
323
|
-
}
|
|
324
|
-
function resolveIdentifierTypes(name, context) {
|
|
325
|
-
const localTypes = context.localBindings?.get(name);
|
|
326
|
-
if (localTypes && localTypes.length > 0) {
|
|
327
|
-
return [...localTypes];
|
|
328
|
-
}
|
|
329
|
-
const symbol = resolveSymbolInScope(name, context);
|
|
330
|
-
if (!symbol) {
|
|
331
|
-
return [];
|
|
332
|
-
}
|
|
333
|
-
return [context.checker.getTypeOfSymbolAtLocation(symbol, context.scopeNode)];
|
|
334
|
-
}
|
|
335
|
-
function resolveSymbolInScope(name, context) {
|
|
336
|
-
const symbols = context.checker.getSymbolsInScope(context.scopeNode, context.ts.SymbolFlags.Value | context.ts.SymbolFlags.Alias);
|
|
337
|
-
for (let i = 0; i < symbols.length; i++) {
|
|
338
|
-
const symbol = symbols[i];
|
|
339
|
-
if (symbol.getName() !== name) {
|
|
340
|
-
continue;
|
|
341
|
-
}
|
|
342
|
-
if (symbol.flags & context.ts.SymbolFlags.Alias) {
|
|
343
|
-
const aliased = context.checker.getAliasedSymbol(symbol);
|
|
344
|
-
if (aliased.flags & context.ts.SymbolFlags.Value) {
|
|
345
|
-
return aliased;
|
|
346
|
-
}
|
|
347
|
-
continue;
|
|
348
|
-
}
|
|
349
|
-
if (symbol.flags & context.ts.SymbolFlags.Value) {
|
|
350
|
-
return symbol;
|
|
351
|
-
}
|
|
352
|
-
}
|
|
353
|
-
return undefined;
|
|
354
|
-
}
|
|
355
|
-
function resolvePropertyTypes(objectTypes, propertyName, context, allowStringIndexFallback) {
|
|
356
|
-
const result = [];
|
|
357
|
-
for (let i = 0; i < objectTypes.length; i++) {
|
|
358
|
-
const targetType = context.checker.getApparentType(objectTypes[i]);
|
|
359
|
-
const prop = context.checker.getPropertyOfType(targetType, propertyName);
|
|
360
|
-
if (prop) {
|
|
361
|
-
result.push(context.checker.getTypeOfSymbolAtLocation(prop, context.scopeNode));
|
|
362
|
-
continue;
|
|
363
|
-
}
|
|
364
|
-
if (allowStringIndexFallback) {
|
|
365
|
-
const stringIndexType = context.checker.getIndexTypeOfType(targetType, context.ts.IndexKind.String);
|
|
366
|
-
if (stringIndexType) {
|
|
367
|
-
result.push(stringIndexType);
|
|
368
|
-
}
|
|
369
|
-
}
|
|
370
|
-
}
|
|
371
|
-
return uniqueTypes(result, context.checker, context.scopeNode, context.ts);
|
|
372
|
-
}
|
|
373
|
-
function resolveNumericElementTypes(objectTypes, index, context) {
|
|
374
|
-
const result = [];
|
|
375
|
-
const indexName = String(index);
|
|
376
|
-
for (let i = 0; i < objectTypes.length; i++) {
|
|
377
|
-
const targetType = context.checker.getApparentType(objectTypes[i]);
|
|
378
|
-
const property = context.checker.getPropertyOfType(targetType, indexName);
|
|
379
|
-
if (property) {
|
|
380
|
-
result.push(context.checker.getTypeOfSymbolAtLocation(property, context.scopeNode));
|
|
381
|
-
}
|
|
382
|
-
const numericIndexType = context.checker.getIndexTypeOfType(targetType, context.ts.IndexKind.Number);
|
|
383
|
-
if (numericIndexType) {
|
|
384
|
-
result.push(numericIndexType);
|
|
385
|
-
}
|
|
386
|
-
}
|
|
387
|
-
return uniqueTypes(result, context.checker, context.scopeNode, context.ts);
|
|
388
|
-
}
|
|
389
|
-
function resolveIndexedTypes(objectTypes, context) {
|
|
390
|
-
const result = [];
|
|
391
|
-
for (let i = 0; i < objectTypes.length; i++) {
|
|
392
|
-
const targetType = context.checker.getApparentType(objectTypes[i]);
|
|
393
|
-
const stringIndexType = context.checker.getIndexTypeOfType(targetType, context.ts.IndexKind.String);
|
|
394
|
-
if (stringIndexType) {
|
|
395
|
-
result.push(stringIndexType);
|
|
396
|
-
}
|
|
397
|
-
const numericIndexType = context.checker.getIndexTypeOfType(targetType, context.ts.IndexKind.Number);
|
|
398
|
-
if (numericIndexType) {
|
|
399
|
-
result.push(numericIndexType);
|
|
400
|
-
}
|
|
401
|
-
}
|
|
402
|
-
return uniqueTypes(result, context.checker, context.scopeNode, context.ts);
|
|
403
|
-
}
|
|
404
|
-
function createMemberCompletionEntries(types, prefix, checker, scopeNode, ts) {
|
|
405
|
-
if (types.length === 0) {
|
|
406
|
-
return [];
|
|
407
|
-
}
|
|
408
|
-
const entries = new Map();
|
|
409
|
-
const typeCandidates = uniqueTypes(types, checker, scopeNode, ts);
|
|
410
|
-
for (let i = 0; i < typeCandidates.length; i++) {
|
|
411
|
-
const props = checker.getPropertiesOfType(checker.getApparentType(typeCandidates[i]));
|
|
412
|
-
for (let j = 0; j < props.length; j++) {
|
|
413
|
-
const prop = props[j];
|
|
414
|
-
const name = prop.getName();
|
|
415
|
-
if (!isValidIdentifier(name) || name.startsWith('__@')) {
|
|
416
|
-
continue;
|
|
417
|
-
}
|
|
418
|
-
if (prefix && !name.startsWith(prefix)) {
|
|
419
|
-
continue;
|
|
420
|
-
}
|
|
421
|
-
if (entries.has(name)) {
|
|
422
|
-
continue;
|
|
423
|
-
}
|
|
424
|
-
entries.set(name, {
|
|
425
|
-
name,
|
|
426
|
-
kind: getCompletionKind(prop, ts),
|
|
427
|
-
kindModifiers: '',
|
|
428
|
-
sortText: '0',
|
|
429
|
-
});
|
|
430
|
-
}
|
|
431
|
-
}
|
|
432
|
-
return Array.from(entries.values()).sort((a, b) => a.name.localeCompare(b.name));
|
|
433
|
-
}
|
|
434
|
-
function createAliasCompletionEntries(bindings, text, position, ts) {
|
|
435
|
-
const prefix = getIdentifierPrefix(text, position);
|
|
436
|
-
if (prefix.isMemberAccess) {
|
|
437
|
-
return [];
|
|
438
|
-
}
|
|
439
|
-
const entries = [];
|
|
440
|
-
for (const binding of bindings.values()) {
|
|
441
|
-
if (prefix.text && !binding.name.startsWith(prefix.text)) {
|
|
442
|
-
continue;
|
|
443
|
-
}
|
|
444
|
-
entries.push({
|
|
445
|
-
name: binding.name,
|
|
446
|
-
kind: ts.ScriptElementKind.localVariableElement,
|
|
447
|
-
kindModifiers: '',
|
|
448
|
-
sortText: '0',
|
|
449
|
-
});
|
|
450
|
-
}
|
|
451
|
-
return entries;
|
|
452
|
-
}
|
|
453
|
-
function getCompletionKind(symbol, ts) {
|
|
454
|
-
const flags = symbol.flags;
|
|
455
|
-
if (flags & ts.SymbolFlags.Method) {
|
|
456
|
-
return ts.ScriptElementKind.memberFunctionElement;
|
|
457
|
-
}
|
|
458
|
-
if (flags & ts.SymbolFlags.Function) {
|
|
459
|
-
return ts.ScriptElementKind.functionElement;
|
|
460
|
-
}
|
|
461
|
-
if (flags & (ts.SymbolFlags.GetAccessor | ts.SymbolFlags.SetAccessor | ts.SymbolFlags.Property)) {
|
|
462
|
-
return ts.ScriptElementKind.memberVariableElement;
|
|
463
|
-
}
|
|
464
|
-
return ts.ScriptElementKind.memberVariableElement;
|
|
465
|
-
}
|
|
466
|
-
function mergeCompletionInfo(completions, extraEntries, forceMemberCompletion) {
|
|
467
|
-
if (!completions) {
|
|
468
|
-
return {
|
|
469
|
-
isGlobalCompletion: !forceMemberCompletion,
|
|
470
|
-
isMemberCompletion: forceMemberCompletion,
|
|
471
|
-
isNewIdentifierLocation: !forceMemberCompletion,
|
|
472
|
-
entries: extraEntries,
|
|
473
|
-
};
|
|
474
|
-
}
|
|
475
|
-
const seen = new Set();
|
|
476
|
-
for (let i = 0; i < completions.entries.length; i++) {
|
|
477
|
-
seen.add(completions.entries[i].name);
|
|
478
|
-
}
|
|
479
|
-
const merged = completions.entries.slice();
|
|
480
|
-
for (let i = 0; i < extraEntries.length; i++) {
|
|
481
|
-
const entry = extraEntries[i];
|
|
482
|
-
if (seen.has(entry.name)) {
|
|
483
|
-
continue;
|
|
484
|
-
}
|
|
485
|
-
seen.add(entry.name);
|
|
486
|
-
merged.push(entry);
|
|
487
|
-
}
|
|
488
|
-
return {
|
|
489
|
-
...completions,
|
|
490
|
-
isMemberCompletion: completions.isMemberCompletion || forceMemberCompletion,
|
|
491
|
-
entries: merged,
|
|
492
|
-
};
|
|
493
|
-
}
|
|
494
|
-
function createBindingTypeMap(bindings) {
|
|
495
|
-
const map = new Map();
|
|
496
|
-
for (const binding of bindings.values()) {
|
|
497
|
-
map.set(binding.name, binding.types);
|
|
498
|
-
}
|
|
499
|
-
return map;
|
|
500
|
-
}
|
|
501
|
-
function getMemberCompletionContext(text, position) {
|
|
502
|
-
const sliceStart = Math.max(0, position - 200);
|
|
503
|
-
const snippet = text.slice(sliceStart, position);
|
|
504
|
-
const match = MEMBER_COMPLETION_PATTERN.exec(snippet);
|
|
505
|
-
if (!match) {
|
|
506
|
-
return undefined;
|
|
507
|
-
}
|
|
508
|
-
return {
|
|
509
|
-
receiver: match[1],
|
|
510
|
-
prefix: match[2] || '',
|
|
511
|
-
};
|
|
512
|
-
}
|
|
513
|
-
function getIdentifierPrefix(text, position) {
|
|
514
|
-
let start = position;
|
|
515
|
-
while (start > 0 && isIdentifierPart(text.charCodeAt(start - 1))) {
|
|
516
|
-
start--;
|
|
517
|
-
}
|
|
518
|
-
const prefix = text.slice(start, position);
|
|
519
|
-
const isMemberAccess = start > 0 && text.charCodeAt(start - 1) === 46;
|
|
520
|
-
return { text: prefix, isMemberAccess };
|
|
521
|
-
}
|
|
522
|
-
function isIdentifierPart(code) {
|
|
523
|
-
return ((code >= 65 && code <= 90) ||
|
|
524
|
-
(code >= 97 && code <= 122) ||
|
|
525
|
-
(code >= 48 && code <= 57) ||
|
|
526
|
-
code === 95 ||
|
|
527
|
-
code === 36);
|
|
528
|
-
}
|
|
529
|
-
function findIdentifierAtPosition(sourceFile, position, ts) {
|
|
530
|
-
const exact = findInnermostNode(sourceFile, normalizePosition(position, sourceFile), ts);
|
|
531
|
-
if (exact && ts.isIdentifier(exact)) {
|
|
532
|
-
return exact;
|
|
533
|
-
}
|
|
534
|
-
if (position > 0) {
|
|
535
|
-
const prev = findInnermostNode(sourceFile, normalizePosition(position - 1, sourceFile), ts);
|
|
536
|
-
if (prev && ts.isIdentifier(prev)) {
|
|
537
|
-
return prev;
|
|
538
|
-
}
|
|
539
|
-
}
|
|
540
|
-
return undefined;
|
|
541
|
-
}
|
|
542
|
-
function normalizePosition(position, sourceFile) {
|
|
543
|
-
const max = Math.max(sourceFile.text.length - 1, 0);
|
|
544
|
-
if (position < 0) {
|
|
545
|
-
return 0;
|
|
546
|
-
}
|
|
547
|
-
if (position > max) {
|
|
548
|
-
return max;
|
|
549
|
-
}
|
|
550
|
-
return position;
|
|
551
|
-
}
|
|
552
|
-
function findInnermostNode(node, position, ts) {
|
|
553
|
-
if (position < node.getFullStart() || position >= node.end) {
|
|
554
|
-
return undefined;
|
|
555
|
-
}
|
|
556
|
-
let match;
|
|
557
|
-
ts.forEachChild(node, (child) => {
|
|
558
|
-
const childMatch = findInnermostNode(child, position, ts);
|
|
559
|
-
if (childMatch) {
|
|
560
|
-
match = childMatch;
|
|
561
|
-
}
|
|
562
|
-
});
|
|
563
|
-
return match || node;
|
|
564
|
-
}
|
|
565
|
-
function collectBindingsAtPosition(position, scopes) {
|
|
566
|
-
const bindings = new Map();
|
|
567
|
-
for (let i = scopes.length - 1; i >= 0; i--) {
|
|
568
|
-
const scope = scopes[i];
|
|
569
|
-
if (position < scope.start || position >= scope.end) {
|
|
570
|
-
continue;
|
|
571
|
-
}
|
|
572
|
-
for (let j = 0; j < scope.bindings.length; j++) {
|
|
573
|
-
const binding = scope.bindings[j];
|
|
574
|
-
if (!bindings.has(binding.name)) {
|
|
575
|
-
bindings.set(binding.name, binding);
|
|
576
|
-
}
|
|
577
|
-
}
|
|
578
|
-
}
|
|
579
|
-
return bindings;
|
|
580
|
-
}
|
|
581
|
-
function formatTypeList(types, checker, scopeNode, ts) {
|
|
582
|
-
if (types.length === 0) {
|
|
583
|
-
return 'any';
|
|
584
|
-
}
|
|
585
|
-
const texts = new Set();
|
|
586
|
-
for (let i = 0; i < types.length; i++) {
|
|
587
|
-
texts.add(checker.typeToString(types[i], scopeNode, ts.TypeFormatFlags.NoTruncation));
|
|
588
|
-
}
|
|
589
|
-
return Array.from(texts).join(' | ');
|
|
590
|
-
}
|
|
591
|
-
function uniqueTypes(types, checker, scopeNode, ts) {
|
|
592
|
-
const seen = new Set();
|
|
593
|
-
const result = [];
|
|
594
|
-
for (let i = 0; i < types.length; i++) {
|
|
595
|
-
const type = types[i];
|
|
596
|
-
const text = checker.typeToString(type, scopeNode, ts.TypeFormatFlags.NoTruncation);
|
|
597
|
-
if (seen.has(text)) {
|
|
598
|
-
continue;
|
|
599
|
-
}
|
|
600
|
-
seen.add(text);
|
|
601
|
-
result.push(type);
|
|
602
|
-
}
|
|
603
|
-
return result;
|
|
604
|
-
}
|
|
605
|
-
function getScopeName(opening, attrName, fallback, ts) {
|
|
606
|
-
const attr = getJsxAttribute(opening, attrName, ts);
|
|
607
|
-
const raw = getAttributeText(attr, ts, true);
|
|
608
|
-
if (raw && isValidIdentifier(raw)) {
|
|
609
|
-
return raw;
|
|
610
|
-
}
|
|
611
|
-
return fallback;
|
|
612
|
-
}
|
|
613
|
-
function getJsxAttribute(opening, attrName, ts) {
|
|
614
|
-
const attrs = opening.attributes.properties;
|
|
615
|
-
for (let i = 0; i < attrs.length; i++) {
|
|
616
|
-
const attr = attrs[i];
|
|
617
|
-
if (!ts.isJsxAttribute(attr)) {
|
|
618
|
-
continue;
|
|
619
|
-
}
|
|
620
|
-
if (getAttributeName(attr.name) === attrName) {
|
|
621
|
-
return attr;
|
|
622
|
-
}
|
|
623
|
-
}
|
|
624
|
-
return undefined;
|
|
625
|
-
}
|
|
626
|
-
function getAttributeText(attr, ts, allowIdentifier = false) {
|
|
627
|
-
if (!attr || !attr.initializer) {
|
|
628
|
-
return undefined;
|
|
629
|
-
}
|
|
630
|
-
if (ts.isStringLiteral(attr.initializer)) {
|
|
631
|
-
return attr.initializer.text;
|
|
632
|
-
}
|
|
633
|
-
if (!ts.isJsxExpression(attr.initializer) || !attr.initializer.expression) {
|
|
634
|
-
return undefined;
|
|
635
|
-
}
|
|
636
|
-
const expr = attr.initializer.expression;
|
|
637
|
-
if (ts.isStringLiteralLike(expr)) {
|
|
638
|
-
return expr.text;
|
|
639
|
-
}
|
|
640
|
-
if (allowIdentifier && ts.isIdentifier(expr)) {
|
|
641
|
-
return expr.text;
|
|
642
|
-
}
|
|
643
|
-
return undefined;
|
|
644
|
-
}
|
|
645
|
-
function getAttributeName(name) {
|
|
646
|
-
if ('text' in name) {
|
|
647
|
-
return String(name.text);
|
|
648
|
-
}
|
|
649
|
-
return name.getText();
|
|
650
|
-
}
|
|
651
|
-
function isSuppressed(position, diagnosticName, scopes) {
|
|
652
|
-
for (let i = scopes.length - 1; i >= 0; i--) {
|
|
653
|
-
const scope = scopes[i];
|
|
654
|
-
if (position < scope.start || position >= scope.end) {
|
|
655
|
-
continue;
|
|
656
|
-
}
|
|
657
|
-
for (let j = 0; j < scope.bindings.length; j++) {
|
|
658
|
-
if (scope.bindings[j].name === diagnosticName) {
|
|
659
|
-
return true;
|
|
660
|
-
}
|
|
661
|
-
}
|
|
662
|
-
}
|
|
663
|
-
return false;
|
|
664
|
-
}
|
|
665
|
-
function parseKForExpression(raw, allowOfKeyword) {
|
|
666
|
-
const value = raw.trim();
|
|
667
|
-
if (!value) {
|
|
668
|
-
return null;
|
|
669
|
-
}
|
|
670
|
-
const tupleMatch = KFOR_TUPLE_PATTERN.exec(value);
|
|
671
|
-
if (tupleMatch) {
|
|
672
|
-
const keyword = tupleMatch[4];
|
|
673
|
-
const source = tupleMatch[5]?.trim();
|
|
674
|
-
if ((!allowOfKeyword && keyword === 'of') || !source) {
|
|
675
|
-
return null;
|
|
676
|
-
}
|
|
677
|
-
return {
|
|
678
|
-
aliases: uniqueIdentifiers([tupleMatch[1], tupleMatch[2], tupleMatch[3]].filter(Boolean)),
|
|
679
|
-
source,
|
|
680
|
-
};
|
|
681
|
-
}
|
|
682
|
-
const singleMatch = KFOR_SINGLE_PATTERN.exec(value);
|
|
683
|
-
if (singleMatch) {
|
|
684
|
-
const keyword = singleMatch[2];
|
|
685
|
-
const source = singleMatch[3]?.trim();
|
|
686
|
-
if ((!allowOfKeyword && keyword === 'of') || !source) {
|
|
687
|
-
return null;
|
|
688
|
-
}
|
|
689
|
-
return {
|
|
690
|
-
aliases: uniqueIdentifiers([singleMatch[1]]),
|
|
691
|
-
source,
|
|
692
|
-
};
|
|
693
|
-
}
|
|
694
|
-
return null;
|
|
695
|
-
}
|
|
696
|
-
function isValidIdentifier(name) {
|
|
697
|
-
return IDENTIFIER_RE.test(name);
|
|
698
|
-
}
|
|
699
|
-
function uniqueIdentifiers(names) {
|
|
700
|
-
const seen = new Set();
|
|
701
|
-
const result = [];
|
|
702
|
-
for (let i = 0; i < names.length; i++) {
|
|
703
|
-
const name = names[i];
|
|
704
|
-
if (!isValidIdentifier(name) || seen.has(name)) {
|
|
705
|
-
continue;
|
|
706
|
-
}
|
|
707
|
-
seen.add(name);
|
|
708
|
-
result.push(name);
|
|
709
|
-
}
|
|
710
|
-
return result;
|
|
711
|
-
}
|
|
712
132
|
module.exports = init;
|