Skip to content

Commit

Permalink
refactor(language-service): consistent style of source and virtual co…
Browse files Browse the repository at this point in the history
…de operation (#5053)
  • Loading branch information
KazariEX authored Dec 20, 2024
1 parent 0a085fe commit d6faebb
Show file tree
Hide file tree
Showing 12 changed files with 347 additions and 285 deletions.
28 changes: 13 additions & 15 deletions packages/language-service/lib/ideFeatures/nameCasing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,21 +19,20 @@ export async function convertTagName(
return;
}

const rootCode = sourceFile?.generated?.root;
if (!(rootCode instanceof VueVirtualCode)) {
const root = sourceFile?.generated?.root;
if (!(root instanceof VueVirtualCode)) {
return;
}

const desc = rootCode._sfc;
if (!desc.template) {
const { template } = root._sfc;
if (!template) {
return;
}

const template = desc.template;
const document = context.documents.get(sourceFile.id, sourceFile.languageId, sourceFile.snapshot);
const edits: vscode.TextEdit[] = [];
const components = await tsPluginClient?.getComponentNames(rootCode.fileName) ?? [];
const tags = getTemplateTagsAndAttrs(rootCode);
const components = await tsPluginClient?.getComponentNames(root.fileName) ?? [];
const tags = getTemplateTagsAndAttrs(root);

for (const [tagName, { offsets }] of tags) {
const componentName = components.find(component => component === tagName || hyphenateTag(component) === tagName);
Expand Down Expand Up @@ -67,26 +66,25 @@ export async function convertAttrName(
return;
}

const rootCode = sourceFile?.generated?.root;
if (!(rootCode instanceof VueVirtualCode)) {
const root = sourceFile?.generated?.root;
if (!(root instanceof VueVirtualCode)) {
return;
}

const desc = rootCode._sfc;
if (!desc.template) {
const { template } = root._sfc;
if (!template) {
return;
}

const template = desc.template;
const document = context.documents.get(uri, sourceFile.languageId, sourceFile.snapshot);
const edits: vscode.TextEdit[] = [];
const components = await tsPluginClient?.getComponentNames(rootCode.fileName) ?? [];
const tags = getTemplateTagsAndAttrs(rootCode);
const components = await tsPluginClient?.getComponentNames(root.fileName) ?? [];
const tags = getTemplateTagsAndAttrs(root);

for (const [tagName, { attrs }] of tags) {
const componentName = components.find(component => component === tagName || hyphenateTag(component) === tagName);
if (componentName) {
const props = (await tsPluginClient?.getComponentProps(rootCode.fileName, componentName) ?? []).map(prop => prop.name);
const props = (await tsPluginClient?.getComponentProps(root.fileName, componentName) ?? []).map(prop => prop.name);
for (const [attrName, { offsets }] of attrs) {
const propName = props.find(prop => prop === attrName || hyphenateAttr(prop) === attrName);
if (propName) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,8 @@ export function create(
return;
}

const decoded = context.decodeEmbeddedDocumentUri(URI.parse(document.uri));
const uri = URI.parse(document.uri);
const decoded = context.decodeEmbeddedDocumentUri(uri);
const sourceScript = decoded && context.language.scripts.get(decoded[0]);
const virtualCode = decoded && sourceScript?.generated?.embeddedCodes.get(decoded[1]);
if (!sourceScript) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,15 @@ export function create(): LanguageServicePlugin {
return;
}

const result: vscode.CompletionItem[] = [];
const decoded = context.decodeEmbeddedDocumentUri(URI.parse(document.uri));
const uri = URI.parse(document.uri);
const decoded = context.decodeEmbeddedDocumentUri(uri);
const sourceScript = decoded && context.language.scripts.get(decoded[0]);
const virtualCode = decoded && sourceScript?.generated?.embeddedCodes.get(decoded[1]);
if (!sourceScript || !virtualCode) {
if (!sourceScript?.generated || !virtualCode) {
return;
}

const root = sourceScript?.generated?.root;
const root = sourceScript.generated.root;
if (!(root instanceof VueVirtualCode)) {
return;
}
Expand All @@ -43,6 +43,7 @@ export function create(): LanguageServicePlugin {
return;
}

const result: vscode.CompletionItem[] = [];
const mappings = [...context.language.maps.forEach(virtualCode)];

addDefineCompletionItem(
Expand Down
22 changes: 13 additions & 9 deletions packages/language-service/lib/plugins/vue-document-drop.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,15 @@ export function create(
return;
}

const decoded = context.decodeEmbeddedDocumentUri(URI.parse(document.uri));
const uri = URI.parse(document.uri);
const decoded = context.decodeEmbeddedDocumentUri(uri);
const sourceScript = decoded && context.language.scripts.get(decoded[0]);
const virtualCode = decoded && sourceScript?.generated?.embeddedCodes.get(decoded[1]);
const vueVirtualCode = sourceScript?.generated?.root;
if (!sourceScript || !virtualCode || !(vueVirtualCode instanceof VueVirtualCode)) {
if (!sourceScript?.generated) {
return;
}

const root = sourceScript.generated.root;
if (!(root instanceof VueVirtualCode)) {
return;
}

Expand All @@ -55,34 +59,34 @@ export function create(
baseName = baseName.slice(0, baseName.lastIndexOf('.'));

const newName = capitalize(camelize(baseName));
const { _sfc: sfc } = vueVirtualCode;
const sfc = root._sfc;
const script = sfc.scriptSetup ?? sfc.script;

if (!script) {
return;
}

const additionalEdit: vscode.WorkspaceEdit = {};
const code = [...forEachEmbeddedCode(vueVirtualCode)].find(code => code.id === (sfc.scriptSetup ? 'scriptsetup_raw' : 'script_raw'))!;
const code = [...forEachEmbeddedCode(root)].find(code => code.id === (sfc.scriptSetup ? 'scriptsetup_raw' : 'script_raw'))!;
const lastImportNode = getLastImportNode(ts, script.ast);
const incomingFileName = context.project.typescript?.uriConverter.asFileName(URI.parse(importUri))
?? URI.parse(importUri).fsPath.replace(/\\/g, '/');

let importPath: string | undefined;

const serviceScript = sourceScript.generated?.languagePlugin.typescript?.getServiceScript(vueVirtualCode);
const serviceScript = sourceScript.generated?.languagePlugin.typescript?.getServiceScript(root);
if (tsPluginClient && serviceScript) {
const tsDocumentUri = context.encodeEmbeddedDocumentUri(sourceScript.id, serviceScript.code.id);
const tsDocument = context.documents.get(tsDocumentUri, serviceScript.code.languageId, serviceScript.code.snapshot);
const preferences = await getUserPreferences(context, tsDocument);
const importPathRequest = await tsPluginClient.getImportPathForFile(vueVirtualCode.fileName, incomingFileName, preferences);
const importPathRequest = await tsPluginClient.getImportPathForFile(root.fileName, incomingFileName, preferences);
if (importPathRequest) {
importPath = importPathRequest;
}
}

if (!importPath) {
importPath = path.relative(path.dirname(vueVirtualCode.fileName), incomingFileName)
importPath = path.relative(path.dirname(root.fileName), incomingFileName)
|| importUri.slice(importUri.lastIndexOf('/') + 1);

if (!importPath.startsWith('./') && !importPath.startsWith('../')) {
Expand Down
94 changes: 50 additions & 44 deletions packages/language-service/lib/plugins/vue-document-links.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,63 +13,69 @@ export function create(): LanguageServicePlugin {
return {
provideDocumentLinks(document) {

const decoded = context.decodeEmbeddedDocumentUri(URI.parse(document.uri));
const uri = URI.parse(document.uri);
const decoded = context.decodeEmbeddedDocumentUri(uri);
const sourceScript = decoded && context.language.scripts.get(decoded[0]);
const virtualCode = decoded && sourceScript?.generated?.embeddedCodes.get(decoded[1]);
if (!sourceScript?.generated || virtualCode?.id !== 'template') {
return;
}

if (sourceScript?.generated?.root instanceof VueVirtualCode && virtualCode?.id === 'template') {
const root = sourceScript.generated.root;
if (!(root instanceof VueVirtualCode)) {
return;
}

const result: vscode.DocumentLink[] = [];
const codegen = tsCodegen.get(sourceScript.generated.root._sfc);
const scopedClasses = codegen?.generatedTemplate.get()?.scopedClasses ?? [];
const styleClasses = new Map<string, {
index: number;
style: Sfc['styles'][number];
classOffset: number;
}[]>();
const option = sourceScript.generated.root.vueCompilerOptions.experimentalResolveStyleCssClasses;
const result: vscode.DocumentLink[] = [];
const codegen = tsCodegen.get(root._sfc);
const scopedClasses = codegen?.generatedTemplate.get()?.scopedClasses ?? [];
const styleClasses = new Map<string, {
index: number;
style: Sfc['styles'][number];
classOffset: number;
}[]>();
const option = root.vueCompilerOptions.experimentalResolveStyleCssClasses;

for (let i = 0; i < sourceScript.generated.root._sfc.styles.length; i++) {
const style = sourceScript.generated.root._sfc.styles[i];
if (option === 'always' || (option === 'scoped' && style.scoped)) {
for (const className of style.classNames) {
if (!styleClasses.has(className.text.slice(1))) {
styleClasses.set(className.text.slice(1), []);
}
styleClasses.get(className.text.slice(1))!.push({
index: i,
style,
classOffset: className.offset,
});
for (let i = 0; i < root._sfc.styles.length; i++) {
const style = root._sfc.styles[i];
if (option === 'always' || (option === 'scoped' && style.scoped)) {
for (const className of style.classNames) {
if (!styleClasses.has(className.text.slice(1))) {
styleClasses.set(className.text.slice(1), []);
}
styleClasses.get(className.text.slice(1))!.push({
index: i,
style,
classOffset: className.offset,
});
}
}
}

for (const { className, offset } of scopedClasses) {
const styles = styleClasses.get(className);
if (styles) {
for (const style of styles) {
const styleDocumentUri = context.encodeEmbeddedDocumentUri(decoded![0], 'style_' + style.index);
const styleVirtualCode = sourceScript.generated.embeddedCodes.get('style_' + style.index);
if (!styleVirtualCode) {
continue;
}
const styleDocument = context.documents.get(styleDocumentUri, styleVirtualCode.languageId, styleVirtualCode.snapshot);
const start = styleDocument.positionAt(style.classOffset);
const end = styleDocument.positionAt(style.classOffset + className.length + 1);
result.push({
range: {
start: document.positionAt(offset),
end: document.positionAt(offset + className.length),
},
target: context.encodeEmbeddedDocumentUri(decoded![0], 'style_' + style.index) + `#L${start.line + 1},${start.character + 1}-L${end.line + 1},${end.character + 1}`,
});
for (const { className, offset } of scopedClasses) {
const styles = styleClasses.get(className);
if (styles) {
for (const style of styles) {
const styleDocumentUri = context.encodeEmbeddedDocumentUri(decoded![0], 'style_' + style.index);
const styleVirtualCode = sourceScript.generated.embeddedCodes.get('style_' + style.index);
if (!styleVirtualCode) {
continue;
}
const styleDocument = context.documents.get(styleDocumentUri, styleVirtualCode.languageId, styleVirtualCode.snapshot);
const start = styleDocument.positionAt(style.classOffset);
const end = styleDocument.positionAt(style.classOffset + className.length + 1);
result.push({
range: {
start: document.positionAt(offset),
end: document.positionAt(offset + className.length),
},
target: context.encodeEmbeddedDocumentUri(decoded![0], 'style_' + style.index) + `#L${start.line + 1},${start.character + 1}-L${end.line + 1},${end.character + 1}`,
});
}
}

return result;
}

return result;
},
};
},
Expand Down
31 changes: 21 additions & 10 deletions packages/language-service/lib/plugins/vue-extract-file.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,16 +35,21 @@ export function create(
return;
}

const decoded = context.decodeEmbeddedDocumentUri(URI.parse(document.uri));
const uri = URI.parse(document.uri);
const decoded = context.decodeEmbeddedDocumentUri(uri);
const sourceScript = decoded && context.language.scripts.get(decoded[0]);
const virtualCode = decoded && sourceScript?.generated?.embeddedCodes.get(decoded[1]);
if (!(sourceScript?.generated?.root instanceof VueVirtualCode) || virtualCode?.id !== 'template') {
if (!sourceScript?.generated || virtualCode?.id !== 'template') {
return;
}

const { _sfc: sfc } = sourceScript.generated.root;
const script = sfc.scriptSetup ?? sfc.script;
const root = sourceScript.generated.root;
if (!(root instanceof VueVirtualCode)) {
return;
}

const sfc = root._sfc;
const script = sfc.scriptSetup ?? sfc.script;
if (!sfc.template || !script) {
return;
}
Expand All @@ -71,19 +76,22 @@ export function create(

const { uri, range, newName } = codeAction.data as ActionData;
const [startOffset, endOffset]: [number, number] = range;

const parsedUri = URI.parse(uri);
const decoded = context.decodeEmbeddedDocumentUri(parsedUri);
const sourceScript = decoded && context.language.scripts.get(decoded[0]);
const virtualCode = decoded && sourceScript?.generated?.embeddedCodes.get(decoded[1]);
if (!(sourceScript?.generated?.root instanceof VueVirtualCode) || virtualCode?.id !== 'template') {
if (!sourceScript?.generated || virtualCode?.id !== 'template') {
return codeAction;
}

const document = context.documents.get(parsedUri, virtualCode.languageId, virtualCode.snapshot);
const sfcDocument = context.documents.get(sourceScript.id, sourceScript.languageId, sourceScript.snapshot);
const { _sfc: sfc } = sourceScript.generated.root;
const script = sfc.scriptSetup ?? sfc.script;
const root = sourceScript.generated.root;
if (!(root instanceof VueVirtualCode)) {
return codeAction;
}

const sfc = root._sfc;
const script = sfc.scriptSetup ?? sfc.script;
if (!sfc.template || !script) {
return codeAction;
}
Expand All @@ -93,13 +101,16 @@ export function create(
return codeAction;
}

const toExtract = await tsPluginClient?.collectExtractProps(sourceScript.generated.root.fileName, templateCodeRange) ?? [];
const toExtract = await tsPluginClient?.collectExtractProps(root.fileName, templateCodeRange) ?? [];
if (!toExtract) {
return codeAction;
}

const templateInitialIndent = await context.env.getConfiguration!<boolean>('vue.format.template.initialIndent') ?? true;
const scriptInitialIndent = await context.env.getConfiguration!<boolean>('vue.format.script.initialIndent') ?? false;

const document = context.documents.get(parsedUri, virtualCode.languageId, virtualCode.snapshot);
const sfcDocument = context.documents.get(sourceScript.id, sourceScript.languageId, sourceScript.snapshot);
const newUri = sfcDocument.uri.slice(0, sfcDocument.uri.lastIndexOf('/') + 1) + `${newName}.vue`;
const lastImportNode = getLastImportNode(ts, script.ast);

Expand Down
Loading

0 comments on commit d6faebb

Please sign in to comment.