@vue/language-service 1.7.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 (48) hide show
  1. package/LICENSE +21 -0
  2. package/data/language-blocks/en.json +626 -0
  3. package/data/language-blocks/fr.json +626 -0
  4. package/data/language-blocks/ja.json +626 -0
  5. package/data/language-blocks/ko.json +626 -0
  6. package/data/language-blocks/zh-cn.json +626 -0
  7. package/data/language-blocks/zh-tw.json +626 -0
  8. package/data/model-modifiers/en.json +104 -0
  9. package/data/model-modifiers/fr.json +104 -0
  10. package/data/model-modifiers/ja.json +104 -0
  11. package/data/model-modifiers/ko.json +104 -0
  12. package/data/model-modifiers/zh-cn.json +104 -0
  13. package/data/model-modifiers/zh-tw.json +104 -0
  14. package/data/template/en.json +866 -0
  15. package/data/template/fr.json +866 -0
  16. package/data/template/ja.json +866 -0
  17. package/data/template/ko.json +866 -0
  18. package/data/template/zh-cn.json +866 -0
  19. package/data/template/zh-tw.json +866 -0
  20. package/out/helpers.d.ts +15 -0
  21. package/out/helpers.js +246 -0
  22. package/out/ideFeatures/nameCasing.d.ts +13 -0
  23. package/out/ideFeatures/nameCasing.js +173 -0
  24. package/out/index.d.ts +5 -0
  25. package/out/index.js +24 -0
  26. package/out/languageService.d.ts +9 -0
  27. package/out/languageService.js +243 -0
  28. package/out/plugins/data.d.ts +4 -0
  29. package/out/plugins/data.js +90 -0
  30. package/out/plugins/vue-autoinsert-dotvalue.d.ts +7 -0
  31. package/out/plugins/vue-autoinsert-dotvalue.js +157 -0
  32. package/out/plugins/vue-autoinsert-parentheses.d.ts +3 -0
  33. package/out/plugins/vue-autoinsert-parentheses.js +67 -0
  34. package/out/plugins/vue-autoinsert-space.d.ts +3 -0
  35. package/out/plugins/vue-autoinsert-space.js +30 -0
  36. package/out/plugins/vue-codelens-references.d.ts +2 -0
  37. package/out/plugins/vue-codelens-references.js +53 -0
  38. package/out/plugins/vue-template.d.ts +10 -0
  39. package/out/plugins/vue-template.js +538 -0
  40. package/out/plugins/vue-twoslash-queries.d.ts +3 -0
  41. package/out/plugins/vue-twoslash-queries.js +59 -0
  42. package/out/plugins/vue-visualize-hidden-callback-param.d.ts +3 -0
  43. package/out/plugins/vue-visualize-hidden-callback-param.js +42 -0
  44. package/out/plugins/vue.d.ts +8 -0
  45. package/out/plugins/vue.js +137 -0
  46. package/out/types.d.ts +10 -0
  47. package/out/types.js +31 -0
  48. package/package.json +44 -0
@@ -0,0 +1,53 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const language_core_1 = require("@vue/language-core");
4
+ function default_1() {
5
+ return (context) => {
6
+ if (!context)
7
+ return {};
8
+ return {
9
+ provideReferencesCodeLensRanges(document) {
10
+ return worker(document.uri, async () => {
11
+ const result = [];
12
+ for (const [_, map] of context.documents.getMapsBySourceFileUri(document.uri)?.maps ?? []) {
13
+ for (const mapping of map.map.mappings) {
14
+ if (!mapping.data.referencesCodeLens)
15
+ continue;
16
+ result.push({
17
+ start: document.positionAt(mapping.sourceRange[0]),
18
+ end: document.positionAt(mapping.sourceRange[1]),
19
+ });
20
+ }
21
+ }
22
+ return result;
23
+ });
24
+ },
25
+ async resolveReferencesCodeLensLocations(document, range, references) {
26
+ await worker(document.uri, async (vueFile) => {
27
+ const document = context.documents.getDocumentByFileName(vueFile.snapshot, vueFile.fileName);
28
+ const offset = document.offsetAt(range.start);
29
+ const blocks = [
30
+ vueFile.sfc.script,
31
+ vueFile.sfc.scriptSetup,
32
+ vueFile.sfc.template,
33
+ ...vueFile.sfc.styles,
34
+ ...vueFile.sfc.customBlocks,
35
+ ];
36
+ const sourceBlock = blocks.find(block => block && offset >= block.startTagEnd && offset <= block.endTagStart);
37
+ references = references.filter(reference => reference.uri !== document.uri // different file
38
+ || sourceBlock !== blocks.find(block => block && document.offsetAt(reference.range.start) >= block.startTagEnd && document.offsetAt(reference.range.end) <= block.endTagStart) // different block
39
+ );
40
+ });
41
+ return references;
42
+ },
43
+ };
44
+ function worker(uri, callback) {
45
+ const [virtualFile] = context.documents.getVirtualFileByUri(uri);
46
+ if (!(virtualFile instanceof language_core_1.VueFile))
47
+ return;
48
+ return callback(virtualFile);
49
+ }
50
+ };
51
+ }
52
+ exports.default = default_1;
53
+ //# sourceMappingURL=vue-codelens-references.js.map
@@ -0,0 +1,10 @@
1
+ import createHtmlPlugin from 'volar-service-html';
2
+ import * as html from 'vscode-html-languageservice';
3
+ import { TextDocument } from 'vscode-languageserver-textdocument';
4
+ import { VueCompilerOptions } from '../types';
5
+ export default function useVueTemplateLanguagePlugin<T extends ReturnType<typeof createHtmlPlugin>>(options: {
6
+ getScanner(document: TextDocument, t: ReturnType<T>): html.Scanner | undefined;
7
+ templateLanguagePlugin: T;
8
+ isSupportedDocument: (document: TextDocument) => boolean;
9
+ vueCompilerOptions: VueCompilerOptions;
10
+ }): T;
@@ -0,0 +1,538 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const vue = require("@vue/language-core");
4
+ const shared_1 = require("@vue/shared");
5
+ const html = require("vscode-html-languageservice");
6
+ const vscode = require("vscode-languageserver-protocol");
7
+ const helpers_1 = require("../helpers");
8
+ const nameCasing_1 = require("../ideFeatures/nameCasing");
9
+ const types_1 = require("../types");
10
+ const data_1 = require("./data");
11
+ let builtInData;
12
+ let modelData;
13
+ function useVueTemplateLanguagePlugin(options) {
14
+ const plugin = (_context, modules) => {
15
+ const templatePlugin = options.templateLanguagePlugin(_context);
16
+ const triggerCharacters = [
17
+ ...templatePlugin.triggerCharacters ?? [],
18
+ '@', // vue event shorthand
19
+ ];
20
+ if (!_context?.typescript || !modules?.typescript)
21
+ return { triggerCharacters };
22
+ builtInData ??= (0, data_1.loadTemplateData)(_context.env.locale ?? 'en');
23
+ modelData ??= (0, data_1.loadModelModifiersData)(_context.env.locale ?? 'en');
24
+ // https://vuejs.org/api/built-in-directives.html#v-on
25
+ // https://vuejs.org/api/built-in-directives.html#v-bind
26
+ const eventModifiers = {};
27
+ const propModifiers = {};
28
+ const vOn = builtInData.globalAttributes?.find(x => x.name === 'v-on');
29
+ const vBind = builtInData.globalAttributes?.find(x => x.name === 'v-bind');
30
+ if (vOn) {
31
+ const markdown = (typeof vOn.description === 'string' ? vOn.description : vOn.description?.value) ?? '';
32
+ const modifiers = markdown
33
+ .split('\n- ')[4]
34
+ .split('\n').slice(2, -1);
35
+ for (let text of modifiers) {
36
+ text = text.substring(' - `.'.length);
37
+ const [name, disc] = text.split('` - ');
38
+ eventModifiers[name] = disc;
39
+ }
40
+ }
41
+ if (vBind) {
42
+ const markdown = (typeof vBind.description === 'string' ? vBind.description : vBind.description?.value) ?? '';
43
+ const modifiers = markdown
44
+ .split('\n- ')[4]
45
+ .split('\n').slice(2, -1);
46
+ for (let text of modifiers) {
47
+ text = text.substring(' - `.'.length);
48
+ const [name, disc] = text.split('` - ');
49
+ propModifiers[name] = disc;
50
+ }
51
+ }
52
+ const ts = modules.typescript;
53
+ const _ts = _context.typescript;
54
+ return {
55
+ ...templatePlugin,
56
+ triggerCharacters,
57
+ async provideCompletionItems(document, position, context, token) {
58
+ if (!options.isSupportedDocument(document))
59
+ return;
60
+ for (const [_, map] of _context.documents.getMapsByVirtualFileUri(document.uri)) {
61
+ const virtualFile = _context.documents.getSourceByUri(map.sourceFileDocument.uri)?.root;
62
+ if (virtualFile && virtualFile instanceof vue.VueFile) {
63
+ await provideHtmlData(map, virtualFile);
64
+ }
65
+ }
66
+ const htmlComplete = await templatePlugin.provideCompletionItems?.(document, position, context, token);
67
+ if (!htmlComplete)
68
+ return;
69
+ for (const [_, map] of _context.documents.getMapsByVirtualFileUri(document.uri)) {
70
+ const virtualFile = _context.documents.getSourceByUri(map.sourceFileDocument.uri)?.root;
71
+ if (virtualFile && virtualFile instanceof vue.VueFile) {
72
+ afterHtmlCompletion(htmlComplete, map, virtualFile);
73
+ }
74
+ }
75
+ return htmlComplete;
76
+ },
77
+ async provideInlayHints(document) {
78
+ if (!options.isSupportedDocument(document))
79
+ return;
80
+ const enabled = await _context.env.getConfiguration?.('vue.inlayHints.missingProps') ?? false;
81
+ if (!enabled)
82
+ return;
83
+ const result = [];
84
+ for (const [_, map] of _context.documents.getMapsByVirtualFileUri(document.uri)) {
85
+ const virtualFile = _context.documents.getSourceByUri(map.sourceFileDocument.uri)?.root;
86
+ const scanner = options.getScanner(document, templatePlugin);
87
+ if (virtualFile && virtualFile instanceof vue.VueFile && scanner) {
88
+ // visualize missing required props
89
+ const casing = await (0, nameCasing_1.getNameCasing)(ts, _context, map.sourceFileDocument.uri);
90
+ const nativeTags = (0, helpers_1.checkNativeTags)(ts, _ts.languageService, virtualFile.fileName);
91
+ const components = (0, helpers_1.checkComponentNames)(ts, _ts.languageService, virtualFile, nativeTags);
92
+ const componentProps = {};
93
+ let token;
94
+ let current;
95
+ while ((token = scanner.scan()) !== html.TokenType.EOS) {
96
+ if (token === html.TokenType.StartTag) {
97
+ const tagName = scanner.getTokenText();
98
+ const component = tagName.indexOf('.') >= 0
99
+ ? components.find(component => component === tagName.split('.')[0])
100
+ : components.find(component => component === tagName || (0, shared_1.hyphenate)(component) === tagName);
101
+ const checkTag = tagName.indexOf('.') >= 0 ? tagName : component;
102
+ if (checkTag) {
103
+ componentProps[checkTag] ??= (0, helpers_1.checkPropsOfTag)(ts, _ts.languageService, virtualFile, checkTag, nativeTags, true);
104
+ current = {
105
+ unburnedRequiredProps: [...componentProps[checkTag]],
106
+ labelOffset: scanner.getTokenOffset() + scanner.getTokenLength(),
107
+ insertOffset: scanner.getTokenOffset() + scanner.getTokenLength(),
108
+ };
109
+ }
110
+ }
111
+ else if (token === html.TokenType.AttributeName) {
112
+ if (current) {
113
+ let attrText = scanner.getTokenText();
114
+ if (attrText === 'v-bind') {
115
+ current.unburnedRequiredProps = [];
116
+ }
117
+ else {
118
+ // remove modifiers
119
+ if (attrText.indexOf('.') >= 0) {
120
+ attrText = attrText.split('.')[0];
121
+ }
122
+ // normalize
123
+ if (attrText.startsWith('v-bind:')) {
124
+ attrText = attrText.substring('v-bind:'.length);
125
+ }
126
+ else if (attrText.startsWith(':')) {
127
+ attrText = attrText.substring(':'.length);
128
+ }
129
+ else if (attrText.startsWith('v-model:')) {
130
+ attrText = attrText.substring('v-model:'.length);
131
+ }
132
+ else if (attrText === 'v-model') {
133
+ attrText = options.vueCompilerOptions.target >= 3 ? 'modelValue' : 'value'; // TODO: support for experimentalModelPropName?
134
+ }
135
+ else if (attrText.startsWith('@')) {
136
+ attrText = 'on-' + (0, shared_1.hyphenate)(attrText.substring('@'.length));
137
+ }
138
+ current.unburnedRequiredProps = current.unburnedRequiredProps.filter(propName => {
139
+ return attrText !== propName
140
+ && attrText !== (0, shared_1.hyphenate)(propName);
141
+ });
142
+ }
143
+ }
144
+ }
145
+ else if (token === html.TokenType.StartTagSelfClose || token === html.TokenType.StartTagClose) {
146
+ if (current) {
147
+ for (const requiredProp of current.unburnedRequiredProps) {
148
+ result.push({
149
+ label: `${requiredProp}!`,
150
+ paddingLeft: true,
151
+ position: document.positionAt(current.labelOffset),
152
+ kind: vscode.InlayHintKind.Parameter,
153
+ textEdits: [{
154
+ range: {
155
+ start: document.positionAt(current.insertOffset),
156
+ end: document.positionAt(current.insertOffset),
157
+ },
158
+ newText: ` :${casing.attr === types_1.AttrNameCasing.Kebab ? (0, shared_1.hyphenate)(requiredProp) : requiredProp}=`,
159
+ }],
160
+ });
161
+ }
162
+ current = undefined;
163
+ }
164
+ }
165
+ if (token === html.TokenType.AttributeName || token === html.TokenType.AttributeValue) {
166
+ if (current) {
167
+ current.insertOffset = scanner.getTokenOffset() + scanner.getTokenLength();
168
+ }
169
+ }
170
+ }
171
+ }
172
+ }
173
+ return result;
174
+ },
175
+ provideHover(document, position, token) {
176
+ if (!options.isSupportedDocument(document))
177
+ return;
178
+ if (_context.documents.isVirtualFileUri(document.uri))
179
+ templatePlugin.updateCustomData([]);
180
+ return templatePlugin.provideHover?.(document, position, token);
181
+ },
182
+ async provideDiagnostics(document, token) {
183
+ if (!options.isSupportedDocument(document))
184
+ return;
185
+ const originalResult = await templatePlugin.provideDiagnostics?.(document, token);
186
+ for (const [_, map] of _context.documents.getMapsByVirtualFileUri(document.uri)) {
187
+ const virtualFile = _context.documents.getSourceByUri(map.sourceFileDocument.uri)?.root;
188
+ if (!virtualFile || !(virtualFile instanceof vue.VueFile))
189
+ continue;
190
+ const templateErrors = [];
191
+ const sfcVueTemplateCompiled = virtualFile.compiledSFCTemplate;
192
+ if (sfcVueTemplateCompiled) {
193
+ for (const error of sfcVueTemplateCompiled.errors) {
194
+ onCompilerError(error, vscode.DiagnosticSeverity.Error);
195
+ }
196
+ for (const warning of sfcVueTemplateCompiled.warnings) {
197
+ onCompilerError(warning, vscode.DiagnosticSeverity.Warning);
198
+ }
199
+ function onCompilerError(error, severity) {
200
+ const templateHtmlRange = {
201
+ start: error.loc?.start.offset ?? 0,
202
+ end: error.loc?.end.offset ?? 0,
203
+ };
204
+ let errorMessage = error.message;
205
+ templateErrors.push({
206
+ range: {
207
+ start: document.positionAt(templateHtmlRange.start),
208
+ end: document.positionAt(templateHtmlRange.end),
209
+ },
210
+ severity,
211
+ code: error.code,
212
+ source: 'vue',
213
+ message: errorMessage,
214
+ });
215
+ }
216
+ }
217
+ return [
218
+ ...originalResult ?? [],
219
+ ...templateErrors,
220
+ ];
221
+ }
222
+ },
223
+ async provideDocumentSemanticTokens(document, range, legend, token) {
224
+ if (!options.isSupportedDocument(document))
225
+ return;
226
+ const result = await templatePlugin.provideDocumentSemanticTokens?.(document, range, legend, token) ?? [];
227
+ const scanner = options.getScanner(document, templatePlugin);
228
+ if (!scanner)
229
+ return;
230
+ for (const [_, map] of _context.documents.getMapsByVirtualFileUri(document.uri)) {
231
+ const virtualFile = _context.documents.getSourceByUri(map.sourceFileDocument.uri)?.root;
232
+ if (!virtualFile || !(virtualFile instanceof vue.VueFile))
233
+ continue;
234
+ const nativeTags = (0, helpers_1.checkNativeTags)(ts, _ts.languageService, virtualFile.fileName);
235
+ const templateScriptData = (0, helpers_1.checkComponentNames)(ts, _ts.languageService, virtualFile, nativeTags);
236
+ const components = new Set([
237
+ ...templateScriptData,
238
+ ...templateScriptData.map(shared_1.hyphenate),
239
+ ]);
240
+ const offsetRange = {
241
+ start: document.offsetAt(range.start),
242
+ end: document.offsetAt(range.end),
243
+ };
244
+ let token = scanner.scan();
245
+ while (token !== html.TokenType.EOS) {
246
+ const tokenOffset = scanner.getTokenOffset();
247
+ // TODO: fix source map perf and break in while condition
248
+ if (tokenOffset > offsetRange.end)
249
+ break;
250
+ if (tokenOffset >= offsetRange.start && (token === html.TokenType.StartTag || token === html.TokenType.EndTag)) {
251
+ const tokenText = scanner.getTokenText();
252
+ if (components.has(tokenText) || tokenText.indexOf('.') >= 0) {
253
+ const tokenLength = scanner.getTokenLength();
254
+ const tokenPosition = document.positionAt(tokenOffset);
255
+ if (components.has(tokenText)) {
256
+ let tokenType = legend.tokenTypes.indexOf('component');
257
+ if (tokenType === -1) {
258
+ tokenType = legend.tokenTypes.indexOf('class');
259
+ }
260
+ result.push([tokenPosition.line, tokenPosition.character, tokenLength, tokenType, 0]);
261
+ }
262
+ }
263
+ }
264
+ token = scanner.scan();
265
+ }
266
+ }
267
+ return result;
268
+ },
269
+ };
270
+ async function provideHtmlData(map, vueSourceFile) {
271
+ const casing = await (0, nameCasing_1.getNameCasing)(ts, _context, map.sourceFileDocument.uri);
272
+ if (builtInData.tags) {
273
+ for (const tag of builtInData.tags) {
274
+ if (tag.name === 'slot')
275
+ continue;
276
+ if (tag.name === 'component')
277
+ continue;
278
+ if (tag.name === 'template')
279
+ continue;
280
+ if (casing.tag === types_1.TagNameCasing.Kebab) {
281
+ tag.name = (0, shared_1.hyphenate)(tag.name);
282
+ }
283
+ else {
284
+ tag.name = (0, shared_1.camelize)((0, shared_1.capitalize)(tag.name));
285
+ }
286
+ }
287
+ }
288
+ const nativeTags = (0, helpers_1.checkNativeTags)(ts, _ts.languageService, vueSourceFile.fileName);
289
+ templatePlugin.updateCustomData([
290
+ html.newHTMLDataProvider('vue-template-built-in', builtInData),
291
+ {
292
+ getId: () => 'vue-template',
293
+ isApplicable: () => true,
294
+ provideTags: () => {
295
+ const components = (0, helpers_1.checkComponentNames)(ts, _ts.languageService, vueSourceFile, nativeTags)
296
+ .filter(name => name !== 'Transition'
297
+ && name !== 'TransitionGroup'
298
+ && name !== 'KeepAlive'
299
+ && name !== 'Suspense'
300
+ && name !== 'Teleport');
301
+ const scriptSetupRanges = vueSourceFile.sfc.scriptSetupAst ? vue.parseScriptSetupRanges(ts, vueSourceFile.sfc.scriptSetupAst, options.vueCompilerOptions) : undefined;
302
+ const names = new Set();
303
+ const tags = [];
304
+ for (const tag of components) {
305
+ if (casing.tag === types_1.TagNameCasing.Kebab) {
306
+ names.add((0, shared_1.hyphenate)(tag));
307
+ }
308
+ else if (casing.tag === types_1.TagNameCasing.Pascal) {
309
+ names.add(tag);
310
+ }
311
+ }
312
+ for (const binding of scriptSetupRanges?.bindings ?? []) {
313
+ const name = vueSourceFile.sfc.scriptSetup.content.substring(binding.start, binding.end);
314
+ if (casing.tag === types_1.TagNameCasing.Kebab) {
315
+ names.add((0, shared_1.hyphenate)(name));
316
+ }
317
+ else if (casing.tag === types_1.TagNameCasing.Pascal) {
318
+ names.add(name);
319
+ }
320
+ }
321
+ for (const name of names) {
322
+ tags.push({
323
+ name: name,
324
+ attributes: [],
325
+ });
326
+ }
327
+ return tags;
328
+ },
329
+ provideAttributes: (tag) => {
330
+ const attrs = (0, helpers_1.getElementAttrs)(ts, _ts.languageService, vueSourceFile.fileName, tag);
331
+ const props = new Set((0, helpers_1.checkPropsOfTag)(ts, _ts.languageService, vueSourceFile, tag, nativeTags));
332
+ const events = (0, helpers_1.checkEventsOfTag)(ts, _ts.languageService, vueSourceFile, tag, nativeTags);
333
+ const attributes = [];
334
+ for (const prop of [...props, ...attrs]) {
335
+ const isGlobal = !props.has(prop);
336
+ const name = casing.attr === types_1.AttrNameCasing.Camel ? prop : (0, shared_1.hyphenate)(prop);
337
+ if ((0, shared_1.hyphenate)(name).startsWith('on-')) {
338
+ const propNameBase = name.startsWith('on-')
339
+ ? name.slice('on-'.length)
340
+ : (name['on'.length].toLowerCase() + name.slice('onX'.length));
341
+ const propKey = createInternalItemId('componentEvent', [isGlobal ? '*' : tag, propNameBase]);
342
+ attributes.push({
343
+ name: 'v-on:' + propNameBase,
344
+ description: propKey,
345
+ }, {
346
+ name: '@' + propNameBase,
347
+ description: propKey,
348
+ });
349
+ }
350
+ {
351
+ const propName = name;
352
+ const propKey = createInternalItemId('componentProp', [isGlobal ? '*' : tag, propName]);
353
+ attributes.push({
354
+ name: propName,
355
+ description: propKey,
356
+ }, {
357
+ name: ':' + propName,
358
+ description: propKey,
359
+ }, {
360
+ name: 'v-bind:' + propName,
361
+ description: propKey,
362
+ });
363
+ }
364
+ }
365
+ for (const event of events) {
366
+ const name = casing.attr === types_1.AttrNameCasing.Camel ? event : (0, shared_1.hyphenate)(event);
367
+ const propKey = createInternalItemId('componentEvent', [tag, name]);
368
+ attributes.push({
369
+ name: 'v-on:' + name,
370
+ description: propKey,
371
+ });
372
+ attributes.push({
373
+ name: '@' + name,
374
+ description: propKey,
375
+ });
376
+ }
377
+ const models = [];
378
+ for (const prop of [...props, ...attrs]) {
379
+ if (prop.startsWith('onUpdate:')) {
380
+ const isGlobal = !props.has(prop);
381
+ models.push([isGlobal, prop.substring('onUpdate:'.length)]);
382
+ }
383
+ }
384
+ for (const event of events) {
385
+ if (event.startsWith('update:')) {
386
+ models.push([false, event.substring('update:'.length)]);
387
+ }
388
+ }
389
+ for (const [isGlobal, model] of models) {
390
+ const name = casing.attr === types_1.AttrNameCasing.Camel ? model : (0, shared_1.hyphenate)(model);
391
+ const propKey = createInternalItemId('componentProp', [isGlobal ? '*' : tag, name]);
392
+ attributes.push({
393
+ name: 'v-model:' + name,
394
+ description: propKey,
395
+ });
396
+ if (model === 'modelValue') {
397
+ attributes.push({
398
+ name: 'v-model',
399
+ description: propKey,
400
+ });
401
+ }
402
+ }
403
+ return attributes;
404
+ },
405
+ provideValues: () => [],
406
+ },
407
+ ]);
408
+ }
409
+ function afterHtmlCompletion(completionList, map, vueSourceFile) {
410
+ const replacement = getReplacement(completionList, map.sourceFileDocument);
411
+ const nativeTags = (0, helpers_1.checkNativeTags)(ts, _ts.languageService, vueSourceFile.fileName);
412
+ const componentNames = new Set((0, helpers_1.checkComponentNames)(ts, _ts.languageService, vueSourceFile, nativeTags).map(shared_1.hyphenate));
413
+ if (replacement) {
414
+ const isEvent = replacement.text.startsWith('v-on:') || replacement.text.startsWith('@');
415
+ const isProp = replacement.text.startsWith('v-bind:') || replacement.text.startsWith(':');
416
+ const isModel = replacement.text.startsWith('v-model:') || replacement.text.split('.')[0] === 'v-model';
417
+ const hasModifier = replacement.text.includes('.');
418
+ const validModifiers = isEvent ? eventModifiers
419
+ : isProp ? propModifiers
420
+ : undefined;
421
+ const modifiers = replacement.text.split('.').slice(1);
422
+ const textWithoutModifier = replacement.text.split('.')[0];
423
+ if (validModifiers && hasModifier) {
424
+ for (const modifier in validModifiers) {
425
+ if (modifiers.includes(modifier))
426
+ continue;
427
+ const modifierDes = validModifiers[modifier];
428
+ const insertText = textWithoutModifier + modifiers.slice(0, -1).map(m => '.' + m).join('') + '.' + modifier;
429
+ const newItem = {
430
+ label: modifier,
431
+ filterText: insertText,
432
+ documentation: {
433
+ kind: 'markdown',
434
+ value: modifierDes,
435
+ },
436
+ textEdit: {
437
+ range: replacement.textEdit.range,
438
+ newText: insertText,
439
+ },
440
+ kind: vscode.CompletionItemKind.EnumMember,
441
+ };
442
+ completionList.items.push(newItem);
443
+ }
444
+ }
445
+ else if (hasModifier && isModel) {
446
+ for (const modifier of modelData.globalAttributes ?? []) {
447
+ if (modifiers.includes(modifier.name))
448
+ continue;
449
+ const insertText = textWithoutModifier + modifiers.slice(0, -1).map(m => '.' + m).join('') + '.' + modifier.name;
450
+ const newItem = {
451
+ label: modifier.name,
452
+ filterText: insertText,
453
+ documentation: {
454
+ kind: 'markdown',
455
+ value: (typeof modifier.description === 'object' ? modifier.description.value : modifier.description)
456
+ + '\n\n' + modifier.references?.map(ref => `[${ref.name}](${ref.url})`).join(' | '),
457
+ },
458
+ textEdit: {
459
+ range: replacement.textEdit.range,
460
+ newText: insertText,
461
+ },
462
+ kind: vscode.CompletionItemKind.EnumMember,
463
+ };
464
+ completionList.items.push(newItem);
465
+ }
466
+ }
467
+ }
468
+ for (const item of completionList.items) {
469
+ const itemIdKey = typeof item.documentation === 'string' ? item.documentation : item.documentation?.value;
470
+ const itemId = itemIdKey ? readInternalItemId(itemIdKey) : undefined;
471
+ if (itemId) {
472
+ item.documentation = undefined;
473
+ }
474
+ if (itemIdKey && itemId) {
475
+ if (itemId.type === 'componentProp' || itemId.type === 'componentEvent') {
476
+ const [componentName] = itemId.args;
477
+ if (componentName !== '*') {
478
+ item.sortText = '\u0000' + (item.sortText ?? item.label);
479
+ }
480
+ if (itemId.type === 'componentProp') {
481
+ if (componentName !== '*') {
482
+ item.kind = vscode.CompletionItemKind.Field;
483
+ }
484
+ }
485
+ else {
486
+ item.kind = componentName !== '*' ? vscode.CompletionItemKind.Function : vscode.CompletionItemKind.Event;
487
+ }
488
+ }
489
+ else if (item.label === 'v-if'
490
+ || item.label === 'v-else-if'
491
+ || item.label === 'v-else'
492
+ || item.label === 'v-for') {
493
+ item.kind = vscode.CompletionItemKind.Method;
494
+ item.sortText = '\u0003' + (item.sortText ?? item.label);
495
+ }
496
+ else if (item.label.startsWith('v-')) {
497
+ item.kind = vscode.CompletionItemKind.Function;
498
+ item.sortText = '\u0002' + (item.sortText ?? item.label);
499
+ }
500
+ else {
501
+ item.sortText = '\u0001' + (item.sortText ?? item.label);
502
+ }
503
+ }
504
+ else if (item.kind === vscode.CompletionItemKind.Property && componentNames.has((0, shared_1.hyphenate)(item.label))) {
505
+ item.kind = vscode.CompletionItemKind.Variable;
506
+ item.sortText = '\u0000' + (item.sortText ?? item.label);
507
+ }
508
+ }
509
+ templatePlugin.updateCustomData([]);
510
+ }
511
+ };
512
+ return plugin;
513
+ }
514
+ exports.default = useVueTemplateLanguagePlugin;
515
+ function createInternalItemId(type, args) {
516
+ return '__VLS_::' + type + '::' + args.join(',');
517
+ }
518
+ function readInternalItemId(key) {
519
+ if (key.startsWith('__VLS_::')) {
520
+ const strs = key.split('::');
521
+ return {
522
+ type: strs[1],
523
+ args: strs[2].split(','),
524
+ };
525
+ }
526
+ }
527
+ function getReplacement(list, doc) {
528
+ for (const item of list.items) {
529
+ if (item.textEdit && 'range' in item.textEdit) {
530
+ return {
531
+ item: item,
532
+ textEdit: item.textEdit,
533
+ text: doc.getText(item.textEdit.range)
534
+ };
535
+ }
536
+ }
537
+ }
538
+ //# sourceMappingURL=vue-template.js.map
@@ -0,0 +1,3 @@
1
+ import { Service } from '@volar/language-service';
2
+ declare const _default: () => Service;
3
+ export default _default;
@@ -0,0 +1,59 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const language_service_1 = require("@volar/language-service");
4
+ const vue = require("@vue/language-core");
5
+ const plugin = (context, modules) => {
6
+ if (!modules?.typescript)
7
+ return {};
8
+ if (!context?.typescript)
9
+ return {};
10
+ const ts = modules.typescript;
11
+ const _ts = context.typescript;
12
+ return {
13
+ provideInlayHints(document, range) {
14
+ return worker(document.uri, (vueFile) => {
15
+ const hoverOffsets = [];
16
+ const inlayHints = [];
17
+ for (const pointer of document.getText(range).matchAll(/<!--\s*\^\?\s*-->/g)) {
18
+ const offset = pointer.index + pointer[0].indexOf('^?') + document.offsetAt(range.start);
19
+ const position = document.positionAt(offset);
20
+ hoverOffsets.push([position, document.offsetAt({
21
+ line: position.line - 1,
22
+ character: position.character,
23
+ })]);
24
+ }
25
+ (0, language_service_1.forEachEmbeddedFile)(vueFile, (embedded) => {
26
+ if (embedded.kind === language_service_1.FileKind.TypeScriptHostFile) {
27
+ for (const [_, map] of context.documents.getMapsByVirtualFileName(embedded.fileName)) {
28
+ for (const [pointerPosition, hoverOffset] of hoverOffsets) {
29
+ for (const [tsOffset, mapping] of map.map.toGeneratedOffsets(hoverOffset)) {
30
+ if (mapping.data.hover) {
31
+ const quickInfo = _ts.languageService.getQuickInfoAtPosition(embedded.fileName, tsOffset);
32
+ if (quickInfo) {
33
+ inlayHints.push({
34
+ position: { line: pointerPosition.line, character: pointerPosition.character + 2 },
35
+ label: ts.displayPartsToString(quickInfo.displayParts),
36
+ paddingLeft: true,
37
+ paddingRight: false,
38
+ });
39
+ }
40
+ break;
41
+ }
42
+ }
43
+ }
44
+ }
45
+ }
46
+ });
47
+ return inlayHints;
48
+ });
49
+ },
50
+ };
51
+ function worker(uri, callback) {
52
+ const [virtualFile] = context.documents.getVirtualFileByUri(uri);
53
+ if (!(virtualFile instanceof vue.VueFile))
54
+ return;
55
+ return callback(virtualFile);
56
+ }
57
+ };
58
+ exports.default = () => plugin;
59
+ //# sourceMappingURL=vue-twoslash-queries.js.map