@vue/language-service 3.0.2 → 3.0.4

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