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