Skip to content

Commit

Permalink
refactor: vue language plugin (vuejs#1395)
Browse files Browse the repository at this point in the history
  • Loading branch information
johnsoncodehk committed Jun 5, 2022
1 parent 4146340 commit 62f3c89
Show file tree
Hide file tree
Showing 42 changed files with 1,137 additions and 1,269 deletions.
4 changes: 2 additions & 2 deletions packages/typescript-language-service/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@ export { getSemanticTokenLegend } from './services/semanticTokens';
import * as path from 'path';

export interface Settings {
getFormatOptions?(document: TextDocument, options?: vscode.FormattingOptions): Promise<ts.FormatCodeSettings>;
getPreferences?(document: TextDocument): Promise<ts.UserPreferences>;
getFormatOptions?(uri: string, options?: vscode.FormattingOptions): Promise<ts.FormatCodeSettings>;
getPreferences?(uri: string): Promise<ts.UserPreferences>;
}

export function createLanguageService(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@ export function register(
if (!document) return;

const [formatOptions, preferences] = await Promise.all([
settings.getFormatOptions?.(document) ?? {},
settings.getPreferences?.(document) ?? {},
settings.getFormatOptions?.(document.uri) ?? {},
settings.getPreferences?.(document.uri) ?? {},
]);

const fileName = shared.uriToFsPath(document.uri);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ export function register(
const data: Data = codeAction.data;
const document = getTextDocument(data.uri);
const [formatOptions, preferences] = document ? await Promise.all([
settings.getFormatOptions?.(document) ?? {},
settings.getPreferences?.(document) ?? {},
settings.getFormatOptions?.(document.uri) ?? {},
settings.getPreferences?.(document.uri) ?? {},
]) : [{}, {}];

if (data?.type === 'fixAll') {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export function register(
if (!document)
return;

const preferences = await settings.getPreferences?.(document) ?? {};
const preferences = await settings.getPreferences?.(document.uri) ?? {};
const fileName = shared.uriToFsPath(document.uri);
const offset = document.offsetAt(position);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ export function register(
}

const [formatOptions, preferences] = document ? await Promise.all([
settings.getFormatOptions?.(document) ?? {},
settings.getPreferences?.(document) ?? {},
settings.getFormatOptions?.(document.uri) ?? {},
settings.getPreferences?.(document.uri) ?? {},
]) : [{}, {}];

let details: ts.CompletionEntryDetails | undefined;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ export function register(

const document = getTextDocument(oldUri);
const [formatOptions, preferences] = document ? await Promise.all([
settings.getFormatOptions?.(document) ?? {},
settings.getPreferences?.(document) ?? {},
settings.getFormatOptions?.(document.uri) ?? {},
settings.getPreferences?.(document.uri) ?? {},
]) : [{}, {}];

const fileToRename = shared.uriToFsPath(oldUri);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export function register(
if (!document) return [];

const fileName = shared.uriToFsPath(document.uri);
const tsOptions = await settings.getFormatOptions?.(document, options) ?? options;
const tsOptions = await settings.getFormatOptions?.(document.uri, options) ?? options;

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

const fileName = shared.uriToFsPath(document.uri);
const tsOptions = await settings.getFormatOptions?.(document, options) ?? options;
const tsOptions = await settings.getFormatOptions?.(document.uri, options) ?? options;

let scriptEdits: ReturnType<typeof languageService.getFormattingEditsForRange> | undefined;
try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export function register(
const document = getTextDocument(uri);
if (!document) return;

const preferences = await settings.getPreferences?.(document) ?? {};
const preferences = await settings.getPreferences?.(document.uri) ?? {};
const fileName = shared.uriToFsPath(document.uri);
const start = document.offsetAt(range.start);
const end = document.offsetAt(range.end);
Expand Down
6 changes: 3 additions & 3 deletions packages/typescript-language-service/src/services/rename.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,13 @@ export function register(

if (renameInfo.fileToRename) {
const [formatOptions, preferences] = await Promise.all([
settings.getFormatOptions?.(document) ?? {},
settings.getPreferences?.(document) ?? {},
settings.getFormatOptions?.(document.uri) ?? {},
settings.getPreferences?.(document.uri) ?? {},
]);
return renameFile(renameInfo.fileToRename, newName, formatOptions, preferences);
}

const { providePrefixAndSuffixTextForRename } = await settings.getPreferences?.(document) ?? { providePrefixAndSuffixTextForRename: true };
const { providePrefixAndSuffixTextForRename } = await settings.getPreferences?.(document.uri) ?? { providePrefixAndSuffixTextForRename: true };
const entries = languageService.findRenameLocations(fileName, offset, false, false, providePrefixAndSuffixTextForRename);
if (!entries)
return;
Expand Down
4 changes: 2 additions & 2 deletions packages/vue-code-gen/src/generators/script.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ export function generate(
},
SourceMaps.Mode.Expand,
{
vueTag: 'sfc',
vueTag: undefined,
capabilities: {},
},
);
Expand Down Expand Up @@ -98,7 +98,7 @@ export function generate(
// fix https://github.com/johnsoncodehk/volar/issues/435
codeGen.addMapping2({
data: {
vueTag: 'sfc',
vueTag: undefined,
capabilities: {},
},
mode: SourceMaps.Mode.Expand,
Expand Down
172 changes: 54 additions & 118 deletions packages/vue-code-gen/src/generators/template.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,6 @@ export function generate(
const tsCodeGen = new CodeGen<EmbeddedFileMappingData>();
const tsFormatCodeGen = new CodeGen<EmbeddedFileMappingData>();
const cssCodeGen = new CodeGen<EmbeddedFileMappingData>();
const attrNames = new Set<string>();
const slots = new Map<string, {
varName: string,
loc: SourceMaps.Range,
Expand All @@ -85,16 +84,7 @@ export function generate(
loc: SourceMaps.Range,
}>();
const cssScopedClassesSet = new Set(cssScopedClasses);
const tags: Record<string, {
offsets: number[],
props: Record<string, {
argName: string,
offsets: number[],
}>,
events: Record<string, {
offsets: number[],
}>,
}> = {};
const tagOffsetsMap: Record<string, number[]> = {};
const tagResolves: Record<string, {
component: string,
emit: string,
Expand All @@ -109,13 +99,24 @@ export function generate(

let elementIndex = 0;

for (const childNode of templateAst.children) {
collectTags(childNode);
}
for (const tagName in tags) {
walkElementNodes(templateAst, node => {

if (!tagOffsetsMap[node.tag]) {
tagOffsetsMap[node.tag] = [];
}

const offsets = tagOffsetsMap[node.tag];

offsets.push(node.loc.start.offset + node.loc.source.indexOf(node.tag)); // start tag
if (!node.isSelfClosing && sourceLang === 'html') {
offsets.push(node.loc.start.offset + node.loc.source.lastIndexOf(node.tag)); // end tag
}
});

for (const tagName in tagOffsetsMap) {

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

const var_correctTagName = `__VLS_${elementIndex++}`;
Expand Down Expand Up @@ -227,7 +228,7 @@ export function generate(
tagResolves[tagName] = {
component: var_rawComponent,
emit: var_emit,
offsets: tag.offsets.map(offset => htmlToTemplate(offset, offset)?.start).filter(notEmpty),
offsets: tagOffsets.map(offset => htmlToTemplate(offset, offset)?.start).filter(notEmpty),
};
}

Expand Down Expand Up @@ -284,103 +285,10 @@ export function generate(
codeGen: tsCodeGen,
formatCodeGen: tsFormatCodeGen,
cssCodeGen: cssCodeGen,
tagNames: tagResolves,
attrNames,
tagNames: tagOffsetsMap,
identifiers,
};

function collectTags(node: CompilerDOM.TemplateChildNode) {
if (node.type === CompilerDOM.NodeTypes.ELEMENT) {
const patchForNode = getPatchForSlotNode(node);
if (patchForNode) {
collectTags(patchForNode);
return;
}
if (!tags[node.tag]) {
tags[node.tag] = {
offsets: [],
props: {},
events: {},
};
}
const resolvedTag = tags[node.tag];
resolvedTag.offsets.push(node.loc.start.offset + node.loc.source.indexOf(node.tag)); // start tag
if (!node.isSelfClosing && sourceLang === 'html') {
resolvedTag.offsets.push(node.loc.start.offset + node.loc.source.lastIndexOf(node.tag)); // end tag
}
for (const prop of node.props) {
if (
prop.type === CompilerDOM.NodeTypes.DIRECTIVE
&& prop.arg?.type === CompilerDOM.NodeTypes.SIMPLE_EXPRESSION
&& prop.arg.isStatic
) {

let propName = prop.arg.constType === CompilerDOM.ConstantTypes.CAN_STRINGIFY
? prop.arg.content
: prop.arg.loc.source;

if (prop.modifiers.some(m => m === 'prop' || m === 'attr')) {
propName = propName.substring(1);
}

if (prop.name === 'bind' || prop.name === 'model') {
addProp(propName, propName, prop.arg.loc.start.offset);
}
else if (prop.name === 'on') {
addEvent(propName, prop.arg.loc.start.offset);
}
}
else if (
prop.type === CompilerDOM.NodeTypes.DIRECTIVE
&& !prop.arg
&& prop.name === 'model'
) {
addProp(getModelValuePropName(node, vueVersion), 'v-model', prop.loc.start.offset);
}
else if (
prop.type === CompilerDOM.NodeTypes.ATTRIBUTE
) {
addProp(prop.name, prop.name, prop.loc.start.offset);
}
}
for (const childNode of node.children) {
collectTags(childNode);
}

function addProp(propName: string, argName: string, offset: number) {
if (!resolvedTag.props[propName]) {
resolvedTag.props[propName] = {
argName,
offsets: [],
};
}
resolvedTag.props[propName].offsets.push(offset);
}
function addEvent(eventName: string, offset: number) {
if (!resolvedTag.events[eventName]) {
resolvedTag.events[eventName] = {
offsets: [],
};
}
resolvedTag.events[eventName].offsets.push(offset);
}
}
else if (node.type === CompilerDOM.NodeTypes.IF) {
// v-if / v-else-if / v-else
for (let i = 0; i < node.branches.length; i++) {
const branch = node.branches[i];
for (const childNode of branch.children) {
collectTags(childNode);
}
}
}
else if (node.type === CompilerDOM.NodeTypes.FOR) {
// v-for
for (const childNode of node.children) {
collectTags(childNode);
}
}
}
function visitNode(node: CompilerDOM.TemplateChildNode, parentEl: CompilerDOM.ElementNode | undefined): void {
if (node.type === CompilerDOM.NodeTypes.ELEMENT) {
visitElementNode(node, parentEl);
Expand Down Expand Up @@ -909,10 +817,6 @@ export function generate(
continue;
}

if (prop.arg?.type === CompilerDOM.NodeTypes.SIMPLE_EXPRESSION) {
attrNames.add(prop.arg.content);
}

// camelize name
writePropStart(isStatic);
const diagStart = tsCodeGen.getText().length;
Expand Down Expand Up @@ -1053,8 +957,6 @@ export function generate(
continue;
}

attrNames.add(prop.name);

// camelize name
writePropStart(true);
const diagStart = tsCodeGen.getText().length;
Expand Down Expand Up @@ -1973,6 +1875,40 @@ export function generate(
}
};

export function walkElementNodes(node: CompilerDOM.RootNode | CompilerDOM.TemplateChildNode, cb: (node: CompilerDOM.ElementNode) => void) {
if (node.type === CompilerDOM.NodeTypes.ROOT) {
for (const child of node.children) {
walkElementNodes(child, cb);
}
}
else if (node.type === CompilerDOM.NodeTypes.ELEMENT) {
const patchForNode = getPatchForSlotNode(node);
if (patchForNode) {
walkElementNodes(patchForNode, cb);
return;
}
cb(node);
for (const child of node.children) {
walkElementNodes(child, cb);
}
}
else if (node.type === CompilerDOM.NodeTypes.IF) {
// v-if / v-else-if / v-else
for (let i = 0; i < node.branches.length; i++) {
const branch = node.branches[i];
for (const childNode of branch.children) {
walkElementNodes(childNode, cb);
}
}
}
else if (node.type === CompilerDOM.NodeTypes.FOR) {
// v-for
for (const child of node.children) {
walkElementNodes(child, cb);
}
}
}

function toUnicode(str: string) {
return str.split('').map(value => {
var temp = value.charCodeAt(0).toString(16).padStart(4, '0');
Expand Down
4 changes: 2 additions & 2 deletions packages/vue-code-gen/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { generate as generateScript, getSlotsPropertyName, getVueLibraryName } from './generators/script';
import { generate as generateTemplateScript, isIntrinsicElement } from './generators/template';
import { generate as generateTemplateScript, isIntrinsicElement, walkElementNodes } from './generators/template';
import { parseScriptRanges } from './parsers/scriptRanges';
import { parseScriptSetupRanges } from './parsers/scriptSetupRanges';
import * as CompilerDOM from '@vue/compiler-dom';
import * as CompilerVue2 from './vue2TemplateCompiler';

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

/**
* @param templateAst Use `require('@vue/compiler-dom').compile` or `require('@volar/vue-code-gen').compileTemplate`, provide to resolve variables unused in script setup
Expand Down
2 changes: 1 addition & 1 deletion packages/vue-code-gen/src/types.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
export interface EmbeddedFileMappingData {
vueTag: 'sfc' | 'template' | 'script' | 'scriptSetup' | 'scriptSrc' | 'style' | 'customBlock' | undefined,
vueTag: 'template' | 'script' | 'scriptSetup' | 'scriptSrc' | 'style' | 'customBlock' | undefined,
vueTagIndex?: number,
normalizeNewName?: (newName: string) => string,
applyNewName?: (oldName: string, newName: string) => string,
Expand Down
1 change: 1 addition & 0 deletions packages/vue-language-service/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
"@volar/vue-language-service-types": "0.36.1",
"@volar/vue-typescript": "0.36.1",
"@vscode/emmet-helper": "^2.8.4",
"@vue/compiler-dom": "^3.2.36",
"@vue/reactivity": "^3.2.36",
"@vue/shared": "^3.2.36",
"semver": "^7.3.7",
Expand Down

0 comments on commit 62f3c89

Please sign in to comment.