@vue/language-service 3.0.3 → 3.0.5

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 (40) hide show
  1. package/index.d.ts +1 -12
  2. package/index.js +25 -20
  3. package/lib/data.d.ts +4 -0
  4. package/lib/data.js +148 -0
  5. package/lib/plugins/css.js +9 -17
  6. package/lib/plugins/typescript-semantic-tokens.d.ts +2 -2
  7. package/lib/plugins/typescript-semantic-tokens.js +7 -15
  8. package/lib/plugins/vue-autoinsert-dotvalue.d copy.d.ts +2 -0
  9. package/lib/plugins/vue-autoinsert-dotvalue.d copy.js +3 -0
  10. package/lib/plugins/vue-autoinsert-dotvalue.d.ts +2 -2
  11. package/lib/plugins/vue-autoinsert-dotvalue.js +19 -47
  12. package/lib/plugins/vue-autoinsert-space.js +2 -6
  13. package/lib/plugins/vue-compiler-dom-errors.js +17 -35
  14. package/lib/plugins/vue-component-semantic-tokens.d.ts +2 -2
  15. package/lib/plugins/vue-component-semantic-tokens.js +35 -49
  16. package/lib/plugins/vue-destructured-props-hints.d.ts +7 -0
  17. package/lib/plugins/vue-destructured-props-hints.js +220 -0
  18. package/lib/plugins/vue-document-drop.d.ts +2 -2
  19. package/lib/plugins/vue-document-drop.js +14 -31
  20. package/lib/plugins/vue-document-highlights.d.ts +1 -2
  21. package/lib/plugins/vue-document-highlights.js +5 -12
  22. package/lib/plugins/vue-extract-file.d.ts +2 -2
  23. package/lib/plugins/vue-extract-file.js +11 -26
  24. package/lib/plugins/vue-global-types-error.js +17 -14
  25. package/lib/plugins/vue-inlayhints.js +14 -15
  26. package/lib/plugins/vue-missing-props-hints.d.ts +2 -2
  27. package/lib/plugins/vue-missing-props-hints.js +10 -26
  28. package/lib/plugins/vue-scoped-class-links.d.ts +2 -0
  29. package/lib/plugins/vue-scoped-class-links.js +62 -0
  30. package/lib/plugins/vue-sfc.js +141 -144
  31. package/lib/plugins/vue-suggest-define-assignment.js +4 -14
  32. package/lib/plugins/vue-template-ref-links.d.ts +2 -0
  33. package/lib/plugins/vue-template-ref-links.js +57 -0
  34. package/lib/plugins/vue-template.d.ts +2 -2
  35. package/lib/plugins/vue-template.js +322 -356
  36. package/lib/plugins/vue-twoslash-queries.d.ts +2 -2
  37. package/lib/plugins/vue-twoslash-queries.js +7 -16
  38. package/lib/utils.d.ts +8 -0
  39. package/lib/utils.js +43 -0
  40. package/package.json +7 -7
@@ -7,8 +7,9 @@ const volar_service_html_1 = require("volar-service-html");
7
7
  const volar_service_pug_1 = require("volar-service-pug");
8
8
  const html = require("vscode-html-languageservice");
9
9
  const vscode_uri_1 = require("vscode-uri");
10
+ const data_1 = require("../data");
10
11
  const nameCasing_1 = require("../nameCasing");
11
- const data_1 = require("./data");
12
+ const utils_1 = require("../utils");
12
13
  const specialTags = new Set([
13
14
  'slot',
14
15
  'component',
@@ -24,11 +25,9 @@ const specialProps = new Set([
24
25
  ]);
25
26
  let builtInData;
26
27
  let modelData;
27
- function create(mode, getTsPluginClient) {
28
+ function create(languageId, { getComponentNames, getElementAttrs, getComponentProps, getComponentEvents, getComponentDirectives, getComponentSlots, }) {
28
29
  let customData = [];
29
30
  let extraCustomData = [];
30
- let lastCompletionComponentNames = new Set();
31
- const cachedPropInfos = new Map();
32
31
  const onDidChangeCustomDataListeners = new Set();
33
32
  const onDidChangeCustomData = (listener) => {
34
33
  onDidChangeCustomDataListeners.add(listener);
@@ -38,8 +37,9 @@ function create(mode, getTsPluginClient) {
38
37
  },
39
38
  };
40
39
  };
41
- const baseService = mode === 'pug'
40
+ const baseService = languageId === 'jade'
42
41
  ? (0, volar_service_pug_1.create)({
42
+ useDefaultDataProvider: false,
43
43
  getCustomData() {
44
44
  return [
45
45
  ...customData,
@@ -50,6 +50,7 @@ function create(mode, getTsPluginClient) {
50
50
  })
51
51
  : (0, volar_service_html_1.create)({
52
52
  documentSelector: ['html', 'markdown'],
53
+ useDefaultDataProvider: false,
53
54
  getCustomData() {
54
55
  return [
55
56
  ...customData,
@@ -58,8 +59,9 @@ function create(mode, getTsPluginClient) {
58
59
  },
59
60
  onDidChangeCustomData,
60
61
  });
62
+ const htmlDataProvider = html.getDefaultHTMLDataProvider();
61
63
  return {
62
- name: `vue-template (${mode})`,
64
+ name: `vue-template (${languageId})`,
63
65
  capabilities: {
64
66
  ...baseService.capabilities,
65
67
  completionProvider: {
@@ -71,7 +73,6 @@ function create(mode, getTsPluginClient) {
71
73
  hoverProvider: true,
72
74
  },
73
75
  create(context) {
74
- const tsPluginClient = getTsPluginClient?.(context);
75
76
  const baseServiceInstance = baseService.create(context);
76
77
  builtInData ??= (0, data_1.loadTemplateData)(context.env.locale ?? 'en');
77
78
  modelData ??= (0, data_1.loadModelModifiersData)(context.env.locale ?? 'en');
@@ -84,7 +85,9 @@ function create(mode, getTsPluginClient) {
84
85
  const vBind = builtInData.globalAttributes?.find(x => x.name === 'v-bind');
85
86
  const vModel = builtInData.globalAttributes?.find(x => x.name === 'v-model');
86
87
  if (vOn) {
87
- const markdown = (typeof vOn.description === 'string' ? vOn.description : vOn.description?.value) ?? '';
88
+ const markdown = typeof vOn.description === 'object'
89
+ ? vOn.description.value
90
+ : vOn.description ?? '';
88
91
  const modifiers = markdown
89
92
  .split('\n- ')[4]
90
93
  .split('\n').slice(2, -1);
@@ -95,7 +98,9 @@ function create(mode, getTsPluginClient) {
95
98
  }
96
99
  }
97
100
  if (vBind) {
98
- const markdown = (typeof vBind.description === 'string' ? vBind.description : vBind.description?.value) ?? '';
101
+ const markdown = typeof vBind.description === 'object'
102
+ ? vBind.description.value
103
+ : vBind.description ?? '';
99
104
  const modifiers = markdown
100
105
  .split('\n- ')[4]
101
106
  .split('\n').slice(2, -1);
@@ -109,7 +114,7 @@ function create(mode, getTsPluginClient) {
109
114
  for (const modifier of modelData.globalAttributes ?? []) {
110
115
  const description = typeof modifier.description === 'object'
111
116
  ? modifier.description.value
112
- : modifier.description;
117
+ : modifier.description ?? '';
113
118
  const references = modifier.references?.map(ref => `[${ref.name}](${ref.url})`).join(' | ');
114
119
  vModelModifiers[modifier.name] = description + '\n\n' + references;
115
120
  }
@@ -123,94 +128,211 @@ function create(mode, getTsPluginClient) {
123
128
  disposable?.dispose();
124
129
  },
125
130
  async provideCompletionItems(document, position, completionContext, token) {
126
- if (!isSupportedDocument(document)) {
131
+ const info = (0, utils_1.getEmbeddedInfo)(context, document, 'template', languageId);
132
+ if (!info) {
127
133
  return;
128
134
  }
129
- if (!context.project.vue) {
135
+ const { sourceScript, root } = info;
136
+ const { result: completionList, target, info: { components, propMap, }, } = await runWithVueData(sourceScript.id, root, () => baseServiceInstance.provideCompletionItems(document, position, completionContext, token));
137
+ if (!completionList) {
130
138
  return;
131
139
  }
132
- let sync;
133
- let currentVersion;
134
- const uri = vscode_uri_1.URI.parse(document.uri);
135
- const decoded = context.decodeEmbeddedDocumentUri(uri);
136
- const sourceScript = decoded && context.language.scripts.get(decoded[0]);
137
- const root = sourceScript?.generated?.root;
138
- if (root instanceof language_core_1.VueVirtualCode) {
139
- // #4298: Precompute HTMLDocument before provideHtmlData to avoid parseHTMLDocument requesting component names from tsserver
140
- baseServiceInstance.provideCompletionItems?.(document, position, completionContext, token);
141
- sync = (await provideHtmlData(sourceScript.id, root)).sync;
142
- currentVersion = await sync();
143
- }
144
- let htmlComplete = await baseServiceInstance.provideCompletionItems?.(document, position, completionContext, token);
145
- while (currentVersion !== (currentVersion = await sync?.())) {
146
- htmlComplete = await baseServiceInstance.provideCompletionItems?.(document, position, completionContext, token);
140
+ switch (target) {
141
+ case 'tag': {
142
+ completionList.items.forEach(transformTag);
143
+ break;
144
+ }
145
+ case 'attribute': {
146
+ addDirectiveModifiers(completionList, document);
147
+ completionList.items.forEach(transformAttribute);
148
+ break;
149
+ }
147
150
  }
148
- if (!htmlComplete) {
149
- return;
151
+ updateExtraCustomData([]);
152
+ return completionList;
153
+ function transformTag(item) {
154
+ const tagName = (0, shared_1.capitalize)((0, shared_1.camelize)(item.label));
155
+ if (components?.includes(tagName)) {
156
+ item.kind = 6;
157
+ item.sortText = '\u0000' + (item.sortText ?? item.label);
158
+ }
150
159
  }
151
- if (sourceScript?.generated) {
152
- const virtualCode = sourceScript.generated.embeddedCodes.get('template');
153
- if (virtualCode) {
154
- const embeddedDocumentUri = context.encodeEmbeddedDocumentUri(sourceScript.id, virtualCode.id);
155
- afterHtmlCompletion(htmlComplete, context.documents.get(embeddedDocumentUri, virtualCode.languageId, virtualCode.snapshot));
160
+ function transformAttribute(item) {
161
+ let prop = propMap.get(item.label);
162
+ if (prop) {
163
+ if (prop.info?.documentation) {
164
+ item.documentation = {
165
+ kind: 'markdown',
166
+ value: prop.info.documentation,
167
+ };
168
+ }
169
+ if (prop.info?.deprecated) {
170
+ item.tags = [1];
171
+ }
172
+ }
173
+ else {
174
+ let name = item.label;
175
+ for (const str of ['v-bind:', ':']) {
176
+ if (name.startsWith(str) && name !== str) {
177
+ name = name.slice(str.length);
178
+ break;
179
+ }
180
+ }
181
+ if (specialProps.has(name)) {
182
+ prop = {
183
+ name,
184
+ kind: 'prop',
185
+ isGlobal: true,
186
+ };
187
+ }
188
+ }
189
+ const tokens = [];
190
+ if (prop) {
191
+ const { isEvent, propName } = getPropName(prop.name, prop.kind === 'event');
192
+ if (prop.kind === 'prop') {
193
+ if (!prop.isGlobal || specialProps.has(propName)) {
194
+ item.kind = 5;
195
+ }
196
+ }
197
+ else if (isEvent) {
198
+ item.kind = 23;
199
+ if (propName.startsWith('vue:')) {
200
+ tokens.push('\u0004');
201
+ }
202
+ }
203
+ if (!prop.isGlobal || specialProps.has(propName)) {
204
+ tokens.push('\u0000');
205
+ if (item.label.startsWith(':')) {
206
+ tokens.push('\u0001');
207
+ }
208
+ else if (item.label.startsWith('@')) {
209
+ tokens.push('\u0002');
210
+ }
211
+ else if (item.label.startsWith('v-bind:')) {
212
+ tokens.push('\u0003');
213
+ }
214
+ else if (item.label.startsWith('v-model:')) {
215
+ tokens.push('\u0004');
216
+ }
217
+ else if (item.label.startsWith('v-on:')) {
218
+ tokens.push('\u0005');
219
+ }
220
+ else {
221
+ tokens.push('\u0000');
222
+ }
223
+ if (specialProps.has(propName)) {
224
+ tokens.push('\u0001');
225
+ }
226
+ else {
227
+ tokens.push('\u0000');
228
+ }
229
+ }
230
+ }
231
+ else if (item.label === 'v-if'
232
+ || item.label === 'v-else-if'
233
+ || item.label === 'v-else'
234
+ || item.label === 'v-for') {
235
+ item.kind = 14;
236
+ tokens.push('\u0003');
237
+ }
238
+ else if (item.label.startsWith('v-')) {
239
+ item.kind = 3;
240
+ tokens.push('\u0002');
241
+ }
242
+ else {
243
+ tokens.push('\u0001');
156
244
  }
245
+ item.sortText = tokens.join('') + (item.sortText ?? item.label);
157
246
  }
158
- return htmlComplete;
159
247
  },
160
248
  provideHover(document, position, token) {
161
- if (!isSupportedDocument(document)) {
249
+ const info = (0, utils_1.getEmbeddedInfo)(context, document, 'template', languageId);
250
+ if (!info) {
162
251
  return;
163
252
  }
164
253
  if (context.decodeEmbeddedDocumentUri(vscode_uri_1.URI.parse(document.uri))) {
165
- updateExtraCustomData([]);
254
+ updateExtraCustomData([
255
+ htmlDataProvider,
256
+ ]);
166
257
  }
167
258
  return baseServiceInstance.provideHover?.(document, position, token);
168
259
  },
169
260
  };
170
- async function provideHtmlData(sourceDocumentUri, vueCode) {
261
+ async function runWithVueData(sourceDocumentUri, root, fn) {
262
+ // #4298: Precompute HTMLDocument before provideHtmlData to avoid parseHTMLDocument requesting component names from tsserver
263
+ await fn();
264
+ const { sync } = await provideHtmlData(sourceDocumentUri, root);
265
+ let lastSync = await sync();
266
+ let result = await fn();
267
+ while (lastSync.version !== (lastSync = await sync()).version) {
268
+ result = await fn();
269
+ }
270
+ return { result, ...lastSync };
271
+ }
272
+ async function provideHtmlData(sourceDocumentUri, root) {
171
273
  await (initializing ??= initialize());
172
274
  const casing = await (0, nameCasing_1.checkCasing)(context, sourceDocumentUri);
173
- if (builtInData.tags) {
174
- for (const tag of builtInData.tags) {
175
- if (isItemKey(tag.name)) {
176
- continue;
177
- }
178
- if (specialTags.has(tag.name)) {
179
- tag.name = generateItemKey('specialTag', tag.name, '');
180
- }
181
- else if (casing.tag === nameCasing_1.TagNameCasing.Kebab) {
182
- tag.name = (0, language_core_1.hyphenateTag)(tag.name);
183
- }
184
- else {
185
- tag.name = (0, shared_1.camelize)((0, shared_1.capitalize)(tag.name));
186
- }
275
+ for (const tag of builtInData.tags ?? []) {
276
+ if (specialTags.has(tag.name)) {
277
+ continue;
278
+ }
279
+ if (casing.tag === nameCasing_1.TagNameCasing.Kebab) {
280
+ tag.name = (0, language_core_1.hyphenateTag)(tag.name);
281
+ }
282
+ else {
283
+ tag.name = (0, shared_1.camelize)((0, shared_1.capitalize)(tag.name));
187
284
  }
188
285
  }
189
- const promises = [];
190
- const tagInfos = new Map();
191
286
  let version = 0;
287
+ let target;
192
288
  let components;
193
- cachedPropInfos.clear();
289
+ let values;
290
+ const tasks = [];
291
+ const tagMap = new Map();
292
+ const propMap = new Map();
194
293
  updateExtraCustomData([
294
+ {
295
+ getId: () => htmlDataProvider.getId(),
296
+ isApplicable: () => true,
297
+ provideTags() {
298
+ target = 'tag';
299
+ return htmlDataProvider.provideTags()
300
+ .filter(tag => !specialTags.has(tag.name));
301
+ },
302
+ provideAttributes(tag) {
303
+ target = 'attribute';
304
+ const attrs = htmlDataProvider.provideAttributes(tag);
305
+ if (tag === 'slot') {
306
+ const nameAttr = attrs.find(attr => attr.name === 'name');
307
+ if (nameAttr) {
308
+ nameAttr.valueSet = 'slot';
309
+ }
310
+ }
311
+ return attrs;
312
+ },
313
+ provideValues(tag, attr) {
314
+ target = 'value';
315
+ return htmlDataProvider.provideValues(tag, attr);
316
+ },
317
+ },
195
318
  html.newHTMLDataProvider('vue-template-built-in', builtInData),
196
319
  {
197
320
  getId: () => 'vue-template',
198
321
  isApplicable: () => true,
199
322
  provideTags: () => {
200
323
  if (!components) {
201
- promises.push((async () => {
202
- components = (await tsPluginClient?.getComponentNames(vueCode.fileName) ?? [])
324
+ components = [];
325
+ tasks.push((async () => {
326
+ components = (await getComponentNames(root.fileName) ?? [])
203
327
  .filter(name => name !== 'Transition'
204
328
  && name !== 'TransitionGroup'
205
329
  && name !== 'KeepAlive'
206
330
  && name !== 'Suspense'
207
331
  && name !== 'Teleport');
208
- lastCompletionComponentNames = new Set(components);
209
332
  version++;
210
333
  })());
211
- return [];
212
334
  }
213
- const scriptSetupRanges = language_core_1.tsCodegen.get(vueCode.sfc)?.getScriptSetupRanges();
335
+ const scriptSetupRanges = language_core_1.tsCodegen.get(root.sfc)?.getScriptSetupRanges();
214
336
  const names = new Set();
215
337
  const tags = [];
216
338
  for (const tag of components) {
@@ -222,7 +344,7 @@ function create(mode, getTsPluginClient) {
222
344
  }
223
345
  }
224
346
  for (const binding of scriptSetupRanges?.bindings ?? []) {
225
- const name = vueCode.sfc.scriptSetup.content.slice(binding.range.start, binding.range.end);
347
+ const name = root.sfc.scriptSetup.content.slice(binding.range.start, binding.range.end);
226
348
  if (casing.tag === nameCasing_1.TagNameCasing.Kebab) {
227
349
  names.add((0, language_core_1.hyphenateTag)(name));
228
350
  }
@@ -239,84 +361,98 @@ function create(mode, getTsPluginClient) {
239
361
  return tags;
240
362
  },
241
363
  provideAttributes: tag => {
242
- const tagInfo = tagInfos.get(tag);
364
+ let tagInfo = tagMap.get(tag);
243
365
  if (!tagInfo) {
244
- promises.push((async () => {
245
- const attrs = await tsPluginClient?.getElementAttrs(vueCode.fileName, tag) ?? [];
246
- const propInfos = await tsPluginClient?.getComponentProps(vueCode.fileName, tag) ?? [];
247
- const events = await tsPluginClient?.getComponentEvents(vueCode.fileName, tag) ?? [];
248
- const directives = await tsPluginClient?.getComponentDirectives(vueCode.fileName) ?? [];
249
- tagInfos.set(tag, {
250
- attrs,
251
- propInfos: propInfos.filter(prop => !prop.name.startsWith('ref_')),
252
- events,
253
- directives,
366
+ tagInfo = {
367
+ attrs: [],
368
+ propInfos: [],
369
+ events: [],
370
+ directives: [],
371
+ };
372
+ tagMap.set(tag, tagInfo);
373
+ tasks.push((async () => {
374
+ tagMap.set(tag, {
375
+ attrs: await getElementAttrs(root.fileName, tag) ?? [],
376
+ propInfos: await getComponentProps(root.fileName, tag) ?? [],
377
+ events: await getComponentEvents(root.fileName, tag) ?? [],
378
+ directives: await getComponentDirectives(root.fileName) ?? [],
254
379
  });
255
380
  version++;
256
381
  })());
257
- return [];
258
382
  }
259
383
  const { attrs, propInfos, events, directives } = tagInfo;
260
- for (const prop of propInfos) {
384
+ for (let i = 0; i < propInfos.length; i++) {
385
+ const prop = propInfos[i];
386
+ if (prop.name.startsWith('ref_')) {
387
+ propInfos.splice(i--, 1);
388
+ continue;
389
+ }
261
390
  if ((0, language_core_1.hyphenateTag)(prop.name).startsWith('on-vnode-')) {
262
- prop.name = 'onVue:' + prop.name.slice('onVnode'.length);
391
+ prop.name = 'onVue:' + prop.name['onVnode'.length].toLowerCase()
392
+ + prop.name.slice('onVnodeX'.length);
263
393
  }
264
394
  }
265
395
  const attributes = [];
266
- const propsSet = new Set(propInfos.map(prop => prop.name));
396
+ const propNameSet = new Set(propInfos.map(prop => prop.name));
267
397
  for (const prop of [
268
398
  ...propInfos,
269
399
  ...attrs.map(attr => ({ name: attr })),
270
400
  ]) {
271
- const isGlobal = prop.isAttribute || !propsSet.has(prop.name);
272
- const name = casing.attr === nameCasing_1.AttrNameCasing.Camel ? prop.name : (0, language_core_1.hyphenateAttr)(prop.name);
273
- const isEvent = (0, language_core_1.hyphenateAttr)(name).startsWith('on-');
401
+ const isGlobal = prop.isAttribute || !propNameSet.has(prop.name);
402
+ const propName = casing.attr === nameCasing_1.AttrNameCasing.Camel ? prop.name : (0, language_core_1.hyphenateAttr)(prop.name);
403
+ const isEvent = (0, language_core_1.hyphenateAttr)(propName).startsWith('on-');
274
404
  if (isEvent) {
275
- const propNameBase = name.startsWith('on-')
276
- ? name.slice('on-'.length)
277
- : (name['on'.length].toLowerCase() + name.slice('onX'.length));
278
- const propKey = generateItemKey('componentEvent', isGlobal ? '*' : tag, propNameBase);
279
- attributes.push({
280
- name: 'v-on:' + propNameBase,
281
- description: propKey,
282
- }, {
283
- name: '@' + propNameBase,
284
- description: propKey,
285
- });
405
+ const eventName = casing.attr === nameCasing_1.AttrNameCasing.Camel
406
+ ? propName['on'.length].toLowerCase() + propName.slice('onX'.length)
407
+ : propName.slice('on-'.length);
408
+ for (const name of [
409
+ 'v-on:' + eventName,
410
+ '@' + eventName,
411
+ ]) {
412
+ attributes.push({ name });
413
+ propMap.set(name, {
414
+ name: propName,
415
+ kind: 'event',
416
+ isGlobal,
417
+ info: prop,
418
+ });
419
+ }
286
420
  }
287
421
  else {
288
- const propName = name;
289
422
  const propInfo = propInfos.find(prop => {
290
423
  const name = casing.attr === nameCasing_1.AttrNameCasing.Camel ? prop.name : (0, language_core_1.hyphenateAttr)(prop.name);
291
424
  return name === propName;
292
425
  });
293
- const propKey = generateItemKey('componentProp', isGlobal ? '*' : tag, propName, propInfo?.deprecated);
294
- if (propInfo) {
295
- cachedPropInfos.set(propName, propInfo);
426
+ for (const name of [
427
+ propName,
428
+ ':' + propName,
429
+ 'v-bind:' + propName,
430
+ ]) {
431
+ attributes.push({
432
+ name,
433
+ valueSet: prop.values?.some(value => typeof value === 'string') ? '__deferred__' : undefined,
434
+ });
435
+ propMap.set(name, {
436
+ name: propName,
437
+ kind: 'prop',
438
+ isGlobal,
439
+ info: propInfo,
440
+ });
296
441
  }
297
- attributes.push({
298
- name: propName,
299
- description: propKey,
300
- valueSet: prop.values?.some(value => typeof value === 'string') ? '__deferred__' : undefined,
301
- }, {
302
- name: ':' + propName,
303
- description: propKey,
304
- }, {
305
- name: 'v-bind:' + propName,
306
- description: propKey,
307
- });
308
442
  }
309
443
  }
310
444
  for (const event of events) {
311
- const name = casing.attr === nameCasing_1.AttrNameCasing.Camel ? event : (0, language_core_1.hyphenateAttr)(event);
312
- const propKey = generateItemKey('componentEvent', tag, name);
313
- attributes.push({
314
- name: 'v-on:' + name,
315
- description: propKey,
316
- }, {
317
- name: '@' + name,
318
- description: propKey,
319
- });
445
+ const eventName = casing.attr === nameCasing_1.AttrNameCasing.Camel ? event : (0, language_core_1.hyphenateAttr)(event);
446
+ for (const name of [
447
+ 'v-on:' + eventName,
448
+ '@' + eventName,
449
+ ]) {
450
+ attributes.push({ name });
451
+ propMap.set(name, {
452
+ name: eventName,
453
+ kind: 'event',
454
+ });
455
+ }
320
456
  }
321
457
  for (const directive of directives) {
322
458
  const name = (0, language_core_1.hyphenateAttr)(directive);
@@ -330,7 +466,7 @@ function create(mode, getTsPluginClient) {
330
466
  ...attrs.map(attr => ({ name: attr })),
331
467
  ]) {
332
468
  if (prop.name.startsWith('onUpdate:')) {
333
- const isGlobal = !propsSet.has(prop.name);
469
+ const isGlobal = !propNameSet.has(prop.name);
334
470
  models.push([isGlobal, prop.name.slice('onUpdate:'.length)]);
335
471
  }
336
472
  }
@@ -341,224 +477,92 @@ function create(mode, getTsPluginClient) {
341
477
  }
342
478
  for (const [isGlobal, model] of models) {
343
479
  const name = casing.attr === nameCasing_1.AttrNameCasing.Camel ? model : (0, language_core_1.hyphenateAttr)(model);
344
- const propKey = generateItemKey('componentProp', isGlobal ? '*' : tag, name);
345
- attributes.push({
346
- name: 'v-model:' + name,
347
- description: propKey,
480
+ attributes.push({ name: 'v-model:' + name });
481
+ propMap.set('v-model:' + name, {
482
+ name,
483
+ kind: 'prop',
484
+ isGlobal,
348
485
  });
349
486
  if (model === 'modelValue') {
350
- attributes.push({
351
- name: 'v-model',
352
- description: propKey,
487
+ propMap.set('v-model', {
488
+ name,
489
+ kind: 'prop',
490
+ isGlobal,
353
491
  });
354
492
  }
355
493
  }
356
494
  return attributes;
357
495
  },
358
- provideValues: () => [],
496
+ provideValues: (tag, attr) => {
497
+ if (!values) {
498
+ values = [];
499
+ tasks.push((async () => {
500
+ if (tag === 'slot' && attr === 'name') {
501
+ values = await getComponentSlots(root.fileName) ?? [];
502
+ }
503
+ version++;
504
+ })());
505
+ }
506
+ return values.map(value => ({
507
+ name: value,
508
+ }));
509
+ },
359
510
  },
360
511
  ]);
361
512
  return {
362
513
  async sync() {
363
- await Promise.all(promises);
364
- return version;
514
+ await Promise.all(tasks);
515
+ return {
516
+ version,
517
+ target,
518
+ info: {
519
+ components,
520
+ propMap,
521
+ },
522
+ };
365
523
  },
366
524
  };
367
525
  }
368
- function afterHtmlCompletion(completionList, document) {
369
- addDirectiveModifiers();
370
- function addDirectiveModifiers() {
371
- const replacement = getReplacement(completionList, document);
372
- if (!replacement?.text.includes('.')) {
373
- return;
374
- }
375
- const [text, ...modifiers] = replacement.text.split('.');
376
- const isVOn = text.startsWith('v-on:') || text.startsWith('@') && text.length > 1;
377
- const isVBind = text.startsWith('v-bind:') || text.startsWith(':') && text.length > 1;
378
- const isVModel = text.startsWith('v-model:') || text === 'v-model';
379
- const currentModifiers = isVOn
380
- ? vOnModifiers
381
- : isVBind
382
- ? vBindModifiers
383
- : isVModel
384
- ? vModelModifiers
385
- : undefined;
386
- if (!currentModifiers) {
387
- return;
388
- }
389
- for (const modifier in currentModifiers) {
390
- if (modifiers.includes(modifier)) {
391
- continue;
392
- }
393
- const description = currentModifiers[modifier];
394
- const insertText = text + modifiers.slice(0, -1).map(m => '.' + m).join('') + '.' + modifier;
395
- const newItem = {
396
- label: modifier,
397
- filterText: insertText,
398
- documentation: {
399
- kind: 'markdown',
400
- value: description,
401
- },
402
- textEdit: {
403
- range: replacement.textEdit.range,
404
- newText: insertText,
405
- },
406
- kind: 20,
407
- };
408
- completionList.items.push(newItem);
409
- }
526
+ function addDirectiveModifiers(completionList, document) {
527
+ const replacement = getReplacement(completionList, document);
528
+ if (!replacement?.text.includes('.')) {
529
+ return;
410
530
  }
411
- completionList.items = completionList.items.filter(item => !specialTags.has(parseLabel(item.label).name));
412
- const htmlDocumentations = new Map();
413
- for (const item of completionList.items) {
414
- const documentation = typeof item.documentation === 'string' ? item.documentation : item.documentation?.value;
415
- if (documentation && !isItemKey(documentation)) {
416
- htmlDocumentations.set(item.label, documentation);
417
- }
531
+ const [text, ...modifiers] = replacement.text.split('.');
532
+ const isVOn = text.startsWith('v-on:') || text.startsWith('@') && text.length > 1;
533
+ const isVBind = text.startsWith('v-bind:') || text.startsWith(':') && text.length > 1;
534
+ const isVModel = text.startsWith('v-model:') || text === 'v-model';
535
+ const currentModifiers = isVOn
536
+ ? vOnModifiers
537
+ : isVBind
538
+ ? vBindModifiers
539
+ : isVModel
540
+ ? vModelModifiers
541
+ : undefined;
542
+ if (!currentModifiers) {
543
+ return;
418
544
  }
419
- for (const item of completionList.items) {
420
- const parsedLabel = parseItemKey(item.label);
421
- if (parsedLabel) {
422
- const name = parsedLabel.tag;
423
- item.label = parsedLabel.leadingSlash ? '/' + name : name;
424
- const text = parsedLabel.leadingSlash ? `/${name}>` : name;
425
- if (item.textEdit) {
426
- item.textEdit.newText = text;
427
- }
428
- if (item.insertText) {
429
- item.insertText = text;
430
- }
431
- if (item.sortText) {
432
- item.sortText = text;
433
- }
434
- }
435
- const itemKey = typeof item.documentation === 'string' ? item.documentation : item.documentation?.value;
436
- let parsedItem = itemKey ? parseItemKey(itemKey) : undefined;
437
- let propInfo;
438
- if (parsedItem) {
439
- const documentations = [];
440
- propInfo = cachedPropInfos.get(parsedItem.prop);
441
- if (propInfo?.commentMarkdown) {
442
- documentations.push(propInfo.commentMarkdown);
443
- }
444
- let { isEvent, propName } = getPropName(parsedItem);
445
- if (isEvent) {
446
- // click -> onclick
447
- propName = 'on' + propName;
448
- }
449
- if (htmlDocumentations.has(propName)) {
450
- documentations.push(htmlDocumentations.get(propName));
451
- }
452
- if (documentations.length) {
453
- item.documentation = {
454
- kind: 'markdown',
455
- value: documentations.join('\n\n'),
456
- };
457
- }
458
- else {
459
- item.documentation = undefined;
460
- }
461
- }
462
- else {
463
- let propName = item.label;
464
- for (const str of ['v-bind:', ':']) {
465
- if (propName.startsWith(str) && propName !== str) {
466
- propName = propName.slice(str.length);
467
- break;
468
- }
469
- }
470
- // for special props without internal item key
471
- if (specialProps.has(propName)) {
472
- parsedItem = {
473
- type: 'componentProp',
474
- tag: '^',
475
- prop: propName,
476
- deprecated: false,
477
- leadingSlash: false,
478
- };
479
- }
480
- propInfo = cachedPropInfos.get(propName);
481
- if (propInfo?.commentMarkdown) {
482
- const originalDocumentation = typeof item.documentation === 'string'
483
- ? item.documentation
484
- : item.documentation?.value;
485
- item.documentation = {
486
- kind: 'markdown',
487
- value: [
488
- propInfo.commentMarkdown,
489
- originalDocumentation,
490
- ].filter(str => !!str).join('\n\n'),
491
- };
492
- }
493
- }
494
- if (propInfo?.deprecated) {
495
- item.tags = [1];
496
- }
497
- const tokens = [];
498
- if (item.kind === 10
499
- && lastCompletionComponentNames.has((0, language_core_1.hyphenateTag)(item.label))) {
500
- item.kind = 6;
501
- tokens.push('\u0000');
502
- }
503
- else if (parsedItem) {
504
- const isComponent = parsedItem.tag !== '*';
505
- const { isEvent, propName } = getPropName(parsedItem);
506
- if (parsedItem.type === 'componentProp') {
507
- if (isComponent || specialProps.has(propName)) {
508
- item.kind = 5;
509
- }
510
- }
511
- else if (isEvent) {
512
- item.kind = 23;
513
- if (propName.startsWith('vue:')) {
514
- tokens.push('\u0004');
515
- }
516
- }
517
- if (isComponent || specialProps.has(propName)) {
518
- tokens.push('\u0000');
519
- if (item.label.startsWith(':')) {
520
- tokens.push('\u0001');
521
- }
522
- else if (item.label.startsWith('@')) {
523
- tokens.push('\u0002');
524
- }
525
- else if (item.label.startsWith('v-bind:')) {
526
- tokens.push('\u0003');
527
- }
528
- else if (item.label.startsWith('v-model:')) {
529
- tokens.push('\u0004');
530
- }
531
- else if (item.label.startsWith('v-on:')) {
532
- tokens.push('\u0005');
533
- }
534
- else {
535
- tokens.push('\u0000');
536
- }
537
- if (specialProps.has(propName)) {
538
- tokens.push('\u0001');
539
- }
540
- else {
541
- tokens.push('\u0000');
542
- }
543
- }
544
- }
545
- else if (item.label === 'v-if'
546
- || item.label === 'v-else-if'
547
- || item.label === 'v-else'
548
- || item.label === 'v-for') {
549
- item.kind = 14;
550
- tokens.push('\u0003');
545
+ for (const modifier in currentModifiers) {
546
+ if (modifiers.includes(modifier)) {
547
+ continue;
551
548
  }
552
- else if (item.label.startsWith('v-')) {
553
- item.kind = 3;
554
- tokens.push('\u0002');
555
- }
556
- else {
557
- tokens.push('\u0001');
558
- }
559
- item.sortText = tokens.join('') + (item.sortText ?? item.label);
549
+ const description = currentModifiers[modifier];
550
+ const insertText = text + modifiers.slice(0, -1).map(m => '.' + m).join('') + '.' + modifier;
551
+ const newItem = {
552
+ label: modifier,
553
+ filterText: insertText,
554
+ documentation: {
555
+ kind: 'markdown',
556
+ value: description,
557
+ },
558
+ textEdit: {
559
+ range: replacement.textEdit.range,
560
+ newText: insertText,
561
+ },
562
+ kind: 20,
563
+ };
564
+ completionList.items.push(newItem);
560
565
  }
561
- updateExtraCustomData([]);
562
566
  }
563
567
  async function initialize() {
564
568
  customData = await getHtmlCustomData();
@@ -589,41 +593,6 @@ function create(mode, getTsPluginClient) {
589
593
  extraCustomData = extraData;
590
594
  onDidChangeCustomDataListeners.forEach(l => l());
591
595
  }
592
- function isSupportedDocument(document) {
593
- if (mode === 'pug') {
594
- return document.languageId === 'jade';
595
- }
596
- else {
597
- return document.languageId === 'html';
598
- }
599
- }
600
- }
601
- function parseLabel(label) {
602
- const leadingSlash = label.startsWith('/');
603
- const name = label.slice(leadingSlash ? 1 : 0);
604
- return {
605
- name,
606
- leadingSlash,
607
- };
608
- }
609
- function generateItemKey(type, tag, prop, deprecated) {
610
- return `__VLS_data=${type},${tag},${prop},${Number(deprecated)}`;
611
- }
612
- function isItemKey(key) {
613
- return key.startsWith('__VLS_data=');
614
- }
615
- function parseItemKey(key) {
616
- const { leadingSlash, name } = parseLabel(key);
617
- if (isItemKey(name)) {
618
- const strs = name.slice('__VLS_data='.length).split(',');
619
- return {
620
- type: strs[0],
621
- tag: strs[1],
622
- prop: strs[2],
623
- deprecated: strs[3] === '1',
624
- leadingSlash,
625
- };
626
- }
627
596
  }
628
597
  function getReplacement(list, doc) {
629
598
  for (const item of list.items) {
@@ -636,14 +605,11 @@ function getReplacement(list, doc) {
636
605
  }
637
606
  }
638
607
  }
639
- function getPropName(parsedItem) {
640
- const name = (0, language_core_1.hyphenateAttr)(parsedItem.prop);
608
+ function getPropName(prop, isEvent) {
609
+ const name = (0, language_core_1.hyphenateAttr)(prop);
641
610
  if (name.startsWith('on-')) {
642
611
  return { isEvent: true, propName: name.slice('on-'.length) };
643
612
  }
644
- else if (parsedItem.type === 'componentEvent') {
645
- return { isEvent: true, propName: name };
646
- }
647
- return { isEvent: false, propName: name };
613
+ return { isEvent, propName: name };
648
614
  }
649
615
  //# sourceMappingURL=vue-template.js.map