Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor(language-service): consistent style of source and virtual code operation #5053

Merged
merged 7 commits into from
Dec 20, 2024
Merged
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
Loading