@vue/language-service 2.1.8 → 2.2.0

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 (51) 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 +44 -1422
  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 +3 -1
  34. package/lib/ideFeatures/nameCasing.js +14 -16
  35. package/lib/plugins/data.js +47 -20
  36. package/lib/plugins/vue-autoinsert-dotvalue.d.ts +1 -0
  37. package/lib/plugins/vue-autoinsert-dotvalue.js +5 -3
  38. package/lib/plugins/vue-autoinsert-space.js +1 -1
  39. package/lib/plugins/vue-complete-define-assignment.d.ts +2 -0
  40. package/lib/plugins/vue-complete-define-assignment.js +83 -0
  41. package/lib/plugins/vue-directive-comments.js +10 -8
  42. package/lib/plugins/vue-document-drop.js +15 -12
  43. package/lib/plugins/vue-document-links.js +45 -39
  44. package/lib/plugins/vue-extract-file.js +19 -10
  45. package/lib/plugins/vue-inlayhints.d.ts +1 -1
  46. package/lib/plugins/vue-inlayhints.js +65 -56
  47. package/lib/plugins/vue-sfc.js +29 -27
  48. package/lib/plugins/vue-template.js +194 -162
  49. package/lib/plugins/vue-twoslash-queries.js +9 -4
  50. package/package.json +9 -9
  51. package/scripts/update-html-data.js +74 -70
@@ -11,8 +11,19 @@ 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) {
@@ -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);
@@ -123,12 +134,14 @@ function create(mode, ts, getTsPluginClient) {
123
134
  const vueCompilerOptions = context.project.vue.compilerOptions;
124
135
  let sync;
125
136
  let currentVersion;
126
- const decoded = context.decodeEmbeddedDocumentUri(vscode_uri_1.URI.parse(document.uri));
137
+ const uri = vscode_uri_1.URI.parse(document.uri);
138
+ const decoded = context.decodeEmbeddedDocumentUri(uri);
127
139
  const sourceScript = decoded && context.language.scripts.get(decoded[0]);
128
- if (sourceScript?.generated?.root instanceof language_core_1.VueVirtualCode) {
140
+ const root = sourceScript?.generated?.root;
141
+ if (root instanceof language_core_1.VueVirtualCode) {
129
142
  // #4298: Precompute HTMLDocument before provideHtmlData to avoid parseHTMLDocument requesting component names from tsserver
130
143
  baseServiceInstance.provideCompletionItems?.(document, position, completionContext, token);
131
- sync = (await provideHtmlData(vueCompilerOptions, sourceScript.id, sourceScript.generated.root)).sync;
144
+ sync = (await provideHtmlData(vueCompilerOptions, sourceScript.id, root)).sync;
132
145
  currentVersion = await sync();
133
146
  }
134
147
  let htmlComplete = await baseServiceInstance.provideCompletionItems?.(document, position, completionContext, token);
@@ -159,7 +172,6 @@ function create(mode, ts, getTsPluginClient) {
159
172
  if (!enabled) {
160
173
  return;
161
174
  }
162
- const result = [];
163
175
  const uri = vscode_uri_1.URI.parse(document.uri);
164
176
  const decoded = context.decodeEmbeddedDocumentUri(uri);
165
177
  const sourceScript = decoded && context.language.scripts.get(decoded[0]);
@@ -167,88 +179,98 @@ function create(mode, ts, getTsPluginClient) {
167
179
  if (!virtualCode) {
168
180
  return;
169
181
  }
170
- const code = context.language.scripts.get(decoded[0])?.generated?.root;
182
+ const root = sourceScript?.generated?.root;
183
+ if (!(root instanceof language_core_1.VueVirtualCode)) {
184
+ return;
185
+ }
171
186
  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
- }
187
+ if (!scanner) {
188
+ return;
189
+ }
190
+ const result = [];
191
+ // visualize missing required props
192
+ const casing = await (0, nameCasing_1.getNameCasing)(context, decoded[0]);
193
+ const components = await tsPluginClient?.getComponentNames(root.fileName) ?? [];
194
+ const componentProps = {};
195
+ let token;
196
+ let current;
197
+ while ((token = scanner.scan()) !== html.TokenType.EOS) {
198
+ if (token === html.TokenType.StartTag) {
199
+ const tagName = scanner.getTokenText();
200
+ const checkTag = tagName.includes('.')
201
+ ? tagName
202
+ : components.find(component => component === tagName || (0, language_core_1.hyphenateTag)(component) === tagName);
203
+ if (checkTag) {
204
+ componentProps[checkTag] ??= (await tsPluginClient?.getComponentProps(root.fileName, checkTag) ?? [])
205
+ .filter(prop => prop.required)
206
+ .map(prop => prop.name);
207
+ current = {
208
+ unburnedRequiredProps: [...componentProps[checkTag]],
209
+ labelOffset: scanner.getTokenOffset() + scanner.getTokenLength(),
210
+ insertOffset: scanner.getTokenOffset() + scanner.getTokenLength(),
211
+ };
193
212
  }
194
- else if (token === html.TokenType.AttributeName) {
195
- if (current) {
196
- let attrText = scanner.getTokenText();
197
- if (attrText === 'v-bind') {
198
- current.unburnedRequiredProps = [];
213
+ }
214
+ else if (token === html.TokenType.AttributeName) {
215
+ if (current) {
216
+ let attrText = scanner.getTokenText();
217
+ if (attrText === 'v-bind') {
218
+ current.unburnedRequiredProps = [];
219
+ }
220
+ else {
221
+ // remove modifiers
222
+ if (attrText.includes('.')) {
223
+ attrText = attrText.split('.')[0];
199
224
  }
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
- });
225
+ // normalize
226
+ if (attrText.startsWith('v-bind:')) {
227
+ attrText = attrText.slice('v-bind:'.length);
225
228
  }
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
- });
229
+ else if (attrText.startsWith(':')) {
230
+ attrText = attrText.slice(':'.length);
231
+ }
232
+ else if (attrText.startsWith('v-model:')) {
233
+ attrText = attrText.slice('v-model:'.length);
244
234
  }
245
- current = undefined;
235
+ else if (attrText === 'v-model') {
236
+ attrText = vueCompilerOptions.target >= 3 ? 'modelValue' : 'value'; // TODO: support for experimentalModelPropName?
237
+ }
238
+ else if (attrText.startsWith('v-on:')) {
239
+ attrText = 'on-' + (0, language_core_1.hyphenateAttr)(attrText.slice('v-on:'.length));
240
+ }
241
+ else if (attrText.startsWith('@')) {
242
+ attrText = 'on-' + (0, language_core_1.hyphenateAttr)(attrText.slice('@'.length));
243
+ }
244
+ current.unburnedRequiredProps = current.unburnedRequiredProps.filter(propName => {
245
+ return attrText !== propName
246
+ && attrText !== (0, language_core_1.hyphenateAttr)(propName);
247
+ });
246
248
  }
247
249
  }
248
- if (token === html.TokenType.AttributeName || token === html.TokenType.AttributeValue) {
249
- if (current) {
250
- current.insertOffset = scanner.getTokenOffset() + scanner.getTokenLength();
250
+ }
251
+ else if (token === html.TokenType.StartTagSelfClose || token === html.TokenType.StartTagClose) {
252
+ if (current) {
253
+ for (const requiredProp of current.unburnedRequiredProps) {
254
+ result.push({
255
+ label: `${requiredProp}!`,
256
+ paddingLeft: true,
257
+ position: document.positionAt(current.labelOffset),
258
+ kind: 2,
259
+ textEdits: [{
260
+ range: {
261
+ start: document.positionAt(current.insertOffset),
262
+ end: document.positionAt(current.insertOffset),
263
+ },
264
+ newText: ` :${casing.attr === types_1.AttrNameCasing.Kebab ? (0, language_core_1.hyphenateAttr)(requiredProp) : requiredProp}=`,
265
+ }],
266
+ });
251
267
  }
268
+ current = undefined;
269
+ }
270
+ }
271
+ if (token === html.TokenType.AttributeName || token === html.TokenType.AttributeValue) {
272
+ if (current) {
273
+ current.insertOffset = scanner.getTokenOffset() + scanner.getTokenLength();
252
274
  }
253
275
  }
254
276
  }
@@ -267,7 +289,6 @@ function create(mode, ts, getTsPluginClient) {
267
289
  if (!isSupportedDocument(document)) {
268
290
  return;
269
291
  }
270
- const originalResult = await baseServiceInstance.provideDiagnostics?.(document, token);
271
292
  const uri = vscode_uri_1.URI.parse(document.uri);
272
293
  const decoded = context.decodeEmbeddedDocumentUri(uri);
273
294
  const sourceScript = decoded && context.language.scripts.get(decoded[0]);
@@ -275,12 +296,13 @@ function create(mode, ts, getTsPluginClient) {
275
296
  if (!virtualCode) {
276
297
  return;
277
298
  }
278
- const code = context.language.scripts.get(decoded[0])?.generated?.root;
279
- if (!(code instanceof language_core_1.VueVirtualCode)) {
299
+ const root = sourceScript?.generated?.root;
300
+ if (!(root instanceof language_core_1.VueVirtualCode)) {
280
301
  return;
281
302
  }
303
+ const originalResult = await baseServiceInstance.provideDiagnostics?.(document, token);
282
304
  const templateErrors = [];
283
- const { template } = code._sfc;
305
+ const { template } = root._sfc;
284
306
  if (template) {
285
307
  for (const error of template.errors) {
286
308
  onCompilerError(error, 1);
@@ -323,20 +345,23 @@ function create(mode, ts, getTsPluginClient) {
323
345
  if (!languageService) {
324
346
  return;
325
347
  }
326
- const decoded = context.decodeEmbeddedDocumentUri(vscode_uri_1.URI.parse(document.uri));
348
+ const uri = vscode_uri_1.URI.parse(document.uri);
349
+ const decoded = context.decodeEmbeddedDocumentUri(uri);
327
350
  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 [];
351
+ const root = sourceScript?.generated?.root;
352
+ if (!(root instanceof language_core_1.VueVirtualCode)) {
353
+ return;
354
+ }
355
+ const { template } = root._sfc;
356
+ if (!template) {
357
+ return;
332
358
  }
333
- const { template } = sourceScript.generated.root._sfc;
334
359
  const spans = common_1.getComponentSpans.call({
335
360
  files: context.language.scripts,
336
361
  languageService,
337
362
  typescript: ts,
338
363
  vueOptions: vueCompilerOptions,
339
- }, sourceScript.generated.root, template, {
364
+ }, root, template, {
340
365
  start: document.offsetAt(range.start),
341
366
  length: document.offsetAt(range.end) - document.offsetAt(range.start),
342
367
  });
@@ -362,7 +387,7 @@ function create(mode, ts, getTsPluginClient) {
362
387
  continue;
363
388
  }
364
389
  if (specialTags.has(tag.name)) {
365
- tag.name = parseItemKey('specialTag', tag.name, '');
390
+ tag.name = generateItemKey('specialTag', tag.name, '');
366
391
  }
367
392
  else if (casing.tag === types_1.TagNameCasing.Kebab) {
368
393
  tag.name = (0, language_core_1.hyphenateTag)(tag.name);
@@ -411,7 +436,7 @@ function create(mode, ts, getTsPluginClient) {
411
436
  }
412
437
  }
413
438
  for (const binding of scriptSetupRanges?.bindings ?? []) {
414
- const name = vueCode._sfc.scriptSetup.content.substring(binding.start, binding.end);
439
+ const name = vueCode._sfc.scriptSetup.content.slice(binding.range.start, binding.range.end);
415
440
  if (casing.tag === types_1.TagNameCasing.Kebab) {
416
441
  names.add((0, language_core_1.hyphenateTag)(name));
417
442
  }
@@ -444,7 +469,9 @@ function create(mode, ts, getTsPluginClient) {
444
469
  return [];
445
470
  }
446
471
  const { attrs, propsInfo, events } = tagInfo;
447
- const props = propsInfo.map(prop => prop.name);
472
+ const props = propsInfo.map(prop => (0, language_core_1.hyphenateTag)(prop.name).startsWith('on-vnode-')
473
+ ? 'onVue:' + prop.name.slice('onVnode'.length)
474
+ : prop.name);
448
475
  const attributes = [];
449
476
  const _tsCodegen = language_core_1.tsCodegen.get(vueCode._sfc);
450
477
  if (_tsCodegen) {
@@ -456,8 +483,8 @@ function create(mode, ts, getTsPluginClient) {
456
483
  return [];
457
484
  }
458
485
  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)) ?? [],
486
+ ..._tsCodegen.scriptRanges.get()?.bindings.map(({ range }) => vueCode._sfc.script.content.slice(range.start, range.end)) ?? [],
487
+ ..._tsCodegen.scriptSetupRanges.get()?.bindings.map(({ range }) => vueCode._sfc.scriptSetup.content.slice(range.start, range.end)) ?? [],
461
488
  ...templateContextProps,
462
489
  ];
463
490
  ctxVars = [...new Set(ctxVars)];
@@ -477,7 +504,7 @@ function create(mode, ts, getTsPluginClient) {
477
504
  const propNameBase = name.startsWith('on-')
478
505
  ? name.slice('on-'.length)
479
506
  : (name['on'.length].toLowerCase() + name.slice('onX'.length));
480
- const propKey = parseItemKey('componentEvent', isGlobal ? '*' : tag, propNameBase);
507
+ const propKey = generateItemKey('componentEvent', isGlobal ? '*' : tag, propNameBase);
481
508
  attributes.push({
482
509
  name: 'v-on:' + propNameBase,
483
510
  description: propKey,
@@ -488,7 +515,7 @@ function create(mode, ts, getTsPluginClient) {
488
515
  }
489
516
  else {
490
517
  const propName = name;
491
- const propKey = parseItemKey('componentProp', isGlobal ? '*' : tag, propName);
518
+ const propKey = generateItemKey('componentProp', isGlobal ? '*' : tag, propName);
492
519
  const propDescription = propsInfo.find(prop => {
493
520
  const name = casing.attr === types_1.AttrNameCasing.Camel ? prop.name : (0, language_core_1.hyphenateAttr)(prop.name);
494
521
  return name === propName;
@@ -510,7 +537,7 @@ function create(mode, ts, getTsPluginClient) {
510
537
  }
511
538
  for (const event of events) {
512
539
  const name = casing.attr === types_1.AttrNameCasing.Camel ? event : (0, language_core_1.hyphenateAttr)(event);
513
- const propKey = parseItemKey('componentEvent', tag, name);
540
+ const propKey = generateItemKey('componentEvent', tag, name);
514
541
  attributes.push({
515
542
  name: 'v-on:' + name,
516
543
  description: propKey,
@@ -523,17 +550,17 @@ function create(mode, ts, getTsPluginClient) {
523
550
  for (const prop of [...props, ...attrs]) {
524
551
  if (prop.startsWith('onUpdate:')) {
525
552
  const isGlobal = !propsSet.has(prop);
526
- models.push([isGlobal, prop.substring('onUpdate:'.length)]);
553
+ models.push([isGlobal, prop.slice('onUpdate:'.length)]);
527
554
  }
528
555
  }
529
556
  for (const event of events) {
530
557
  if (event.startsWith('update:')) {
531
- models.push([false, event.substring('update:'.length)]);
558
+ models.push([false, event.slice('update:'.length)]);
532
559
  }
533
560
  }
534
561
  for (const [isGlobal, model] of models) {
535
562
  const name = casing.attr === types_1.AttrNameCasing.Camel ? model : (0, language_core_1.hyphenateAttr)(model);
536
- const propKey = parseItemKey('componentProp', isGlobal ? '*' : tag, name);
563
+ const propKey = generateItemKey('componentProp', isGlobal ? '*' : tag, name);
537
564
  attributes.push({
538
565
  name: 'v-model:' + name,
539
566
  description: propKey,
@@ -558,30 +585,35 @@ function create(mode, ts, getTsPluginClient) {
558
585
  };
559
586
  }
560
587
  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';
588
+ do {
589
+ const replacement = getReplacement(completionList, document);
590
+ if (!replacement) {
591
+ break;
592
+ }
566
593
  const hasModifier = replacement.text.includes('.');
567
- const validModifiers = isEvent ? eventModifiers
568
- : isProp ? propModifiers
594
+ if (!hasModifier) {
595
+ break;
596
+ }
597
+ const [text, ...modifiers] = replacement.text.split('.');
598
+ const isVOn = text.startsWith('v-on:') || text.startsWith('@') && text.length > 1;
599
+ const isVBind = text.startsWith('v-bind:') || text.startsWith(':') && text.length > 1;
600
+ const isVModel = text.startsWith('v-model:') || text === 'v-model';
601
+ const validModifiers = isVOn ? vOnModifiers
602
+ : isVBind ? vBindModifiers
569
603
  : undefined;
570
- const modifiers = replacement.text.split('.').slice(1);
571
- const textWithoutModifier = replacement.text.split('.')[0];
572
- if (validModifiers && hasModifier) {
604
+ if (validModifiers) {
573
605
  for (const modifier in validModifiers) {
574
606
  if (modifiers.includes(modifier)) {
575
607
  continue;
576
608
  }
577
- const modifierDes = validModifiers[modifier];
578
- const insertText = textWithoutModifier + modifiers.slice(0, -1).map(m => '.' + m).join('') + '.' + modifier;
609
+ const description = validModifiers[modifier];
610
+ const insertText = text + modifiers.slice(0, -1).map(m => '.' + m).join('') + '.' + modifier;
579
611
  const newItem = {
580
612
  label: modifier,
581
613
  filterText: insertText,
582
614
  documentation: {
583
615
  kind: 'markdown',
584
- value: modifierDes,
616
+ value: description,
585
617
  },
586
618
  textEdit: {
587
619
  range: replacement.textEdit.range,
@@ -592,12 +624,12 @@ function create(mode, ts, getTsPluginClient) {
592
624
  completionList.items.push(newItem);
593
625
  }
594
626
  }
595
- else if (hasModifier && isModel) {
627
+ else if (isVModel) {
596
628
  for (const modifier of modelData.globalAttributes ?? []) {
597
629
  if (modifiers.includes(modifier.name)) {
598
630
  continue;
599
631
  }
600
- const insertText = textWithoutModifier + modifiers.slice(0, -1).map(m => '.' + m).join('') + '.' + modifier.name;
632
+ const insertText = text + modifiers.slice(0, -1).map(m => '.' + m).join('') + '.' + modifier.name;
601
633
  const newItem = {
602
634
  label: modifier.name,
603
635
  filterText: insertText,
@@ -615,7 +647,7 @@ function create(mode, ts, getTsPluginClient) {
615
647
  completionList.items.push(newItem);
616
648
  }
617
649
  }
618
- }
650
+ } while (0);
619
651
  completionList.items = completionList.items.filter(item => !specialTags.has(parseLabel(item.label).name));
620
652
  const htmlDocumentations = new Map();
621
653
  for (const item of completionList.items) {
@@ -625,29 +657,30 @@ function create(mode, ts, getTsPluginClient) {
625
657
  }
626
658
  }
627
659
  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;
660
+ const parsedLabelKey = parseItemKey(item.label);
661
+ if (parsedLabelKey) {
662
+ const name = parsedLabelKey.tag;
663
+ item.label = parsedLabelKey.leadingSlash ? '/' + name : name;
664
+ const text = parsedLabelKey.leadingSlash ? `/${name}>` : name;
632
665
  if (item.textEdit) {
633
- item.textEdit.newText = name;
666
+ item.textEdit.newText = text;
634
667
  }
635
668
  ;
636
669
  if (item.insertText) {
637
- item.insertText = name;
670
+ item.insertText = text;
638
671
  }
639
672
  if (item.sortText) {
640
- item.sortText = name;
673
+ item.sortText = text;
641
674
  }
642
675
  }
643
676
  const itemKeyStr = typeof item.documentation === 'string' ? item.documentation : item.documentation?.value;
644
- let resolvedKey = itemKeyStr ? resolveItemKey(itemKeyStr) : undefined;
645
- if (resolvedKey) {
677
+ let parsedItemKey = itemKeyStr ? parseItemKey(itemKeyStr) : undefined;
678
+ if (parsedItemKey) {
646
679
  const documentations = [];
647
- if (tsDocumentations.has(resolvedKey.prop)) {
648
- documentations.push(tsDocumentations.get(resolvedKey.prop));
680
+ if (tsDocumentations.has(parsedItemKey.prop)) {
681
+ documentations.push(tsDocumentations.get(parsedItemKey.prop));
649
682
  }
650
- let { isEvent, propName } = getPropName(resolvedKey);
683
+ let { isEvent, propName } = getPropName(parsedItemKey);
651
684
  if (isEvent) {
652
685
  // click -> onclick
653
686
  propName = 'on' + propName;
@@ -667,14 +700,15 @@ function create(mode, ts, getTsPluginClient) {
667
700
  }
668
701
  else {
669
702
  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 = {
703
+ for (const str of ['v-bind:', ':']) {
704
+ if (propName.startsWith(str) && propName !== str) {
705
+ propName = propName.slice(str.length);
706
+ break;
707
+ }
708
+ }
709
+ // for special props without internal item key
710
+ if (specialProps.has(propName)) {
711
+ parsedItemKey = {
678
712
  type: 'componentProp',
679
713
  tag: '^',
680
714
  prop: propName,
@@ -693,27 +727,26 @@ function create(mode, ts, getTsPluginClient) {
693
727
  }
694
728
  }
695
729
  const tokens = [];
696
- if (item.kind === 10 && lastCompletionComponentNames.has((0, language_core_1.hyphenateTag)(item.label))) {
730
+ if (item.kind === 10
731
+ && lastCompletionComponentNames.has((0, language_core_1.hyphenateTag)(item.label))) {
697
732
  item.kind = 6;
698
733
  tokens.push('\u0000');
699
734
  }
700
- else if (resolvedKey) {
701
- const isComponent = resolvedKey.tag !== '*';
702
- const { isEvent, propName } = getPropName(resolvedKey);
703
- if (resolvedKey.type === 'componentProp') {
735
+ else if (parsedItemKey) {
736
+ const isComponent = parsedItemKey.tag !== '*';
737
+ const { isEvent, propName } = getPropName(parsedItemKey);
738
+ if (parsedItemKey.type === 'componentProp') {
704
739
  if (isComponent || specialProps.has(propName)) {
705
740
  item.kind = 5;
706
741
  }
707
742
  }
708
743
  else if (isEvent) {
709
744
  item.kind = 23;
710
- if (propName.startsWith('vnode-')) {
745
+ if (propName.startsWith('vue:')) {
711
746
  tokens.push('\u0004');
712
747
  }
713
748
  }
714
- if (isComponent
715
- || (isComponent && isEvent)
716
- || specialProps.has(propName)) {
749
+ if (isComponent || specialProps.has(propName)) {
717
750
  tokens.push('\u0000');
718
751
  if (item.label.startsWith(':')) {
719
752
  tokens.push('\u0001');
@@ -724,9 +757,12 @@ function create(mode, ts, getTsPluginClient) {
724
757
  else if (item.label.startsWith('v-bind:')) {
725
758
  tokens.push('\u0003');
726
759
  }
727
- else if (item.label.startsWith('v-on:')) {
760
+ else if (item.label.startsWith('v-model:')) {
728
761
  tokens.push('\u0004');
729
762
  }
763
+ else if (item.label.startsWith('v-on:')) {
764
+ tokens.push('\u0005');
765
+ }
730
766
  else {
731
767
  tokens.push('\u0000');
732
768
  }
@@ -738,10 +774,6 @@ function create(mode, ts, getTsPluginClient) {
738
774
  }
739
775
  }
740
776
  }
741
- else if (specialProps.has(item.label)) {
742
- item.kind = 5;
743
- tokens.push('\u0000', '\u0000', '\u0001');
744
- }
745
777
  else if (item.label === 'v-if'
746
778
  || item.label === 'v-else-if'
747
779
  || item.label === 'v-else'
@@ -818,13 +850,13 @@ function parseLabel(label) {
818
850
  leadingSlash
819
851
  };
820
852
  }
821
- function parseItemKey(type, tag, prop) {
853
+ function generateItemKey(type, tag, prop) {
822
854
  return '__VLS_data=' + type + ',' + tag + ',' + prop;
823
855
  }
824
856
  function isItemKey(key) {
825
857
  return key.startsWith('__VLS_data=');
826
858
  }
827
- function resolveItemKey(key) {
859
+ function parseItemKey(key) {
828
860
  const { leadingSlash, name } = parseLabel(key);
829
861
  if (isItemKey(name)) {
830
862
  const strs = name.slice('__VLS_data='.length).split(',');