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": "`
+
+
+
+ {{ foo }}
+
+
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}