From d3be3372439252632f16061c1c8fcf5e32eb041c Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Mon, 25 Aug 2025 07:53:01 -0700 Subject: [PATCH 01/20] docs(vscode): add descriptions for premium feature configurations (#5612) Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: KazariEX <85992002+KazariEX@users.noreply.github.com> --- .github/workflows/copilot-setup-steps.yml | 29 +++++++++++++++++++++++ extensions/vscode/package.json | 6 ++--- extensions/vscode/package.nls.ja.json | 3 +++ extensions/vscode/package.nls.json | 3 +++ extensions/vscode/package.nls.ru.json | 3 +++ extensions/vscode/package.nls.zh-CN.json | 3 +++ extensions/vscode/package.nls.zh-TW.json | 3 +++ 7 files changed, 47 insertions(+), 3 deletions(-) create mode 100644 .github/workflows/copilot-setup-steps.yml diff --git a/.github/workflows/copilot-setup-steps.yml b/.github/workflows/copilot-setup-steps.yml new file mode 100644 index 0000000000..a9fd8c5cb4 --- /dev/null +++ b/.github/workflows/copilot-setup-steps.yml @@ -0,0 +1,29 @@ +name: Copilot Setup Steps + +on: + workflow_dispatch: + push: + paths: + - .github/workflows/copilot-setup-steps.yml + pull_request: + paths: + - .github/workflows/copilot-setup-steps.yml + +permissions: + contents: read + +jobs: + copilot-setup-steps: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - uses: pnpm/action-setup@v4 + + - uses: actions/setup-node@v4 + with: + node-version: 20 + cache: pnpm + + - run: pnpm install diff --git a/extensions/vscode/package.json b/extensions/vscode/package.json index d5eb8a7196..3ae9dc4b70 100644 --- a/extensions/vscode/package.json +++ b/extensions/vscode/package.json @@ -267,17 +267,17 @@ "vue.editor.focusMode": { "type": "boolean", "default": true, - "markdownDescription": "Sponsor this extension to unlock premium features. [Learn more](https://youtu.be/RcPcO4_Ct_U)" + "markdownDescription": "%configuration.editor.focusMode%" }, "vue.editor.reactivityVisualization": { "type": "boolean", "default": true, - "markdownDescription": "Sponsor this extension to unlock premium features. [Learn more](https://youtu.be/RcPcO4_Ct_U)" + "markdownDescription": "%configuration.editor.reactivityVisualization%" }, "vue.editor.templateInterpolationDecorators": { "type": "boolean", "default": true, - "markdownDescription": "Sponsor this extension to unlock premium features. [Learn more](https://youtu.be/RcPcO4_Ct_U)" + "markdownDescription": "%configuration.editor.templateInterpolationDecorators%" }, "vue.server.includeLanguages": { "type": "array", diff --git a/extensions/vscode/package.nls.ja.json b/extensions/vscode/package.nls.ja.json index aee8afda88..eb4167632b 100644 --- a/extensions/vscode/package.nls.ja.json +++ b/extensions/vscode/package.nls.ja.json @@ -16,6 +16,9 @@ "configuration.format.script.initialIndent": "` + + From dfa4128731a28009cee25f179ee3b991569e2c0a Mon Sep 17 00:00:00 2001 From: KazariEX Date: Sat, 30 Aug 2025 01:53:10 +0800 Subject: [PATCH 04/20] fix(language-core): do not generate variables for builtin directives --- .../language-core/lib/codegen/template/elementDirectives.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/language-core/lib/codegen/template/elementDirectives.ts b/packages/language-core/lib/codegen/template/elementDirectives.ts index 2dce0a93e6..adf8e61292 100644 --- a/packages/language-core/lib/codegen/template/elementDirectives.ts +++ b/packages/language-core/lib/codegen/template/elementDirectives.ts @@ -36,7 +36,10 @@ export function* generateElementDirectives( ) { continue; } - ctx.accessExternalVariable(camelize('v-' + prop.name), prop.loc.start.offset); + + if (!builtInDirectives.has(prop.name)) { + ctx.accessExternalVariable(camelize('v-' + prop.name), prop.loc.start.offset); + } yield* wrapWith( prop.loc.start.offset, From 740fd20b2c90403f2aab133ca52aed532c8c0867 Mon Sep 17 00:00:00 2001 From: KazariEX Date: Tue, 2 Sep 2025 16:37:32 +0800 Subject: [PATCH 05/20] refactor(language-core): transform template code features internally revert 053aa3c --- .../language-core/lib/codegen/script/index.ts | 18 +++++++-- .../lib/codegen/template/index.ts | 26 +++++++++++-- packages/language-core/lib/plugins/vue-tsx.ts | 37 ++++--------------- 3 files changed, 43 insertions(+), 38 deletions(-) diff --git a/packages/language-core/lib/codegen/script/index.ts b/packages/language-core/lib/codegen/script/index.ts index 69cf33c303..ffbb52d9ae 100644 --- a/packages/language-core/lib/codegen/script/index.ts +++ b/packages/language-core/lib/codegen/script/index.ts @@ -7,13 +7,11 @@ import { codeFeatures } from '../codeFeatures'; import type { TemplateCodegenContext } from '../template/context'; import { endOfLine, generateSfcBlockSection, newLine } from '../utils'; import { generateComponentSelf } from './componentSelf'; -import { type ScriptCodegenContext } from './context'; +import { createScriptCodegenContext, type ScriptCodegenContext } from './context'; import { generateScriptSetup, generateScriptSetupImports } from './scriptSetup'; import { generateSrc } from './src'; import { generateTemplate } from './template'; -export * from './context'; - export interface ScriptCodegenOptions { ts: typeof ts; compilerOptions: ts.CompilerOptions; @@ -28,7 +26,19 @@ export interface ScriptCodegenOptions { templateRefNames: Set; } -export function* generateScript( +export { generate as generateScript }; + +function generate(options: ScriptCodegenOptions) { + const context = createScriptCodegenContext(options); + const codegen = generateScript(options, context); + + return { + ...context, + codes: [...codegen], + }; +} + +function* generateScript( options: ScriptCodegenOptions, ctx: ScriptCodegenContext, ): Generator { diff --git a/packages/language-core/lib/codegen/template/index.ts b/packages/language-core/lib/codegen/template/index.ts index f623349b67..f091bf0f9e 100644 --- a/packages/language-core/lib/codegen/template/index.ts +++ b/packages/language-core/lib/codegen/template/index.ts @@ -5,13 +5,11 @@ import { getSlotsPropertyName } from '../../utils/shared'; import { codeFeatures } from '../codeFeatures'; import { endOfLine, newLine } from '../utils'; import { wrapWith } from '../utils/wrapWith'; -import type { TemplateCodegenContext } from './context'; +import { createTemplateCodegenContext, type TemplateCodegenContext } from './context'; import { generateObjectProperty } from './objectProperty'; import { generateStyleScopedClassReferences } from './styleScopedClasses'; import { generateTemplateChild, getVForNode } from './templateChild'; -export * from './context'; - export interface TemplateCodegenOptions { ts: typeof ts; compilerOptions: ts.CompilerOptions; @@ -28,7 +26,27 @@ export interface TemplateCodegenOptions { selfComponentName?: string; } -export function* generateTemplate( +export { generate as generateTemplate }; + +function generate(options: TemplateCodegenOptions) { + const context = createTemplateCodegenContext(options, options.template.ast); + const codegen = generateTemplate(options, context); + + const codes: Code[] = []; + for (const code of codegen) { + if (typeof code === 'object') { + code[3] = context.resolveCodeFeatures(code[3]); + } + codes.push(code); + } + + return { + ...context, + codes, + }; +} + +function* generateTemplate( options: TemplateCodegenOptions, ctx: TemplateCodegenContext, ): Generator { diff --git a/packages/language-core/lib/plugins/vue-tsx.ts b/packages/language-core/lib/plugins/vue-tsx.ts index 731f06ce97..7829cde983 100644 --- a/packages/language-core/lib/plugins/vue-tsx.ts +++ b/packages/language-core/lib/plugins/vue-tsx.ts @@ -1,12 +1,12 @@ import { camelize, capitalize } from '@vue/shared'; import { computed } from 'alien-signals'; import * as path from 'path-browserify'; -import { createScriptCodegenContext, generateScript, type ScriptCodegenOptions } from '../codegen/script'; -import { createTemplateCodegenContext, generateTemplate, type TemplateCodegenOptions } from '../codegen/template'; +import { generateScript } from '../codegen/script'; +import { generateTemplate } from '../codegen/template'; import { parseScriptRanges } from '../parsers/scriptRanges'; import { parseScriptSetupRanges } from '../parsers/scriptSetupRanges'; import { parseVueCompilerOptions } from '../parsers/vueCompilerOptions'; -import type { Code, Sfc, VueLanguagePlugin } from '../types'; +import type { Sfc, VueLanguagePlugin } from '../types'; import { computedSet } from '../utils/signals'; import { CompilerOptionsResolver } from '../utils/ts'; @@ -178,8 +178,7 @@ function createTsx( if (getResolvedOptions().skipTemplateCodegen || !sfc.template) { return; } - - const options: TemplateCodegenOptions = { + return generateTemplate({ ts, compilerOptions: ctx.compilerOptions, vueCompilerOptions: getResolvedOptions(), @@ -193,26 +192,11 @@ function createTsx( propsAssignName: getSetupPropsAssignName(), inheritAttrs: getSetupInheritAttrs(), selfComponentName: getComponentSelfName(), - }; - const context = createTemplateCodegenContext(options, sfc.template.ast); - const codegen = generateTemplate(options, context); - - const codes: Code[] = []; - for (const code of codegen) { - if (typeof code === 'object') { - code[3] = context.resolveCodeFeatures(code[3]); - } - codes.push(code); - } - - return { - ...context, - codes, - }; + }); }); const getGeneratedScript = computed(() => { - const options: ScriptCodegenOptions = { + return generateScript({ ts, compilerOptions: ctx.compilerOptions, vueCompilerOptions: getResolvedOptions(), @@ -224,14 +208,7 @@ function createTsx( templateCodegen: getGeneratedTemplate(), destructuredPropNames: getSetupDestructuredPropNames(), templateRefNames: getSetupTemplateRefNames(), - }; - const context = createScriptCodegenContext(options); - const codegen = generateScript(options, context); - - return { - ...context, - codes: [...codegen], - }; + }); }); return { From 4a1a9320c3c72b8c091e539dc18d6128012978e8 Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Wed, 3 Sep 2025 15:55:17 +0800 Subject: [PATCH 06/20] refactor(vscode): simplify welcome activation and execution functions --- extensions/vscode/index.ts | 15 +++++++-------- extensions/vscode/lib/welcome.ts | 4 ++-- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/extensions/vscode/index.ts b/extensions/vscode/index.ts index 871091230c..68dc09ccd2 100644 --- a/extensions/vscode/index.ts +++ b/extensions/vscode/index.ts @@ -16,7 +16,7 @@ import { } from 'reactive-vscode'; import * as vscode from 'vscode'; import { config } from './lib/config'; -import { activate as activateWelcome, executeWelcome } from './lib/welcome'; +import * as welcome from './lib/welcome'; let client: lsp.BaseLanguageClient | undefined; let needRestart = false; @@ -40,7 +40,7 @@ for ( } } -export const { activate, deactivate } = defineExtension(() => { +export = defineExtension(() => { const context = extensionContext.value!; const volarLabs = createLabsInfo(); const activeTextEditor = useActiveTextEditor(); @@ -98,10 +98,10 @@ export const { activate, deactivate } = defineExtension(() => { activateAutoInsertion(selectors, client); activateDocumentDropEdit(selectors, client); - activateWelcome(context); + welcome.activate(context); }, { immediate: true }); - useCommand('vue.welcome', () => executeWelcome(context)); + useCommand('vue.welcome', () => welcome.execute(context)); useCommand('vue.action.restartServer', async () => { await executeCommand('typescript.restartTsServer'); await client?.stop(); @@ -183,9 +183,9 @@ else { const extensionJsPath = require.resolve('./dist/extension.js', { paths: [tsExtension.extensionPath], }); + const vuePluginName = require('./package.json').contributes.typescriptServerPlugins[0].name; - // @ts-expect-error - fs.readFileSync = (...args) => { + fs.readFileSync = (...args: any[]) => { if (args[0] === extensionJsPath) { let text = readFileSync(...args) as string; @@ -194,7 +194,7 @@ else { 'languages:Array.isArray(e.languages)', [ 'languages:', - `e.name==='vue-typescript-plugin-pack'?[${ + `e.name==='${vuePluginName}'?[${ config.server.includeLanguages .map(lang => `'${lang}'`) .join(',') @@ -215,7 +215,6 @@ else { ); // sort plugins for johnsoncodehk.tsslint, zardoy.ts-essential-plugins - const vuePluginName = require('./package.json').contributes.typescriptServerPlugins[0].name; text = text.replace( '"--globalPlugins",i.plugins', s => s + `.sort((a,b)=>(b.name==="${vuePluginName}"?-1:0)-(a.name==="${vuePluginName}"?-1:0))`, diff --git a/extensions/vscode/lib/welcome.ts b/extensions/vscode/lib/welcome.ts index d499baea4e..a2088ae82f 100644 --- a/extensions/vscode/lib/welcome.ts +++ b/extensions/vscode/lib/welcome.ts @@ -10,11 +10,11 @@ export function activate(context: vscode.ExtensionContext) { && context.globalState.get('vue-welcome') !== welcomeVersion ) { context.globalState.update('vue-welcome', welcomeVersion); - vscode.commands.executeCommand('vue.welcome'); + execute(context); } } -export function executeWelcome(context: vscode.ExtensionContext) { +export function execute(context: vscode.ExtensionContext) { if (panel) { panel.reveal(vscode.ViewColumn.One); return; From a95c00aac44e12e5eb6c0ec67b1c97e816a557d7 Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Wed, 3 Sep 2025 16:04:32 +0800 Subject: [PATCH 07/20] chore: bump tsslint --- extensions/vscode/index.ts | 9 ++-- package.json | 6 +-- packages/language-server/tests/server.ts | 10 +++-- pnpm-lock.yaml | 53 ++++++++++++------------ 4 files changed, 40 insertions(+), 38 deletions(-) diff --git a/extensions/vscode/index.ts b/extensions/vscode/index.ts index 68dc09ccd2..c1aadde72c 100644 --- a/extensions/vscode/index.ts +++ b/extensions/vscode/index.ts @@ -162,11 +162,10 @@ function launch(context: vscode.ExtensionContext) { command, args, { isAsync: true, lowPriority: true }, - ).then(res => { - client.sendNotification('tsserver/response', [seq, res?.body]); - }, () => { - client.sendNotification('tsserver/response', [seq, undefined]); - }); + ).then( + res => client.sendNotification('tsserver/response', [seq, res?.body]), + () => client.sendNotification('tsserver/response', [seq, undefined]), + ); }); client.start(); diff --git a/package.json b/package.json index ee4e36e1a6..9ca82c9b14 100644 --- a/package.json +++ b/package.json @@ -18,9 +18,9 @@ "devDependencies": { "@lerna-lite/cli": "^4.1.2", "@lerna-lite/publish": "^4.1.2", - "@tsslint/cli": "^2.0.1", - "@tsslint/config": "^2.0.1", - "@tsslint/eslint": "^2.0.1", + "@tsslint/cli": "^2.0.4", + "@tsslint/config": "^2.0.4", + "@tsslint/eslint": "^2.0.4", "@typescript-eslint/eslint-plugin": "^8.19.0", "dprint": "^0.50.0", "typescript": "latest", diff --git a/packages/language-server/tests/server.ts b/packages/language-server/tests/server.ts index 57de35d76d..3ec7fcd9a1 100644 --- a/packages/language-server/tests/server.ts +++ b/packages/language-server/tests/server.ts @@ -44,13 +44,15 @@ export async function getLanguageServer(): Promise<{ return null; }); }); - serverHandle.connection.onNotification('tsserver/request', async ([id, command, args]) => { - const res = await tsserver.message({ + serverHandle.connection.onNotification('tsserver/request', ([id, command, args]) => { + tsserver.message({ seq: seq++, command: command, arguments: args, - }); - serverHandle!.connection.sendNotification('tsserver/response', [id, res.body]); + }).then( + res => serverHandle!.connection.sendNotification('tsserver/response', [id, res?.body]), + () => serverHandle!.connection.sendNotification('tsserver/response', [id, undefined]), + ); }); await serverHandle.initialize( diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d2316b8439..82544806f8 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -15,14 +15,14 @@ importers: specifier: ^4.1.2 version: 4.1.2(@types/node@22.15.2)(conventional-commits-filter@5.0.0)(typescript@5.9.2) '@tsslint/cli': - specifier: ^2.0.1 - version: 2.0.1(typescript@5.9.2) + specifier: ^2.0.4 + version: 2.0.4(typescript@5.9.2) '@tsslint/config': - specifier: ^2.0.1 - version: 2.0.1(typescript@5.9.2) + specifier: ^2.0.4 + version: 2.0.4(typescript@5.9.2) '@tsslint/eslint': - specifier: ^2.0.1 - version: 2.0.1(typescript@5.9.2) + specifier: ^2.0.4 + version: 2.0.4(typescript@5.9.2) '@typescript-eslint/eslint-plugin': specifier: ^8.19.0 version: 8.39.1(@typescript-eslint/parser@8.39.1(eslint@9.27.0)(typescript@5.9.2))(eslint@9.27.0)(typescript@5.9.2) @@ -1138,23 +1138,23 @@ packages: resolution: {integrity: sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==} engines: {node: '>=18'} - '@tsslint/cli@2.0.1': - resolution: {integrity: sha512-7g+jVQrs9d3D9kMpT1TBlLBQhmQriDGekyywwvrBnmocA1KJvVskms7t3xHxkGupNrF5dH+TZaQVacwCKw93Sg==} + '@tsslint/cli@2.0.4': + resolution: {integrity: sha512-TT+vWaWE5OYFT1kvPwD4UQo4ngX9KIGNed1D4Ji6QoL2+RcTLa18i5Hml4j2pKaaivmNl225MZtsgO3neeyhZQ==} hasBin: true peerDependencies: typescript: '*' - '@tsslint/config@2.0.1': - resolution: {integrity: sha512-al2vj06aenhqMs2/Vm3p2gVNAFCbpuw7C3E+DFWXVpKH3+g48cJaPymRI3Ma4mQqmvSMIPF5VFN/H1SSYzdwNw==} + '@tsslint/config@2.0.4': + resolution: {integrity: sha512-s5SJlEB/Pd7EKNnGirSY2VjcpWwj1HDg8oNUJ37nJnEJ4KqHOybpuIshRckYRctAE1UV5uzRyQWQXNecNX+s3w==} - '@tsslint/core@2.0.1': - resolution: {integrity: sha512-Freonkuz+cuIyMahmDgz3xLxfgKON79Y/K7LcKexJVqRnIYMIQcA0wxRKjk0eCNOj59PlzV8RYwYGvFaxDd8Qw==} + '@tsslint/core@2.0.4': + resolution: {integrity: sha512-1aYlKG3VoV2sobr70Nzt9mlNS76yWhssCmEQ0L41kZnASLVgIoVrwFcYR6U+4gnkugK1Q3ThzjT17eV4TxoNpg==} - '@tsslint/eslint@2.0.1': - resolution: {integrity: sha512-g5VU/BwFFHT7GAs7M1nzEguGWO7MeebYobzmr3nQlQFfJAgfRf52Ffl779KL+IMY9EdeDA6is9Wgg/bGug/Qvg==} + '@tsslint/eslint@2.0.4': + resolution: {integrity: sha512-9syUwbfnrlxUP7KzMPtNwPTvupSiAk6MJEEfOWXNxGoaz+o4MUMA9cRSGjrseCVKiTg4hPUcwy0mgUNVfNvqcw==} - '@tsslint/types@2.0.1': - resolution: {integrity: sha512-0XllhTjC3eVmbVNPAIAGlmAsIOymKbi5IzAk5QnreBDBxBA9jCPGl1NB0ijtBMrUai6zWiRB3/hPvikeEGs3hQ==} + '@tsslint/types@2.0.4': + resolution: {integrity: sha512-+2CQVAw+pgU+wk+Y+fb65dEobjsv1fdCOxACo9WgVuKEteodQLtAofQfYthhd3UAkUlTKC6RVDUB4UArtgRDVw==} '@tufjs/canonical-json@2.0.0': resolution: {integrity: sha512-yVtV8zsdo8qFHe+/3kw81dSLyF7D576A5cCFCi4X7B39tWT7SekaEFUnvnWJHz+9qO7qJTah1JbrDjWKqFtdWA==} @@ -4673,11 +4673,11 @@ snapshots: '@sindresorhus/merge-streams@4.0.0': {} - '@tsslint/cli@2.0.1(typescript@5.9.2)': + '@tsslint/cli@2.0.4(typescript@5.9.2)': dependencies: '@clack/prompts': 0.8.2 - '@tsslint/config': 2.0.1(typescript@5.9.2) - '@tsslint/core': 2.0.1 + '@tsslint/config': 2.0.4(typescript@5.9.2) + '@tsslint/core': 2.0.4 '@volar/language-core': 2.4.23 '@volar/language-hub': 0.0.1 '@volar/typescript': 2.4.23 @@ -4685,22 +4685,23 @@ snapshots: json5: 2.2.3 typescript: 5.9.2 - '@tsslint/config@2.0.1(typescript@5.9.2)': + '@tsslint/config@2.0.4(typescript@5.9.2)': dependencies: - '@tsslint/types': 2.0.1 + '@tsslint/types': 2.0.4 + minimatch: 10.0.1 ts-api-utils: 2.1.0(typescript@5.9.2) transitivePeerDependencies: - typescript - '@tsslint/core@2.0.1': + '@tsslint/core@2.0.4': dependencies: - '@tsslint/types': 2.0.1 + '@tsslint/types': 2.0.4 esbuild: 0.21.5 minimatch: 10.0.1 - '@tsslint/eslint@2.0.1(typescript@5.9.2)': + '@tsslint/eslint@2.0.4(typescript@5.9.2)': dependencies: - '@tsslint/config': 2.0.1(typescript@5.9.2) + '@tsslint/config': 2.0.4(typescript@5.9.2) '@typescript-eslint/parser': 8.39.1(eslint@9.27.0)(typescript@5.9.2) eslint: 9.27.0 transitivePeerDependencies: @@ -4708,7 +4709,7 @@ snapshots: - supports-color - typescript - '@tsslint/types@2.0.1': {} + '@tsslint/types@2.0.4': {} '@tufjs/canonical-json@2.0.0': {} From 99264b191d8ccdf5311bc896652ecceaab664ff5 Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Wed, 3 Sep 2025 17:22:19 +0800 Subject: [PATCH 08/20] chore: update VSCode settings --- .vscode/settings.json | 7 ------- 1 file changed, 7 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 28d945c5b2..f64fd89dc2 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -13,11 +13,6 @@ "editor.defaultFormatter": "dprint.dprint" }, "editor.formatOnSave": true, - "editor.codeActionsOnSave": { - "source.organizeImports": "always" - }, - "editor.detectIndentation": false, - "editor.insertSpaces": false, "files.exclude": { "packages/*/*.d.ts": true, "packages/*/*.js": true, @@ -26,7 +21,5 @@ "packages/*/lib/**/*.js": true, "packages/*/lib/**/*.map": true }, - "json.format.keepLines": true, - "typescript.format.semicolons": "insert", "typescript.tsdk": "node_modules/typescript/lib" } From 08a5af5bbcc6e73a9a30ade6fc379525faa45d2c Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Wed, 3 Sep 2025 18:00:22 +0800 Subject: [PATCH 09/20] refactor(language-core): move utils/ts.ts to compilerOptions.ts --- packages/language-core/index.ts | 2 +- .../language-core/lib/{utils/ts.ts => compilerOptions.ts} | 8 +++----- packages/language-core/lib/plugins/vue-tsx.ts | 2 +- 3 files changed, 5 insertions(+), 7 deletions(-) rename packages/language-core/lib/{utils/ts.ts => compilerOptions.ts} (97%) diff --git a/packages/language-core/index.ts b/packages/language-core/index.ts index a09efc0f4b..54dffda72f 100644 --- a/packages/language-core/index.ts +++ b/packages/language-core/index.ts @@ -1,5 +1,6 @@ export * from './lib/codegen/globalTypes'; export * from './lib/codegen/template'; +export * from './lib/compilerOptions'; export * from './lib/languagePlugin'; export * from './lib/parsers/scriptSetupRanges'; export * from './lib/plugins'; @@ -7,7 +8,6 @@ export * from './lib/types'; export * from './lib/utils/collectBindings'; export * from './lib/utils/parseSfc'; export * from './lib/utils/shared'; -export * from './lib/utils/ts'; export * from './lib/virtualFile/vueFile'; export { tsCodegen } from './lib/plugins/vue-tsx'; diff --git a/packages/language-core/lib/utils/ts.ts b/packages/language-core/lib/compilerOptions.ts similarity index 97% rename from packages/language-core/lib/utils/ts.ts rename to packages/language-core/lib/compilerOptions.ts index ad116a047d..98eb4ef338 100644 --- a/packages/language-core/lib/utils/ts.ts +++ b/packages/language-core/lib/compilerOptions.ts @@ -1,9 +1,9 @@ import { camelize, NOOP as noop } from '@vue/shared'; import { posix as path } from 'path-browserify'; import type * as ts from 'typescript'; -import { generateGlobalTypes, getGlobalTypesFileName } from '../codegen/globalTypes'; -import type { RawVueCompilerOptions, VueCompilerOptions, VueLanguagePlugin } from '../types'; -import { hyphenateTag } from './shared'; +import { generateGlobalTypes, getGlobalTypesFileName } from './codegen/globalTypes'; +import type { RawVueCompilerOptions, VueCompilerOptions, VueLanguagePlugin } from './types'; +import { hyphenateTag } from './utils/shared'; interface ParseConfigHost extends Omit {} @@ -107,7 +107,6 @@ export function createParsedCommandLine( } export class CompilerOptionsResolver { - configRoots = new Set(); options: Omit = {}; target: number | undefined; globalTypesPath: string | undefined; @@ -118,7 +117,6 @@ export class CompilerOptionsResolver { ) {} addConfig(options: RawVueCompilerOptions, rootDir: string) { - this.configRoots.add(rootDir); for (const key in options) { switch (key) { case 'target': diff --git a/packages/language-core/lib/plugins/vue-tsx.ts b/packages/language-core/lib/plugins/vue-tsx.ts index 7829cde983..8597cdeee6 100644 --- a/packages/language-core/lib/plugins/vue-tsx.ts +++ b/packages/language-core/lib/plugins/vue-tsx.ts @@ -3,12 +3,12 @@ import { computed } from 'alien-signals'; import * as path from 'path-browserify'; import { generateScript } from '../codegen/script'; import { generateTemplate } from '../codegen/template'; +import { CompilerOptionsResolver } from '../compilerOptions'; import { parseScriptRanges } from '../parsers/scriptRanges'; import { parseScriptSetupRanges } from '../parsers/scriptSetupRanges'; import { parseVueCompilerOptions } from '../parsers/vueCompilerOptions'; import type { Sfc, VueLanguagePlugin } from '../types'; import { computedSet } from '../utils/signals'; -import { CompilerOptionsResolver } from '../utils/ts'; export const tsCodegen = new WeakMap>(); From a66f2330b5aa6f811f571159245c46af0c36c534 Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Thu, 4 Sep 2025 18:02:48 +0800 Subject: [PATCH 10/20] refactor(typescript-plugin): explicitly request parameters (#5623) --- packages/typescript-plugin/index.ts | 121 ++++++++++++------ packages/typescript-plugin/lib/common.ts | 27 ++-- .../lib/requests/collectExtractProps.ts | 28 ++-- .../lib/requests/getComponentDirectives.ts | 16 +-- .../lib/requests/getComponentEvents.ts | 19 +-- .../lib/requests/getComponentNames.ts | 17 +-- .../lib/requests/getComponentProps.ts | 18 +-- .../lib/requests/getComponentSlots.ts | 21 +-- .../lib/requests/getElementAttrs.ts | 21 +-- .../lib/requests/getElementNames.ts | 16 +-- .../lib/requests/getImportPathForFile.ts | 11 +- .../lib/requests/getPropertiesAtLocation.ts | 24 ++-- .../typescript-plugin/lib/requests/index.ts | 60 ++++++--- .../typescript-plugin/lib/requests/types.ts | 9 -- .../typescript-plugin/lib/requests/utils.ts | 17 +-- 15 files changed, 204 insertions(+), 221 deletions(-) delete mode 100644 packages/typescript-plugin/lib/requests/types.ts diff --git a/packages/typescript-plugin/index.ts b/packages/typescript-plugin/index.ts index 08b4f9463d..3d553f85f6 100644 --- a/packages/typescript-plugin/index.ts +++ b/packages/typescript-plugin/index.ts @@ -2,6 +2,7 @@ import { createLanguageServicePlugin } from '@volar/typescript/lib/quickstart/cr import * as vue from '@vue/language-core'; import type * as ts from 'typescript'; import { createVueLanguageServiceProxy } from './lib/common'; +import type { Requests } from './lib/requests'; import { collectExtractProps } from './lib/requests/collectExtractProps'; import { getComponentDirectives } from './lib/requests/getComponentDirectives'; import { getComponentEvents } from './lib/requests/getComponentEvents'; @@ -12,7 +13,6 @@ import { getElementAttrs } from './lib/requests/getElementAttrs'; import { getElementNames } from './lib/requests/getElementNames'; import { getImportPathForFile } from './lib/requests/getImportPathForFile'; import { getPropertiesAtLocation } from './lib/requests/getPropertiesAtLocation'; -import type { RequestContext } from './lib/requests/types'; const windowsPathReg = /\\/g; const project2Service = new WeakMap< @@ -43,6 +43,7 @@ export = createLanguageServicePlugin( language, info.languageService, vueOptions, + fileName => fileName, ); // #3963 @@ -85,89 +86,131 @@ export = createLanguageServicePlugin( return; } - session.addProtocolHandler('_vue:projectInfo', ({ arguments: args }) => { - return (session as any).handlers.get('projectInfo')?.({ arguments: args }); + session.addProtocolHandler('_vue:projectInfo', request => { + return (session as any).handlers.get('projectInfo')?.(request); }); - session.addProtocolHandler('_vue:documentHighlights-full', ({ arguments: args }) => { - return (session as any).handlers.get('documentHighlights-full')?.({ arguments: args }); + session.addProtocolHandler('_vue:documentHighlights-full', request => { + return (session as any).handlers.get('documentHighlights-full')?.(request); }); - session.addProtocolHandler('_vue:encodedSemanticClassifications-full', ({ arguments: args }) => { - return (session as any).handlers.get('encodedSemanticClassifications-full')?.({ arguments: args }); + session.addProtocolHandler('_vue:encodedSemanticClassifications-full', request => { + return (session as any).handlers.get('encodedSemanticClassifications-full')?.(request); }); - session.addProtocolHandler('_vue:quickinfo', ({ arguments: args }) => { - return (session as any).handlers.get('quickinfo')?.({ arguments: args }); + session.addProtocolHandler('_vue:quickinfo', request => { + return (session as any).handlers.get('quickinfo')?.(request); }); - session.addProtocolHandler('_vue:collectExtractProps', ({ arguments: args }) => { + session.addProtocolHandler( + '_vue:collectExtractProps', + request => { + const [fileName, templateCodeRange]: Parameters = request.arguments; + const { language, program, sourceScript, virtualCode } = getLanguageServiceAndVirtualCode(fileName); + return { + response: collectExtractProps(ts, language, program, sourceScript, virtualCode, templateCodeRange), + }; + }, + ); + session.addProtocolHandler('_vue:getImportPathForFile', request => { + const [fileName, incomingFileName, preferences]: Parameters = + request.arguments; + const { languageServiceHost, program } = getLanguageService(fileName); return { - response: collectExtractProps.apply(getRequestContext(args[0]), args), + response: getImportPathForFile(ts, languageServiceHost, program, fileName, incomingFileName, preferences), }; }); - session.addProtocolHandler('_vue:getImportPathForFile', ({ arguments: args }) => { + session.addProtocolHandler('_vue:getPropertiesAtLocation', request => { + const [fileName, position]: Parameters = request.arguments; + const { language, program, sourceScript, virtualCode } = getLanguageServiceAndVirtualCode(fileName); return { - response: getImportPathForFile.apply(getRequestContext(args[0]), args), + response: getPropertiesAtLocation(ts, language, program, sourceScript, virtualCode, position), }; }); - session.addProtocolHandler('_vue:getPropertiesAtLocation', ({ arguments: args }) => { + session.addProtocolHandler('_vue:getComponentDirectives', request => { + const [fileName]: Parameters = request.arguments; + const { program } = getLanguageService(fileName); return { - response: getPropertiesAtLocation.apply(getRequestContext(args[0]), args), + response: getComponentDirectives(ts, program, fileName), }; }); - session.addProtocolHandler('_vue:getComponentDirectives', ({ arguments: args }) => { + session.addProtocolHandler('_vue:getComponentEvents', request => { + const [fileName, tag]: Parameters = request.arguments; + const { program } = getLanguageService(fileName); return { - response: getComponentDirectives.apply(getRequestContext(args[0]), args), + response: getComponentEvents(ts, program, fileName, tag), }; }); - session.addProtocolHandler('_vue:getComponentEvents', ({ arguments: args }) => { + session.addProtocolHandler('_vue:getComponentNames', request => { + const [fileName]: Parameters = request.arguments; + const { program } = getLanguageService(fileName); return { - response: getComponentEvents.apply(getRequestContext(args[0]), args), + response: getComponentNames(ts, program, fileName), }; }); - session.addProtocolHandler('_vue:getComponentNames', ({ arguments: args }) => { + session.addProtocolHandler('_vue:getComponentProps', request => { + const [fileName, tag]: Parameters = request.arguments; + const { program } = getLanguageService(fileName); return { - response: getComponentNames.apply(getRequestContext(args[0]), args) ?? [], + response: getComponentProps(ts, program, fileName, tag), }; }); - session.addProtocolHandler('_vue:getComponentProps', ({ arguments: args }) => { + session.addProtocolHandler('_vue:getComponentSlots', request => { + const [fileName]: Parameters = request.arguments; + const { program, virtualCode } = getLanguageServiceAndVirtualCode(fileName); return { - response: getComponentProps.apply(getRequestContext(args[0]), args), + response: getComponentSlots(ts, program, virtualCode), }; }); - session.addProtocolHandler('_vue:getComponentSlots', ({ arguments: args }) => { + session.addProtocolHandler('_vue:getElementAttrs', request => { + const [fileName, tag]: Parameters = request.arguments; + const { program } = getLanguageService(fileName); return { - response: getComponentSlots.apply(getRequestContext(args[0]), args), + response: getElementAttrs(ts, program, fileName, tag), }; }); - session.addProtocolHandler('_vue:getElementAttrs', ({ arguments: args }) => { + session.addProtocolHandler('_vue:getElementNames', request => { + const [fileName]: Parameters = request.arguments; + const { program } = getLanguageService(fileName); return { - response: getElementAttrs.apply(getRequestContext(args[0]), args), - }; - }); - session.addProtocolHandler('_vue:getElementNames', ({ arguments: args }) => { - return { - response: getElementNames.apply(getRequestContext(args[0]), args), + response: getElementNames(ts, program, fileName), }; }); projectService.logger.info('Vue specific commands are successfully added.'); } - function getRequestContext(fileName: string): RequestContext { - const fileAndProject = (info.session as any).getFileAndProject({ + function getLanguageServiceAndVirtualCode(fileName: string) { + const service = getLanguageService(fileName); + const sourceScript = service?.language.scripts.get(fileName); + if (!sourceScript) { + throw new Error('No source script found for file: ' + fileName); + } + const virtualCode = sourceScript.generated?.root; + if (!(virtualCode instanceof vue.VueVirtualCode)) { + throw new Error('No virtual code found for file: ' + fileName); + } + return { + ...service, + sourceScript, + virtualCode, + }; + } + + function getLanguageService(fileName: string) { + const { project } = (info.session as any).getFileAndProject({ file: fileName, projectFileName: undefined, }) as { file: ts.server.NormalizedPath; project: ts.server.Project; }; - const service = project2Service.get(fileAndProject.project); + const service = project2Service.get(project); if (!service) { - throw 'No RequestContext'; + throw new Error('No vue service for project: ' + project.getProjectName()); } + const [language, languageServiceHost, languageService] = service; return { typescript: ts, - languageService: service[2], - languageServiceHost: service[1], - language: service[0], + program: languageService.getProgram()!, + languageServiceHost, + language, }; } }, diff --git a/packages/typescript-plugin/lib/common.ts b/packages/typescript-plugin/lib/common.ts index e7710bcdc8..ca4d4554ce 100644 --- a/packages/typescript-plugin/lib/common.ts +++ b/packages/typescript-plugin/lib/common.ts @@ -4,23 +4,24 @@ import type * as ts from 'typescript'; const windowsPathReg = /\\/g; -export function createVueLanguageServiceProxy( +export function createVueLanguageServiceProxy( ts: typeof import('typescript'), - language: Language, + language: Language, languageService: ts.LanguageService, vueOptions: VueCompilerOptions, + asScriptId: (fileName: string) => T, ) { const proxyCache = new Map(); const getProxyMethod = (target: ts.LanguageService, p: string | symbol): Function | undefined => { switch (p) { case 'getCompletionsAtPosition': - return getCompletionsAtPosition(ts, language, vueOptions, target[p]); + return getCompletionsAtPosition(ts, language, asScriptId, vueOptions, target[p]); case 'getCompletionEntryDetails': return getCompletionEntryDetails(language, target[p]); case 'getCodeFixesAtPosition': return getCodeFixesAtPosition(target[p]); case 'getDefinitionAndBoundSpan': - return getDefinitionAndBoundSpan(ts, language, languageService, vueOptions, target[p]); + return getDefinitionAndBoundSpan(ts, language, asScriptId, languageService, vueOptions, target[p]); } }; @@ -43,9 +44,10 @@ export function createVueLanguageServiceProxy( }); } -function getCompletionsAtPosition( +function getCompletionsAtPosition( ts: typeof import('typescript'), - language: Language, + language: Language, + asScriptId: (fileName: string) => T, vueOptions: VueCompilerOptions, getCompletionsAtPosition: ts.LanguageService['getCompletionsAtPosition'], ): ts.LanguageService['getCompletionsAtPosition'] { @@ -61,7 +63,7 @@ function getCompletionsAtPosition( ); // filter global variables in template and styles - const sourceScript = language.scripts.get(fileName); + const sourceScript = language.scripts.get(asScriptId(fileName)); const root = sourceScript?.generated?.root; if (root instanceof VueVirtualCode) { const blocks = [ @@ -129,8 +131,8 @@ function getCompletionsAtPosition( }; } -function getCompletionEntryDetails( - language: Language, +function getCompletionEntryDetails( + language: Language, getCompletionEntryDetails: ts.LanguageService['getCompletionEntryDetails'], ): ts.LanguageService['getCompletionEntryDetails'] { return (...args) => { @@ -187,9 +189,10 @@ function getCodeFixesAtPosition( }; } -function getDefinitionAndBoundSpan( +function getDefinitionAndBoundSpan( ts: typeof import('typescript'), - language: Language, + language: Language, + asScriptId: (fileName: string) => T, languageService: ts.LanguageService, vueOptions: VueCompilerOptions, getDefinitionAndBoundSpan: ts.LanguageService['getDefinitionAndBoundSpan'], @@ -201,7 +204,7 @@ function getDefinitionAndBoundSpan( } const program = languageService.getProgram()!; - const sourceScript = language.scripts.get(fileName); + const sourceScript = language.scripts.get(asScriptId(fileName)); if (!sourceScript?.generated) { return result; } diff --git a/packages/typescript-plugin/lib/requests/collectExtractProps.ts b/packages/typescript-plugin/lib/requests/collectExtractProps.ts index 4aa3548111..9a99d5da1f 100644 --- a/packages/typescript-plugin/lib/requests/collectExtractProps.ts +++ b/packages/typescript-plugin/lib/requests/collectExtractProps.ts @@ -1,5 +1,5 @@ -import { isSemanticTokensEnabled, VueVirtualCode } from '@vue/language-core'; -import type { RequestContext } from './types'; +import { isSemanticTokensEnabled, type Language, type SourceScript, type VueVirtualCode } from '@vue/language-core'; +import type * as ts from 'typescript'; interface ExtractPropsInfo { name: string; @@ -8,25 +8,19 @@ interface ExtractPropsInfo { } export function collectExtractProps( - this: RequestContext, - fileName: string, + ts: typeof import('typescript'), + language: Language, + program: ts.Program, + sourceScript: SourceScript, + virtualCode: VueVirtualCode, templateCodeRange: [number, number], ): ExtractPropsInfo[] { - const { typescript: ts, languageService, language } = this; - - const sourceScript = language.scripts.get(fileName); - const root = sourceScript?.generated?.root; - if (!sourceScript?.generated || !(root instanceof VueVirtualCode)) { - return []; - } - const result = new Map(); - const program = languageService.getProgram()!; - const sourceFile = program.getSourceFile(fileName)!; + const sourceFile = program.getSourceFile(virtualCode.fileName)!; const checker = program.getTypeChecker(); - const script = sourceScript.generated.languagePlugin.typescript?.getServiceScript(root); - const maps = script ? [...language.maps.forEach(script.code)].map(([, map]) => map) : []; - const { sfc } = root; + const serviceScript = sourceScript.generated!.languagePlugin.typescript?.getServiceScript(virtualCode); + const maps = serviceScript ? [...language.maps.forEach(serviceScript.code)].map(([, map]) => map) : []; + const { sfc } = virtualCode; sourceFile.forEachChild(function visit(node) { if ( diff --git a/packages/typescript-plugin/lib/requests/getComponentDirectives.ts b/packages/typescript-plugin/lib/requests/getComponentDirectives.ts index 24f204975c..e2f6f765b6 100644 --- a/packages/typescript-plugin/lib/requests/getComponentDirectives.ts +++ b/packages/typescript-plugin/lib/requests/getComponentDirectives.ts @@ -1,5 +1,4 @@ -import { VueVirtualCode } from '@vue/language-core'; -import type { RequestContext } from './types'; +import type * as ts from 'typescript'; import { getVariableType } from './utils'; const builtInDirectives = new Set([ @@ -12,18 +11,11 @@ const builtInDirectives = new Set([ ]); export function getComponentDirectives( - this: RequestContext, + ts: typeof import('typescript'), + program: ts.Program, fileName: string, ): string[] { - const { typescript: ts, language, languageService } = this; - - const sourceScript = language.scripts.get(fileName); - const root = sourceScript?.generated?.root; - if (!sourceScript?.generated || !(root instanceof VueVirtualCode)) { - return []; - } - - const directives = getVariableType(ts, languageService, root, '__VLS_directives'); + const directives = getVariableType(ts, program, fileName, '__VLS_directives'); if (!directives) { return []; } diff --git a/packages/typescript-plugin/lib/requests/getComponentEvents.ts b/packages/typescript-plugin/lib/requests/getComponentEvents.ts index ceea3992f7..8490d5bc8a 100644 --- a/packages/typescript-plugin/lib/requests/getComponentEvents.ts +++ b/packages/typescript-plugin/lib/requests/getComponentEvents.ts @@ -1,28 +1,19 @@ -import { VueVirtualCode } from '@vue/language-core'; -import type { RequestContext } from './types'; +import type * as ts from 'typescript'; import { getComponentType, getVariableType } from './utils'; export function getComponentEvents( - this: RequestContext, + ts: typeof import('typescript'), + program: ts.Program, fileName: string, tag: string, ): string[] { - const { typescript: ts, language, languageService } = this; - - const sourceScript = language.scripts.get(fileName); - const root = sourceScript?.generated?.root; - if (!sourceScript?.generated || !(root instanceof VueVirtualCode)) { - return []; - } - - const program = languageService.getProgram()!; const checker = program.getTypeChecker(); - const components = getVariableType(ts, languageService, root, '__VLS_components'); + const components = getVariableType(ts, program, fileName, '__VLS_components'); if (!components) { return []; } - const componentType = getComponentType(ts, languageService, root, components, fileName, tag); + const componentType = getComponentType(ts, program, fileName, components, tag); if (!componentType) { return []; } diff --git a/packages/typescript-plugin/lib/requests/getComponentNames.ts b/packages/typescript-plugin/lib/requests/getComponentNames.ts index f4c4200b7c..b1317a9113 100644 --- a/packages/typescript-plugin/lib/requests/getComponentNames.ts +++ b/packages/typescript-plugin/lib/requests/getComponentNames.ts @@ -1,26 +1,17 @@ -import { VueVirtualCode } from '@vue/language-core'; -import type { RequestContext } from './types'; +import type * as ts from 'typescript'; import { getSelfComponentName, getVariableType } from './utils'; export function getComponentNames( - this: RequestContext, + ts: typeof import('typescript'), + program: ts.Program, fileName: string, ): string[] { - const { typescript: ts, language, languageService } = this; - - const sourceScript = language.scripts.get(fileName); - const root = sourceScript?.generated?.root; - if (!sourceScript?.generated || !(root instanceof VueVirtualCode)) { - return []; - } - - const names = getVariableType(ts, languageService, root, '__VLS_components') + const names = getVariableType(ts, program, fileName, '__VLS_components') ?.type ?.getProperties() .map(c => c.name) .filter(entry => !entry.includes('$') && !entry.startsWith('_')) ?? []; - names.push(getSelfComponentName(fileName)); return names; } diff --git a/packages/typescript-plugin/lib/requests/getComponentProps.ts b/packages/typescript-plugin/lib/requests/getComponentProps.ts index e30130a259..aa5d93e5b8 100644 --- a/packages/typescript-plugin/lib/requests/getComponentProps.ts +++ b/packages/typescript-plugin/lib/requests/getComponentProps.ts @@ -1,6 +1,4 @@ -import { VueVirtualCode } from '@vue/language-core'; import type * as ts from 'typescript'; -import type { RequestContext } from './types'; import { getComponentType, getVariableType } from './utils'; export interface ComponentPropInfo { @@ -13,30 +11,22 @@ export interface ComponentPropInfo { } export function getComponentProps( - this: RequestContext, + ts: typeof import('typescript'), + program: ts.Program, fileName: string, tag: string, ): ComponentPropInfo[] { - const { typescript: ts, language, languageService } = this; - - const sourceScript = language.scripts.get(fileName); - const root = sourceScript?.generated?.root; - if (!sourceScript?.generated || !(root instanceof VueVirtualCode)) { - return []; - } - - const components = getVariableType(ts, languageService, root, '__VLS_components'); + const components = getVariableType(ts, program, fileName, '__VLS_components'); if (!components) { return []; } - const componentType = getComponentType(ts, languageService, root, components, fileName, tag); + const componentType = getComponentType(ts, program, fileName, components, tag); if (!componentType) { return []; } const result = new Map(); - const program = languageService.getProgram()!; const checker = program.getTypeChecker(); for (const sig of componentType.getCallSignatures()) { diff --git a/packages/typescript-plugin/lib/requests/getComponentSlots.ts b/packages/typescript-plugin/lib/requests/getComponentSlots.ts index dce94857cb..611dc7c573 100644 --- a/packages/typescript-plugin/lib/requests/getComponentSlots.ts +++ b/packages/typescript-plugin/lib/requests/getComponentSlots.ts @@ -1,26 +1,19 @@ -import { tsCodegen, VueVirtualCode } from '@vue/language-core'; -import type { RequestContext } from './types'; +import { tsCodegen, type VueVirtualCode } from '@vue/language-core'; +import type * as ts from 'typescript'; import { getVariableType } from './utils'; export function getComponentSlots( - this: RequestContext, - fileName: string, + ts: typeof import('typescript'), + program: ts.Program, + virtualCode: VueVirtualCode, ): string[] { - const { typescript: ts, language, languageService } = this; - - const sourceScript = language.scripts.get(fileName); - const root = sourceScript?.generated?.root; - if (!sourceScript?.generated || !(root instanceof VueVirtualCode)) { - return []; - } - - const codegen = tsCodegen.get(root.sfc); + const codegen = tsCodegen.get(virtualCode.sfc); if (!codegen) { return []; } const assignName = codegen.getSetupSlotsAssignName() ?? `__VLS_slots`; - const slots = getVariableType(ts, languageService, root, assignName); + const slots = getVariableType(ts, program, virtualCode.fileName, assignName); if (!slots) { return []; } diff --git a/packages/typescript-plugin/lib/requests/getElementAttrs.ts b/packages/typescript-plugin/lib/requests/getElementAttrs.ts index 0361f57030..fbfeceb4d0 100644 --- a/packages/typescript-plugin/lib/requests/getElementAttrs.ts +++ b/packages/typescript-plugin/lib/requests/getElementAttrs.ts @@ -1,28 +1,19 @@ -import { VueVirtualCode } from '@vue/language-core'; -import type { RequestContext } from './types'; +import type * as ts from 'typescript'; import { getVariableType } from './utils'; export function getElementAttrs( - this: RequestContext, + ts: typeof import('typescript'), + program: ts.Program, fileName: string, - tagName: string, + tag: string, ): string[] { - const { typescript: ts, language, languageService } = this; - - const sourceScript = language.scripts.get(fileName); - const root = sourceScript?.generated?.root; - if (!sourceScript?.generated || !(root instanceof VueVirtualCode)) { - return []; - } - - const program = languageService.getProgram()!; const checker = program.getTypeChecker(); - const elements = getVariableType(ts, languageService, root, '__VLS_elements'); + const elements = getVariableType(ts, program, fileName, '__VLS_elements'); if (!elements) { return []; } - const elementType = elements.type.getProperty(tagName); + const elementType = elements.type.getProperty(tag); if (!elementType) { return []; } diff --git a/packages/typescript-plugin/lib/requests/getElementNames.ts b/packages/typescript-plugin/lib/requests/getElementNames.ts index 3e384963b3..f8b473512e 100644 --- a/packages/typescript-plugin/lib/requests/getElementNames.ts +++ b/packages/typescript-plugin/lib/requests/getElementNames.ts @@ -1,20 +1,12 @@ -import { VueVirtualCode } from '@vue/language-core'; -import type { RequestContext } from './types'; +import type * as ts from 'typescript'; import { getVariableType } from './utils'; export function getElementNames( - this: RequestContext, + ts: typeof import('typescript'), + program: ts.Program, fileName: string, ): string[] { - const { typescript: ts, language, languageService } = this; - - const sourceScript = language.scripts.get(fileName); - const root = sourceScript?.generated?.root; - if (!sourceScript?.generated || !(root instanceof VueVirtualCode)) { - return []; - } - - return getVariableType(ts, languageService, root, '__VLS_elements') + return getVariableType(ts, program, fileName, '__VLS_elements') ?.type ?.getProperties() .map(c => c.name) diff --git a/packages/typescript-plugin/lib/requests/getImportPathForFile.ts b/packages/typescript-plugin/lib/requests/getImportPathForFile.ts index 1b92b07f95..571490f788 100644 --- a/packages/typescript-plugin/lib/requests/getImportPathForFile.ts +++ b/packages/typescript-plugin/lib/requests/getImportPathForFile.ts @@ -1,16 +1,15 @@ import type * as ts from 'typescript'; -import type { RequestContext } from './types'; export function getImportPathForFile( - this: RequestContext, + ts: typeof import('typescript'), + languageServiceHost: ts.LanguageServiceHost, + program: ts.Program, fileName: string, incomingFileName: string, preferences: ts.UserPreferences, ): { path?: string } { - const { typescript: ts, languageService, languageServiceHost } = this; - const program = languageService.getProgram(); - const incomingFile = program?.getSourceFile(incomingFileName); - const sourceFile = program?.getSourceFile(fileName); + const incomingFile = program.getSourceFile(incomingFileName); + const sourceFile = program.getSourceFile(fileName); if (!program || !sourceFile || !incomingFile) { return {}; } diff --git a/packages/typescript-plugin/lib/requests/getPropertiesAtLocation.ts b/packages/typescript-plugin/lib/requests/getPropertiesAtLocation.ts index 1d9e12e217..061601a9b4 100644 --- a/packages/typescript-plugin/lib/requests/getPropertiesAtLocation.ts +++ b/packages/typescript-plugin/lib/requests/getPropertiesAtLocation.ts @@ -1,24 +1,17 @@ /// -import { isCompletionEnabled, VueVirtualCode } from '@vue/language-core'; +import { isCompletionEnabled, type Language, type SourceScript, type VueVirtualCode } from '@vue/language-core'; import type * as ts from 'typescript'; -import type { RequestContext } from './types'; export function getPropertiesAtLocation( - this: RequestContext, - fileName: string, + ts: typeof import('typescript'), + language: Language, + program: ts.Program, + sourceScript: SourceScript, + virtualCode: VueVirtualCode, position: number, ): string[] { - const { languageService, language, typescript: ts } = this; - - // mapping - const sourceScript = language.scripts.get(fileName); - const root = sourceScript?.generated?.root; - if (!sourceScript?.generated || !(root instanceof VueVirtualCode)) { - return []; - } - - const virtualScript = sourceScript.generated.languagePlugin.typescript?.getServiceScript(root); + const virtualScript = sourceScript.generated!.languagePlugin.typescript?.getServiceScript(virtualCode); if (!virtualScript) { return []; } @@ -41,8 +34,7 @@ export function getPropertiesAtLocation( } position += sourceScript.snapshot.getLength(); - const program = languageService.getProgram()!; - const sourceFile = program.getSourceFile(fileName); + const sourceFile = program.getSourceFile(virtualCode.fileName); if (!sourceFile) { return []; } diff --git a/packages/typescript-plugin/lib/requests/index.ts b/packages/typescript-plugin/lib/requests/index.ts index 5f71d49db8..da1284cfb3 100644 --- a/packages/typescript-plugin/lib/requests/index.ts +++ b/packages/typescript-plugin/lib/requests/index.ts @@ -1,22 +1,48 @@ import type * as ts from 'typescript'; -type Request any> = ( - ...args: Parameters -) => MaybePromise | null | undefined>; -type MaybePromise = T | Promise; +type Response = T | null | undefined | Promise; export interface Requests { - collectExtractProps: Request; - getImportPathForFile: Request; - getPropertiesAtLocation: Request; - getComponentDirectives: Request; - getComponentEvents: Request; - getComponentNames: Request; - getComponentProps: Request; - getComponentSlots: Request; - getElementAttrs: Request; - getElementNames: Request; - getDocumentHighlights: Request<(fileName: string, position: number) => ts.DocumentHighlights[]>; - getEncodedSemanticClassifications: Request<(fileName: string, span: ts.TextSpan) => ts.Classifications>; - getQuickInfoAtPosition: Request<(fileName: string, position: ts.LineAndCharacter) => string>; + collectExtractProps( + fileName: string, + templateCodeRange: [number, number], + ): Response>; + getImportPathForFile( + fileName: string, + incomingFileName: string, + preferences: ts.UserPreferences, + ): Response>; + getPropertiesAtLocation( + fileName: string, + position: number, + ): Response< + ReturnType + >; + getComponentDirectives(fileName: string): Response< + ReturnType + >; + getComponentEvents( + fileName: string, + tag: string, + ): Response>; + getComponentNames( + fileName: string, + ): Response>; + getComponentProps( + fileName: string, + tag: string, + ): Response>; + getComponentSlots( + fileName: string, + ): Response>; + getElementAttrs( + fileName: string, + tag: string, + ): Response>; + getElementNames(fileName: string): Response>; + getDocumentHighlights(fileName: string, position: number): Response; + getEncodedSemanticClassifications(fileName: string, span: ts.TextSpan): Response< + ts.Classifications + >; + getQuickInfoAtPosition(fileName: string, position: ts.LineAndCharacter): Response; } diff --git a/packages/typescript-plugin/lib/requests/types.ts b/packages/typescript-plugin/lib/requests/types.ts deleted file mode 100644 index 77ebce0657..0000000000 --- a/packages/typescript-plugin/lib/requests/types.ts +++ /dev/null @@ -1,9 +0,0 @@ -import type { Language } from '@vue/language-core'; -import type * as ts from 'typescript'; - -export interface RequestContext { - typescript: typeof ts; - languageService: ts.LanguageService; - languageServiceHost: ts.LanguageServiceHost; - language: Language; -} diff --git a/packages/typescript-plugin/lib/requests/utils.ts b/packages/typescript-plugin/lib/requests/utils.ts index bdabe5a0c4..8e46b317e3 100644 --- a/packages/typescript-plugin/lib/requests/utils.ts +++ b/packages/typescript-plugin/lib/requests/utils.ts @@ -1,17 +1,14 @@ -import type { VueVirtualCode } from '@vue/language-core'; import { camelize, capitalize } from '@vue/shared'; import * as path from 'path-browserify'; import type * as ts from 'typescript'; export function getComponentType( ts: typeof import('typescript'), - languageService: ts.LanguageService, - vueCode: VueVirtualCode, - components: NonNullable>, + program: ts.Program, fileName: string, + components: NonNullable>, tag: string, ) { - const program = languageService.getProgram()!; const checker = program.getTypeChecker(); const name = tag.split('.'); @@ -23,7 +20,7 @@ export function getComponentType( if (!componentSymbol) { const name = getSelfComponentName(fileName); if (name === capitalize(camelize(tag))) { - componentType = getVariableType(ts, languageService, vueCode, '__VLS_self')?.type; + componentType = getVariableType(ts, program, fileName, '__VLS_self')?.type; } } else { @@ -46,13 +43,11 @@ export function getSelfComponentName(fileName: string) { export function getVariableType( ts: typeof import('typescript'), - languageService: ts.LanguageService, - vueCode: VueVirtualCode, + program: ts.Program, + fileName: string, name: string, ) { - const program = languageService.getProgram()!; - - const tsSourceFile = program.getSourceFile(vueCode.fileName); + const tsSourceFile = program.getSourceFile(fileName); if (tsSourceFile) { const checker = program.getTypeChecker(); const node = searchVariableDeclarationNode(ts, tsSourceFile, name); From 320fc626c871e9ff17c0e4e92ea7ddb0c7641f34 Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Thu, 4 Sep 2025 18:18:10 +0800 Subject: [PATCH 11/20] fix(typescript-plugin): add isTsPlugin param for repl support --- packages/typescript-plugin/index.ts | 4 ++-- .../typescript-plugin/lib/requests/collectExtractProps.ts | 3 ++- .../typescript-plugin/lib/requests/getPropertiesAtLocation.ts | 3 ++- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/packages/typescript-plugin/index.ts b/packages/typescript-plugin/index.ts index 3d553f85f6..de44d369d9 100644 --- a/packages/typescript-plugin/index.ts +++ b/packages/typescript-plugin/index.ts @@ -104,7 +104,7 @@ export = createLanguageServicePlugin( const [fileName, templateCodeRange]: Parameters = request.arguments; const { language, program, sourceScript, virtualCode } = getLanguageServiceAndVirtualCode(fileName); return { - response: collectExtractProps(ts, language, program, sourceScript, virtualCode, templateCodeRange), + response: collectExtractProps(ts, language, program, sourceScript, virtualCode, templateCodeRange, true), }; }, ); @@ -120,7 +120,7 @@ export = createLanguageServicePlugin( const [fileName, position]: Parameters = request.arguments; const { language, program, sourceScript, virtualCode } = getLanguageServiceAndVirtualCode(fileName); return { - response: getPropertiesAtLocation(ts, language, program, sourceScript, virtualCode, position), + response: getPropertiesAtLocation(ts, language, program, sourceScript, virtualCode, position, true), }; }); session.addProtocolHandler('_vue:getComponentDirectives', request => { diff --git a/packages/typescript-plugin/lib/requests/collectExtractProps.ts b/packages/typescript-plugin/lib/requests/collectExtractProps.ts index 9a99d5da1f..5914dc152e 100644 --- a/packages/typescript-plugin/lib/requests/collectExtractProps.ts +++ b/packages/typescript-plugin/lib/requests/collectExtractProps.ts @@ -14,6 +14,7 @@ export function collectExtractProps( sourceScript: SourceScript, virtualCode: VueVirtualCode, templateCodeRange: [number, number], + isTsPlugin: boolean, ): ExtractPropsInfo[] { const result = new Map(); const sourceFile = program.getSourceFile(virtualCode.fileName)!; @@ -33,7 +34,7 @@ export function collectExtractProps( for (const map of maps) { let mapped = false; for ( - const source of map.toSourceLocation(name.getEnd() - sourceScript.snapshot.getLength()) + const source of map.toSourceLocation(name.getEnd() - (isTsPlugin ? sourceScript.snapshot.getLength() : 0)) ) { if ( source[0] >= sfc.template!.startTagEnd + templateCodeRange[0] diff --git a/packages/typescript-plugin/lib/requests/getPropertiesAtLocation.ts b/packages/typescript-plugin/lib/requests/getPropertiesAtLocation.ts index 061601a9b4..734051552a 100644 --- a/packages/typescript-plugin/lib/requests/getPropertiesAtLocation.ts +++ b/packages/typescript-plugin/lib/requests/getPropertiesAtLocation.ts @@ -10,6 +10,7 @@ export function getPropertiesAtLocation( sourceScript: SourceScript, virtualCode: VueVirtualCode, position: number, + isTsPlugin: boolean, ): string[] { const virtualScript = sourceScript.generated!.languagePlugin.typescript?.getServiceScript(virtualCode); if (!virtualScript) { @@ -32,7 +33,7 @@ export function getPropertiesAtLocation( if (!mapped) { return []; } - position += sourceScript.snapshot.getLength(); + position += isTsPlugin ? sourceScript.snapshot.getLength() : 0; const sourceFile = program.getSourceFile(virtualCode.fileName); if (!sourceFile) { From 9f15d5f7551b4fe3e5deb551f8936555b1849516 Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Fri, 5 Sep 2025 00:27:29 +0800 Subject: [PATCH 12/20] refactor(language-service): simplified getEmbeddedInfo --- packages/language-service/lib/plugins/css.ts | 12 ++--- .../lib/plugins/typescript-semantic-tokens.ts | 11 ++-- .../lib/plugins/vue-autoinsert-dotvalue.ts | 13 +++-- .../lib/plugins/vue-compiler-dom-errors.ts | 9 ++-- .../plugins/vue-component-semantic-tokens.ts | 13 +++-- .../lib/plugins/vue-document-drop.ts | 28 +++++----- .../lib/plugins/vue-document-highlights.ts | 13 +++-- .../lib/plugins/vue-extract-file.ts | 24 ++++----- .../lib/plugins/vue-global-types-error.ts | 13 +++-- .../lib/plugins/vue-inlayhints.ts | 9 ++-- .../lib/plugins/vue-missing-props-hints.ts | 17 +++---- .../lib/plugins/vue-scoped-class-links.ts | 15 +++--- .../language-service/lib/plugins/vue-sfc.ts | 23 ++++----- .../plugins/vue-suggest-define-assignment.ts | 11 ++-- .../lib/plugins/vue-template-ref-links.ts | 13 +++-- .../lib/plugins/vue-template.ts | 21 +++++--- .../lib/plugins/vue-twoslash-queries.ts | 19 +++---- packages/language-service/lib/utils.ts | 51 ++++--------------- 18 files changed, 133 insertions(+), 182 deletions(-) diff --git a/packages/language-service/lib/plugins/css.ts b/packages/language-service/lib/plugins/css.ts index fb168961a8..1ebfa4fe92 100644 --- a/packages/language-service/lib/plugins/css.ts +++ b/packages/language-service/lib/plugins/css.ts @@ -2,7 +2,7 @@ import type { LanguageServicePlugin, TextDocument, VirtualCode } from '@volar/la import { isRenameEnabled } from '@vue/language-core'; import { create as baseCreate, type Provide } from 'volar-service-css'; import type * as css from 'vscode-css-languageservice'; -import { getEmbeddedInfo } from '../utils'; +import { resolveEmbeddedCode } from '../utils'; export function create(): LanguageServicePlugin { const base = baseCreate({ scssDocumentSelector: ['scss', 'postcss'] }); @@ -54,19 +54,17 @@ export function create(): LanguageServicePlugin { document: TextDocument, position: css.Position, ) { - const info = getEmbeddedInfo(context, document, id => id.startsWith('style_')); - if (!info) { + const info = resolveEmbeddedCode(context, document.uri); + if (!info?.code.id.startsWith('style_')) { return false; } - const { sourceScript, virtualCode, root } = info; - - const block = root.sfc.styles.find(style => style.name === virtualCode.id); + const block = info.root.sfc.styles.find(style => style.name === info.code.id); if (!block) { return false; } let script: VirtualCode | undefined; - for (const [key, value] of sourceScript.generated.embeddedCodes) { + for (const [key, value] of info.script.generated.embeddedCodes) { if (key.startsWith('script_')) { script = value; break; diff --git a/packages/language-service/lib/plugins/typescript-semantic-tokens.ts b/packages/language-service/lib/plugins/typescript-semantic-tokens.ts index b1600f6ca5..0c0627e59e 100644 --- a/packages/language-service/lib/plugins/typescript-semantic-tokens.ts +++ b/packages/language-service/lib/plugins/typescript-semantic-tokens.ts @@ -1,6 +1,6 @@ import type { LanguageServicePlugin } from '@volar/language-service'; import { convertClassificationsToSemanticTokens } from 'volar-service-typescript/lib/semanticFeatures/semanticTokens'; -import { getEmbeddedInfo } from '../utils'; +import { resolveEmbeddedCode } from '../utils'; export function create( { getEncodedSemanticClassifications }: import('@vue/typescript-plugin/lib/requests').Requests, @@ -38,12 +38,10 @@ export function create( create(context) { return { async provideDocumentSemanticTokens(document, range, legend) { - const info = getEmbeddedInfo(context, document, 'main'); - if (!info) { + const info = resolveEmbeddedCode(context, document.uri); + if (info?.code.id !== 'main') { return; } - const { root } = info; - const start = document.offsetAt(range.start); const end = document.offsetAt(range.end); const span = { @@ -51,10 +49,9 @@ export function create( length: end - start, }; const classifications = await getEncodedSemanticClassifications( - root.fileName, + info.root.fileName, span, ); - if (classifications) { return convertClassificationsToSemanticTokens(document, span, legend, classifications); } diff --git a/packages/language-service/lib/plugins/vue-autoinsert-dotvalue.ts b/packages/language-service/lib/plugins/vue-autoinsert-dotvalue.ts index 265d046b0b..01a268ba31 100644 --- a/packages/language-service/lib/plugins/vue-autoinsert-dotvalue.ts +++ b/packages/language-service/lib/plugins/vue-autoinsert-dotvalue.ts @@ -1,7 +1,7 @@ import type { LanguageServicePlugin, TextDocument } from '@volar/language-service'; import { hyphenateAttr } from '@vue/language-core'; import type * as ts from 'typescript'; -import { getEmbeddedInfo } from '../utils'; +import { resolveEmbeddedCode } from '../utils'; export function create( ts: typeof import('typescript'), @@ -18,8 +18,8 @@ export function create( create(context) { return { async provideAutoInsertSnippet(document, selection, change) { - const info = getEmbeddedInfo(context, document, id => id.startsWith('script_')); - if (!info) { + const info = resolveEmbeddedCode(context, document.uri); + if (!info?.code.id.startsWith('script_')) { return; } @@ -34,10 +34,9 @@ export function create( let sourceOffset: number | undefined; - const { sourceScript, virtualCode, root } = info; - const { sfc } = root; + const { sfc } = info.root; const scriptBlocks = [sfc.script, sfc.scriptSetup].filter(block => !!block); - const map = context.language.maps.get(virtualCode, sourceScript); + const map = context.language.maps.get(info.code, info.script); if (!scriptBlocks.length) { return; @@ -61,7 +60,7 @@ export function create( } } - const props = await getPropertiesAtLocation(root.fileName, sourceOffset) ?? []; + const props = await getPropertiesAtLocation(info.root.fileName, sourceOffset) ?? []; if (props.some(prop => prop === 'value')) { return '${1:.value}'; } diff --git a/packages/language-service/lib/plugins/vue-compiler-dom-errors.ts b/packages/language-service/lib/plugins/vue-compiler-dom-errors.ts index e4581673da..bf1d957349 100644 --- a/packages/language-service/lib/plugins/vue-compiler-dom-errors.ts +++ b/packages/language-service/lib/plugins/vue-compiler-dom-errors.ts @@ -1,5 +1,5 @@ import type { Diagnostic, DiagnosticSeverity, LanguageServicePlugin } from '@volar/language-service'; -import { getEmbeddedInfo } from '../utils'; +import { resolveEmbeddedCode } from '../utils'; export function create(): LanguageServicePlugin { return { @@ -13,13 +13,12 @@ export function create(): LanguageServicePlugin { create(context) { return { provideDiagnostics(document) { - const info = getEmbeddedInfo(context, document, 'template'); - if (!info) { + const info = resolveEmbeddedCode(context, document.uri); + if (info?.code.id !== 'template') { return; } - const { root } = info; - const { template } = root.sfc; + const { template } = info.root.sfc; if (!template) { return; } diff --git a/packages/language-service/lib/plugins/vue-component-semantic-tokens.ts b/packages/language-service/lib/plugins/vue-component-semantic-tokens.ts index 1b301aed5c..f07aad54a4 100644 --- a/packages/language-service/lib/plugins/vue-component-semantic-tokens.ts +++ b/packages/language-service/lib/plugins/vue-component-semantic-tokens.ts @@ -1,7 +1,7 @@ import type { LanguageServicePlugin, SemanticToken } from '@volar/language-service'; import { forEachElementNode, hyphenateTag } from '@vue/language-core'; import type * as ts from 'typescript'; -import { getEmbeddedInfo } from '../utils'; +import { resolveEmbeddedCode } from '../utils'; export function create( { getComponentNames, getElementNames }: import('@vue/typescript-plugin/lib/requests').Requests, @@ -19,13 +19,12 @@ export function create( create(context) { return { async provideDocumentSemanticTokens(document, range, legend) { - const info = getEmbeddedInfo(context, document, 'template'); - if (!info) { + const info = resolveEmbeddedCode(context, document.uri); + if (info?.code.id !== 'template') { return; } - const { root } = info; - const { template } = root.sfc; + const { template } = info.root.sfc; if (!template?.ast) { return; } @@ -34,8 +33,8 @@ export function create( const start = document.offsetAt(range.start); const end = document.offsetAt(range.end); - const validComponentNames = await getComponentNames(root.fileName) ?? []; - const elements = new Set(await getElementNames(root.fileName) ?? []); + const validComponentNames = await getComponentNames(info.root.fileName) ?? []; + const elements = new Set(await getElementNames(info.root.fileName) ?? []); const components = new Set([ ...validComponentNames, ...validComponentNames.map(hyphenateTag), diff --git a/packages/language-service/lib/plugins/vue-document-drop.ts b/packages/language-service/lib/plugins/vue-document-drop.ts index dc4639c7ea..1ad7a466ec 100644 --- a/packages/language-service/lib/plugins/vue-document-drop.ts +++ b/packages/language-service/lib/plugins/vue-document-drop.ts @@ -6,7 +6,7 @@ import { getUserPreferences } from 'volar-service-typescript/lib/configs/getUser import { URI } from 'vscode-uri'; import { checkCasing, TagNameCasing } from '../nameCasing'; import { createAddComponentToOptionEdit, getLastImportNode } from '../plugins/vue-extract-file'; -import { getEmbeddedInfo } from '../utils'; +import { resolveEmbeddedCode } from '../utils'; export function create( ts: typeof import('typescript'), @@ -20,11 +20,13 @@ export function create( create(context) { return { async provideDocumentDropEdits(document, _position, dataTransfer) { - const info = getEmbeddedInfo(context, document, 'template', 'html'); - if (!info) { + if (document.languageId !== 'html') { + return; + } + const info = resolveEmbeddedCode(context, document.uri); + if (info?.code.id !== 'template') { return; } - const { sourceScript, root } = info; let importUri: string | undefined; for (const [mimeType, item] of dataTransfer) { @@ -32,22 +34,22 @@ export function create( importUri = item.value as string; } } - if (!importUri || !root.vueCompilerOptions.extensions.some(ext => importUri.endsWith(ext))) { + if (!importUri || !info.root.vueCompilerOptions.extensions.some(ext => importUri.endsWith(ext))) { return; } - const { sfc } = root; + const { sfc } = info.root; const script = sfc.scriptSetup ?? sfc.script; if (!script) { return; } - const casing = await checkCasing(context, sourceScript.id); + const casing = await checkCasing(context, info.script.id); const baseName = path.basename(importUri); const newName = capitalize(camelize(baseName.slice(0, baseName.lastIndexOf('.')))); const additionalEdit: WorkspaceEdit = {}; - const code = [...forEachEmbeddedCode(root)].find(code => + const code = [...forEachEmbeddedCode(info.root)].find(code => code.id === (sfc.scriptSetup ? 'scriptsetup_raw' : 'script_raw') )!; const lastImportNode = getLastImportNode(ts, script.ast); @@ -55,9 +57,9 @@ export function create( let importPath: string | undefined; - const serviceScript = sourceScript.generated.languagePlugin.typescript?.getServiceScript(root); + const serviceScript = info.script.generated.languagePlugin.typescript?.getServiceScript(info.root); if (serviceScript) { - const tsDocumentUri = context.encodeEmbeddedDocumentUri(sourceScript.id, serviceScript.code.id); + const tsDocumentUri = context.encodeEmbeddedDocumentUri(info.script.id, serviceScript.code.id); const tsDocument = context.documents.get( tsDocumentUri, serviceScript.code.languageId, @@ -66,7 +68,7 @@ export function create( const preferences = await getUserPreferences(context, tsDocument); importPath = ( await getImportPathForFile( - root.fileName, + info.root.fileName, incomingFileName, preferences, ) ?? {} @@ -74,7 +76,7 @@ export function create( } if (!importPath) { - importPath = path.relative(path.dirname(root.fileName), incomingFileName) + importPath = path.relative(path.dirname(info.root.fileName), incomingFileName) || importUri.slice(importUri.lastIndexOf('/') + 1); if (!importPath.startsWith('./') && !importPath.startsWith('../')) { @@ -82,7 +84,7 @@ export function create( } } - const embeddedDocumentUriStr = context.encodeEmbeddedDocumentUri(sourceScript.id, code.id).toString(); + const embeddedDocumentUriStr = context.encodeEmbeddedDocumentUri(info.script.id, code.id).toString(); additionalEdit.changes ??= {}; additionalEdit.changes[embeddedDocumentUriStr] = []; diff --git a/packages/language-service/lib/plugins/vue-document-highlights.ts b/packages/language-service/lib/plugins/vue-document-highlights.ts index 837d1584b1..02eb9fdb52 100644 --- a/packages/language-service/lib/plugins/vue-document-highlights.ts +++ b/packages/language-service/lib/plugins/vue-document-highlights.ts @@ -1,6 +1,6 @@ import type { DocumentHighlightKind, LanguageServicePlugin } from '@volar/language-service'; import { forEachElementNode, getElementTagOffsets } from '@vue/language-core'; -import { getEmbeddedInfo } from '../utils'; +import { resolveEmbeddedCode } from '../utils'; export function create( { getDocumentHighlights }: import('@vue/typescript-plugin/lib/requests').Requests, @@ -13,13 +13,12 @@ export function create( create(context) { return { async provideDocumentHighlights(document, position) { - const info = getEmbeddedInfo(context, document, 'main'); - if (!info) { + const info = resolveEmbeddedCode(context, document.uri); + if (info?.code.id !== 'main') { return; } - const { root } = info; - const { template } = root.sfc; + const { template } = info.root.sfc; const offset = document.offsetAt(position); if (template?.ast && offset >= template.startTagEnd && offset <= template.endTagStart) { @@ -36,10 +35,10 @@ export function create( } } - const result = await getDocumentHighlights(root.fileName, offset); + const result = await getDocumentHighlights(info.root.fileName, offset); return result - ?.filter(({ fileName }) => fileName === root.fileName) + ?.filter(({ fileName }) => fileName === info.root.fileName) .flatMap(({ highlightSpans }) => highlightSpans) .map(({ textSpan, kind }) => ({ range: { diff --git a/packages/language-service/lib/plugins/vue-extract-file.ts b/packages/language-service/lib/plugins/vue-extract-file.ts index 06235d620b..5d444c4edf 100644 --- a/packages/language-service/lib/plugins/vue-extract-file.ts +++ b/packages/language-service/lib/plugins/vue-extract-file.ts @@ -3,7 +3,7 @@ import type { ExpressionNode, TemplateChildNode } from '@vue/compiler-dom'; import { type Sfc, tsCodegen } from '@vue/language-core'; import type * as ts from 'typescript'; import { URI } from 'vscode-uri'; -import { getEmbeddedInfo } from '../utils'; +import { resolveEmbeddedCode } from '../utils'; interface ActionData { uri: string; @@ -38,13 +38,12 @@ export function create( return; } - const info = getEmbeddedInfo(context, document, 'template'); - if (!info) { + const info = resolveEmbeddedCode(context, document.uri); + if (info?.code.id !== 'template') { return; } - const { root } = info; - const { sfc } = root; + const { sfc } = info.root; const script = sfc.scriptSetup ?? sfc.script; if (!sfc.template || !script) { return; @@ -72,13 +71,12 @@ export function create( const { uri, range, newName } = codeAction.data as ActionData; const [startOffset, endOffset]: [number, number] = range; - const info = getEmbeddedInfo(context, { uri } as any, 'template'); - if (!info) { + const info = resolveEmbeddedCode(context, uri); + if (info?.code.id !== 'template') { return codeAction; } - const { sourceScript, virtualCode, root } = info; - const { sfc } = root; + const { sfc } = info.root; const script = sfc.scriptSetup ?? sfc.script; if (!sfc.template || !script) { return codeAction; @@ -89,15 +87,15 @@ export function create( return codeAction; } - const toExtract = await collectExtractProps(root.fileName, templateCodeRange) ?? []; + const toExtract = await collectExtractProps(info.root.fileName, templateCodeRange) ?? []; const templateInitialIndent = await context.env.getConfiguration!('vue.format.template.initialIndent') ?? true; const scriptInitialIndent = await context.env.getConfiguration!('vue.format.script.initialIndent') ?? false; - const document = context.documents.get(URI.parse(uri), virtualCode.languageId, virtualCode.snapshot); - const sfcDocument = context.documents.get(sourceScript.id, sourceScript.languageId, sourceScript.snapshot); + const document = context.documents.get(URI.parse(uri), info.code.languageId, info.code.snapshot); + const sfcDocument = context.documents.get(info.script.id, info.script.languageId, info.script.snapshot); const newUri = sfcDocument.uri.slice(0, sfcDocument.uri.lastIndexOf('/') + 1) + `${newName}.vue`; const lastImportNode = getLastImportNode(ts, script.ast); @@ -175,7 +173,7 @@ export function create( // editing vue sfc { textDocument: { - uri: sourceScript.id.toString(), + uri: info.script.id.toString(), version: null, }, edits: sfcEdits, diff --git a/packages/language-service/lib/plugins/vue-global-types-error.ts b/packages/language-service/lib/plugins/vue-global-types-error.ts index dde863dae7..7cfb9cf3d2 100644 --- a/packages/language-service/lib/plugins/vue-global-types-error.ts +++ b/packages/language-service/lib/plugins/vue-global-types-error.ts @@ -1,5 +1,5 @@ import type { DiagnosticSeverity, LanguageServicePlugin } from '@volar/language-service'; -import { getEmbeddedInfo } from '../utils'; +import { resolveEmbeddedCode } from '../utils'; export function create(): LanguageServicePlugin { return { @@ -13,17 +13,16 @@ export function create(): LanguageServicePlugin { create(context) { return { provideDiagnostics(document) { - const info = getEmbeddedInfo(context, document, 'root_tags'); - if (!info) { + const info = resolveEmbeddedCode(context, document.uri); + if (info?.code.id !== 'root_tags') { return; } - const { sourceScript, root } = info; - if (sourceScript.id.scheme !== 'file') { + if (info.script.id.scheme !== 'file') { return; } - const { vueCompilerOptions } = root; - const globalTypesPath = vueCompilerOptions.globalTypesPath(root.fileName); + const { vueCompilerOptions } = info.root; + const globalTypesPath = vueCompilerOptions.globalTypesPath(info.root.fileName); if (globalTypesPath) { return; } diff --git a/packages/language-service/lib/plugins/vue-inlayhints.ts b/packages/language-service/lib/plugins/vue-inlayhints.ts index 4e75c68be4..99d8510b48 100644 --- a/packages/language-service/lib/plugins/vue-inlayhints.ts +++ b/packages/language-service/lib/plugins/vue-inlayhints.ts @@ -1,7 +1,7 @@ import type { InlayHint, InlayHintKind, LanguageServicePlugin } from '@volar/language-service'; import { collectBindingIdentifiers, tsCodegen } from '@vue/language-core'; import type * as ts from 'typescript'; -import { getEmbeddedInfo } from '../utils'; +import { resolveEmbeddedCode } from '../utils'; export function create(ts: typeof import('typescript')): LanguageServicePlugin { return { @@ -12,11 +12,10 @@ export function create(ts: typeof import('typescript')): LanguageServicePlugin { create(context) { return { async provideInlayHints(document, range) { - const info = getEmbeddedInfo(context, document, 'main'); - if (!info) { + const info = resolveEmbeddedCode(context, document.uri); + if (info?.code.id !== 'main') { return; } - const { root } = info; const settings: Record = {}; async function getSettingEnabled(key: string) { @@ -24,7 +23,7 @@ export function create(ts: typeof import('typescript')): LanguageServicePlugin { } const result: InlayHint[] = []; - const { sfc } = root; + const { sfc } = info.root; const codegen = tsCodegen.get(sfc); const inlayHints = [ diff --git a/packages/language-service/lib/plugins/vue-missing-props-hints.ts b/packages/language-service/lib/plugins/vue-missing-props-hints.ts index 12ad3b8563..5a213dd90e 100644 --- a/packages/language-service/lib/plugins/vue-missing-props-hints.ts +++ b/packages/language-service/lib/plugins/vue-missing-props-hints.ts @@ -8,7 +8,7 @@ import type { import { hyphenateAttr, hyphenateTag } from '@vue/language-core'; import * as html from 'vscode-html-languageservice'; import { AttrNameCasing, checkCasing } from '../nameCasing'; -import { getEmbeddedInfo } from '../utils'; +import { resolveEmbeddedCode } from '../utils'; export function create( { getComponentNames, getElementNames, getComponentProps }: import('@vue/typescript-plugin/lib/requests').Requests, @@ -23,11 +23,10 @@ export function create( return { async provideInlayHints(document, range, cancellationToken) { - const info = getEmbeddedInfo(context, document, 'template'); - if (!info) { + const info = resolveEmbeddedCode(context, document.uri); + if (info?.code.id !== 'template') { return; } - const { sourceScript, root } = info; const enabled = await context.env.getConfiguration?.('vue.inlayHints.missingProps') ?? false; if (!enabled) { @@ -40,12 +39,12 @@ export function create( } const result: InlayHint[] = []; - const casing = await checkCasing(context, sourceScript.id); - const components = await getComponentNames(root.fileName) ?? []; + const casing = await checkCasing(context, info.script.id); + const components = await getComponentNames(info.root.fileName) ?? []; const componentProps: Record = {}; intrinsicElementNames ??= new Set( - await getElementNames(root.fileName) ?? [], + await getElementNames(info.root.fileName) ?? [], ); let token: html.TokenType; @@ -76,7 +75,7 @@ export function create( if (cancellationToken.isCancellationRequested) { break; } - componentProps[checkTag] = (await getComponentProps(root.fileName, checkTag) ?? []) + componentProps[checkTag] = (await getComponentProps(info.root.fileName, checkTag) ?? []) .filter(prop => prop.required) .map(prop => prop.name); } @@ -109,7 +108,7 @@ export function create( attrText = attrText.slice('v-model:'.length); } else if (attrText === 'v-model') { - attrText = root.vueCompilerOptions.target >= 3 ? 'modelValue' : 'value'; // TODO: support for experimentalModelPropName? + attrText = info.root.vueCompilerOptions.target >= 3 ? 'modelValue' : 'value'; // TODO: support for experimentalModelPropName? } else if (attrText.startsWith('v-on:')) { attrText = 'on-' + hyphenateAttr(attrText.slice('v-on:'.length)); diff --git a/packages/language-service/lib/plugins/vue-scoped-class-links.ts b/packages/language-service/lib/plugins/vue-scoped-class-links.ts index 8fc02c9e85..f5979c2ea1 100644 --- a/packages/language-service/lib/plugins/vue-scoped-class-links.ts +++ b/packages/language-service/lib/plugins/vue-scoped-class-links.ts @@ -1,6 +1,6 @@ import type { LanguageServicePlugin } from '@volar/language-service'; import { tsCodegen } from '@vue/language-core'; -import { getEmbeddedInfo } from '../utils'; +import { resolveEmbeddedCode } from '../utils'; export function create(): LanguageServicePlugin { return { @@ -11,16 +11,15 @@ export function create(): LanguageServicePlugin { create(context) { return { provideDocumentLinks(document) { - const info = getEmbeddedInfo(context, document, 'template'); - if (!info) { + const info = resolveEmbeddedCode(context, document.uri); + if (info?.code.id !== 'template') { return; } - const { sourceScript, root } = info; - const { sfc } = root; + const { sfc } = info.root; const codegen = tsCodegen.get(sfc); - const option = root.vueCompilerOptions.resolveStyleClassNames; + const option = info.root.vueCompilerOptions.resolveStyleClassNames; const scopedClasses = codegen?.getGeneratedTemplate()?.scopedClasses ?? []; const styleClasses = new Map(); @@ -30,8 +29,8 @@ export function create(): LanguageServicePlugin { continue; } - const styleDocumentUri = context.encodeEmbeddedDocumentUri(sourceScript.id, 'style_' + i); - const styleVirtualCode = sourceScript.generated.embeddedCodes.get('style_' + i); + const styleDocumentUri = context.encodeEmbeddedDocumentUri(info.script.id, 'style_' + i); + const styleVirtualCode = info.script.generated.embeddedCodes.get('style_' + i); if (!styleVirtualCode) { continue; } diff --git a/packages/language-service/lib/plugins/vue-sfc.ts b/packages/language-service/lib/plugins/vue-sfc.ts index 68095b32cd..6d57055199 100644 --- a/packages/language-service/lib/plugins/vue-sfc.ts +++ b/packages/language-service/lib/plugins/vue-sfc.ts @@ -11,7 +11,7 @@ import { VueVirtualCode } from '@vue/language-core'; import { create as createHtmlService } from 'volar-service-html'; import * as html from 'vscode-html-languageservice'; import { loadLanguageBlocks } from '../data'; -import { getEmbeddedInfo } from '../utils'; +import { resolveEmbeddedCode } from '../utils'; let sfcDataProvider: html.IHTMLDataProvider | undefined; @@ -24,16 +24,15 @@ export function create(): LanguageServicePlugin { return [sfcDataProvider]; }, async getFormattingOptions(document, options, context) { - const info = getEmbeddedInfo(context, document, 'root_tags'); - if (!info) { + const info = resolveEmbeddedCode(context, document.uri); + if (info?.code.id !== 'root_tags') { return {}; } - const { root } = info; const formatSettings = await context.env.getConfiguration?.('html.format') ?? {}; const blockTypes = ['template', 'script', 'style']; - for (const customBlock of root.sfc.customBlocks) { + for (const customBlock of info.root.sfc.customBlocks) { blockTypes.push(customBlock.type); } @@ -91,13 +90,12 @@ export function create(): LanguageServicePlugin { }, async provideDiagnostics(document, token) { - const info = getEmbeddedInfo(context, document, 'root_tags'); - if (!info) { + const info = resolveEmbeddedCode(context, document.uri); + if (info?.code.id !== 'root_tags') { return []; } - const { root } = info; - const { vueSfc, sfc } = root; + const { vueSfc, sfc } = info.root; if (!vueSfc) { return; } @@ -137,14 +135,13 @@ export function create(): LanguageServicePlugin { }, provideDocumentSymbols(document) { - const info = getEmbeddedInfo(context, document, 'root_tags'); - if (!info) { + const info = resolveEmbeddedCode(context, document.uri); + if (info?.code.id !== 'root_tags') { return; } - const { root } = info; const result: DocumentSymbol[] = []; - const { sfc } = root; + const { sfc } = info.root; if (sfc.template) { result.push({ diff --git a/packages/language-service/lib/plugins/vue-suggest-define-assignment.ts b/packages/language-service/lib/plugins/vue-suggest-define-assignment.ts index 46eda5ac48..121ae5370e 100644 --- a/packages/language-service/lib/plugins/vue-suggest-define-assignment.ts +++ b/packages/language-service/lib/plugins/vue-suggest-define-assignment.ts @@ -1,6 +1,6 @@ import type { CompletionItem, CompletionItemKind, LanguageServicePlugin } from '@volar/language-service'; import { type TextRange, tsCodegen } from '@vue/language-core'; -import { getEmbeddedInfo } from '../utils'; +import { resolveEmbeddedCode } from '../utils'; export function create(): LanguageServicePlugin { return { @@ -12,18 +12,17 @@ export function create(): LanguageServicePlugin { return { isAdditionalCompletion: true, async provideCompletionItems(document) { - const info = getEmbeddedInfo(context, document, id => id.startsWith('script_')); - if (!info) { + const info = resolveEmbeddedCode(context, document.uri); + if (!info?.code.id.startsWith('script_')) { return; } - const { virtualCode, root } = info; const enabled = await context.env.getConfiguration?.('vue.suggest.defineAssignment') ?? true; if (!enabled) { return; } - const { sfc } = root; + const { sfc } = info.root; const codegen = tsCodegen.get(sfc); const scriptSetup = sfc.scriptSetup; const scriptSetupRanges = codegen?.getScriptSetupRanges(); @@ -32,7 +31,7 @@ export function create(): LanguageServicePlugin { } const result: CompletionItem[] = []; - const mappings = [...context.language.maps.forEach(virtualCode)]; + const mappings = [...context.language.maps.forEach(info.code)]; addDefineCompletionItem( scriptSetupRanges.defineProps?.statement, diff --git a/packages/language-service/lib/plugins/vue-template-ref-links.ts b/packages/language-service/lib/plugins/vue-template-ref-links.ts index 73c74f72dc..f6f77a39f0 100644 --- a/packages/language-service/lib/plugins/vue-template-ref-links.ts +++ b/packages/language-service/lib/plugins/vue-template-ref-links.ts @@ -1,6 +1,6 @@ import type { LanguageServicePlugin } from '@volar/language-service'; import { tsCodegen } from '@vue/language-core'; -import { getEmbeddedInfo } from '../utils'; +import { resolveEmbeddedCode } from '../utils'; export function create(): LanguageServicePlugin { return { @@ -11,24 +11,23 @@ export function create(): LanguageServicePlugin { create(context) { return { provideDocumentLinks(document) { - const info = getEmbeddedInfo(context, document, 'scriptsetup_raw'); - if (!info) { + const info = resolveEmbeddedCode(context, document.uri); + if (info?.code.id !== 'scriptsetup_raw') { return; } - const { sourceScript, root } = info; - const { sfc } = root; + const { sfc } = info.root; const codegen = tsCodegen.get(sfc); if (!sfc.scriptSetup) { return; } - const templateVirtualCode = sourceScript.generated.embeddedCodes.get('template'); + const templateVirtualCode = info.script.generated.embeddedCodes.get('template'); if (!templateVirtualCode) { return; } - const templateDocumentUri = context.encodeEmbeddedDocumentUri(sourceScript.id, 'template'); + const templateDocumentUri = context.encodeEmbeddedDocumentUri(info.script.id, 'template'); const templateDocument = context.documents.get( templateDocumentUri, templateVirtualCode.languageId, diff --git a/packages/language-service/lib/plugins/vue-template.ts b/packages/language-service/lib/plugins/vue-template.ts index 0e330be4ed..7cc3784b9d 100644 --- a/packages/language-service/lib/plugins/vue-template.ts +++ b/packages/language-service/lib/plugins/vue-template.ts @@ -15,7 +15,7 @@ import * as html from 'vscode-html-languageservice'; import { URI, Utils } from 'vscode-uri'; import { loadModelModifiersData, loadTemplateData } from '../data'; import { AttrNameCasing, checkCasing, TagNameCasing } from '../nameCasing'; -import { getEmbeddedInfo } from '../utils'; +import { resolveEmbeddedCode } from '../utils'; const specialTags = new Set([ 'slot', @@ -158,11 +158,13 @@ export function create( }, async provideCompletionItems(document, position, completionContext, token) { - const info = getEmbeddedInfo(context, document, 'template', languageId); - if (!info) { + if (document.languageId !== languageId) { + return; + } + const info = resolveEmbeddedCode(context, document.uri); + if (info?.code.id !== 'template') { return; } - const { sourceScript, root } = info; const { result: completionList, @@ -172,8 +174,8 @@ export function create( propMap, }, } = await runWithVueData( - sourceScript.id, - root, + info.script.id, + info.root, () => baseServiceInstance.provideCompletionItems!( document, @@ -313,8 +315,11 @@ export function create( }, provideHover(document, position, token) { - const info = getEmbeddedInfo(context, document, 'template', languageId); - if (!info) { + if (document.languageId !== languageId) { + return; + } + const info = resolveEmbeddedCode(context, document.uri); + if (info?.code.id !== 'template') { return; } diff --git a/packages/language-service/lib/plugins/vue-twoslash-queries.ts b/packages/language-service/lib/plugins/vue-twoslash-queries.ts index b67f327790..3ee6e50049 100644 --- a/packages/language-service/lib/plugins/vue-twoslash-queries.ts +++ b/packages/language-service/lib/plugins/vue-twoslash-queries.ts @@ -1,5 +1,5 @@ import type { InlayHint, LanguageServicePlugin, Position } from '@volar/language-service'; -import { getEmbeddedInfo } from '../utils'; +import { resolveEmbeddedCode } from '../utils'; const twoslashTemplateReg = //g; const twoslashScriptReg = /(?<=^|\n)\s*\/\/\s*\^\?/g; @@ -15,20 +15,16 @@ export function create( create(context) { return { async provideInlayHints(document, range) { - const info = getEmbeddedInfo( - context, - document, - id => id === 'template' || id.startsWith('script_'), - ); - if (!info) { + const info = resolveEmbeddedCode(context, document.uri); + if (info?.code.id !== 'template' && !info?.code.id.startsWith('script_')) { return; } - const { sourceScript, virtualCode, root } = info; const hoverOffsets: [Position, number][] = []; const inlayHints: InlayHint[] = []; + const twoslashReg = info.code.id === 'template' ? twoslashTemplateReg : twoslashScriptReg; + const sourceDocument = context.documents.get(info.script.id, info.script.languageId, info.script.snapshot); - const twoslashReg = virtualCode.id === 'template' ? twoslashTemplateReg : twoslashScriptReg; for (const pointer of document.getText(range).matchAll(twoslashReg)) { const offset = pointer.index + pointer[0].indexOf('^?') + document.offsetAt(range.start); const position = document.positionAt(offset); @@ -41,12 +37,11 @@ export function create( ]); } - const sourceDocument = context.documents.get(sourceScript.id, sourceScript.languageId, sourceScript.snapshot); for (const [pointerPosition, hoverOffset] of hoverOffsets) { - const map = context.language.maps.get(virtualCode, sourceScript); + const map = context.language.maps.get(info.code, info.script); for (const [sourceOffset] of map.toSourceLocation(hoverOffset)) { const quickInfo = await getQuickInfoAtPosition( - root.fileName, + info.root.fileName, sourceDocument.positionAt(sourceOffset), ); if (quickInfo) { diff --git a/packages/language-service/lib/utils.ts b/packages/language-service/lib/utils.ts index f915843766..50aa552e5f 100644 --- a/packages/language-service/lib/utils.ts +++ b/packages/language-service/lib/utils.ts @@ -1,52 +1,21 @@ -import { type LanguageServiceContext, type SourceScript, type TextDocument } from '@volar/language-service'; -import { VueVirtualCode } from '@vue/language-core'; +import type { LanguageServiceContext, SourceScript } from '@volar/language-service'; +import type { VueVirtualCode } from '@vue/language-core'; import { URI } from 'vscode-uri'; -export function getEmbeddedInfo( +export function resolveEmbeddedCode( context: LanguageServiceContext, - document: TextDocument, - embeddedCodeId?: string | ((id: string) => boolean), - languageId?: string, + uriStr: string, ) { - const uri = URI.parse(document.uri); + const uri = URI.parse(uriStr); const decoded = context.decodeEmbeddedDocumentUri(uri); if (!decoded) { return; } - - if (embeddedCodeId) { - if (typeof embeddedCodeId === 'string') { - if (decoded[1] !== embeddedCodeId) { - return; - } - } - else if (!embeddedCodeId(decoded[1])) { - return; - } - } - - if (languageId && document.languageId !== languageId) { - return; - } - - const sourceScript = context.language.scripts.get(decoded[0]); - if (!sourceScript?.generated) { - return; - } - - const virtualCode = sourceScript.generated.embeddedCodes.get(decoded[1]); - if (!virtualCode) { - return; - } - - const root = sourceScript.generated.root; - if (!(root instanceof VueVirtualCode)) { - return; - } - + const sourceScript = context.language.scripts.get(decoded[0])!; + const code = sourceScript.generated!.embeddedCodes.get(decoded[1])!; return { - sourceScript: sourceScript as Required>, - virtualCode, - root, + script: sourceScript as Required>, + code, + root: sourceScript.generated!.root as VueVirtualCode, }; } From fc6b99217f62454ace34b9de14a1ac07840ca0f1 Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Fri, 5 Sep 2025 01:37:43 +0800 Subject: [PATCH 13/20] chore(vscode): revert extension display name revert #5582 --- extensions/vscode/lib/generated-meta.ts | 4 ++-- extensions/vscode/lib/welcome.ts | 7 +++---- extensions/vscode/package.json | 2 +- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/extensions/vscode/lib/generated-meta.ts b/extensions/vscode/lib/generated-meta.ts index 9b070f905b..15a5674918 100644 --- a/extensions/vscode/lib/generated-meta.ts +++ b/extensions/vscode/lib/generated-meta.ts @@ -4,8 +4,8 @@ // Meta info export const publisher = 'Vue'; export const name = 'volar'; -export const version = '3.0.5'; -export const displayName = 'Vue.js'; +export const version = '3.0.6'; +export const displayName = 'Vue (Official)'; export const description = 'Language Support for Vue'; export const extensionId = `${publisher}.${name}`; diff --git a/extensions/vscode/lib/welcome.ts b/extensions/vscode/lib/welcome.ts index a2088ae82f..de8b174c5c 100644 --- a/extensions/vscode/lib/welcome.ts +++ b/extensions/vscode/lib/welcome.ts @@ -40,7 +40,7 @@ export function execute(context: vscode.ExtensionContext) { } function getWelcomeHtml(context: vscode.ExtensionContext) { - const version = context.extension.packageJSON.version; + const { version, displayName } = context.extension.packageJSON; return /* HTML */ ` @@ -48,7 +48,7 @@ function getWelcomeHtml(context: vscode.ExtensionContext) { - Vue.js + ${displayName}