Skip to content

Commit 62f3c89

Browse files
refactor: vue language plugin (#1395)
1 parent 4146340 commit 62f3c89

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+1137
-1269
lines changed

packages/typescript-language-service/src/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,8 @@ export { getSemanticTokenLegend } from './services/semanticTokens';
3333
import * as path from 'path';
3434

3535
export interface Settings {
36-
getFormatOptions?(document: TextDocument, options?: vscode.FormattingOptions): Promise<ts.FormatCodeSettings>;
37-
getPreferences?(document: TextDocument): Promise<ts.UserPreferences>;
36+
getFormatOptions?(uri: string, options?: vscode.FormattingOptions): Promise<ts.FormatCodeSettings>;
37+
getPreferences?(uri: string): Promise<ts.UserPreferences>;
3838
}
3939

4040
export function createLanguageService(

packages/typescript-language-service/src/services/codeAction.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,8 @@ export function register(
4040
if (!document) return;
4141

4242
const [formatOptions, preferences] = await Promise.all([
43-
settings.getFormatOptions?.(document) ?? {},
44-
settings.getPreferences?.(document) ?? {},
43+
settings.getFormatOptions?.(document.uri) ?? {},
44+
settings.getPreferences?.(document.uri) ?? {},
4545
]);
4646

4747
const fileName = shared.uriToFsPath(document.uri);

packages/typescript-language-service/src/services/codeActionResolve.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ export function register(
1515
const data: Data = codeAction.data;
1616
const document = getTextDocument(data.uri);
1717
const [formatOptions, preferences] = document ? await Promise.all([
18-
settings.getFormatOptions?.(document) ?? {},
19-
settings.getPreferences?.(document) ?? {},
18+
settings.getFormatOptions?.(document.uri) ?? {},
19+
settings.getPreferences?.(document.uri) ?? {},
2020
]) : [{}, {}];
2121

2222
if (data?.type === 'fixAll') {

packages/typescript-language-service/src/services/completions/basic.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ export function register(
2727
if (!document)
2828
return;
2929

30-
const preferences = await settings.getPreferences?.(document) ?? {};
30+
const preferences = await settings.getPreferences?.(document.uri) ?? {};
3131
const fileName = shared.uriToFsPath(document.uri);
3232
const offset = document.offsetAt(position);
3333

packages/typescript-language-service/src/services/completions/resolve.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,8 @@ export function register(
3030
}
3131

3232
const [formatOptions, preferences] = document ? await Promise.all([
33-
settings.getFormatOptions?.(document) ?? {},
34-
settings.getPreferences?.(document) ?? {},
33+
settings.getFormatOptions?.(document.uri) ?? {},
34+
settings.getPreferences?.(document.uri) ?? {},
3535
]) : [{}, {}];
3636

3737
let details: ts.CompletionEntryDetails | undefined;

packages/typescript-language-service/src/services/fileRename.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ export function register(
1414

1515
const document = getTextDocument(oldUri);
1616
const [formatOptions, preferences] = document ? await Promise.all([
17-
settings.getFormatOptions?.(document) ?? {},
18-
settings.getPreferences?.(document) ?? {},
17+
settings.getFormatOptions?.(document.uri) ?? {},
18+
settings.getPreferences?.(document.uri) ?? {},
1919
]) : [{}, {}];
2020

2121
const fileToRename = shared.uriToFsPath(oldUri);

packages/typescript-language-service/src/services/formatting.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ export function register(
1616
if (!document) return [];
1717

1818
const fileName = shared.uriToFsPath(document.uri);
19-
const tsOptions = await settings.getFormatOptions?.(document, options) ?? options;
19+
const tsOptions = await settings.getFormatOptions?.(document.uri, options) ?? options;
2020

2121
let scriptEdits: ReturnType<typeof languageService.getFormattingEditsForRange> | undefined;
2222
try {
@@ -46,7 +46,7 @@ export function register(
4646
if (!document) return [];
4747

4848
const fileName = shared.uriToFsPath(document.uri);
49-
const tsOptions = await settings.getFormatOptions?.(document, options) ?? options;
49+
const tsOptions = await settings.getFormatOptions?.(document.uri, options) ?? options;
5050

5151
let scriptEdits: ReturnType<typeof languageService.getFormattingEditsForRange> | undefined;
5252
try {

packages/typescript-language-service/src/services/inlayHints.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ export function register(
1515
const document = getTextDocument(uri);
1616
if (!document) return;
1717

18-
const preferences = await settings.getPreferences?.(document) ?? {};
18+
const preferences = await settings.getPreferences?.(document.uri) ?? {};
1919
const fileName = shared.uriToFsPath(document.uri);
2020
const start = document.offsetAt(range.start);
2121
const end = document.offsetAt(range.end);

packages/typescript-language-service/src/services/rename.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,13 @@ export function register(
2525

2626
if (renameInfo.fileToRename) {
2727
const [formatOptions, preferences] = await Promise.all([
28-
settings.getFormatOptions?.(document) ?? {},
29-
settings.getPreferences?.(document) ?? {},
28+
settings.getFormatOptions?.(document.uri) ?? {},
29+
settings.getPreferences?.(document.uri) ?? {},
3030
]);
3131
return renameFile(renameInfo.fileToRename, newName, formatOptions, preferences);
3232
}
3333

34-
const { providePrefixAndSuffixTextForRename } = await settings.getPreferences?.(document) ?? { providePrefixAndSuffixTextForRename: true };
34+
const { providePrefixAndSuffixTextForRename } = await settings.getPreferences?.(document.uri) ?? { providePrefixAndSuffixTextForRename: true };
3535
const entries = languageService.findRenameLocations(fileName, offset, false, false, providePrefixAndSuffixTextForRename);
3636
if (!entries)
3737
return;

packages/vue-code-gen/src/generators/script.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ export function generate(
6666
},
6767
SourceMaps.Mode.Expand,
6868
{
69-
vueTag: 'sfc',
69+
vueTag: undefined,
7070
capabilities: {},
7171
},
7272
);
@@ -98,7 +98,7 @@ export function generate(
9898
// fix https://github.com/johnsoncodehk/volar/issues/435
9999
codeGen.addMapping2({
100100
data: {
101-
vueTag: 'sfc',
101+
vueTag: undefined,
102102
capabilities: {},
103103
},
104104
mode: SourceMaps.Mode.Expand,

packages/vue-code-gen/src/generators/template.ts

Lines changed: 54 additions & 118 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,6 @@ export function generate(
7575
const tsCodeGen = new CodeGen<EmbeddedFileMappingData>();
7676
const tsFormatCodeGen = new CodeGen<EmbeddedFileMappingData>();
7777
const cssCodeGen = new CodeGen<EmbeddedFileMappingData>();
78-
const attrNames = new Set<string>();
7978
const slots = new Map<string, {
8079
varName: string,
8180
loc: SourceMaps.Range,
@@ -85,16 +84,7 @@ export function generate(
8584
loc: SourceMaps.Range,
8685
}>();
8786
const cssScopedClassesSet = new Set(cssScopedClasses);
88-
const tags: Record<string, {
89-
offsets: number[],
90-
props: Record<string, {
91-
argName: string,
92-
offsets: number[],
93-
}>,
94-
events: Record<string, {
95-
offsets: number[],
96-
}>,
97-
}> = {};
87+
const tagOffsetsMap: Record<string, number[]> = {};
9888
const tagResolves: Record<string, {
9989
component: string,
10090
emit: string,
@@ -109,13 +99,24 @@ export function generate(
10999

110100
let elementIndex = 0;
111101

112-
for (const childNode of templateAst.children) {
113-
collectTags(childNode);
114-
}
115-
for (const tagName in tags) {
102+
walkElementNodes(templateAst, node => {
103+
104+
if (!tagOffsetsMap[node.tag]) {
105+
tagOffsetsMap[node.tag] = [];
106+
}
107+
108+
const offsets = tagOffsetsMap[node.tag];
109+
110+
offsets.push(node.loc.start.offset + node.loc.source.indexOf(node.tag)); // start tag
111+
if (!node.isSelfClosing && sourceLang === 'html') {
112+
offsets.push(node.loc.start.offset + node.loc.source.lastIndexOf(node.tag)); // end tag
113+
}
114+
});
115+
116+
for (const tagName in tagOffsetsMap) {
116117

117-
const tag = tags[tagName];
118-
const tagRanges = tag.offsets.map(offset => ({ start: offset, end: offset + tagName.length }));
118+
const tagOffsets = tagOffsetsMap[tagName];
119+
const tagRanges = tagOffsets.map(offset => ({ start: offset, end: offset + tagName.length }));
119120
const isNamespacedTag = tagName.indexOf('.') >= 0;
120121

121122
const var_correctTagName = `__VLS_${elementIndex++}`;
@@ -227,7 +228,7 @@ export function generate(
227228
tagResolves[tagName] = {
228229
component: var_rawComponent,
229230
emit: var_emit,
230-
offsets: tag.offsets.map(offset => htmlToTemplate(offset, offset)?.start).filter(notEmpty),
231+
offsets: tagOffsets.map(offset => htmlToTemplate(offset, offset)?.start).filter(notEmpty),
231232
};
232233
}
233234

@@ -284,103 +285,10 @@ export function generate(
284285
codeGen: tsCodeGen,
285286
formatCodeGen: tsFormatCodeGen,
286287
cssCodeGen: cssCodeGen,
287-
tagNames: tagResolves,
288-
attrNames,
288+
tagNames: tagOffsetsMap,
289289
identifiers,
290290
};
291291

292-
function collectTags(node: CompilerDOM.TemplateChildNode) {
293-
if (node.type === CompilerDOM.NodeTypes.ELEMENT) {
294-
const patchForNode = getPatchForSlotNode(node);
295-
if (patchForNode) {
296-
collectTags(patchForNode);
297-
return;
298-
}
299-
if (!tags[node.tag]) {
300-
tags[node.tag] = {
301-
offsets: [],
302-
props: {},
303-
events: {},
304-
};
305-
}
306-
const resolvedTag = tags[node.tag];
307-
resolvedTag.offsets.push(node.loc.start.offset + node.loc.source.indexOf(node.tag)); // start tag
308-
if (!node.isSelfClosing && sourceLang === 'html') {
309-
resolvedTag.offsets.push(node.loc.start.offset + node.loc.source.lastIndexOf(node.tag)); // end tag
310-
}
311-
for (const prop of node.props) {
312-
if (
313-
prop.type === CompilerDOM.NodeTypes.DIRECTIVE
314-
&& prop.arg?.type === CompilerDOM.NodeTypes.SIMPLE_EXPRESSION
315-
&& prop.arg.isStatic
316-
) {
317-
318-
let propName = prop.arg.constType === CompilerDOM.ConstantTypes.CAN_STRINGIFY
319-
? prop.arg.content
320-
: prop.arg.loc.source;
321-
322-
if (prop.modifiers.some(m => m === 'prop' || m === 'attr')) {
323-
propName = propName.substring(1);
324-
}
325-
326-
if (prop.name === 'bind' || prop.name === 'model') {
327-
addProp(propName, propName, prop.arg.loc.start.offset);
328-
}
329-
else if (prop.name === 'on') {
330-
addEvent(propName, prop.arg.loc.start.offset);
331-
}
332-
}
333-
else if (
334-
prop.type === CompilerDOM.NodeTypes.DIRECTIVE
335-
&& !prop.arg
336-
&& prop.name === 'model'
337-
) {
338-
addProp(getModelValuePropName(node, vueVersion), 'v-model', prop.loc.start.offset);
339-
}
340-
else if (
341-
prop.type === CompilerDOM.NodeTypes.ATTRIBUTE
342-
) {
343-
addProp(prop.name, prop.name, prop.loc.start.offset);
344-
}
345-
}
346-
for (const childNode of node.children) {
347-
collectTags(childNode);
348-
}
349-
350-
function addProp(propName: string, argName: string, offset: number) {
351-
if (!resolvedTag.props[propName]) {
352-
resolvedTag.props[propName] = {
353-
argName,
354-
offsets: [],
355-
};
356-
}
357-
resolvedTag.props[propName].offsets.push(offset);
358-
}
359-
function addEvent(eventName: string, offset: number) {
360-
if (!resolvedTag.events[eventName]) {
361-
resolvedTag.events[eventName] = {
362-
offsets: [],
363-
};
364-
}
365-
resolvedTag.events[eventName].offsets.push(offset);
366-
}
367-
}
368-
else if (node.type === CompilerDOM.NodeTypes.IF) {
369-
// v-if / v-else-if / v-else
370-
for (let i = 0; i < node.branches.length; i++) {
371-
const branch = node.branches[i];
372-
for (const childNode of branch.children) {
373-
collectTags(childNode);
374-
}
375-
}
376-
}
377-
else if (node.type === CompilerDOM.NodeTypes.FOR) {
378-
// v-for
379-
for (const childNode of node.children) {
380-
collectTags(childNode);
381-
}
382-
}
383-
}
384292
function visitNode(node: CompilerDOM.TemplateChildNode, parentEl: CompilerDOM.ElementNode | undefined): void {
385293
if (node.type === CompilerDOM.NodeTypes.ELEMENT) {
386294
visitElementNode(node, parentEl);
@@ -909,10 +817,6 @@ export function generate(
909817
continue;
910818
}
911819

912-
if (prop.arg?.type === CompilerDOM.NodeTypes.SIMPLE_EXPRESSION) {
913-
attrNames.add(prop.arg.content);
914-
}
915-
916820
// camelize name
917821
writePropStart(isStatic);
918822
const diagStart = tsCodeGen.getText().length;
@@ -1053,8 +957,6 @@ export function generate(
1053957
continue;
1054958
}
1055959

1056-
attrNames.add(prop.name);
1057-
1058960
// camelize name
1059961
writePropStart(true);
1060962
const diagStart = tsCodeGen.getText().length;
@@ -1973,6 +1875,40 @@ export function generate(
19731875
}
19741876
};
19751877

1878+
export function walkElementNodes(node: CompilerDOM.RootNode | CompilerDOM.TemplateChildNode, cb: (node: CompilerDOM.ElementNode) => void) {
1879+
if (node.type === CompilerDOM.NodeTypes.ROOT) {
1880+
for (const child of node.children) {
1881+
walkElementNodes(child, cb);
1882+
}
1883+
}
1884+
else if (node.type === CompilerDOM.NodeTypes.ELEMENT) {
1885+
const patchForNode = getPatchForSlotNode(node);
1886+
if (patchForNode) {
1887+
walkElementNodes(patchForNode, cb);
1888+
return;
1889+
}
1890+
cb(node);
1891+
for (const child of node.children) {
1892+
walkElementNodes(child, cb);
1893+
}
1894+
}
1895+
else if (node.type === CompilerDOM.NodeTypes.IF) {
1896+
// v-if / v-else-if / v-else
1897+
for (let i = 0; i < node.branches.length; i++) {
1898+
const branch = node.branches[i];
1899+
for (const childNode of branch.children) {
1900+
walkElementNodes(childNode, cb);
1901+
}
1902+
}
1903+
}
1904+
else if (node.type === CompilerDOM.NodeTypes.FOR) {
1905+
// v-for
1906+
for (const child of node.children) {
1907+
walkElementNodes(child, cb);
1908+
}
1909+
}
1910+
}
1911+
19761912
function toUnicode(str: string) {
19771913
return str.split('').map(value => {
19781914
var temp = value.charCodeAt(0).toString(16).padStart(4, '0');

packages/vue-code-gen/src/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
import { generate as generateScript, getSlotsPropertyName, getVueLibraryName } from './generators/script';
2-
import { generate as generateTemplateScript, isIntrinsicElement } from './generators/template';
2+
import { generate as generateTemplateScript, isIntrinsicElement, walkElementNodes } from './generators/template';
33
import { parseScriptRanges } from './parsers/scriptRanges';
44
import { parseScriptSetupRanges } from './parsers/scriptSetupRanges';
55
import * as CompilerDOM from '@vue/compiler-dom';
66
import * as CompilerVue2 from './vue2TemplateCompiler';
77

88
export * from './types';
99
export * from '@vue/compiler-dom';
10-
export { isIntrinsicElement, getSlotsPropertyName, getVueLibraryName };
10+
export { isIntrinsicElement, getSlotsPropertyName, getVueLibraryName, walkElementNodes };
1111

1212
/**
1313
* @param templateAst Use `require('@vue/compiler-dom').compile` or `require('@volar/vue-code-gen').compileTemplate`, provide to resolve variables unused in script setup

packages/vue-code-gen/src/types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
export interface EmbeddedFileMappingData {
2-
vueTag: 'sfc' | 'template' | 'script' | 'scriptSetup' | 'scriptSrc' | 'style' | 'customBlock' | undefined,
2+
vueTag: 'template' | 'script' | 'scriptSetup' | 'scriptSrc' | 'style' | 'customBlock' | undefined,
33
vueTagIndex?: number,
44
normalizeNewName?: (newName: string) => string,
55
applyNewName?: (oldName: string, newName: string) => string,

packages/vue-language-service/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
"@volar/vue-language-service-types": "0.36.1",
2929
"@volar/vue-typescript": "0.36.1",
3030
"@vscode/emmet-helper": "^2.8.4",
31+
"@vue/compiler-dom": "^3.2.36",
3132
"@vue/reactivity": "^3.2.36",
3233
"@vue/shared": "^3.2.36",
3334
"semver": "^7.3.7",

0 commit comments

Comments
 (0)