@vue/language-service 2.1.10 → 2.2.2
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/data/language-blocks/cs.json +29 -930
- package/data/language-blocks/en.json +28 -929
- package/data/language-blocks/fr.json +28 -929
- package/data/language-blocks/it.json +28 -929
- package/data/language-blocks/ja.json +28 -929
- package/data/language-blocks/ko.json +28 -929
- package/data/language-blocks/pt.json +28 -929
- package/data/language-blocks/ru.json +28 -929
- package/data/language-blocks/zh-cn.json +30 -931
- package/data/language-blocks/zh-hk.json +28 -929
- package/data/locale.json +54 -0
- package/data/model-modifiers/cs.json +6 -165
- package/data/model-modifiers/en.json +6 -165
- package/data/model-modifiers/fr.json +6 -165
- package/data/model-modifiers/it.json +6 -165
- package/data/model-modifiers/ja.json +6 -165
- package/data/model-modifiers/ko.json +6 -165
- package/data/model-modifiers/pt.json +6 -165
- package/data/model-modifiers/ru.json +6 -165
- package/data/model-modifiers/zh-cn.json +6 -165
- package/data/model-modifiers/zh-hk.json +6 -165
- package/data/template/cs.json +59 -1429
- package/data/template/en.json +52 -1422
- package/data/template/fr.json +55 -1425
- package/data/template/it.json +49 -1427
- package/data/template/ja.json +53 -1423
- package/data/template/ko.json +44 -1422
- package/data/template/pt.json +44 -1422
- package/data/template/ru.json +52 -1422
- package/data/template/zh-cn.json +53 -1423
- package/data/template/zh-hk.json +44 -1422
- package/index.d.ts +2 -2
- package/index.js +14 -10
- package/lib/ideFeatures/nameCasing.js +15 -17
- package/lib/plugins/data.js +47 -20
- package/lib/plugins/utils.d.ts +3 -0
- package/lib/plugins/utils.js +14 -0
- package/lib/plugins/vue-autoinsert-dotvalue.d.ts +0 -9
- package/lib/plugins/vue-autoinsert-dotvalue.js +37 -53
- package/lib/plugins/vue-autoinsert-space.js +1 -1
- package/lib/plugins/vue-complete-define-assignment.js +14 -16
- package/lib/plugins/vue-directive-comments.js +10 -8
- package/lib/plugins/vue-document-drop.js +16 -13
- package/lib/plugins/vue-document-links.js +45 -39
- package/lib/plugins/vue-extract-file.d.ts +2 -1
- package/lib/plugins/vue-extract-file.js +29 -15
- package/lib/plugins/vue-inlayhints.d.ts +2 -2
- package/lib/plugins/vue-inlayhints.js +65 -56
- package/lib/plugins/vue-sfc.js +29 -27
- package/lib/plugins/vue-template.js +223 -201
- package/lib/plugins/vue-twoslash-queries.js +9 -4
- package/package.json +12 -11
- package/scripts/update-html-data.js +74 -70
|
@@ -11,15 +11,26 @@ const vscode_uri_1 = require("vscode-uri");
|
|
|
11
11
|
const nameCasing_1 = require("../ideFeatures/nameCasing");
|
|
12
12
|
const types_1 = require("../types");
|
|
13
13
|
const data_1 = require("./data");
|
|
14
|
-
const specialTags = new Set([
|
|
15
|
-
|
|
14
|
+
const specialTags = new Set([
|
|
15
|
+
'slot',
|
|
16
|
+
'component',
|
|
17
|
+
'template',
|
|
18
|
+
]);
|
|
19
|
+
const specialProps = new Set([
|
|
20
|
+
'class',
|
|
21
|
+
'data-allow-mismatch',
|
|
22
|
+
'is',
|
|
23
|
+
'key',
|
|
24
|
+
'ref',
|
|
25
|
+
'style',
|
|
26
|
+
]);
|
|
16
27
|
let builtInData;
|
|
17
28
|
let modelData;
|
|
18
29
|
function create(mode, ts, getTsPluginClient) {
|
|
19
30
|
let customData = [];
|
|
20
31
|
let extraCustomData = [];
|
|
21
32
|
let lastCompletionComponentNames = new Set();
|
|
22
|
-
const
|
|
33
|
+
const cachedPropInfos = new Map();
|
|
23
34
|
const onDidChangeCustomDataListeners = new Set();
|
|
24
35
|
const onDidChangeCustomData = (listener) => {
|
|
25
36
|
onDidChangeCustomDataListeners.add(listener);
|
|
@@ -79,8 +90,8 @@ function create(mode, ts, getTsPluginClient) {
|
|
|
79
90
|
modelData ??= (0, data_1.loadModelModifiersData)(context.env.locale ?? 'en');
|
|
80
91
|
// https://vuejs.org/api/built-in-directives.html#v-on
|
|
81
92
|
// https://vuejs.org/api/built-in-directives.html#v-bind
|
|
82
|
-
const
|
|
83
|
-
const
|
|
93
|
+
const vOnModifiers = {};
|
|
94
|
+
const vBindModifiers = {};
|
|
84
95
|
const vOn = builtInData.globalAttributes?.find(x => x.name === 'v-on');
|
|
85
96
|
const vBind = builtInData.globalAttributes?.find(x => x.name === 'v-bind');
|
|
86
97
|
if (vOn) {
|
|
@@ -89,9 +100,9 @@ function create(mode, ts, getTsPluginClient) {
|
|
|
89
100
|
.split('\n- ')[4]
|
|
90
101
|
.split('\n').slice(2, -1);
|
|
91
102
|
for (let text of modifiers) {
|
|
92
|
-
text = text.
|
|
93
|
-
const [name,
|
|
94
|
-
|
|
103
|
+
text = text.slice(' - `.'.length);
|
|
104
|
+
const [name, desc] = text.split('` - ');
|
|
105
|
+
vOnModifiers[name] = desc;
|
|
95
106
|
}
|
|
96
107
|
}
|
|
97
108
|
if (vBind) {
|
|
@@ -100,9 +111,9 @@ function create(mode, ts, getTsPluginClient) {
|
|
|
100
111
|
.split('\n- ')[4]
|
|
101
112
|
.split('\n').slice(2, -1);
|
|
102
113
|
for (let text of modifiers) {
|
|
103
|
-
text = text.
|
|
104
|
-
const [name,
|
|
105
|
-
|
|
114
|
+
text = text.slice(' - `.'.length);
|
|
115
|
+
const [name, desc] = text.split('` - ');
|
|
116
|
+
vBindModifiers[name] = desc;
|
|
106
117
|
}
|
|
107
118
|
}
|
|
108
119
|
const disposable = context.env.onDidChangeConfiguration?.(() => initializing = undefined);
|
|
@@ -120,15 +131,16 @@ function create(mode, ts, getTsPluginClient) {
|
|
|
120
131
|
if (!context.project.vue) {
|
|
121
132
|
return;
|
|
122
133
|
}
|
|
123
|
-
const vueCompilerOptions = context.project.vue.compilerOptions;
|
|
124
134
|
let sync;
|
|
125
135
|
let currentVersion;
|
|
126
|
-
const
|
|
136
|
+
const uri = vscode_uri_1.URI.parse(document.uri);
|
|
137
|
+
const decoded = context.decodeEmbeddedDocumentUri(uri);
|
|
127
138
|
const sourceScript = decoded && context.language.scripts.get(decoded[0]);
|
|
128
|
-
|
|
139
|
+
const root = sourceScript?.generated?.root;
|
|
140
|
+
if (root instanceof language_core_1.VueVirtualCode) {
|
|
129
141
|
// #4298: Precompute HTMLDocument before provideHtmlData to avoid parseHTMLDocument requesting component names from tsserver
|
|
130
142
|
baseServiceInstance.provideCompletionItems?.(document, position, completionContext, token);
|
|
131
|
-
sync = (await provideHtmlData(
|
|
143
|
+
sync = (await provideHtmlData(sourceScript.id, root)).sync;
|
|
132
144
|
currentVersion = await sync();
|
|
133
145
|
}
|
|
134
146
|
let htmlComplete = await baseServiceInstance.provideCompletionItems?.(document, position, completionContext, token);
|
|
@@ -159,7 +171,6 @@ function create(mode, ts, getTsPluginClient) {
|
|
|
159
171
|
if (!enabled) {
|
|
160
172
|
return;
|
|
161
173
|
}
|
|
162
|
-
const result = [];
|
|
163
174
|
const uri = vscode_uri_1.URI.parse(document.uri);
|
|
164
175
|
const decoded = context.decodeEmbeddedDocumentUri(uri);
|
|
165
176
|
const sourceScript = decoded && context.language.scripts.get(decoded[0]);
|
|
@@ -167,88 +178,98 @@ function create(mode, ts, getTsPluginClient) {
|
|
|
167
178
|
if (!virtualCode) {
|
|
168
179
|
return;
|
|
169
180
|
}
|
|
170
|
-
const
|
|
181
|
+
const root = sourceScript?.generated?.root;
|
|
182
|
+
if (!(root instanceof language_core_1.VueVirtualCode)) {
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
171
185
|
const scanner = getScanner(baseServiceInstance, document);
|
|
172
|
-
if (
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
186
|
+
if (!scanner) {
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
const result = [];
|
|
190
|
+
// visualize missing required props
|
|
191
|
+
const casing = await (0, nameCasing_1.getNameCasing)(context, decoded[0]);
|
|
192
|
+
const components = await tsPluginClient?.getComponentNames(root.fileName) ?? [];
|
|
193
|
+
const componentProps = {};
|
|
194
|
+
let token;
|
|
195
|
+
let current;
|
|
196
|
+
while ((token = scanner.scan()) !== html.TokenType.EOS) {
|
|
197
|
+
if (token === html.TokenType.StartTag) {
|
|
198
|
+
const tagName = scanner.getTokenText();
|
|
199
|
+
const checkTag = tagName.includes('.')
|
|
200
|
+
? tagName
|
|
201
|
+
: components.find(component => component === tagName || (0, language_core_1.hyphenateTag)(component) === tagName);
|
|
202
|
+
if (checkTag) {
|
|
203
|
+
componentProps[checkTag] ??= (await tsPluginClient?.getComponentProps(root.fileName, checkTag) ?? [])
|
|
204
|
+
.filter(prop => prop.required)
|
|
205
|
+
.map(prop => prop.name);
|
|
206
|
+
current = {
|
|
207
|
+
unburnedRequiredProps: [...componentProps[checkTag]],
|
|
208
|
+
labelOffset: scanner.getTokenOffset() + scanner.getTokenLength(),
|
|
209
|
+
insertOffset: scanner.getTokenOffset() + scanner.getTokenLength(),
|
|
210
|
+
};
|
|
193
211
|
}
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
212
|
+
}
|
|
213
|
+
else if (token === html.TokenType.AttributeName) {
|
|
214
|
+
if (current) {
|
|
215
|
+
let attrText = scanner.getTokenText();
|
|
216
|
+
if (attrText === 'v-bind') {
|
|
217
|
+
current.unburnedRequiredProps = [];
|
|
218
|
+
}
|
|
219
|
+
else {
|
|
220
|
+
// remove modifiers
|
|
221
|
+
if (attrText.includes('.')) {
|
|
222
|
+
attrText = attrText.split('.')[0];
|
|
199
223
|
}
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
attrText = attrText.split('.')[0];
|
|
204
|
-
}
|
|
205
|
-
// normalize
|
|
206
|
-
if (attrText.startsWith('v-bind:')) {
|
|
207
|
-
attrText = attrText.substring('v-bind:'.length);
|
|
208
|
-
}
|
|
209
|
-
else if (attrText.startsWith(':')) {
|
|
210
|
-
attrText = attrText.substring(':'.length);
|
|
211
|
-
}
|
|
212
|
-
else if (attrText.startsWith('v-model:')) {
|
|
213
|
-
attrText = attrText.substring('v-model:'.length);
|
|
214
|
-
}
|
|
215
|
-
else if (attrText === 'v-model') {
|
|
216
|
-
attrText = vueCompilerOptions.target >= 3 ? 'modelValue' : 'value'; // TODO: support for experimentalModelPropName?
|
|
217
|
-
}
|
|
218
|
-
else if (attrText.startsWith('@')) {
|
|
219
|
-
attrText = 'on-' + (0, language_core_1.hyphenateAttr)(attrText.substring('@'.length));
|
|
220
|
-
}
|
|
221
|
-
current.unburnedRequiredProps = current.unburnedRequiredProps.filter(propName => {
|
|
222
|
-
return attrText !== propName
|
|
223
|
-
&& attrText !== (0, language_core_1.hyphenateAttr)(propName);
|
|
224
|
-
});
|
|
224
|
+
// normalize
|
|
225
|
+
if (attrText.startsWith('v-bind:')) {
|
|
226
|
+
attrText = attrText.slice('v-bind:'.length);
|
|
225
227
|
}
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
},
|
|
241
|
-
newText: ` :${casing.attr === types_1.AttrNameCasing.Kebab ? (0, language_core_1.hyphenateAttr)(requiredProp) : requiredProp}=`,
|
|
242
|
-
}],
|
|
243
|
-
});
|
|
228
|
+
else if (attrText.startsWith(':')) {
|
|
229
|
+
attrText = attrText.slice(':'.length);
|
|
230
|
+
}
|
|
231
|
+
else if (attrText.startsWith('v-model:')) {
|
|
232
|
+
attrText = attrText.slice('v-model:'.length);
|
|
233
|
+
}
|
|
234
|
+
else if (attrText === 'v-model') {
|
|
235
|
+
attrText = vueCompilerOptions.target >= 3 ? 'modelValue' : 'value'; // TODO: support for experimentalModelPropName?
|
|
236
|
+
}
|
|
237
|
+
else if (attrText.startsWith('v-on:')) {
|
|
238
|
+
attrText = 'on-' + (0, language_core_1.hyphenateAttr)(attrText.slice('v-on:'.length));
|
|
239
|
+
}
|
|
240
|
+
else if (attrText.startsWith('@')) {
|
|
241
|
+
attrText = 'on-' + (0, language_core_1.hyphenateAttr)(attrText.slice('@'.length));
|
|
244
242
|
}
|
|
245
|
-
current =
|
|
243
|
+
current.unburnedRequiredProps = current.unburnedRequiredProps.filter(propName => {
|
|
244
|
+
return attrText !== propName
|
|
245
|
+
&& attrText !== (0, language_core_1.hyphenateAttr)(propName);
|
|
246
|
+
});
|
|
246
247
|
}
|
|
247
248
|
}
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
249
|
+
}
|
|
250
|
+
else if (token === html.TokenType.StartTagSelfClose || token === html.TokenType.StartTagClose) {
|
|
251
|
+
if (current) {
|
|
252
|
+
for (const requiredProp of current.unburnedRequiredProps) {
|
|
253
|
+
result.push({
|
|
254
|
+
label: `${requiredProp}!`,
|
|
255
|
+
paddingLeft: true,
|
|
256
|
+
position: document.positionAt(current.labelOffset),
|
|
257
|
+
kind: 2,
|
|
258
|
+
textEdits: [{
|
|
259
|
+
range: {
|
|
260
|
+
start: document.positionAt(current.insertOffset),
|
|
261
|
+
end: document.positionAt(current.insertOffset),
|
|
262
|
+
},
|
|
263
|
+
newText: ` :${casing.attr === types_1.AttrNameCasing.Kebab ? (0, language_core_1.hyphenateAttr)(requiredProp) : requiredProp}=`,
|
|
264
|
+
}],
|
|
265
|
+
});
|
|
251
266
|
}
|
|
267
|
+
current = undefined;
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
if (token === html.TokenType.AttributeName || token === html.TokenType.AttributeValue) {
|
|
271
|
+
if (current) {
|
|
272
|
+
current.insertOffset = scanner.getTokenOffset() + scanner.getTokenLength();
|
|
252
273
|
}
|
|
253
274
|
}
|
|
254
275
|
}
|
|
@@ -267,7 +288,6 @@ function create(mode, ts, getTsPluginClient) {
|
|
|
267
288
|
if (!isSupportedDocument(document)) {
|
|
268
289
|
return;
|
|
269
290
|
}
|
|
270
|
-
const originalResult = await baseServiceInstance.provideDiagnostics?.(document, token);
|
|
271
291
|
const uri = vscode_uri_1.URI.parse(document.uri);
|
|
272
292
|
const decoded = context.decodeEmbeddedDocumentUri(uri);
|
|
273
293
|
const sourceScript = decoded && context.language.scripts.get(decoded[0]);
|
|
@@ -275,12 +295,13 @@ function create(mode, ts, getTsPluginClient) {
|
|
|
275
295
|
if (!virtualCode) {
|
|
276
296
|
return;
|
|
277
297
|
}
|
|
278
|
-
const
|
|
279
|
-
if (!(
|
|
298
|
+
const root = sourceScript?.generated?.root;
|
|
299
|
+
if (!(root instanceof language_core_1.VueVirtualCode)) {
|
|
280
300
|
return;
|
|
281
301
|
}
|
|
302
|
+
const originalResult = await baseServiceInstance.provideDiagnostics?.(document, token);
|
|
282
303
|
const templateErrors = [];
|
|
283
|
-
const { template } =
|
|
304
|
+
const { template } = root._sfc;
|
|
284
305
|
if (template) {
|
|
285
306
|
for (const error of template.errors) {
|
|
286
307
|
onCompilerError(error, 1);
|
|
@@ -323,20 +344,23 @@ function create(mode, ts, getTsPluginClient) {
|
|
|
323
344
|
if (!languageService) {
|
|
324
345
|
return;
|
|
325
346
|
}
|
|
326
|
-
const
|
|
347
|
+
const uri = vscode_uri_1.URI.parse(document.uri);
|
|
348
|
+
const decoded = context.decodeEmbeddedDocumentUri(uri);
|
|
327
349
|
const sourceScript = decoded && context.language.scripts.get(decoded[0]);
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
350
|
+
const root = sourceScript?.generated?.root;
|
|
351
|
+
if (!(root instanceof language_core_1.VueVirtualCode)) {
|
|
352
|
+
return;
|
|
353
|
+
}
|
|
354
|
+
const { template } = root._sfc;
|
|
355
|
+
if (!template) {
|
|
356
|
+
return;
|
|
332
357
|
}
|
|
333
|
-
const { template } = sourceScript.generated.root._sfc;
|
|
334
358
|
const spans = common_1.getComponentSpans.call({
|
|
335
359
|
files: context.language.scripts,
|
|
336
360
|
languageService,
|
|
337
361
|
typescript: ts,
|
|
338
362
|
vueOptions: vueCompilerOptions,
|
|
339
|
-
},
|
|
363
|
+
}, root, template, {
|
|
340
364
|
start: document.offsetAt(range.start),
|
|
341
365
|
length: document.offsetAt(range.end) - document.offsetAt(range.start),
|
|
342
366
|
});
|
|
@@ -353,7 +377,7 @@ function create(mode, ts, getTsPluginClient) {
|
|
|
353
377
|
});
|
|
354
378
|
},
|
|
355
379
|
};
|
|
356
|
-
async function provideHtmlData(
|
|
380
|
+
async function provideHtmlData(sourceDocumentUri, vueCode) {
|
|
357
381
|
await (initializing ??= initialize());
|
|
358
382
|
const casing = await (0, nameCasing_1.getNameCasing)(context, sourceDocumentUri);
|
|
359
383
|
if (builtInData.tags) {
|
|
@@ -362,7 +386,7 @@ function create(mode, ts, getTsPluginClient) {
|
|
|
362
386
|
continue;
|
|
363
387
|
}
|
|
364
388
|
if (specialTags.has(tag.name)) {
|
|
365
|
-
tag.name =
|
|
389
|
+
tag.name = generateItemKey('specialTag', tag.name, '');
|
|
366
390
|
}
|
|
367
391
|
else if (casing.tag === types_1.TagNameCasing.Kebab) {
|
|
368
392
|
tag.name = (0, language_core_1.hyphenateTag)(tag.name);
|
|
@@ -376,8 +400,7 @@ function create(mode, ts, getTsPluginClient) {
|
|
|
376
400
|
const tagInfos = new Map();
|
|
377
401
|
let version = 0;
|
|
378
402
|
let components;
|
|
379
|
-
|
|
380
|
-
tsDocumentations.clear();
|
|
403
|
+
cachedPropInfos.clear();
|
|
381
404
|
updateExtraCustomData([
|
|
382
405
|
html.newHTMLDataProvider('vue-template-built-in', builtInData),
|
|
383
406
|
{
|
|
@@ -397,9 +420,7 @@ function create(mode, ts, getTsPluginClient) {
|
|
|
397
420
|
})());
|
|
398
421
|
return [];
|
|
399
422
|
}
|
|
400
|
-
const scriptSetupRanges = vueCode._sfc
|
|
401
|
-
? (0, language_core_1.parseScriptSetupRanges)(ts, vueCode._sfc.scriptSetup.ast, vueCompilerOptions)
|
|
402
|
-
: undefined;
|
|
423
|
+
const scriptSetupRanges = language_core_1.tsCodegen.get(vueCode._sfc)?.getScriptSetupRanges();
|
|
403
424
|
const names = new Set();
|
|
404
425
|
const tags = [];
|
|
405
426
|
for (const tag of components) {
|
|
@@ -411,7 +432,7 @@ function create(mode, ts, getTsPluginClient) {
|
|
|
411
432
|
}
|
|
412
433
|
}
|
|
413
434
|
for (const binding of scriptSetupRanges?.bindings ?? []) {
|
|
414
|
-
const name = vueCode._sfc.scriptSetup.content.
|
|
435
|
+
const name = vueCode._sfc.scriptSetup.content.slice(binding.range.start, binding.range.end);
|
|
415
436
|
if (casing.tag === types_1.TagNameCasing.Kebab) {
|
|
416
437
|
names.add((0, language_core_1.hyphenateTag)(name));
|
|
417
438
|
}
|
|
@@ -432,42 +453,24 @@ function create(mode, ts, getTsPluginClient) {
|
|
|
432
453
|
if (!tagInfo) {
|
|
433
454
|
promises.push((async () => {
|
|
434
455
|
const attrs = await tsPluginClient?.getElementAttrs(vueCode.fileName, tag) ?? [];
|
|
435
|
-
const
|
|
456
|
+
const propInfos = await tsPluginClient?.getComponentProps(vueCode.fileName, tag) ?? [];
|
|
436
457
|
const events = await tsPluginClient?.getComponentEvents(vueCode.fileName, tag) ?? [];
|
|
458
|
+
const directives = await tsPluginClient?.getComponentDirectives(vueCode.fileName) ?? [];
|
|
437
459
|
tagInfos.set(tag, {
|
|
438
460
|
attrs,
|
|
439
|
-
|
|
461
|
+
propInfos: propInfos.filter(prop => !prop.name.startsWith('ref_')),
|
|
440
462
|
events,
|
|
463
|
+
directives,
|
|
441
464
|
});
|
|
442
465
|
version++;
|
|
443
466
|
})());
|
|
444
467
|
return [];
|
|
445
468
|
}
|
|
446
|
-
const { attrs,
|
|
447
|
-
const props =
|
|
469
|
+
const { attrs, propInfos, events, directives } = tagInfo;
|
|
470
|
+
const props = propInfos.map(prop => (0, language_core_1.hyphenateTag)(prop.name).startsWith('on-vnode-')
|
|
471
|
+
? 'onVue:' + prop.name.slice('onVnode'.length)
|
|
472
|
+
: prop.name);
|
|
448
473
|
const attributes = [];
|
|
449
|
-
const _tsCodegen = language_core_1.tsCodegen.get(vueCode._sfc);
|
|
450
|
-
if (_tsCodegen) {
|
|
451
|
-
if (!templateContextProps) {
|
|
452
|
-
promises.push((async () => {
|
|
453
|
-
templateContextProps = await tsPluginClient?.getTemplateContextProps(vueCode.fileName) ?? [];
|
|
454
|
-
version++;
|
|
455
|
-
})());
|
|
456
|
-
return [];
|
|
457
|
-
}
|
|
458
|
-
let ctxVars = [
|
|
459
|
-
..._tsCodegen.scriptRanges.get()?.bindings.map(binding => vueCode._sfc.script.content.substring(binding.start, binding.end)) ?? [],
|
|
460
|
-
..._tsCodegen.scriptSetupRanges.get()?.bindings.map(binding => vueCode._sfc.scriptSetup.content.substring(binding.start, binding.end)) ?? [],
|
|
461
|
-
...templateContextProps,
|
|
462
|
-
];
|
|
463
|
-
ctxVars = [...new Set(ctxVars)];
|
|
464
|
-
const dirs = ctxVars.map(language_core_1.hyphenateAttr).filter(v => v.startsWith('v-'));
|
|
465
|
-
for (const dir of dirs) {
|
|
466
|
-
attributes.push({
|
|
467
|
-
name: dir,
|
|
468
|
-
});
|
|
469
|
-
}
|
|
470
|
-
}
|
|
471
474
|
const propsSet = new Set(props);
|
|
472
475
|
for (const prop of [...props, ...attrs]) {
|
|
473
476
|
const isGlobal = !propsSet.has(prop);
|
|
@@ -477,7 +480,7 @@ function create(mode, ts, getTsPluginClient) {
|
|
|
477
480
|
const propNameBase = name.startsWith('on-')
|
|
478
481
|
? name.slice('on-'.length)
|
|
479
482
|
: (name['on'.length].toLowerCase() + name.slice('onX'.length));
|
|
480
|
-
const propKey =
|
|
483
|
+
const propKey = generateItemKey('componentEvent', isGlobal ? '*' : tag, propNameBase);
|
|
481
484
|
attributes.push({
|
|
482
485
|
name: 'v-on:' + propNameBase,
|
|
483
486
|
description: propKey,
|
|
@@ -488,13 +491,13 @@ function create(mode, ts, getTsPluginClient) {
|
|
|
488
491
|
}
|
|
489
492
|
else {
|
|
490
493
|
const propName = name;
|
|
491
|
-
const
|
|
492
|
-
const propDescription = propsInfo.find(prop => {
|
|
494
|
+
const propInfo = propInfos.find(prop => {
|
|
493
495
|
const name = casing.attr === types_1.AttrNameCasing.Camel ? prop.name : (0, language_core_1.hyphenateAttr)(prop.name);
|
|
494
496
|
return name === propName;
|
|
495
|
-
})
|
|
496
|
-
|
|
497
|
-
|
|
497
|
+
});
|
|
498
|
+
const propKey = generateItemKey('componentProp', isGlobal ? '*' : tag, propName, propInfo?.deprecated);
|
|
499
|
+
if (propInfo) {
|
|
500
|
+
cachedPropInfos.set(propName, propInfo);
|
|
498
501
|
}
|
|
499
502
|
attributes.push({
|
|
500
503
|
name: propName,
|
|
@@ -510,7 +513,7 @@ function create(mode, ts, getTsPluginClient) {
|
|
|
510
513
|
}
|
|
511
514
|
for (const event of events) {
|
|
512
515
|
const name = casing.attr === types_1.AttrNameCasing.Camel ? event : (0, language_core_1.hyphenateAttr)(event);
|
|
513
|
-
const propKey =
|
|
516
|
+
const propKey = generateItemKey('componentEvent', tag, name);
|
|
514
517
|
attributes.push({
|
|
515
518
|
name: 'v-on:' + name,
|
|
516
519
|
description: propKey,
|
|
@@ -519,21 +522,27 @@ function create(mode, ts, getTsPluginClient) {
|
|
|
519
522
|
description: propKey,
|
|
520
523
|
});
|
|
521
524
|
}
|
|
525
|
+
for (const directive of directives) {
|
|
526
|
+
const name = (0, language_core_1.hyphenateAttr)(directive);
|
|
527
|
+
attributes.push({
|
|
528
|
+
name
|
|
529
|
+
});
|
|
530
|
+
}
|
|
522
531
|
const models = [];
|
|
523
532
|
for (const prop of [...props, ...attrs]) {
|
|
524
533
|
if (prop.startsWith('onUpdate:')) {
|
|
525
534
|
const isGlobal = !propsSet.has(prop);
|
|
526
|
-
models.push([isGlobal, prop.
|
|
535
|
+
models.push([isGlobal, prop.slice('onUpdate:'.length)]);
|
|
527
536
|
}
|
|
528
537
|
}
|
|
529
538
|
for (const event of events) {
|
|
530
539
|
if (event.startsWith('update:')) {
|
|
531
|
-
models.push([false, event.
|
|
540
|
+
models.push([false, event.slice('update:'.length)]);
|
|
532
541
|
}
|
|
533
542
|
}
|
|
534
543
|
for (const [isGlobal, model] of models) {
|
|
535
544
|
const name = casing.attr === types_1.AttrNameCasing.Camel ? model : (0, language_core_1.hyphenateAttr)(model);
|
|
536
|
-
const propKey =
|
|
545
|
+
const propKey = generateItemKey('componentProp', isGlobal ? '*' : tag, name);
|
|
537
546
|
attributes.push({
|
|
538
547
|
name: 'v-model:' + name,
|
|
539
548
|
description: propKey,
|
|
@@ -558,30 +567,35 @@ function create(mode, ts, getTsPluginClient) {
|
|
|
558
567
|
};
|
|
559
568
|
}
|
|
560
569
|
function afterHtmlCompletion(completionList, document) {
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
570
|
+
do {
|
|
571
|
+
const replacement = getReplacement(completionList, document);
|
|
572
|
+
if (!replacement) {
|
|
573
|
+
break;
|
|
574
|
+
}
|
|
566
575
|
const hasModifier = replacement.text.includes('.');
|
|
567
|
-
|
|
568
|
-
|
|
576
|
+
if (!hasModifier) {
|
|
577
|
+
break;
|
|
578
|
+
}
|
|
579
|
+
const [text, ...modifiers] = replacement.text.split('.');
|
|
580
|
+
const isVOn = text.startsWith('v-on:') || text.startsWith('@') && text.length > 1;
|
|
581
|
+
const isVBind = text.startsWith('v-bind:') || text.startsWith(':') && text.length > 1;
|
|
582
|
+
const isVModel = text.startsWith('v-model:') || text === 'v-model';
|
|
583
|
+
const validModifiers = isVOn ? vOnModifiers
|
|
584
|
+
: isVBind ? vBindModifiers
|
|
569
585
|
: undefined;
|
|
570
|
-
|
|
571
|
-
const textWithoutModifier = replacement.text.split('.')[0];
|
|
572
|
-
if (validModifiers && hasModifier) {
|
|
586
|
+
if (validModifiers) {
|
|
573
587
|
for (const modifier in validModifiers) {
|
|
574
588
|
if (modifiers.includes(modifier)) {
|
|
575
589
|
continue;
|
|
576
590
|
}
|
|
577
|
-
const
|
|
578
|
-
const insertText =
|
|
591
|
+
const description = validModifiers[modifier];
|
|
592
|
+
const insertText = text + modifiers.slice(0, -1).map(m => '.' + m).join('') + '.' + modifier;
|
|
579
593
|
const newItem = {
|
|
580
594
|
label: modifier,
|
|
581
595
|
filterText: insertText,
|
|
582
596
|
documentation: {
|
|
583
597
|
kind: 'markdown',
|
|
584
|
-
value:
|
|
598
|
+
value: description,
|
|
585
599
|
},
|
|
586
600
|
textEdit: {
|
|
587
601
|
range: replacement.textEdit.range,
|
|
@@ -592,12 +606,12 @@ function create(mode, ts, getTsPluginClient) {
|
|
|
592
606
|
completionList.items.push(newItem);
|
|
593
607
|
}
|
|
594
608
|
}
|
|
595
|
-
else if (
|
|
609
|
+
else if (isVModel) {
|
|
596
610
|
for (const modifier of modelData.globalAttributes ?? []) {
|
|
597
611
|
if (modifiers.includes(modifier.name)) {
|
|
598
612
|
continue;
|
|
599
613
|
}
|
|
600
|
-
const insertText =
|
|
614
|
+
const insertText = text + modifiers.slice(0, -1).map(m => '.' + m).join('') + '.' + modifier.name;
|
|
601
615
|
const newItem = {
|
|
602
616
|
label: modifier.name,
|
|
603
617
|
filterText: insertText,
|
|
@@ -615,39 +629,42 @@ function create(mode, ts, getTsPluginClient) {
|
|
|
615
629
|
completionList.items.push(newItem);
|
|
616
630
|
}
|
|
617
631
|
}
|
|
618
|
-
}
|
|
632
|
+
} while (0);
|
|
619
633
|
completionList.items = completionList.items.filter(item => !specialTags.has(parseLabel(item.label).name));
|
|
620
634
|
const htmlDocumentations = new Map();
|
|
621
635
|
for (const item of completionList.items) {
|
|
622
636
|
const documentation = typeof item.documentation === 'string' ? item.documentation : item.documentation?.value;
|
|
623
|
-
if (documentation && !isItemKey(documentation)
|
|
637
|
+
if (documentation && !isItemKey(documentation)) {
|
|
624
638
|
htmlDocumentations.set(item.label, documentation);
|
|
625
639
|
}
|
|
626
640
|
}
|
|
627
641
|
for (const item of completionList.items) {
|
|
628
|
-
const
|
|
629
|
-
if (
|
|
630
|
-
const name =
|
|
631
|
-
item.label =
|
|
642
|
+
const parsedLabelKey = parseItemKey(item.label);
|
|
643
|
+
if (parsedLabelKey) {
|
|
644
|
+
const name = parsedLabelKey.tag;
|
|
645
|
+
item.label = parsedLabelKey.leadingSlash ? '/' + name : name;
|
|
646
|
+
const text = parsedLabelKey.leadingSlash ? `/${name}>` : name;
|
|
632
647
|
if (item.textEdit) {
|
|
633
|
-
item.textEdit.newText =
|
|
648
|
+
item.textEdit.newText = text;
|
|
634
649
|
}
|
|
635
650
|
;
|
|
636
651
|
if (item.insertText) {
|
|
637
|
-
item.insertText =
|
|
652
|
+
item.insertText = text;
|
|
638
653
|
}
|
|
639
654
|
if (item.sortText) {
|
|
640
|
-
item.sortText =
|
|
655
|
+
item.sortText = text;
|
|
641
656
|
}
|
|
642
657
|
}
|
|
643
658
|
const itemKeyStr = typeof item.documentation === 'string' ? item.documentation : item.documentation?.value;
|
|
644
|
-
let
|
|
645
|
-
|
|
659
|
+
let parsedItemKey = itemKeyStr ? parseItemKey(itemKeyStr) : undefined;
|
|
660
|
+
let propInfo;
|
|
661
|
+
if (parsedItemKey) {
|
|
646
662
|
const documentations = [];
|
|
647
|
-
|
|
648
|
-
|
|
663
|
+
propInfo = cachedPropInfos.get(parsedItemKey.prop);
|
|
664
|
+
if (propInfo?.commentMarkdown) {
|
|
665
|
+
documentations.push(propInfo.commentMarkdown);
|
|
649
666
|
}
|
|
650
|
-
let { isEvent, propName } = getPropName(
|
|
667
|
+
let { isEvent, propName } = getPropName(parsedItemKey);
|
|
651
668
|
if (isEvent) {
|
|
652
669
|
// click -> onclick
|
|
653
670
|
propName = 'on' + propName;
|
|
@@ -667,53 +684,58 @@ function create(mode, ts, getTsPluginClient) {
|
|
|
667
684
|
}
|
|
668
685
|
else {
|
|
669
686
|
let propName = item.label;
|
|
670
|
-
const
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
687
|
+
for (const str of ['v-bind:', ':']) {
|
|
688
|
+
if (propName.startsWith(str) && propName !== str) {
|
|
689
|
+
propName = propName.slice(str.length);
|
|
690
|
+
break;
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
// for special props without internal item key
|
|
694
|
+
if (specialProps.has(propName)) {
|
|
695
|
+
parsedItemKey = {
|
|
678
696
|
type: 'componentProp',
|
|
679
697
|
tag: '^',
|
|
680
698
|
prop: propName,
|
|
699
|
+
deprecated: false,
|
|
681
700
|
leadingSlash: false
|
|
682
701
|
};
|
|
683
702
|
}
|
|
684
|
-
|
|
703
|
+
propInfo = cachedPropInfos.get(propName);
|
|
704
|
+
if (propInfo?.commentMarkdown) {
|
|
685
705
|
const originalDocumentation = typeof item.documentation === 'string' ? item.documentation : item.documentation?.value;
|
|
686
706
|
item.documentation = {
|
|
687
707
|
kind: 'markdown',
|
|
688
708
|
value: [
|
|
689
|
-
|
|
709
|
+
propInfo.commentMarkdown,
|
|
690
710
|
originalDocumentation,
|
|
691
711
|
].filter(str => !!str).join('\n\n'),
|
|
692
712
|
};
|
|
693
713
|
}
|
|
694
714
|
}
|
|
715
|
+
if (propInfo?.deprecated) {
|
|
716
|
+
item.tags = [1];
|
|
717
|
+
}
|
|
695
718
|
const tokens = [];
|
|
696
|
-
if (item.kind === 10
|
|
719
|
+
if (item.kind === 10
|
|
720
|
+
&& lastCompletionComponentNames.has((0, language_core_1.hyphenateTag)(item.label))) {
|
|
697
721
|
item.kind = 6;
|
|
698
722
|
tokens.push('\u0000');
|
|
699
723
|
}
|
|
700
|
-
else if (
|
|
701
|
-
const isComponent =
|
|
702
|
-
const { isEvent, propName } = getPropName(
|
|
703
|
-
if (
|
|
724
|
+
else if (parsedItemKey) {
|
|
725
|
+
const isComponent = parsedItemKey.tag !== '*';
|
|
726
|
+
const { isEvent, propName } = getPropName(parsedItemKey);
|
|
727
|
+
if (parsedItemKey.type === 'componentProp') {
|
|
704
728
|
if (isComponent || specialProps.has(propName)) {
|
|
705
729
|
item.kind = 5;
|
|
706
730
|
}
|
|
707
731
|
}
|
|
708
732
|
else if (isEvent) {
|
|
709
733
|
item.kind = 23;
|
|
710
|
-
if (propName.startsWith('
|
|
734
|
+
if (propName.startsWith('vue:')) {
|
|
711
735
|
tokens.push('\u0004');
|
|
712
736
|
}
|
|
713
737
|
}
|
|
714
|
-
if (isComponent
|
|
715
|
-
|| (isComponent && isEvent)
|
|
716
|
-
|| specialProps.has(propName)) {
|
|
738
|
+
if (isComponent || specialProps.has(propName)) {
|
|
717
739
|
tokens.push('\u0000');
|
|
718
740
|
if (item.label.startsWith(':')) {
|
|
719
741
|
tokens.push('\u0001');
|
|
@@ -724,9 +746,12 @@ function create(mode, ts, getTsPluginClient) {
|
|
|
724
746
|
else if (item.label.startsWith('v-bind:')) {
|
|
725
747
|
tokens.push('\u0003');
|
|
726
748
|
}
|
|
727
|
-
else if (item.label.startsWith('v-
|
|
749
|
+
else if (item.label.startsWith('v-model:')) {
|
|
728
750
|
tokens.push('\u0004');
|
|
729
751
|
}
|
|
752
|
+
else if (item.label.startsWith('v-on:')) {
|
|
753
|
+
tokens.push('\u0005');
|
|
754
|
+
}
|
|
730
755
|
else {
|
|
731
756
|
tokens.push('\u0000');
|
|
732
757
|
}
|
|
@@ -738,10 +763,6 @@ function create(mode, ts, getTsPluginClient) {
|
|
|
738
763
|
}
|
|
739
764
|
}
|
|
740
765
|
}
|
|
741
|
-
else if (specialProps.has(item.label)) {
|
|
742
|
-
item.kind = 5;
|
|
743
|
-
tokens.push('\u0000', '\u0000', '\u0001');
|
|
744
|
-
}
|
|
745
766
|
else if (item.label === 'v-if'
|
|
746
767
|
|| item.label === 'v-else-if'
|
|
747
768
|
|| item.label === 'v-else'
|
|
@@ -818,13 +839,13 @@ function parseLabel(label) {
|
|
|
818
839
|
leadingSlash
|
|
819
840
|
};
|
|
820
841
|
}
|
|
821
|
-
function
|
|
822
|
-
return
|
|
842
|
+
function generateItemKey(type, tag, prop, deprecated) {
|
|
843
|
+
return `__VLS_data=${type},${tag},${prop},${Number(deprecated)}`;
|
|
823
844
|
}
|
|
824
845
|
function isItemKey(key) {
|
|
825
846
|
return key.startsWith('__VLS_data=');
|
|
826
847
|
}
|
|
827
|
-
function
|
|
848
|
+
function parseItemKey(key) {
|
|
828
849
|
const { leadingSlash, name } = parseLabel(key);
|
|
829
850
|
if (isItemKey(name)) {
|
|
830
851
|
const strs = name.slice('__VLS_data='.length).split(',');
|
|
@@ -832,6 +853,7 @@ function resolveItemKey(key) {
|
|
|
832
853
|
type: strs[0],
|
|
833
854
|
tag: strs[1],
|
|
834
855
|
prop: strs[2],
|
|
856
|
+
deprecated: strs[3] === '1',
|
|
835
857
|
leadingSlash
|
|
836
858
|
};
|
|
837
859
|
}
|