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

feat(language-service): map sfc compiler errors outside the template inner content #5045

Merged
merged 4 commits into from
Feb 15, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 10 additions & 8 deletions packages/component-meta/lib/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -719,6 +719,7 @@ function readVueComponentDefaultProps(
default?: string;
required?: boolean;
}> = {};
const { sfc } = root;

scriptSetupWorker();
scriptWorker();
Expand All @@ -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) {
Expand Down Expand Up @@ -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;
}
}
}
Expand Down
20 changes: 13 additions & 7 deletions packages/language-core/lib/virtualFile/vueFile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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],
Expand All @@ -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();
}
Expand Down
10 changes: 5 additions & 5 deletions packages/language-service/lib/ideFeatures/nameCasing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export async function convertTagName(
return;
}

const { template } = root._sfc;
const { template } = root.sfc;
if (!template) {
return;
}
Expand Down Expand Up @@ -71,7 +71,7 @@ export async function convertAttrName(
return;
}

const { template } = root._sfc;
const { template } = root.sfc;
if (!template) {
return;
}
Expand Down Expand Up @@ -172,8 +172,8 @@ export async function detect(

const result = new Set<TagNameCasing>();

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
Expand Down Expand Up @@ -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)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
11 changes: 5 additions & 6 deletions packages/language-service/lib/plugins/vue-document-drop.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
8 changes: 5 additions & 3 deletions packages/language-service/lib/plugins/vue-document-links.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<string, {
index: number;
Expand All @@ -36,8 +38,8 @@ export function create(): LanguageServicePlugin {
}[]>();
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))) {
Expand Down
4 changes: 2 additions & 2 deletions packages/language-service/lib/plugins/vue-extract-file.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down
12 changes: 6 additions & 6 deletions packages/language-service/lib/plugins/vue-inlayhints.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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);
Expand Down
63 changes: 56 additions & 7 deletions packages/language-service/lib/plugins/vue-sfc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -23,7 +23,7 @@ export function create(): LanguageServicePlugin {
const formatSettings = await context.env.getConfiguration?.<html.HTMLFormatConfiguration>('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);
}

Expand All @@ -41,14 +41,21 @@ 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 htmlServiceInstance = htmlService.create(context);

return {

...htmlPluginInstance,
...htmlServiceInstance,

provideDocumentLinks: undefined,

Expand All @@ -73,11 +80,53 @@ export function create(): LanguageServicePlugin {
return options;
},

provideDiagnostics(document, token) {
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 } = 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, root => {

const result: vscode.DocumentSymbol[] = [];
const sfc = root._sfc;
const { sfc } = root;

if (sfc.template) {
result.push({
Expand Down Expand Up @@ -162,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;
}
Expand Down
8 changes: 4 additions & 4 deletions packages/language-service/lib/plugins/vue-template.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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) {

Expand Down Expand Up @@ -428,7 +428,7 @@ export function create(
return;
}

const { template } = root._sfc;
const { template } = root.sfc;
if (!template) {
return;
}
Expand Down Expand Up @@ -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<string>();
const tags: html.ITagData[] = [];

Expand All @@ -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));
}
Expand Down
Loading