From c70c3f881b69e7f9ee43ead6fd1e51b0bd107ccf Mon Sep 17 00:00:00 2001 From: KazariEX <1364035137@qq.com> Date: Fri, 13 Dec 2024 02:46:29 +0800 Subject: [PATCH 1/3] feat: mapping sfc compiler error outside of template inner content --- .../language-service/lib/plugins/vue-sfc.ts | 55 ++++++++++++++++++- 1 file changed, 52 insertions(+), 3 deletions(-) diff --git a/packages/language-service/lib/plugins/vue-sfc.ts b/packages/language-service/lib/plugins/vue-sfc.ts index 849f59bf55..652778beb7 100644 --- a/packages/language-service/lib/plugins/vue-sfc.ts +++ b/packages/language-service/lib/plugins/vue-sfc.ts @@ -10,7 +10,7 @@ import { loadLanguageBlocks } from './data'; let sfcDataProvider: html.IHTMLDataProvider | undefined; export function create(): LanguageServicePlugin { - const htmlPlugin = createHtmlService({ + const htmlService = createHtmlService({ documentSelector: ['vue-root-tags'], useDefaultDataProvider: false, getCustomData(context) { @@ -41,10 +41,17 @@ export function create(): LanguageServicePlugin { }, }); return { - ...htmlPlugin, + ...htmlService, name: 'vue-sfc', + capabilities: { + ...htmlService.capabilities, + diagnosticProvider: { + interFileDependencies: false, + workspaceDiagnostics: false, + } + }, create(context) { - const htmlPluginInstance = htmlPlugin.create(context); + const htmlPluginInstance = htmlService.create(context); return { @@ -73,6 +80,48 @@ export function create(): LanguageServicePlugin { return options; }, + provideDiagnostics(document, token) { + return worker(document, context, async vueSourceFile => { + const vueSfc = vueSourceFile._vueSfc.get(); + if (!vueSfc) { + return; + } + + const originalResult = await htmlPluginInstance.provideDiagnostics?.(document, token); + const sfcErrors: vscode.Diagnostic[] = []; + const { template } = vueSourceFile._sfc; + + const { + startTagEnd = Infinity, + endTagStart = -Infinity + } = template ?? {}; + + for (const error of vueSfc.errors) { + if ('code' in error) { + const start = error.loc?.start.offset ?? 0; + const end = error.loc?.end.offset ?? 0; + if (end < startTagEnd || start >= endTagStart) { + sfcErrors.push({ + range: { + start: document.positionAt(start), + end: document.positionAt(end), + }, + severity: 1 satisfies typeof vscode.DiagnosticSeverity.Error, + code: error.code, + source: 'vue', + message: error.message, + }); + } + } + } + + return [ + ...originalResult ?? [], + ...sfcErrors + ]; + }); + }, + provideDocumentSymbols(document) { return worker(document, context, vueSourceFile => { From 9029ecb0d6d0f4a5c3959c874674b3306b9d9762 Mon Sep 17 00:00:00 2001 From: KazariEX <1364035137@qq.com> Date: Fri, 13 Dec 2024 02:52:05 +0800 Subject: [PATCH 2/3] refactor: rename --- packages/language-service/lib/plugins/vue-sfc.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/language-service/lib/plugins/vue-sfc.ts b/packages/language-service/lib/plugins/vue-sfc.ts index 652778beb7..34ae8de656 100644 --- a/packages/language-service/lib/plugins/vue-sfc.ts +++ b/packages/language-service/lib/plugins/vue-sfc.ts @@ -51,11 +51,11 @@ export function create(): LanguageServicePlugin { } }, create(context) { - const htmlPluginInstance = htmlService.create(context); + const htmlServiceInstance = htmlService.create(context); return { - ...htmlPluginInstance, + ...htmlServiceInstance, provideDocumentLinks: undefined, @@ -87,7 +87,7 @@ export function create(): LanguageServicePlugin { return; } - const originalResult = await htmlPluginInstance.provideDiagnostics?.(document, token); + const originalResult = await htmlServiceInstance.provideDiagnostics?.(document, token); const sfcErrors: vscode.Diagnostic[] = []; const { template } = vueSourceFile._sfc; @@ -211,7 +211,7 @@ export function create(): LanguageServicePlugin { }, async provideCompletionItems(document, position, context, token) { - const result = await htmlPluginInstance.provideCompletionItems?.(document, position, context, token); + const result = await htmlServiceInstance.provideCompletionItems?.(document, position, context, token); if (!result) { return; } From 59bd506e7aabdc0e1df83b664818ed0ea357efbb Mon Sep 17 00:00:00 2001 From: KazariEX <1364035137@qq.com> Date: Sun, 16 Feb 2025 04:22:11 +0800 Subject: [PATCH 3/3] refactor: improve code consistency --- packages/component-meta/lib/base.ts | 18 +++++++++-------- .../language-core/lib/virtualFile/vueFile.ts | 20 ++++++++++++------- .../lib/ideFeatures/nameCasing.ts | 10 +++++----- .../lib/plugins/vue-autoinsert-dotvalue.ts | 6 ++---- .../plugins/vue-complete-define-assignment.ts | 5 +++-- .../lib/plugins/vue-document-drop.ts | 11 +++++----- .../lib/plugins/vue-document-links.ts | 8 +++++--- .../lib/plugins/vue-extract-file.ts | 4 ++-- .../lib/plugins/vue-inlayhints.ts | 12 +++++------ .../language-service/lib/plugins/vue-sfc.ts | 10 +++++----- .../lib/plugins/vue-template.ts | 8 ++++---- packages/typescript-plugin/lib/common.ts | 4 ++-- .../lib/requests/collectExtractProps.ts | 2 +- 13 files changed, 63 insertions(+), 55 deletions(-) diff --git a/packages/component-meta/lib/base.ts b/packages/component-meta/lib/base.ts index 7e61eac0e1..a944fd7a03 100644 --- a/packages/component-meta/lib/base.ts +++ b/packages/component-meta/lib/base.ts @@ -719,6 +719,7 @@ function readVueComponentDefaultProps( default?: string; required?: boolean; }> = {}; + const { sfc } = root; scriptSetupWorker(); scriptWorker(); @@ -727,12 +728,12 @@ function readVueComponentDefaultProps( function scriptSetupWorker() { - const ast = root._sfc.scriptSetup?.ast; + const ast = sfc.scriptSetup?.ast; if (!ast) { return; } - const codegen = vue.tsCodegen.get(root._sfc); + const codegen = vue.tsCodegen.get(sfc); const scriptSetupRanges = codegen?.getScriptSetupRanges(); if (scriptSetupRanges?.withDefaults?.argNode) { @@ -785,13 +786,14 @@ function readVueComponentDefaultProps( function scriptWorker() { - const sfc = root._sfc; + const ast = sfc.script?.ast; + if (!ast) { + return; + } - if (sfc.script) { - const scriptResult = readTsComponentDefaultProps(sfc.script.ast, 'default', printer, ts); - for (const [key, value] of Object.entries(scriptResult)) { - result[key] = value; - } + const scriptResult = readTsComponentDefaultProps(ast, 'default', printer, ts); + for (const [key, value] of Object.entries(scriptResult)) { + result[key] = value; } } } diff --git a/packages/language-core/lib/virtualFile/vueFile.ts b/packages/language-core/lib/virtualFile/vueFile.ts index af6633abc8..200403b995 100644 --- a/packages/language-core/lib/virtualFile/vueFile.ts +++ b/packages/language-core/lib/virtualFile/vueFile.ts @@ -17,9 +17,10 @@ export class VueVirtualCode implements VirtualCode { // computeds - _vueSfc = computedVueSfc(this.plugins, this.fileName, this.languageId, this._snapshot); - _sfc = computedSfc(this.ts, this.plugins, this.fileName, this._snapshot, this._vueSfc); - _mappings = computed(() => { + private _vueSfc = computedVueSfc(this.plugins, this.fileName, this.languageId, this._snapshot); + private _sfc = computedSfc(this.ts, this.plugins, this.fileName, this._snapshot, this._vueSfc); + private _embeddedCodes = computedEmbeddedCodes(this.plugins, this.fileName, this._sfc); + private _mappings = computed(() => { const snapshot = this._snapshot(); return [{ sourceOffsets: [0], @@ -28,16 +29,21 @@ export class VueVirtualCode implements VirtualCode { data: allCodeFeatures, }]; }); - _embeddedCodes = computedEmbeddedCodes(this.plugins, this.fileName, this._sfc); // others - get embeddedCodes() { - return this._embeddedCodes(); - } get snapshot() { return this._snapshot(); } + get vueSfc() { + return this._vueSfc(); + } + get sfc() { + return this._sfc; + } + get embeddedCodes() { + return this._embeddedCodes(); + } get mappings() { return this._mappings(); } diff --git a/packages/language-service/lib/ideFeatures/nameCasing.ts b/packages/language-service/lib/ideFeatures/nameCasing.ts index 5dd123585c..3ebb4d57dc 100644 --- a/packages/language-service/lib/ideFeatures/nameCasing.ts +++ b/packages/language-service/lib/ideFeatures/nameCasing.ts @@ -24,7 +24,7 @@ export async function convertTagName( return; } - const { template } = root._sfc; + const { template } = root.sfc; if (!template) { return; } @@ -71,7 +71,7 @@ export async function convertAttrName( return; } - const { template } = root._sfc; + const { template } = root.sfc; if (!template) { return; } @@ -172,8 +172,8 @@ export async function detect( const result = new Set(); - if (file._sfc.template?.ast) { - for (const element of vue.forEachElementNode(file._sfc.template.ast)) { + if (file.sfc.template?.ast) { + for (const element of vue.forEachElementNode(file.sfc.template.ast)) { if (element.tagType === 1 satisfies CompilerDOM.ElementTypes) { if (element.tag !== hyphenateTag(element.tag)) { // TagName @@ -208,7 +208,7 @@ function getTemplateTagsAndAttrs(sourceFile: VirtualCode): Tags { if (!(sourceFile instanceof vue.VueVirtualCode)) { return; } - const ast = sourceFile._sfc.template?.ast; + const ast = sourceFile.sfc.template?.ast; const tags: Tags = new Map(); if (ast) { for (const node of vue.forEachElementNode(ast)) { diff --git a/packages/language-service/lib/plugins/vue-autoinsert-dotvalue.ts b/packages/language-service/lib/plugins/vue-autoinsert-dotvalue.ts index eb08119bd5..3b3b6794cf 100644 --- a/packages/language-service/lib/plugins/vue-autoinsert-dotvalue.ts +++ b/packages/language-service/lib/plugins/vue-autoinsert-dotvalue.ts @@ -60,10 +60,8 @@ export function create( return; } - const blocks = [ - root._sfc.script, - root._sfc.scriptSetup, - ].filter(block => !!block); + const { sfc } = root; + const blocks = [sfc.script, sfc.scriptSetup].filter(block => !!block); if (!blocks.length) { return; } diff --git a/packages/language-service/lib/plugins/vue-complete-define-assignment.ts b/packages/language-service/lib/plugins/vue-complete-define-assignment.ts index 092d31feeb..07e9182619 100644 --- a/packages/language-service/lib/plugins/vue-complete-define-assignment.ts +++ b/packages/language-service/lib/plugins/vue-complete-define-assignment.ts @@ -36,8 +36,9 @@ export function create(): LanguageServicePlugin { return; } - const codegen = tsCodegen.get(root._sfc); - const scriptSetup = root._sfc.scriptSetup; + const { sfc } = root; + const codegen = tsCodegen.get(sfc); + const scriptSetup = sfc.scriptSetup; const scriptSetupRanges = codegen?.getScriptSetupRanges(); if (!scriptSetup || !scriptSetupRanges) { return; diff --git a/packages/language-service/lib/plugins/vue-document-drop.ts b/packages/language-service/lib/plugins/vue-document-drop.ts index 9233e91859..0dcc71dbba 100644 --- a/packages/language-service/lib/plugins/vue-document-drop.ts +++ b/packages/language-service/lib/plugins/vue-document-drop.ts @@ -55,17 +55,16 @@ export function create( return; } - let baseName = importUri.slice(importUri.lastIndexOf('/') + 1); - baseName = baseName.slice(0, baseName.lastIndexOf('.')); - - const newName = capitalize(camelize(baseName)); - const sfc = root._sfc; + const { sfc } = root; const script = sfc.scriptSetup ?? sfc.script; - if (!script) { return; } + let baseName = importUri.slice(importUri.lastIndexOf('/') + 1); + baseName = baseName.slice(0, baseName.lastIndexOf('.')); + const newName = capitalize(camelize(baseName)); + const additionalEdit: vscode.WorkspaceEdit = {}; const code = [...forEachEmbeddedCode(root)].find(code => code.id === (sfc.scriptSetup ? 'scriptsetup_raw' : 'script_raw'))!; const lastImportNode = getLastImportNode(ts, script.ast); diff --git a/packages/language-service/lib/plugins/vue-document-links.ts b/packages/language-service/lib/plugins/vue-document-links.ts index a93cea8689..c92a454cf2 100644 --- a/packages/language-service/lib/plugins/vue-document-links.ts +++ b/packages/language-service/lib/plugins/vue-document-links.ts @@ -27,7 +27,9 @@ export function create(): LanguageServicePlugin { } const result: vscode.DocumentLink[] = []; - const codegen = tsCodegen.get(root._sfc); + + const { sfc } = root; + const codegen = tsCodegen.get(sfc); const scopedClasses = codegen?.getGeneratedTemplate()?.scopedClasses ?? []; const styleClasses = new Map(); const option = root.vueCompilerOptions.experimentalResolveStyleCssClasses; - for (let i = 0; i < root._sfc.styles.length; i++) { - const style = root._sfc.styles[i]; + for (let i = 0; i < sfc.styles.length; i++) { + const style = sfc.styles[i]; if (option === 'always' || (option === 'scoped' && style.scoped)) { for (const className of style.classNames) { if (!styleClasses.has(className.text.slice(1))) { diff --git a/packages/language-service/lib/plugins/vue-extract-file.ts b/packages/language-service/lib/plugins/vue-extract-file.ts index 8c87985f96..c730c33c57 100644 --- a/packages/language-service/lib/plugins/vue-extract-file.ts +++ b/packages/language-service/lib/plugins/vue-extract-file.ts @@ -53,7 +53,7 @@ export function create( return; } - const sfc = root._sfc; + const { sfc } = root; const script = sfc.scriptSetup ?? sfc.script; if (!sfc.template || !script) { return; @@ -95,7 +95,7 @@ export function create( return codeAction; } - const sfc = root._sfc; + const { sfc } = root; const script = sfc.scriptSetup ?? sfc.script; if (!sfc.template || !script) { return codeAction; diff --git a/packages/language-service/lib/plugins/vue-inlayhints.ts b/packages/language-service/lib/plugins/vue-inlayhints.ts index b5253c1478..f10a06c7d2 100644 --- a/packages/language-service/lib/plugins/vue-inlayhints.ts +++ b/packages/language-service/lib/plugins/vue-inlayhints.ts @@ -30,21 +30,21 @@ export function create(ts: typeof import('typescript')): LanguageServicePlugin { const result: vscode.InlayHint[] = []; - const codegen = tsCodegen.get(virtualCode._sfc); + const codegen = tsCodegen.get(virtualCode.sfc); const inlayHints = [ ...codegen?.getGeneratedTemplate()?.inlayHints ?? [], ...codegen?.getGeneratedScript()?.inlayHints ?? [], ]; const scriptSetupRanges = codegen?.getScriptSetupRanges(); - if (scriptSetupRanges?.defineProps?.destructured && virtualCode._sfc.scriptSetup?.ast) { + if (scriptSetupRanges?.defineProps?.destructured && virtualCode.sfc.scriptSetup?.ast) { const setting = 'vue.inlayHints.destructuredProps'; const enabled = await getSettingEnabled(setting); if (enabled) { for (const [prop, isShorthand] of findDestructuredProps( ts, - virtualCode._sfc.scriptSetup.ast, + virtualCode.sfc.scriptSetup.ast, scriptSetupRanges.defineProps.destructured.keys() )) { const name = prop.text; @@ -62,9 +62,9 @@ export function create(ts: typeof import('typescript')): LanguageServicePlugin { } const blocks = [ - virtualCode._sfc.template, - virtualCode._sfc.script, - virtualCode._sfc.scriptSetup, + virtualCode.sfc.template, + virtualCode.sfc.script, + virtualCode.sfc.scriptSetup, ]; const start = document.offsetAt(range.start); const end = document.offsetAt(range.end); diff --git a/packages/language-service/lib/plugins/vue-sfc.ts b/packages/language-service/lib/plugins/vue-sfc.ts index 8de6afd2e1..3b26e3cfa3 100644 --- a/packages/language-service/lib/plugins/vue-sfc.ts +++ b/packages/language-service/lib/plugins/vue-sfc.ts @@ -23,7 +23,7 @@ export function create(): LanguageServicePlugin { const formatSettings = await context.env.getConfiguration?.('html.format') ?? {}; const blockTypes = ['template', 'script', 'style']; - for (const customBlock of root._sfc.customBlocks) { + for (const customBlock of root.sfc.customBlocks) { blockTypes.push(customBlock.type); } @@ -81,15 +81,15 @@ export function create(): LanguageServicePlugin { }, provideDiagnostics(document, token) { - return worker(document, context, async vueSourceFile => { - const vueSfc = vueSourceFile._vueSfc.get(); + return worker(document, context, async root => { + const { vueSfc, sfc } = root; if (!vueSfc) { return; } const originalResult = await htmlServiceInstance.provideDiagnostics?.(document, token); const sfcErrors: vscode.Diagnostic[] = []; - const { template } = vueSourceFile._sfc; + const { template } = sfc; const { startTagEnd = Infinity, @@ -126,7 +126,7 @@ export function create(): LanguageServicePlugin { return worker(document, context, root => { const result: vscode.DocumentSymbol[] = []; - const sfc = root._sfc; + const { sfc } = root; if (sfc.template) { result.push({ diff --git a/packages/language-service/lib/plugins/vue-template.ts b/packages/language-service/lib/plugins/vue-template.ts index a90763ae77..d33fee1f99 100644 --- a/packages/language-service/lib/plugins/vue-template.ts +++ b/packages/language-service/lib/plugins/vue-template.ts @@ -365,7 +365,7 @@ export function create( const originalResult = await baseServiceInstance.provideDiagnostics?.(document, token); const templateErrors: vscode.Diagnostic[] = []; - const { template } = root._sfc; + const { template } = root.sfc; if (template) { @@ -428,7 +428,7 @@ export function create( return; } - const { template } = root._sfc; + const { template } = root.sfc; if (!template) { return; } @@ -520,7 +520,7 @@ export function create( })()); return []; } - const scriptSetupRanges = tsCodegen.get(vueCode._sfc)?.getScriptSetupRanges(); + const scriptSetupRanges = tsCodegen.get(vueCode.sfc)?.getScriptSetupRanges(); const names = new Set(); const tags: html.ITagData[] = []; @@ -534,7 +534,7 @@ export function create( } for (const binding of scriptSetupRanges?.bindings ?? []) { - const name = vueCode._sfc.scriptSetup!.content.slice(binding.range.start, binding.range.end); + const name = vueCode.sfc.scriptSetup!.content.slice(binding.range.start, binding.range.end); if (casing.tag === TagNameCasing.Kebab) { names.add(hyphenateTag(name)); } diff --git a/packages/typescript-plugin/lib/common.ts b/packages/typescript-plugin/lib/common.ts index 9824dbb200..6f74181a44 100644 --- a/packages/typescript-plugin/lib/common.ts +++ b/packages/typescript-plugin/lib/common.ts @@ -113,7 +113,7 @@ function getCompletionEntryDetails(language: Language, asScriptId: (fileNa const { fileName } = args[6]?.__isAutoImport; const sourceScript = language.scripts.get(asScriptId(fileName)); if (sourceScript?.generated?.root instanceof VueVirtualCode) { - const sfc = sourceScript.generated.root._vueSfc(); + const sfc = sourceScript.generated.root.vueSfc; if (!sfc?.descriptor.script && !sfc?.descriptor.scriptSetup) { for (const codeAction of details?.codeActions ?? []) { for (const change of codeAction.changes) { @@ -197,7 +197,7 @@ function getEncodedSemanticClassifications( const sourceScript = language.scripts.get(asScriptId(fileName)); const root = sourceScript?.generated?.root; if (root instanceof VueVirtualCode) { - const { template } = root._sfc; + const { template } = root.sfc; if (template) { for (const componentSpan of getComponentSpans.call( { typescript: ts, languageService }, diff --git a/packages/typescript-plugin/lib/requests/collectExtractProps.ts b/packages/typescript-plugin/lib/requests/collectExtractProps.ts index 0c4ee82265..ad9b9972de 100644 --- a/packages/typescript-plugin/lib/requests/collectExtractProps.ts +++ b/packages/typescript-plugin/lib/requests/collectExtractProps.ts @@ -28,7 +28,7 @@ export function collectExtractProps( const checker = program.getTypeChecker(); const script = sourceScript.generated?.languagePlugin.typescript?.getServiceScript(root); const maps = script ? [...language.maps.forEach(script.code)].map(([_sourceScript, map]) => map) : []; - const sfc = root._sfc; + const { sfc } = root; sourceFile.forEachChild(function visit(node) { if (