@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.
Files changed (53) hide show
  1. package/data/language-blocks/cs.json +29 -930
  2. package/data/language-blocks/en.json +28 -929
  3. package/data/language-blocks/fr.json +28 -929
  4. package/data/language-blocks/it.json +28 -929
  5. package/data/language-blocks/ja.json +28 -929
  6. package/data/language-blocks/ko.json +28 -929
  7. package/data/language-blocks/pt.json +28 -929
  8. package/data/language-blocks/ru.json +28 -929
  9. package/data/language-blocks/zh-cn.json +30 -931
  10. package/data/language-blocks/zh-hk.json +28 -929
  11. package/data/locale.json +54 -0
  12. package/data/model-modifiers/cs.json +6 -165
  13. package/data/model-modifiers/en.json +6 -165
  14. package/data/model-modifiers/fr.json +6 -165
  15. package/data/model-modifiers/it.json +6 -165
  16. package/data/model-modifiers/ja.json +6 -165
  17. package/data/model-modifiers/ko.json +6 -165
  18. package/data/model-modifiers/pt.json +6 -165
  19. package/data/model-modifiers/ru.json +6 -165
  20. package/data/model-modifiers/zh-cn.json +6 -165
  21. package/data/model-modifiers/zh-hk.json +6 -165
  22. package/data/template/cs.json +59 -1429
  23. package/data/template/en.json +52 -1422
  24. package/data/template/fr.json +55 -1425
  25. package/data/template/it.json +49 -1427
  26. package/data/template/ja.json +53 -1423
  27. package/data/template/ko.json +44 -1422
  28. package/data/template/pt.json +44 -1422
  29. package/data/template/ru.json +52 -1422
  30. package/data/template/zh-cn.json +53 -1423
  31. package/data/template/zh-hk.json +44 -1422
  32. package/index.d.ts +2 -2
  33. package/index.js +14 -10
  34. package/lib/ideFeatures/nameCasing.js +15 -17
  35. package/lib/plugins/data.js +47 -20
  36. package/lib/plugins/utils.d.ts +3 -0
  37. package/lib/plugins/utils.js +14 -0
  38. package/lib/plugins/vue-autoinsert-dotvalue.d.ts +0 -9
  39. package/lib/plugins/vue-autoinsert-dotvalue.js +37 -53
  40. package/lib/plugins/vue-autoinsert-space.js +1 -1
  41. package/lib/plugins/vue-complete-define-assignment.js +14 -16
  42. package/lib/plugins/vue-directive-comments.js +10 -8
  43. package/lib/plugins/vue-document-drop.js +16 -13
  44. package/lib/plugins/vue-document-links.js +45 -39
  45. package/lib/plugins/vue-extract-file.d.ts +2 -1
  46. package/lib/plugins/vue-extract-file.js +29 -15
  47. package/lib/plugins/vue-inlayhints.d.ts +2 -2
  48. package/lib/plugins/vue-inlayhints.js +65 -56
  49. package/lib/plugins/vue-sfc.js +29 -27
  50. package/lib/plugins/vue-template.js +223 -201
  51. package/lib/plugins/vue-twoslash-queries.js +9 -4
  52. package/package.json +12 -11
  53. 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(['slot', 'component', 'template']);
15
- const specialProps = new Set(['class', 'is', 'key', 'ref', 'style']);
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 tsDocumentations = new Map();
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 eventModifiers = {};
83
- const propModifiers = {};
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.substring(' - `.'.length);
93
- const [name, disc] = text.split('` - ');
94
- eventModifiers[name] = disc;
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.substring(' - `.'.length);
104
- const [name, disc] = text.split('` - ');
105
- propModifiers[name] = disc;
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 decoded = context.decodeEmbeddedDocumentUri(vscode_uri_1.URI.parse(document.uri));
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
- if (sourceScript?.generated?.root instanceof language_core_1.VueVirtualCode) {
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(vueCompilerOptions, sourceScript.id, sourceScript.generated.root)).sync;
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 code = context.language.scripts.get(decoded[0])?.generated?.root;
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 (code instanceof language_core_1.VueVirtualCode && scanner) {
173
- // visualize missing required props
174
- const casing = await (0, nameCasing_1.getNameCasing)(context, decoded[0]);
175
- const components = await tsPluginClient?.getComponentNames(code.fileName) ?? [];
176
- const componentProps = {};
177
- let token;
178
- let current;
179
- while ((token = scanner.scan()) !== html.TokenType.EOS) {
180
- if (token === html.TokenType.StartTag) {
181
- const tagName = scanner.getTokenText();
182
- const checkTag = tagName.indexOf('.') >= 0
183
- ? tagName
184
- : components.find(component => component === tagName || (0, language_core_1.hyphenateTag)(component) === tagName);
185
- if (checkTag) {
186
- componentProps[checkTag] ??= (await tsPluginClient?.getComponentProps(code.fileName, checkTag, true) ?? []).map(prop => prop.name);
187
- current = {
188
- unburnedRequiredProps: [...componentProps[checkTag]],
189
- labelOffset: scanner.getTokenOffset() + scanner.getTokenLength(),
190
- insertOffset: scanner.getTokenOffset() + scanner.getTokenLength(),
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
- else if (token === html.TokenType.AttributeName) {
195
- if (current) {
196
- let attrText = scanner.getTokenText();
197
- if (attrText === 'v-bind') {
198
- current.unburnedRequiredProps = [];
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
- else {
201
- // remove modifiers
202
- if (attrText.indexOf('.') >= 0) {
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
- else if (token === html.TokenType.StartTagSelfClose || token === html.TokenType.StartTagClose) {
229
- if (current) {
230
- for (const requiredProp of current.unburnedRequiredProps) {
231
- result.push({
232
- label: `${requiredProp}!`,
233
- paddingLeft: true,
234
- position: document.positionAt(current.labelOffset),
235
- kind: 2,
236
- textEdits: [{
237
- range: {
238
- start: document.positionAt(current.insertOffset),
239
- end: document.positionAt(current.insertOffset),
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 = undefined;
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
- if (token === html.TokenType.AttributeName || token === html.TokenType.AttributeValue) {
249
- if (current) {
250
- current.insertOffset = scanner.getTokenOffset() + scanner.getTokenLength();
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 code = context.language.scripts.get(decoded[0])?.generated?.root;
279
- if (!(code instanceof language_core_1.VueVirtualCode)) {
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 } = code._sfc;
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 decoded = context.decodeEmbeddedDocumentUri(vscode_uri_1.URI.parse(document.uri));
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
- if (!sourceScript
329
- || !(sourceScript.generated?.root instanceof language_core_1.VueVirtualCode)
330
- || !sourceScript.generated.root._sfc.template) {
331
- return [];
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
- }, sourceScript.generated.root, template, {
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(vueCompilerOptions, sourceDocumentUri, vueCode) {
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 = parseItemKey('specialTag', 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
- let templateContextProps;
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.scriptSetup
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.substring(binding.start, binding.end);
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 propsInfo = await tsPluginClient?.getComponentProps(vueCode.fileName, tag) ?? [];
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
- propsInfo: propsInfo.filter(prop => !prop.name.startsWith('ref_')),
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, propsInfo, events } = tagInfo;
447
- const props = propsInfo.map(prop => prop.name);
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 = parseItemKey('componentEvent', isGlobal ? '*' : tag, propNameBase);
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 propKey = parseItemKey('componentProp', isGlobal ? '*' : tag, propName);
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
- })?.commentMarkdown;
496
- if (propDescription) {
497
- tsDocumentations.set(propName, propDescription);
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 = parseItemKey('componentEvent', tag, name);
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.substring('onUpdate:'.length)]);
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.substring('update:'.length)]);
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 = parseItemKey('componentProp', isGlobal ? '*' : tag, name);
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
- const replacement = getReplacement(completionList, document);
562
- if (replacement) {
563
- const isEvent = replacement.text.startsWith('v-on:') || replacement.text.startsWith('@');
564
- const isProp = replacement.text.startsWith('v-bind:') || replacement.text.startsWith(':');
565
- const isModel = replacement.text.startsWith('v-model:') || replacement.text.split('.')[0] === 'v-model';
570
+ do {
571
+ const replacement = getReplacement(completionList, document);
572
+ if (!replacement) {
573
+ break;
574
+ }
566
575
  const hasModifier = replacement.text.includes('.');
567
- const validModifiers = isEvent ? eventModifiers
568
- : isProp ? propModifiers
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
- const modifiers = replacement.text.split('.').slice(1);
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 modifierDes = validModifiers[modifier];
578
- const insertText = textWithoutModifier + modifiers.slice(0, -1).map(m => '.' + m).join('') + '.' + modifier;
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: modifierDes,
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 (hasModifier && isModel) {
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 = textWithoutModifier + modifiers.slice(0, -1).map(m => '.' + m).join('') + '.' + modifier.name;
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) && item.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 resolvedLabelKey = resolveItemKey(item.label);
629
- if (resolvedLabelKey) {
630
- const name = resolvedLabelKey.tag;
631
- item.label = resolvedLabelKey.leadingSlash ? '/' + name : name;
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 = name;
648
+ item.textEdit.newText = text;
634
649
  }
635
650
  ;
636
651
  if (item.insertText) {
637
- item.insertText = name;
652
+ item.insertText = text;
638
653
  }
639
654
  if (item.sortText) {
640
- item.sortText = name;
655
+ item.sortText = text;
641
656
  }
642
657
  }
643
658
  const itemKeyStr = typeof item.documentation === 'string' ? item.documentation : item.documentation?.value;
644
- let resolvedKey = itemKeyStr ? resolveItemKey(itemKeyStr) : undefined;
645
- if (resolvedKey) {
659
+ let parsedItemKey = itemKeyStr ? parseItemKey(itemKeyStr) : undefined;
660
+ let propInfo;
661
+ if (parsedItemKey) {
646
662
  const documentations = [];
647
- if (tsDocumentations.has(resolvedKey.prop)) {
648
- documentations.push(tsDocumentations.get(resolvedKey.prop));
663
+ propInfo = cachedPropInfos.get(parsedItemKey.prop);
664
+ if (propInfo?.commentMarkdown) {
665
+ documentations.push(propInfo.commentMarkdown);
649
666
  }
650
- let { isEvent, propName } = getPropName(resolvedKey);
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 isVBind = propName.startsWith('v-bind:') ? (propName = propName.slice('v-bind:'.length), true) : false;
671
- const isVBindAbbr = propName.startsWith(':') && propName !== ':' ? (propName = propName.slice(':'.length), true) : false;
672
- /**
673
- * for `is`, `key` and `ref` starting with `v-bind:` or `:`
674
- * that without `internalItemId`.
675
- */
676
- if (isVBind || isVBindAbbr) {
677
- resolvedKey = {
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
- if (tsDocumentations.has(propName)) {
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
- tsDocumentations.get(propName),
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 && lastCompletionComponentNames.has((0, language_core_1.hyphenateTag)(item.label))) {
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 (resolvedKey) {
701
- const isComponent = resolvedKey.tag !== '*';
702
- const { isEvent, propName } = getPropName(resolvedKey);
703
- if (resolvedKey.type === 'componentProp') {
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('vnode-')) {
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-on:')) {
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 parseItemKey(type, tag, prop) {
822
- return '__VLS_data=' + type + ',' + tag + ',' + prop;
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 resolveItemKey(key) {
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
  }