@ktjs/ts-plugin 0.1.6 → 0.1.8
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 -1
- package/dist/index.js +3 -19
- package/dist/kfor-highlighting.js +2 -6
- package/dist/quickinfo.d.ts +3 -0
- package/dist/quickinfo.js +318 -0
- package/dist/scope-analysis.d.ts +2 -1
- package/dist/scope-analysis.js +4 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -8,11 +8,11 @@ TypeScript language service plugin for KT.js `k-for` scope variables in TSX.
|
|
|
8
8
|
- Suppresses TS2304 (`Cannot find name ...`) for aliases declared by `k-for`.
|
|
9
9
|
- Infers alias types from iterable/indexed sources (for example `k-for="item in users"` makes `item` resolve to `users[number]`).
|
|
10
10
|
- Provides hover type info and member completions for inferred aliases.
|
|
11
|
+
- Supports hover type info inside `k-for` expression strings (`item in users`) for alias/source identifiers.
|
|
11
12
|
- Adds `k-for` inline semantic highlighting in string expressions (for example alias/keyword/source in `k-for="item in list"`).
|
|
12
13
|
- Supports Vue-like syntax:
|
|
13
14
|
- `k-for="item in list"`
|
|
14
15
|
- `k-for="(item, i) in list"`
|
|
15
|
-
- `k-for="(value, key, i) in mapLike"`
|
|
16
16
|
|
|
17
17
|
## Install
|
|
18
18
|
|
package/dist/index.js
CHANGED
|
@@ -5,6 +5,7 @@ const constants_1 = require("./constants");
|
|
|
5
5
|
const config_1 = require("./config");
|
|
6
6
|
const identifiers_1 = require("./identifiers");
|
|
7
7
|
const kfor_highlighting_1 = require("./kfor-highlighting");
|
|
8
|
+
const quickinfo_1 = require("./quickinfo");
|
|
8
9
|
const scope_analysis_1 = require("./scope-analysis");
|
|
9
10
|
const type_resolution_1 = require("./type-resolution");
|
|
10
11
|
function init(modules) {
|
|
@@ -70,25 +71,8 @@ function init(modules) {
|
|
|
70
71
|
if (!analysis) {
|
|
71
72
|
return quickInfo;
|
|
72
73
|
}
|
|
73
|
-
const
|
|
74
|
-
|
|
75
|
-
return quickInfo;
|
|
76
|
-
}
|
|
77
|
-
const bindings = (0, scope_analysis_1.collectBindingsAtPosition)(position, analysis.scopes);
|
|
78
|
-
const binding = bindings.get(identifier.text);
|
|
79
|
-
if (!binding) {
|
|
80
|
-
return quickInfo;
|
|
81
|
-
}
|
|
82
|
-
const typeText = (0, type_resolution_1.formatTypeList)(binding.types, analysis.checker, identifier, ts);
|
|
83
|
-
return {
|
|
84
|
-
kind: ts.ScriptElementKind.localVariableElement,
|
|
85
|
-
kindModifiers: '',
|
|
86
|
-
textSpan: {
|
|
87
|
-
start: identifier.getStart(analysis.sourceFile),
|
|
88
|
-
length: identifier.getWidth(analysis.sourceFile),
|
|
89
|
-
},
|
|
90
|
-
displayParts: [{ text: `(k-for) ${binding.name}: ${typeText}`, kind: 'text' }],
|
|
91
|
-
};
|
|
74
|
+
const pluginQuickInfo = (0, quickinfo_1.getKForQuickInfoAtPosition)(analysis, position, ts, config);
|
|
75
|
+
return pluginQuickInfo || quickInfo;
|
|
92
76
|
};
|
|
93
77
|
proxy.getCompletionsAtPosition = (fileName, position, options, formattingSettings) => {
|
|
94
78
|
const completions = languageService.getCompletionsAtPosition(fileName, position, options, formattingSettings);
|
|
@@ -7,7 +7,6 @@ const kfor_parser_1 = require("./kfor-parser");
|
|
|
7
7
|
const identifiers_1 = require("./identifiers");
|
|
8
8
|
const TOKEN_TYPE_VARIABLE = 7;
|
|
9
9
|
const TOKEN_MODIFIER_READONLY = 1 << 3;
|
|
10
|
-
const TOKEN_MODIFIER_LOCAL = 1 << 5;
|
|
11
10
|
const TOKEN_ENCODING_TYPE_OFFSET = 8;
|
|
12
11
|
const IDENTIFIER_PATTERN = /[A-Za-z_$][A-Za-z0-9_$]*/g;
|
|
13
12
|
const KEYWORD_DELIMITER_PATTERN = /\s+(in|of)\s+/;
|
|
@@ -233,14 +232,11 @@ function buildSemanticSpans(tokens, format, ts) {
|
|
|
233
232
|
if (format === ts.SemanticClassificationFormat.TwentyTwenty) {
|
|
234
233
|
for (let i = 0; i < tokens.length; i++) {
|
|
235
234
|
const token = tokens[i];
|
|
236
|
-
if (token.kind === 'keyword') {
|
|
237
|
-
continue;
|
|
238
|
-
}
|
|
239
235
|
spans.push({
|
|
240
236
|
start: token.start,
|
|
241
237
|
length: token.length,
|
|
242
|
-
classification: token.kind === '
|
|
243
|
-
? encodeSemantic2020(TOKEN_TYPE_VARIABLE, TOKEN_MODIFIER_READONLY
|
|
238
|
+
classification: token.kind === 'keyword'
|
|
239
|
+
? encodeSemantic2020(TOKEN_TYPE_VARIABLE, TOKEN_MODIFIER_READONLY)
|
|
244
240
|
: encodeSemantic2020(TOKEN_TYPE_VARIABLE, 0),
|
|
245
241
|
});
|
|
246
242
|
}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type tsModule from 'typescript/lib/tsserverlibrary';
|
|
2
|
+
import type { FileAnalysis, ResolvedConfig } from './types';
|
|
3
|
+
export declare function getKForQuickInfoAtPosition(analysis: FileAnalysis, position: number, ts: typeof tsModule, config: ResolvedConfig): tsModule.QuickInfo | undefined;
|
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getKForQuickInfoAtPosition = getKForQuickInfoAtPosition;
|
|
4
|
+
const ast_1 = require("./ast");
|
|
5
|
+
const completion_1 = require("./completion");
|
|
6
|
+
const jsx_attributes_1 = require("./jsx-attributes");
|
|
7
|
+
const identifiers_1 = require("./identifiers");
|
|
8
|
+
const kfor_parser_1 = require("./kfor-parser");
|
|
9
|
+
const scope_analysis_1 = require("./scope-analysis");
|
|
10
|
+
const type_resolution_1 = require("./type-resolution");
|
|
11
|
+
const IDENTIFIER_PATTERN = /[A-Za-z_$][A-Za-z0-9_$]*/g;
|
|
12
|
+
const KEYWORD_DELIMITER_PATTERN = /\s+(in|of)\s+/;
|
|
13
|
+
function getKForQuickInfoAtPosition(analysis, position, ts, config) {
|
|
14
|
+
const stringQuickInfo = getKForStringQuickInfo(analysis, position, ts, config);
|
|
15
|
+
if (stringQuickInfo) {
|
|
16
|
+
return stringQuickInfo;
|
|
17
|
+
}
|
|
18
|
+
const identifier = (0, ast_1.findIdentifierAtPosition)(analysis.sourceFile, position, ts);
|
|
19
|
+
if (!identifier) {
|
|
20
|
+
return undefined;
|
|
21
|
+
}
|
|
22
|
+
const bindings = (0, scope_analysis_1.collectBindingsAtPosition)(position, analysis.scopes);
|
|
23
|
+
if (bindings.size === 0) {
|
|
24
|
+
return undefined;
|
|
25
|
+
}
|
|
26
|
+
const aliasBinding = bindings.get(identifier.text);
|
|
27
|
+
if (aliasBinding) {
|
|
28
|
+
return createQuickInfo(identifier, `(k-for) ${aliasBinding.name}`, aliasBinding.types, analysis.checker, ts);
|
|
29
|
+
}
|
|
30
|
+
const memberTypes = resolveMemberQuickInfoTypes(identifier, bindings, analysis.sourceFile, analysis.checker, ts);
|
|
31
|
+
if (memberTypes.length > 0) {
|
|
32
|
+
return createQuickInfo(identifier, `(k-for) ${identifier.text}`, memberTypes, analysis.checker, ts);
|
|
33
|
+
}
|
|
34
|
+
return undefined;
|
|
35
|
+
}
|
|
36
|
+
function getKForStringQuickInfo(analysis, position, ts, config) {
|
|
37
|
+
const context = findKForStringContextAtPosition(analysis.sourceFile, position, ts, config);
|
|
38
|
+
if (!context) {
|
|
39
|
+
return undefined;
|
|
40
|
+
}
|
|
41
|
+
if (context.token.kind === 'alias') {
|
|
42
|
+
const bindings = (0, scope_analysis_1.resolveBindingsForForAttribute)(context.opening, context.attr, analysis.checker, config, ts);
|
|
43
|
+
for (let i = 0; i < bindings.length; i++) {
|
|
44
|
+
const binding = bindings[i];
|
|
45
|
+
if (binding.name === context.token.text) {
|
|
46
|
+
return createQuickInfoForSpan(context.token.start, context.token.length, `(k-for) ${binding.name}`, binding.types, analysis.checker, context.opening, ts);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return undefined;
|
|
50
|
+
}
|
|
51
|
+
if (context.token.kind === 'source') {
|
|
52
|
+
const expression = getSourceExpressionForToken(context, ts);
|
|
53
|
+
if (!expression) {
|
|
54
|
+
return undefined;
|
|
55
|
+
}
|
|
56
|
+
const types = (0, type_resolution_1.resolveExpressionTypesFromText)(expression, {
|
|
57
|
+
checker: analysis.checker,
|
|
58
|
+
ts,
|
|
59
|
+
scopeNode: context.opening,
|
|
60
|
+
});
|
|
61
|
+
if (types.length === 0) {
|
|
62
|
+
return undefined;
|
|
63
|
+
}
|
|
64
|
+
return createQuickInfoForSpan(context.token.start, context.token.length, `(k-for source) ${context.token.text}`, types, analysis.checker, context.opening, ts);
|
|
65
|
+
}
|
|
66
|
+
return undefined;
|
|
67
|
+
}
|
|
68
|
+
function resolveMemberQuickInfoTypes(identifier, bindings, sourceFile, checker, ts) {
|
|
69
|
+
const parent = identifier.parent;
|
|
70
|
+
let expressionText;
|
|
71
|
+
if (ts.isPropertyAccessExpression(parent) && parent.name === identifier) {
|
|
72
|
+
expressionText = parent.getText(sourceFile);
|
|
73
|
+
}
|
|
74
|
+
else if (ts.isElementAccessExpression(parent) && parent.argumentExpression === identifier) {
|
|
75
|
+
expressionText = parent.getText(sourceFile);
|
|
76
|
+
}
|
|
77
|
+
if (!expressionText) {
|
|
78
|
+
return [];
|
|
79
|
+
}
|
|
80
|
+
return (0, type_resolution_1.resolveExpressionTypesFromText)(expressionText, {
|
|
81
|
+
checker,
|
|
82
|
+
ts,
|
|
83
|
+
scopeNode: identifier,
|
|
84
|
+
localBindings: (0, completion_1.createBindingTypeMap)(bindings),
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
function findKForStringContextAtPosition(sourceFile, position, ts, config) {
|
|
88
|
+
let found;
|
|
89
|
+
const visit = (node) => {
|
|
90
|
+
if (found) {
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
let opening;
|
|
94
|
+
if (ts.isJsxElement(node)) {
|
|
95
|
+
opening = node.openingElement;
|
|
96
|
+
}
|
|
97
|
+
else if (ts.isJsxSelfClosingElement(node)) {
|
|
98
|
+
opening = node;
|
|
99
|
+
}
|
|
100
|
+
if (opening) {
|
|
101
|
+
const attr = (0, jsx_attributes_1.getJsxAttribute)(opening, config.forAttr, ts);
|
|
102
|
+
if (attr) {
|
|
103
|
+
const tokenContext = findTokenInForAttribute(attr, opening, sourceFile, position, ts, config.allowOfKeyword);
|
|
104
|
+
if (tokenContext) {
|
|
105
|
+
found = tokenContext;
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
ts.forEachChild(node, visit);
|
|
111
|
+
};
|
|
112
|
+
visit(sourceFile);
|
|
113
|
+
return found;
|
|
114
|
+
}
|
|
115
|
+
function findTokenInForAttribute(attr, opening, sourceFile, position, ts, allowOfKeyword) {
|
|
116
|
+
const content = getAttributeRawContent(attr, sourceFile, ts);
|
|
117
|
+
if (!content) {
|
|
118
|
+
return undefined;
|
|
119
|
+
}
|
|
120
|
+
const raw = content.text;
|
|
121
|
+
const rawStart = content.start;
|
|
122
|
+
if (position < rawStart || position >= rawStart + raw.length) {
|
|
123
|
+
return undefined;
|
|
124
|
+
}
|
|
125
|
+
const value = raw.trim();
|
|
126
|
+
if (!value) {
|
|
127
|
+
return undefined;
|
|
128
|
+
}
|
|
129
|
+
const parsed = (0, kfor_parser_1.parseKForExpression)(value, allowOfKeyword);
|
|
130
|
+
if (!parsed) {
|
|
131
|
+
return undefined;
|
|
132
|
+
}
|
|
133
|
+
const delimiterMatch = KEYWORD_DELIMITER_PATTERN.exec(value);
|
|
134
|
+
if (!delimiterMatch) {
|
|
135
|
+
return undefined;
|
|
136
|
+
}
|
|
137
|
+
const trimStart = raw.length - raw.trimStart().length;
|
|
138
|
+
const keyword = delimiterMatch[1];
|
|
139
|
+
const keywordOffsetInDelimiter = delimiterMatch[0].indexOf(keyword);
|
|
140
|
+
const leftSegment = value.slice(0, delimiterMatch.index);
|
|
141
|
+
const rightSegment = value.slice(delimiterMatch.index + delimiterMatch[0].length);
|
|
142
|
+
const leftLeading = leftSegment.length - leftSegment.trimStart().length;
|
|
143
|
+
const leftTrimmed = leftSegment.trim();
|
|
144
|
+
let aliasText = leftTrimmed;
|
|
145
|
+
let aliasStartInRaw = trimStart + leftLeading;
|
|
146
|
+
if (leftTrimmed.startsWith('(') && leftTrimmed.endsWith(')')) {
|
|
147
|
+
aliasText = leftTrimmed.slice(1, -1);
|
|
148
|
+
aliasStartInRaw += 1;
|
|
149
|
+
}
|
|
150
|
+
const rightLeading = rightSegment.length - rightSegment.trimStart().length;
|
|
151
|
+
const sourceText = rightSegment.trim();
|
|
152
|
+
const sourceStartInRaw = trimStart + delimiterMatch.index + delimiterMatch[0].length + rightLeading;
|
|
153
|
+
const aliasTokens = collectAliasTokens(aliasText, aliasStartInRaw, parsed.aliases, rawStart);
|
|
154
|
+
const sourceTokens = collectSourceTokens(sourceText, sourceStartInRaw, rawStart, ts);
|
|
155
|
+
const keywordToken = {
|
|
156
|
+
kind: 'keyword',
|
|
157
|
+
text: keyword,
|
|
158
|
+
start: rawStart + trimStart + delimiterMatch.index + keywordOffsetInDelimiter,
|
|
159
|
+
length: keyword.length,
|
|
160
|
+
};
|
|
161
|
+
const allTokens = [...aliasTokens, keywordToken, ...sourceTokens];
|
|
162
|
+
for (let i = 0; i < allTokens.length; i++) {
|
|
163
|
+
const token = allTokens[i];
|
|
164
|
+
if (position >= token.start && position < token.start + token.length) {
|
|
165
|
+
return {
|
|
166
|
+
opening,
|
|
167
|
+
attr,
|
|
168
|
+
sourceText,
|
|
169
|
+
sourceStart: rawStart + sourceStartInRaw,
|
|
170
|
+
token,
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
return undefined;
|
|
175
|
+
}
|
|
176
|
+
function collectAliasTokens(aliasText, aliasStartInRaw, aliases, rawStart) {
|
|
177
|
+
const result = [];
|
|
178
|
+
const allowed = new Set(aliases);
|
|
179
|
+
let match;
|
|
180
|
+
IDENTIFIER_PATTERN.lastIndex = 0;
|
|
181
|
+
while ((match = IDENTIFIER_PATTERN.exec(aliasText))) {
|
|
182
|
+
const name = match[0];
|
|
183
|
+
if (!allowed.has(name) || !(0, identifiers_1.isValidIdentifier)(name)) {
|
|
184
|
+
continue;
|
|
185
|
+
}
|
|
186
|
+
result.push({
|
|
187
|
+
kind: 'alias',
|
|
188
|
+
text: name,
|
|
189
|
+
start: rawStart + aliasStartInRaw + match.index,
|
|
190
|
+
length: name.length,
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
return result;
|
|
194
|
+
}
|
|
195
|
+
function collectSourceTokens(sourceText, sourceStartInRaw, rawStart, ts) {
|
|
196
|
+
const tokens = collectSourceTokensWithAst(sourceText, sourceStartInRaw, rawStart, ts);
|
|
197
|
+
if (tokens.length > 0) {
|
|
198
|
+
return tokens;
|
|
199
|
+
}
|
|
200
|
+
const result = [];
|
|
201
|
+
let match;
|
|
202
|
+
IDENTIFIER_PATTERN.lastIndex = 0;
|
|
203
|
+
while ((match = IDENTIFIER_PATTERN.exec(sourceText))) {
|
|
204
|
+
const name = match[0];
|
|
205
|
+
if (!(0, identifiers_1.isValidIdentifier)(name)) {
|
|
206
|
+
continue;
|
|
207
|
+
}
|
|
208
|
+
result.push({
|
|
209
|
+
kind: 'source',
|
|
210
|
+
text: name,
|
|
211
|
+
start: rawStart + sourceStartInRaw + match.index,
|
|
212
|
+
length: name.length,
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
return result;
|
|
216
|
+
}
|
|
217
|
+
function collectSourceTokensWithAst(sourceText, sourceStartInRaw, rawStart, ts) {
|
|
218
|
+
const snippet = `(${sourceText});`;
|
|
219
|
+
const tempSourceFile = ts.createSourceFile('__k_for_quickinfo.ts', snippet, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS);
|
|
220
|
+
if (tempSourceFile.statements.length === 0) {
|
|
221
|
+
return [];
|
|
222
|
+
}
|
|
223
|
+
const statement = tempSourceFile.statements[0];
|
|
224
|
+
if (!ts.isExpressionStatement(statement)) {
|
|
225
|
+
return [];
|
|
226
|
+
}
|
|
227
|
+
const tokens = [];
|
|
228
|
+
const visit = (node) => {
|
|
229
|
+
if (ts.isIdentifier(node) && (0, identifiers_1.isValidIdentifier)(node.text)) {
|
|
230
|
+
const start = node.getStart(tempSourceFile) - 1;
|
|
231
|
+
const length = node.getWidth(tempSourceFile);
|
|
232
|
+
if (start >= 0 && start + length <= sourceText.length) {
|
|
233
|
+
tokens.push({
|
|
234
|
+
kind: 'source',
|
|
235
|
+
text: node.text,
|
|
236
|
+
start: rawStart + sourceStartInRaw + start,
|
|
237
|
+
length,
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
ts.forEachChild(node, visit);
|
|
242
|
+
};
|
|
243
|
+
visit(statement.expression);
|
|
244
|
+
return uniqueSourceTokens(tokens);
|
|
245
|
+
}
|
|
246
|
+
function uniqueSourceTokens(tokens) {
|
|
247
|
+
const map = new Map();
|
|
248
|
+
for (let i = 0; i < tokens.length; i++) {
|
|
249
|
+
const token = tokens[i];
|
|
250
|
+
const key = `${token.start}:${token.length}:${token.text}`;
|
|
251
|
+
if (!map.has(key)) {
|
|
252
|
+
map.set(key, token);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
return Array.from(map.values()).sort((left, right) => left.start - right.start);
|
|
256
|
+
}
|
|
257
|
+
function getSourceExpressionForToken(context, ts) {
|
|
258
|
+
const relativeStart = context.token.start - context.sourceStart;
|
|
259
|
+
if (relativeStart < 0 || relativeStart >= context.sourceText.length) {
|
|
260
|
+
return undefined;
|
|
261
|
+
}
|
|
262
|
+
const snippet = `(${context.sourceText});`;
|
|
263
|
+
const tempSourceFile = ts.createSourceFile('__k_for_quickinfo_resolve.ts', snippet, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS);
|
|
264
|
+
const lookupPosition = relativeStart + 1;
|
|
265
|
+
const identifier = (0, ast_1.findIdentifierAtPosition)(tempSourceFile, lookupPosition, ts);
|
|
266
|
+
if (!identifier) {
|
|
267
|
+
return context.token.text;
|
|
268
|
+
}
|
|
269
|
+
if (ts.isPropertyAccessExpression(identifier.parent) && identifier.parent.name === identifier) {
|
|
270
|
+
return identifier.parent.getText(tempSourceFile);
|
|
271
|
+
}
|
|
272
|
+
if (ts.isElementAccessExpression(identifier.parent) && identifier.parent.argumentExpression === identifier) {
|
|
273
|
+
return identifier.parent.getText(tempSourceFile);
|
|
274
|
+
}
|
|
275
|
+
return identifier.getText(tempSourceFile);
|
|
276
|
+
}
|
|
277
|
+
function getAttributeRawContent(attr, sourceFile, ts) {
|
|
278
|
+
const initializer = attr.initializer;
|
|
279
|
+
if (!initializer) {
|
|
280
|
+
return undefined;
|
|
281
|
+
}
|
|
282
|
+
if (ts.isStringLiteral(initializer)) {
|
|
283
|
+
const quotedText = initializer.getText(sourceFile);
|
|
284
|
+
if (quotedText.length < 2) {
|
|
285
|
+
return undefined;
|
|
286
|
+
}
|
|
287
|
+
return {
|
|
288
|
+
start: initializer.getStart(sourceFile) + 1,
|
|
289
|
+
text: quotedText.slice(1, -1),
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
const expression = (0, jsx_attributes_1.getAttributeExpression)(attr, ts);
|
|
293
|
+
if (!expression || !ts.isStringLiteralLike(expression)) {
|
|
294
|
+
return undefined;
|
|
295
|
+
}
|
|
296
|
+
const quotedText = expression.getText(sourceFile);
|
|
297
|
+
if (quotedText.length < 2) {
|
|
298
|
+
return undefined;
|
|
299
|
+
}
|
|
300
|
+
return {
|
|
301
|
+
start: expression.getStart(sourceFile) + 1,
|
|
302
|
+
text: quotedText.slice(1, -1),
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
function createQuickInfo(identifier, label, types, checker, ts) {
|
|
306
|
+
if (types.length === 0) {
|
|
307
|
+
return undefined;
|
|
308
|
+
}
|
|
309
|
+
return createQuickInfoForSpan(identifier.getStart(), identifier.getWidth(), label, types, checker, identifier, ts);
|
|
310
|
+
}
|
|
311
|
+
function createQuickInfoForSpan(start, length, label, types, checker, scopeNode, ts) {
|
|
312
|
+
return {
|
|
313
|
+
kind: ts.ScriptElementKind.localVariableElement,
|
|
314
|
+
kindModifiers: '',
|
|
315
|
+
textSpan: { start, length },
|
|
316
|
+
displayParts: [{ text: `${label}: ${(0, type_resolution_1.formatTypeList)(types, checker, scopeNode, ts)}`, kind: 'text' }],
|
|
317
|
+
};
|
|
318
|
+
}
|
package/dist/scope-analysis.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type tsModule from 'typescript/lib/tsserverlibrary';
|
|
2
|
-
import type { FileAnalysis, KForBinding, KForScope, ResolvedConfig } from './types';
|
|
2
|
+
import type { FileAnalysis, JsxOpeningLikeElement, KForBinding, KForScope, ResolvedConfig } from './types';
|
|
3
3
|
export declare function getFileAnalysis(fileName: string, languageService: tsModule.LanguageService, ts: typeof tsModule, config: ResolvedConfig): FileAnalysis | undefined;
|
|
4
4
|
export declare function isSuppressed(position: number, diagnosticName: string, scopes: KForScope[]): boolean;
|
|
5
5
|
export declare function collectBindingsAtPosition(position: number, scopes: KForScope[]): Map<string, KForBinding>;
|
|
6
|
+
export declare function resolveBindingsForForAttribute(opening: JsxOpeningLikeElement, forAttr: tsModule.JsxAttribute, checker: tsModule.TypeChecker, config: ResolvedConfig, ts: typeof tsModule): KForBinding[];
|
package/dist/scope-analysis.js
CHANGED
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.getFileAnalysis = getFileAnalysis;
|
|
4
4
|
exports.isSuppressed = isSuppressed;
|
|
5
5
|
exports.collectBindingsAtPosition = collectBindingsAtPosition;
|
|
6
|
+
exports.resolveBindingsForForAttribute = resolveBindingsForForAttribute;
|
|
6
7
|
const jsx_attributes_1 = require("./jsx-attributes");
|
|
7
8
|
const kfor_parser_1 = require("./kfor-parser");
|
|
8
9
|
const identifiers_1 = require("./identifiers");
|
|
@@ -53,6 +54,9 @@ function collectBindingsAtPosition(position, scopes) {
|
|
|
53
54
|
}
|
|
54
55
|
return bindings;
|
|
55
56
|
}
|
|
57
|
+
function resolveBindingsForForAttribute(opening, forAttr, checker, config, ts) {
|
|
58
|
+
return resolveScopeBindings(opening, forAttr, checker, config, ts);
|
|
59
|
+
}
|
|
56
60
|
function collectKForScopes(sourceFile, checker, ts, config) {
|
|
57
61
|
const scopes = [];
|
|
58
62
|
const visit = (node) => {
|