diff --git a/extensions/vscode/package.json b/extensions/vscode/package.json
index d7359d7a65..55bc5600ee 100644
--- a/extensions/vscode/package.json
+++ b/extensions/vscode/package.json
@@ -254,34 +254,6 @@
"default": "off",
"description": "Traces the communication between VS Code and the language server."
},
- "vue.server.hybridMode": {
- "type": [
- "boolean",
- "string"
- ],
- "default": "auto",
- "enum": [
- "auto",
- "typeScriptPluginOnly",
- true,
- false
- ],
- "enumDescriptions": [
- "Automatically detect and enable TypeScript Plugin/Hybrid Mode in a safe environment.",
- "Only enable Vue TypeScript Plugin but disable Hybrid Mode.",
- "Enable TypeScript Plugin/Hybrid Mode.",
- "Disable TypeScript Plugin/Hybrid Mode."
- ],
- "description": "Vue language server only handles CSS and HTML language support, and tsserver takes over TS language support via TS plugin."
- },
- "vue.server.compatibleExtensions": {
- "type": "array",
- "items": {
- "type": "string"
- },
- "default": [],
- "description": "Set compatible extensions to skip automatic detection of Hybrid Mode."
- },
"vue.server.includeLanguages": {
"type": "array",
"items": {
@@ -291,14 +263,6 @@
"vue"
]
},
- "vue.server.maxOldSpaceSize": {
- "type": [
- "number",
- "null"
- ],
- "default": null,
- "description": "Set --max-old-space-size option on server process. If you have problem on frequently \"Request textDocument/** failed.\" error, try setting higher memory(MB) on it."
- },
"vue.doctor.status": {
"type": "boolean",
"default": true,
@@ -330,11 +294,6 @@
"customBlocks"
]
},
- "vue.updateImportsOnFileMove.enabled": {
- "type": "boolean",
- "default": true,
- "description": "Enabled update imports on file move."
- },
"vue.codeActions.enabled": {
"type": "boolean",
"default": true,
@@ -472,47 +431,37 @@
"title": "Split
- `)).items.map(item => item.label)
- ).toMatchInlineSnapshot(`
+ let vFoo!: FunctionDirective;
+
+ `)).items.map(item => item.label)
+ ).toMatchInlineSnapshot(`
[
"attr",
"prop"
]
`);
- });
-
- it('$event argument', async () => {
- await requestCompletionItem('fixture.vue', 'vue', ``, 'event');
- });
+});
- it('
- `, 'foo');
- });
+test('
+ `, 'foo');
+});
-
- `, 'default');
- });
+ };
+
+ `, 'default');
+});
- it('#2454', async () => {
- await requestCompletionItem('fixture.vue', 'vue', `
-
+test.skip('#2454', async () => {
+ await requestCompletionItemToVueServer('fixture.vue', 'vue', `
+
-
-
-
- `, 'v-loading');
- });
+
+
+
+ `, 'v-loading');
+});
- it('#2511', async () => {
- await prepareDocument('tsconfigProject/component-for-auto-import.vue', 'vue', ``);
- expect(
- (await requestCompletionItem('tsconfigProject/fixture.vue', 'vue', `
-
- `, 'ComponentForAutoImport')).textEdit
- ).toMatchInlineSnapshot(`
+test.skip('#2511', async () => {
+ await prepareDocument('tsconfigProject/component-for-auto-import.vue', 'vue', ``);
+ expect(
+ (await requestCompletionItemToTsServer('tsconfigProject/fixture.vue', 'vue', `
+
+ `, 'ComponentForAutoImport'))
+ ).toMatchInlineSnapshot(`
{
"newText": "import componentForAutoImport$1 from './component-for-auto-import.vue';",
"range": {
@@ -289,56 +282,52 @@ describe('Completions', async () => {
},
}
`);
- });
-
- it('#3658', async () => {
- await requestCompletionItem('fixture.vue', 'vue', `
-
-
-
- {{ fo| }}
-
-
-
- `, 'foo');
- });
+});
- it('#4639', async () => {
- await requestCompletionItem('fixture.vue', 'vue', `
-
-
-
- `, 'capture');
- });
+test('#3658', async () => {
+ await requestCompletionItemToTsServer('fixture.vue', 'vue', `
+
+
+
+ {{ fo| }}
+
+
+
+ `, 'foo');
+});
- it('Alias path', async () => {
- await requestCompletionItem('tsconfigProject/fixture.vue', 'vue', `
-
- `, 'empty.vue');
- });
+test('#4639', async () => {
+ await requestCompletionItemToVueServer('fixture.vue', 'vue', `
+
+
+
+ `, 'capture');
+});
- it('Relative path', async () => {
- await requestCompletionItem('tsconfigProject/fixture.vue', 'vue', `
-
- `, 'empty.vue');
- });
+test('Alias path', async () => {
+ await requestCompletionItemToTsServer('tsconfigProject/fixture.vue', 'vue', `
+
+ `, 'empty.vue');
+});
- it('Component auto import', async () => {
- await prepareDocument('tsconfigProject/ComponentForAutoImport.vue', 'vue', ``);
- expect(
- (await requestCompletionItem('tsconfigProject/fixture.vue', 'vue', `
-
+test('Relative path', async () => {
+ await requestCompletionItemToTsServer('tsconfigProject/fixture.vue', 'vue', `
+
+ `, 'empty.vue');
+});
-
-
-
- `, 'ComponentForAutoImport'))
- ).toMatchInlineSnapshot(`
+test.skip('Component auto import', async () => {
+ expect(
+ (await requestCompletionItemToTsServer('tsconfigProject/fixture.vue', 'vue', `
+
+
+
+ `, 'Empty'))
+ ).toMatchInlineSnapshot(`
{
"additionalTextEdits": [
{
@@ -391,153 +380,180 @@ describe('Completions', async () => {
},
}
`);
- });
+});
- it('core#8811', async () => {
- await requestCompletionItem('tsconfigProject/fixture.vue', 'vue', `
-
+ };
+
+
+
+
+ `, ':-foo-bar');
+});
+
+test('#4796', async () => {
+ expect(
+ (await requestCompletionItemToVueServer('tsconfigProject/fixture.vue', 'vue', `
-
+
- `, ':-foo-bar');
- });
- it('#4796', async () => {
- expect(
- (await requestCompletionItem('tsconfigProject/fixture.vue', 'vue', `
-
-
-
+
+ `, ':msg'))
+ ).toMatchInlineSnapshot(`
+ {
+ "documentation": {
+ "kind": "markdown",
+ "value": "The message to display",
+ },
+ "insertTextFormat": 1,
+ "kind": 5,
+ "label": ":msg",
+ "sortText": " :msg",
+ "textEdit": {
+ "newText": ":msg="$1"",
+ "range": {
+ "end": {
+ "character": 20,
+ "line": 2,
+ },
+ "start": {
+ "character": 16,
+ "line": 2,
+ },
+ },
+ },
+ }
+ `);
+});
-
- `, ':msg'))
- ).toMatchInlineSnapshot(`
- {
- "documentation": {
- "kind": "markdown",
- "value": "The message to display",
- },
- "insertTextFormat": 1,
- "kind": 5,
- "label": ":msg",
- "sortText": " :msg",
- "textEdit": {
- "newText": ":msg="$1"",
- "range": {
- "end": {
- "character": 21,
- "line": 2,
- },
- "start": {
- "character": 17,
- "line": 2,
- },
- },
- },
- }
- `);
- });
+test('Auto insert defines', async () => {
+ expect(
+ (await requestCompletionItemToVueServer('tsconfigProject/fixture.vue', 'vue', `
+
+ `, 'props'))
+ ).toMatchInlineSnapshot(`
+ {
+ "additionalTextEdits": [
+ {
+ "newText": "const props = ",
+ "range": {
+ "end": {
+ "character": 3,
+ "line": 2,
+ },
+ "start": {
+ "character": 3,
+ "line": 2,
+ },
+ },
+ },
+ ],
+ "commitCharacters": [
+ ".",
+ ",",
+ ";",
+ ],
+ "kind": 6,
+ "label": "props",
+ }
+ `);
+});
- it('Auto insert defines', async () => {
- expect(
- (await requestCompletionItem('tsconfigProject/fixture.vue', 'vue', `
-
- `, 'props'))
- ).toMatchInlineSnapshot(`
- {
- "additionalTextEdits": [
- {
- "newText": "const props = ",
- "range": {
- "end": {
- "character": 4,
- "line": 2,
- },
- "start": {
- "character": 4,
- "line": 2,
- },
- },
- },
- ],
- "commitCharacters": [
- ".",
- ",",
- ";",
- ],
- "kind": 6,
- "label": "props",
- }
- `);
- });
+const openedDocuments: TextDocument[] = [];
- const openedDocuments: TextDocument[] = [];
+afterEach(async () => {
+ const server = await getLanguageServer();
+ for (const document of openedDocuments) {
+ await server.close(document.uri);
+ }
+ openedDocuments.length = 0;
+});
- afterEach(async () => {
+async function requestCompletionItemToVueServer(fileName: string, languageId: string, content: string, itemLabel: string) {
+ const completions = await requestCompletionListToVueServer(fileName, languageId, content);
+ let completion = completions.items.find(item => item.label === itemLabel);
+ expect(completion).toBeDefined();
+ if (completion!.data) {
const server = await getLanguageServer();
- for (const document of openedDocuments) {
- await server.closeTextDocument(document.uri);
- }
- openedDocuments.length = 0;
- });
-
- async function requestCompletionItem(fileName: string, languageId: string, content: string, itemLabel: string) {
- const completions = await requestCompletionList(fileName, languageId, content);
- let completion = completions.items.find(item => item.label === itemLabel);
+ completion = await server.vueserver.sendCompletionResolveRequest(completion!);
expect(completion).toBeDefined();
- if (completion!.data) {
- const server = await getLanguageServer();
- completion = await server.sendCompletionResolveRequest(completion!);
- expect(completion).toBeDefined();
- }
- return completion!;
}
+ return completion!;
+}
+
+async function requestCompletionListToVueServer(fileName: string, languageId: string, content: string) {
+ const offset = content.indexOf('|');
+ expect(offset).toBeGreaterThanOrEqual(0);
+ content = content.slice(0, offset) + content.slice(offset + 1);
+
+ const server = await getLanguageServer();
+ let document = await prepareDocument(fileName, languageId, content);
+
+ const position = document.positionAt(offset);
+ const completions = await server.vueserver.sendCompletionRequest(document.uri, position);
+ expect(completions).toBeDefined();
+
+ return completions!;
+}
+
+async function requestCompletionItemToTsServer(fileName: string, languageId: string, content: string, itemLabel: string) {
+ const completions = await requestCompletionListToTsServer(fileName, languageId, content);
+ let completion = completions.find((item: any) => item.name === itemLabel);
+ expect(completion).toBeDefined();
+ return completion!;
+}
+
+async function requestCompletionListToTsServer(fileName: string, languageId: string, content: string) {
+ const offset = content.indexOf('|');
+ expect(offset).toBeGreaterThanOrEqual(0);
+ content = content.slice(0, offset) + content.slice(offset + 1);
+
+ const server = await getLanguageServer();
+ let document = await prepareDocument(fileName, languageId, content);
+
+ const res = await server.tsserver.message({
+ seq: server.nextSeq(),
+ command: 'completions',
+ arguments: {
+ file: URI.parse(document.uri).fsPath,
+ position: offset,
+ },
+ });
+ expect(res.success).toBe(true);
- async function requestCompletionList(fileName: string, languageId: string, content: string) {
- const offset = content.indexOf('|');
- expect(offset).toBeGreaterThanOrEqual(0);
- content = content.slice(0, offset) + content.slice(offset + 1);
-
- const server = await getLanguageServer();
- let document = await prepareDocument(fileName, languageId, content);
-
- const position = document.positionAt(offset);
- const completions = await server.sendCompletionRequest(document.uri, position);
- expect(completions).toBeDefined();
-
- return completions!;
- }
+ return res.body;
+}
- async function prepareDocument(fileName: string, languageId: string, content: string) {
- const server = await getLanguageServer();
- const uri = URI.file(`${testWorkspacePath}/${fileName}`);
- const document = await server.openInMemoryDocument(uri.toString(), languageId, content);
- if (openedDocuments.every(d => d.uri !== document.uri)) {
- openedDocuments.push(document);
- }
- return document;
+async function prepareDocument(fileName: string, languageId: string, content: string) {
+ const server = await getLanguageServer();
+ const uri = URI.file(`${testWorkspacePath}/${fileName}`);
+ const document = await server.open(uri.toString(), languageId, content);
+ if (openedDocuments.every(d => d.uri !== document.uri)) {
+ openedDocuments.push(document);
}
-});
+ return document;
+}
diff --git a/packages/language-server/tests/definitions.spec.ts b/packages/language-server/tests/definitions.spec.ts
index 39b57b36e8..1b6d834c65 100644
--- a/packages/language-server/tests/definitions.spec.ts
+++ b/packages/language-server/tests/definitions.spec.ts
@@ -1,149 +1,152 @@
-import { Location, TextDocument } from '@volar/language-server';
-import { afterEach, describe, expect, it } from 'vitest';
+import { TextDocument } from '@volar/language-server';
+import { afterEach, expect, test } from 'vitest';
import { URI } from 'vscode-uri';
import { getLanguageServer, testWorkspacePath } from './server.js';
-describe('Definitions', async () => {
-
- it('TS to vue', async () => {
- expect(
- await requestDefinition('tsconfigProject/fixture1.ts', 'typescript', `import C|omponent from './empty.vue';`)
- ).toMatchInlineSnapshot(`
- [
- {
- "range": {
- "end": {
- "character": 0,
- "line": 0,
- },
- "start": {
- "character": 0,
- "line": 0,
- },
- },
- "uri": "file://\${testWorkspacePath}/tsconfigProject/empty.vue",
- },
- ]
- `);
- expect(
- await requestDefinition('tsconfigProject/fixture2.ts', 'typescript', `import Component from '|./empty.vue';`)
- ).toMatchInlineSnapshot(`
- [
- {
- "range": {
- "end": {
- "character": 0,
- "line": 0,
- },
- "start": {
- "character": 0,
- "line": 0,
- },
- },
- "uri": "file://\${testWorkspacePath}/tsconfigProject/empty.vue",
- },
- ]
- `);
- });
+test('TS to vue', async () => {
+ expect(
+ await requestDefinition('tsconfigProject/fixture1.ts', 'typescript', `import C|omponent from './empty.vue';`)
+ ).toMatchInlineSnapshot(`
+ [
+ {
+ "end": {
+ "line": 1,
+ "offset": 1,
+ },
+ "file": "\${testWorkspacePath}/tsconfigProject/empty.vue",
+ "start": {
+ "line": 1,
+ "offset": 1,
+ },
+ },
+ ]
+ `);
+ expect(
+ await requestDefinition('tsconfigProject/fixture2.ts', 'typescript', `import Component from '|./empty.vue';`)
+ ).toMatchInlineSnapshot(`
+ [
+ {
+ "end": {
+ "line": 1,
+ "offset": 1,
+ },
+ "file": "\${testWorkspacePath}/tsconfigProject/empty.vue",
+ "start": {
+ "line": 1,
+ "offset": 1,
+ },
+ },
+ ]
+ `);
+});
- it('Alias path', async () => {
- await prepareDocument('tsconfigProject/foo.ts', 'typescript', `export const foo = 'foo';`);
- expect(
- await requestDefinition('tsconfigProject/fixture.vue', 'vue', `
-
- `)
- ).toMatchInlineSnapshot(`
- [
- {
- "range": {
- "end": {
- "character": 25,
- "line": 0,
- },
- "start": {
- "character": 0,
- "line": 0,
- },
- },
- "uri": "file://\${testWorkspacePath}/tsconfigProject/foo.ts",
- },
- ]
- `);
- });
+test('Alias path', async () => {
+ await prepareDocument('tsconfigProject/foo.ts', 'typescript', `export const foo = 'foo';`);
+ expect(
+ await requestDefinition('tsconfigProject/fixture.vue', 'vue', `
+
+ `)
+ ).toMatchInlineSnapshot(`
+ [
+ {
+ "contextEnd": {
+ "line": 1,
+ "offset": 26,
+ },
+ "contextStart": {
+ "line": 1,
+ "offset": 1,
+ },
+ "end": {
+ "line": 1,
+ "offset": 17,
+ },
+ "file": "\${testWorkspacePath}/tsconfigProject/foo.ts",
+ "start": {
+ "line": 1,
+ "offset": 14,
+ },
+ },
+ ]
+ `);
+});
- it('#2600', async () => {
- await prepareDocument('tsconfigProject/foo.vue', 'vue', `
-
- {{ msg }}
-
+test('#2600', async () => {
+ await prepareDocument('tsconfigProject/foo.vue', 'vue', `
+
+ {{ msg }}
+
-
+ `);
+ expect(
+ await requestDefinition('tsconfigProject/fixture.vue', 'vue', `
+
- `);
- expect(
- await requestDefinition('tsconfigProject/fixture.vue', 'vue', `
-
- `)
- ).toMatchInlineSnapshot(`
- [
- {
- "range": {
- "end": {
- "character": 0,
- "line": 0,
- },
- "start": {
- "character": 0,
- "line": 0,
- },
- },
- "uri": "file://\${testWorkspacePath}/tsconfigProject/foo.vue",
- },
- ]
- `);
- });
-
- const openedDocuments: TextDocument[] = [];
+ `)
+ ).toMatchInlineSnapshot(`
+ [
+ {
+ "end": {
+ "line": 1,
+ "offset": 1,
+ },
+ "file": "\${testWorkspacePath}/tsconfigProject/foo.vue",
+ "start": {
+ "line": 1,
+ "offset": 1,
+ },
+ },
+ ]
+ `);
+});
- afterEach(async () => {
- const server = await getLanguageServer();
- for (const document of openedDocuments) {
- await server.closeTextDocument(document.uri);
- }
- openedDocuments.length = 0;
- });
+const openedDocuments: TextDocument[] = [];
- async function requestDefinition(fileName: string, languageId: string, content: string) {
- const offset = content.indexOf('|');
- expect(offset).toBeGreaterThanOrEqual(0);
- content = content.slice(0, offset) + content.slice(offset + 1);
+afterEach(async () => {
+ const server = await getLanguageServer();
+ for (const document of openedDocuments) {
+ await server.close(document.uri);
+ }
+ openedDocuments.length = 0;
+});
- const server = await getLanguageServer();
- let document = await prepareDocument(fileName, languageId, content);
+async function requestDefinition(fileName: string, languageId: string, content: string) {
+ const offset = content.indexOf('|');
+ expect(offset).toBeGreaterThanOrEqual(0);
+ content = content.slice(0, offset) + content.slice(offset + 1);
- const position = document.positionAt(offset);
- const definition = await server.sendDefinitionRequest(document.uri, position) as Location[] | null;
- expect(definition).toBeDefined();
+ const server = await getLanguageServer();
+ let document = await prepareDocument(fileName, languageId, content);
- for (const loc of definition!) {
- loc.uri = 'file://${testWorkspacePath}' + loc.uri.slice(URI.file(testWorkspacePath).toString().length);
- }
+ const res = await server.tsserver.message({
+ seq: server.nextSeq(),
+ command: 'definition',
+ arguments: {
+ file: URI.parse(document.uri).fsPath,
+ position: offset,
+ },
+ });
+ expect(res.success).toBe(true);
- return definition!;
+ for (const ref of res.body) {
+ ref.file = '${testWorkspacePath}' + ref.file.slice(testWorkspacePath.length);
}
- async function prepareDocument(fileName: string, languageId: string, content: string) {
- const server = await getLanguageServer();
- const uri = URI.file(`${testWorkspacePath}/${fileName}`);
- const document = await server.openInMemoryDocument(uri.toString(), languageId, content);
- if (openedDocuments.every(d => d.uri !== document.uri)) {
- openedDocuments.push(document);
- }
- return document;
+ return res.body;
+}
+
+async function prepareDocument(fileName: string, languageId: string, content: string) {
+ const server = await getLanguageServer();
+ const uri = URI.file(`${testWorkspacePath}/${fileName}`);
+ const document = await server.open(uri.toString(), languageId, content);
+ if (openedDocuments.every(d => d.uri !== document.uri)) {
+ openedDocuments.push(document);
}
-});
+ return document;
+}
diff --git a/packages/language-server/tests/inlayHints.spec.ts b/packages/language-server/tests/inlayHints.spec.ts
index 718b374fdd..2d878b223a 100644
--- a/packages/language-server/tests/inlayHints.spec.ts
+++ b/packages/language-server/tests/inlayHints.spec.ts
@@ -1,247 +1,245 @@
import { TextDocument } from '@volar/language-server';
-import { afterEach, describe, expect, it } from 'vitest';
+import { afterEach, expect, test } from 'vitest';
import { URI } from 'vscode-uri';
import { getLanguageServer, testWorkspacePath } from './server.js';
-describe('Definitions', async () => {
-
- it('Inline handler leading', async () => {
- expect(
- await requestInlayHintsResult('tsconfigProject/fixture.vue', 'vue', `
-
-
-
-
-
- `)
- ).toMatchInlineSnapshot(`
- "
-
-
-
- */a = 1">
-
- "
- `);
- });
-
- it('Missing props', async () => {
- prepareDocument('tsconfigProject/foo.vue', 'vue', `
+test('Inline handler leading', async () => {
+ expect(
+ await requestInlayHintsResult('tsconfigProject/fixture.vue', 'vue', `
- `);
- expect(
- await requestInlayHintsResult('tsconfigProject/fixture.vue', 'vue', `
-
-
-
-
-
- `)
- ).toMatchInlineSnapshot(`
- "
-
-
-
-
-
- "
- `);
- });
-
- it('Options wrapper', async () => {
- expect(
- await requestInlayHintsResult('tsconfigProject/fixture.vue', 'vue', `
-
- `)
- ).toMatchInlineSnapshot(`
- "
-
- "
- `);
- });
-
- it('Destructured props', async () => {
- expect(
- await requestInlayHintsResult('tsconfigProject/fixture.vue', 'vue', `
-
+
+
+ */a = 1">
+
+ "
+ `);
+});
- interface foo extends (typeof foo) {
+test('Missing props', async () => {
+ expect(
+ await requestInlayHintsResult('tsconfigProject/fixture.vue', 'vue', `
+
- function func(foo) { }
-
- class cls {
- foo: string = foo;
- constructor(foo) { }
- }
-
- for (const char of foo) { }
-
- try { } catch (foo) { }
-
- watch(() => foo, (foo) => {
- console.log(foo, bar, props.baz);
- });
-
- `)
- ).toMatchInlineSnapshot(`
- "
-
- "
- `);
- });
-
- it('#4720', async () => {
- expect(
- await requestInlayHintsResult('fixture.vue', 'vue', `
-
-
-
- `)
- ).toMatchInlineSnapshot(`
- "
-
-
-
- "
- `);
- });
-
- it('#4855', async () => {
- expect(
- await requestInlayHintsResult('fixture.vue', 'vue', `
-
- `)
- ).toMatchInlineSnapshot(`
- "
-
- "
- `);
- });
-
- const openedDocuments: TextDocument[] = [];
-
- afterEach(async () => {
- const server = await getLanguageServer();
- for (const document of openedDocuments) {
- await server.closeTextDocument(document.uri);
- }
- openedDocuments.length = 0;
- });
-
- async function requestInlayHintsResult(fileName: string, languageId: string, content: string) {
- const server = await getLanguageServer();
- let document = await prepareDocument(fileName, languageId, content);
-
- const inlayHints = await server.sendInlayHintRequest(document.uri, { start: document.positionAt(0), end: document.positionAt(content.length) });
- expect(inlayHints).toBeDefined();
- expect(inlayHints!.length).greaterThan(0);
-
- let text = document.getText();
- for (const hint of inlayHints!.sort((a, b) => document.offsetAt(b.position) - document.offsetAt(a.position))) {
- const offset = document.offsetAt(hint.position);
- text = text.slice(0, offset) + '/* ' + hint.label + ' */' + text.slice(offset);
- }
-
- return text;
- }
+
+
+
+ `)
+ ).toMatchInlineSnapshot(`
+ "
+
+
+
+
+
+ "
+ `);
+});
+
+test('Options wrapper', async () => {
+ expect(
+ await requestInlayHintsResult('tsconfigProject/fixture.vue', 'vue', `
+
+ `)
+ ).toMatchInlineSnapshot(`
+ "
+
+ "
+ `);
+});
+
+test('Destructured props', async () => {
+ expect(
+ await requestInlayHintsResult('tsconfigProject/fixture.vue', 'vue', `
+
+ `)
+ ).toMatchInlineSnapshot(`
+ "
+
+ "
+ `);
+});
+
+test('#4720', async () => {
+ expect(
+ await requestInlayHintsResult('fixture.vue', 'vue', `
+
+
+
+ `)
+ ).toMatchInlineSnapshot(`
+ "
+
+
+
+ "
+ `);
+});
+
+test('#4855', async () => {
+ expect(
+ await requestInlayHintsResult('fixture.vue', 'vue', `
+
+ `)
+ ).toMatchInlineSnapshot(`
+ "
+
+ "
+ `);
+});
+
+const openedDocuments: TextDocument[] = [];
+
+afterEach(async () => {
+ const server = await getLanguageServer();
+ for (const document of openedDocuments) {
+ await server.close(document.uri);
}
+ openedDocuments.length = 0;
});
+
+async function requestInlayHintsResult(fileName: string, languageId: string, content: string) {
+ const server = await getLanguageServer();
+ let document = await prepareDocument(fileName, languageId, content);
+
+ const inlayHints = await server.vueserver.sendInlayHintRequest(document.uri, { start: document.positionAt(0), end: document.positionAt(content.length) });
+ expect(inlayHints).toBeDefined();
+ expect(inlayHints!.length).greaterThan(0);
+
+ let text = document.getText();
+ for (const hint of inlayHints!.sort((a, b) => document.offsetAt(b.position) - document.offsetAt(a.position))) {
+ const offset = document.offsetAt(hint.position);
+ text = text.slice(0, offset) + '/* ' + hint.label + ' */' + text.slice(offset);
+ }
+
+ return text;
+}
+
+async function prepareDocument(fileName: string, languageId: string, content: string) {
+ const server = await getLanguageServer();
+ const uri = URI.file(`${testWorkspacePath}/${fileName}`);
+ const document = await server.open(uri.toString(), languageId, content);
+ if (openedDocuments.every(d => d.uri !== document.uri)) {
+ openedDocuments.push(document);
+ }
+ return document;
+}
diff --git a/packages/language-server/tests/references.spec.ts b/packages/language-server/tests/references.spec.ts
index f32cfdd005..946a5a7ac1 100644
--- a/packages/language-server/tests/references.spec.ts
+++ b/packages/language-server/tests/references.spec.ts
@@ -1,187 +1,241 @@
import { TextDocument } from '@volar/language-server';
-import { afterEach, describe, expect, it } from 'vitest';
+import { afterEach, expect, test } from 'vitest';
import { URI } from 'vscode-uri';
import { getLanguageServer, testWorkspacePath } from './server.js';
-describe('Definitions', async () => {
-
- it('Default slot', async () => {
- await prepareDocument('tsconfigProject/foo.vue', 'vue', `
-
+test('Default slot', async () => {
+ await prepareDocument('tsconfigProject/foo.vue', 'vue', `
+
+
+
+
+
+
+ `);
+ expect(
+ await requestReferences('tsconfigProject/fixture.vue', 'vue', `
-
-
-
+
- `);
- expect(
- await requestReferences('tsconfigProject/fixture.vue', 'vue', `
-
-
-
- `)
- ).toMatchInlineSnapshot(`
- [
- {
- "range": {
- "end": {
- "character": 16,
- "line": 7,
- },
- "start": {
- "character": 5,
- "line": 7,
- },
- },
- "uri": "file://\${testWorkspacePath}/tsconfigProject/foo.vue",
- },
- {
- "range": {
- "end": {
- "character": 10,
- "line": 2,
- },
- "start": {
- "character": 6,
- "line": 2,
- },
- },
- "uri": "file://\${testWorkspacePath}/tsconfigProject/fixture.vue",
- },
- ]
- `);
- });
+ `)
+ ).toMatchInlineSnapshot(`
+ {
+ "body": {
+ "refs": [
+ {
+ "end": {
+ "line": 3,
+ "offset": 10,
+ },
+ "file": "\${testWorkspacePath}/tsconfigProject/fixture.vue",
+ "isDefinition": true,
+ "isWriteAccess": false,
+ "lineText": " ",
+ "start": {
+ "line": 3,
+ "offset": 6,
+ },
+ },
+ {
+ "end": {
+ "line": 8,
+ "offset": 16,
+ },
+ "file": "\${testWorkspacePath}/tsconfigProject/foo.vue",
+ "isDefinition": false,
+ "isWriteAccess": false,
+ "lineText": " ",
+ "start": {
+ "line": 8,
+ "offset": 5,
+ },
+ },
+ ],
+ "symbolDisplayString": "(property) default?: (props: typeof __VLS_1) => any",
+ "symbolName": "slot",
+ "symbolStartOffset": 6,
+ },
+ "command": "references",
+ "request_seq": 84,
+ "seq": 0,
+ "success": true,
+ "type": "response",
+ }
+ `);
+});
+
+test('Named slot', async () => {
+ await prepareDocument('tsconfigProject/foo.vue', 'vue', `
+
- it('Named slot', async () => {
- await prepareDocument('tsconfigProject/foo.vue', 'vue', `
+
+
+
+ `);
+ expect(
+ await requestReferences('tsconfigProject/fixture.vue', 'vue', `
+
+
+
+ `)
+ ).toMatchInlineSnapshot(`
+ {
+ "body": {
+ "refs": [
+ {
+ "end": {
+ "line": 3,
+ "offset": 19,
+ },
+ "file": "\${testWorkspacePath}/tsconfigProject/fixture.vue",
+ "isDefinition": true,
+ "isWriteAccess": false,
+ "lineText": " ",
+ "start": {
+ "line": 3,
+ "offset": 16,
+ },
+ },
+ {
+ "end": {
+ "line": 7,
+ "offset": 17,
+ },
+ "file": "\${testWorkspacePath}/tsconfigProject/foo.vue",
+ "isDefinition": false,
+ "isWriteAccess": false,
+ "lineText": " ",
+ "start": {
+ "line": 7,
+ "offset": 14,
+ },
+ },
+ ],
+ "symbolDisplayString": "(property) foo?: (props: typeof __VLS_1) => any",
+ "symbolName": "foo",
+ "symbolStartOffset": 16,
+ },
+ "command": "references",
+ "request_seq": 89,
+ "seq": 0,
+ "success": true,
+ "type": "response",
+ }
+ `);
+});
+
+test('v-bind shorthand', async () => {
+ expect(
+ await requestReferences('tsconfigProject/fixture.vue', 'vue', `
-
-
- `);
- expect(
- await requestReferences('tsconfigProject/fixture.vue', 'vue', `
-
-
+
`)
- ).toMatchInlineSnapshot(`
- [
- {
- "range": {
- "end": {
- "character": 17,
- "line": 6,
- },
- "start": {
- "character": 14,
- "line": 6,
- },
- },
- "uri": "file://\${testWorkspacePath}/tsconfigProject/foo.vue",
- },
- {
- "range": {
- "end": {
- "character": 19,
- "line": 2,
- },
- "start": {
- "character": 16,
- "line": 2,
- },
- },
- "uri": "file://\${testWorkspacePath}/tsconfigProject/fixture.vue",
- },
- ]
- `);
- });
-
- it('v-bind shorthand', async () => {
- expect(
- await requestReferences('tsconfigProject/fixture.vue', 'vue', `
-
-
-
-
-
- `)
- ).toMatchInlineSnapshot(`
- [
- {
- "range": {
- "end": {
- "character": 14,
- "line": 6,
- },
- "start": {
- "character": 11,
- "line": 6,
- },
- },
- "uri": "file://\${testWorkspacePath}/tsconfigProject/fixture.vue",
- },
- {
- "range": {
- "end": {
- "character": 13,
- "line": 2,
- },
- "start": {
- "character": 10,
- "line": 2,
- },
- },
- "uri": "file://\${testWorkspacePath}/tsconfigProject/fixture.vue",
- },
- ]
- `);
- });
-
- const openedDocuments: TextDocument[] = [];
-
- afterEach(async () => {
- const server = await getLanguageServer();
- for (const document of openedDocuments) {
- await server.closeTextDocument(document.uri);
+ ).toMatchInlineSnapshot(`
+ {
+ "body": {
+ "refs": [
+ {
+ "contextEnd": {
+ "line": 3,
+ "offset": 18,
+ },
+ "contextStart": {
+ "line": 3,
+ "offset": 4,
+ },
+ "end": {
+ "line": 3,
+ "offset": 13,
+ },
+ "file": "\${testWorkspacePath}/tsconfigProject/fixture.vue",
+ "isDefinition": true,
+ "isWriteAccess": true,
+ "lineText": " const foo = 1;",
+ "start": {
+ "line": 3,
+ "offset": 10,
+ },
+ },
+ {
+ "end": {
+ "line": 7,
+ "offset": 14,
+ },
+ "file": "\${testWorkspacePath}/tsconfigProject/fixture.vue",
+ "isDefinition": false,
+ "isWriteAccess": false,
+ "lineText": " ",
+ "start": {
+ "line": 7,
+ "offset": 11,
+ },
+ },
+ ],
+ "symbolDisplayString": "const foo: 1",
+ "symbolName": "foo",
+ "symbolStartOffset": 10,
+ },
+ "command": "references",
+ "request_seq": 93,
+ "seq": 0,
+ "success": true,
+ "type": "response",
}
- openedDocuments.length = 0;
- });
+ `);
+});
+
+const openedDocuments: TextDocument[] = [];
- async function requestReferences(fileName: string, languageId: string, content: string) {
- const offset = content.indexOf('|');
- expect(offset).toBeGreaterThanOrEqual(0);
- content = content.slice(0, offset) + content.slice(offset + 1);
+afterEach(async () => {
+ const server = await getLanguageServer();
+ for (const document of openedDocuments) {
+ await server.close(document.uri);
+ }
+ openedDocuments.length = 0;
+});
- const server = await getLanguageServer();
- let document = await prepareDocument(fileName, languageId, content);
+async function requestReferences(fileName: string, languageId: string, content: string) {
+ const offset = content.indexOf('|');
+ expect(offset).toBeGreaterThanOrEqual(0);
+ content = content.slice(0, offset) + content.slice(offset + 1);
- const position = document.positionAt(offset);
- const references = await server.sendReferencesRequest(document.uri, position, { includeDeclaration: false });
- expect(references).toBeDefined();
+ const server = await getLanguageServer();
+ let document = await prepareDocument(fileName, languageId, content);
- for (const loc of references!) {
- loc.uri = 'file://${testWorkspacePath}' + loc.uri.slice(URI.file(testWorkspacePath).toString().length);
- }
+ const res = await server.tsserver.message({
+ seq: server.nextSeq(),
+ command: 'references',
+ arguments: {
+ file: URI.parse(document.uri).fsPath,
+ position: offset,
+ includeDeclaration: false,
+ },
+ });
+ expect(res.success).toBe(true);
- return references!;
+ for (const ref of res!.body.refs) {
+ ref.file = '${testWorkspacePath}' + ref.file.slice(testWorkspacePath.length);
}
- async function prepareDocument(fileName: string, languageId: string, content: string) {
- const server = await getLanguageServer();
- const uri = URI.file(`${testWorkspacePath}/${fileName}`);
- const document = await server.openInMemoryDocument(uri.toString(), languageId, content);
- if (openedDocuments.every(d => d.uri !== document.uri)) {
- openedDocuments.push(document);
- }
- return document;
+ return res!;
+}
+
+async function prepareDocument(fileName: string, languageId: string, content: string) {
+ const server = await getLanguageServer();
+ const uri = URI.file(`${testWorkspacePath}/${fileName}`);
+ const document = await server.open(uri.toString(), languageId, content);
+ if (openedDocuments.every(d => d.uri !== document.uri)) {
+ openedDocuments.push(document);
}
-});
+ return document;
+}
diff --git a/packages/language-server/tests/renaming.spec.ts b/packages/language-server/tests/renaming.spec.ts
index 1a81801da8..e59da6be8e 100644
--- a/packages/language-server/tests/renaming.spec.ts
+++ b/packages/language-server/tests/renaming.spec.ts
@@ -1,14 +1,12 @@
import { TextDocument } from '@volar/language-server';
-import { afterEach, describe, expect, it } from 'vitest';
+import { afterEach, expect, test } from 'vitest';
import { URI } from 'vscode-uri';
import { getLanguageServer, testWorkspacePath } from './server.js';
-describe('Renaming', async () => {
-
- it('#2410', async () => {
- expect(
- await requestRename('fixture.vue', 'vue', `<|h1>`, 'h2')
- ).toMatchInlineSnapshot(`
+test('#2410', async () => {
+ expect(
+ await requestRenameToVueServer('fixture.vue', 'vue', `<|h1>`, 'h2')
+ ).toMatchInlineSnapshot(`
{
"changes": {
"file://\${testWorkspacePath}/fixture.vue": [
@@ -42,9 +40,9 @@ describe('Renaming', async () => {
},
}
`);
- expect(
- await requestRename('fixture.vue', 'vue', ``, 'h2')
- ).toMatchInlineSnapshot(`
+ expect(
+ await requestRenameToVueServer('fixture.vue', 'vue', ``, 'h2')
+ ).toMatchInlineSnapshot(`
{
"changes": {
"file://\${testWorkspacePath}/fixture.vue": [
@@ -78,932 +76,1160 @@ describe('Renaming', async () => {
},
}
`);
- });
+});
- it('CSS', async () => {
- expect(
- await requestRename('fixture.vue', 'vue', `
-
-
-
-
-
-
-
- `, 'bar')
- ).toMatchInlineSnapshot(`
- {
- "changes": {
- "file://\${testWorkspacePath}/fixture.vue": [
- {
- "newText": "bar",
- "range": {
- "end": {
- "character": 28,
- "line": 2,
- },
- "start": {
- "character": 25,
- "line": 2,
- },
- },
- },
- {
- "newText": "bar",
- "range": {
- "end": {
- "character": 8,
- "line": 7,
- },
- "start": {
- "character": 5,
- "line": 7,
- },
- },
- },
- ],
- },
- }
- `);
- expect(
- await requestRename('fixture.vue', 'vue', `
-
-
-
-
-
- `, 'bar')
- ).toMatchInlineSnapshot(`
- {
- "changes": {
- "file://\${testWorkspacePath}/fixture.vue": [
- {
- "newText": "bar",
- "range": {
- "end": {
- "character": 20,
- "line": 2,
- },
- "start": {
- "character": 17,
- "line": 2,
- },
- },
- },
- {
- "newText": "bar",
- "range": {
- "end": {
- "character": 8,
- "line": 6,
- },
- "start": {
- "character": 5,
- "line": 6,
- },
- },
- },
- ],
- },
- }
- `);
- expect(
- await requestRename('fixture.vue', 'vue', `
-
-
-
-
-
- `, 'bar')
- ).toMatchInlineSnapshot(`
- {
- "changes": {
- "file://\${testWorkspacePath}/fixture.vue": [
- {
- "newText": "bar",
- "range": {
- "end": {
- "character": 28,
- "line": 7,
- },
- "start": {
- "character": 25,
- "line": 7,
- },
- },
- },
- {
- "newText": "bar",
- "range": {
- "end": {
- "character": 13,
- "line": 2,
- },
- "start": {
- "character": 10,
- "line": 2,
- },
- },
- },
- {
- "newText": "bar",
- "range": {
- "end": {
- "character": 35,
- "line": 11,
- },
- "start": {
- "character": 32,
- "line": 11,
- },
- },
- },
- {
- "newText": "bar",
- "range": {
- "end": {
- "character": 29,
- "line": 11,
- },
- "start": {
- "character": 26,
- "line": 11,
- },
- },
- },
- {
- "newText": "bar",
- "range": {
- "end": {
- "character": 35,
- "line": 10,
- },
- "start": {
- "character": 32,
- "line": 10,
- },
- },
- },
- {
- "newText": "bar",
- "range": {
- "end": {
- "character": 29,
- "line": 10,
- },
- "start": {
- "character": 26,
- "line": 10,
- },
- },
- },
- {
- "newText": "bar",
- "range": {
- "end": {
- "character": 29,
- "line": 9,
- },
- "start": {
- "character": 26,
- "line": 9,
- },
- },
- },
- {
- "newText": "bar",
- "range": {
- "end": {
- "character": 29,
- "line": 8,
- },
- "start": {
- "character": 26,
- "line": 8,
- },
- },
- },
- ],
- },
- }
- `);
- });
+test('CSS', async () => {
+ expect(
+ await requestRenameToTsServer('fixture.vue', 'vue', `
+
+
+
- it('Component props', async () => {
- await prepareDocument('tsconfigProject/foo.vue', 'vue', `
+
+
+
+ `)
+ ).toMatchInlineSnapshot(`
+ {
+ "info": {
+ "canRename": true,
+ "displayName": "'foo'",
+ "fullDisplayName": "'foo'",
+ "kind": "property",
+ "kindModifiers": "",
+ "triggerSpan": {
+ "end": {
+ "line": 3,
+ "offset": 28,
+ },
+ "start": {
+ "line": 3,
+ "offset": 25,
+ },
+ },
+ },
+ "locs": [
+ {
+ "file": "\${testWorkspacePath}/fixture.vue",
+ "locs": [
+ {
+ "end": {
+ "line": 3,
+ "offset": 28,
+ },
+ "start": {
+ "line": 3,
+ "offset": 25,
+ },
+ },
+ {
+ "end": {
+ "line": 8,
+ "offset": 8,
+ },
+ "start": {
+ "line": 8,
+ "offset": 5,
+ },
+ },
+ ],
+ },
+ ],
+ }
+ `);
+ expect(
+ await requestRenameToTsServer('fixture.vue', 'vue', `
-
-
+
+
+ `)
+ ).toMatchInlineSnapshot(`
+ {
+ "info": {
+ "canRename": true,
+ "displayName": "'foo'",
+ "fullDisplayName": "__type.'foo'",
+ "kind": "property",
+ "kindModifiers": "",
+ "triggerSpan": {
+ "end": {
+ "line": 3,
+ "offset": 20,
+ },
+ "start": {
+ "line": 3,
+ "offset": 17,
+ },
+ },
+ },
+ "locs": [
+ {
+ "file": "\${testWorkspacePath}/fixture.vue",
+ "locs": [
+ {
+ "end": {
+ "line": 3,
+ "offset": 20,
+ },
+ "start": {
+ "line": 3,
+ "offset": 17,
+ },
+ },
+ {
+ "end": {
+ "line": 7,
+ "offset": 8,
+ },
+ "start": {
+ "line": 7,
+ "offset": 5,
+ },
+ },
+ ],
+ },
+ ],
+ }
+ `);
+ expect(
+ await requestRenameToTsServer('fixture.vue', 'vue', `
- `);
- expect(
- await requestRename('tsconfigProject/fixture.vue', 'vue', `
-
- {{ aaaBbb }}
-
-
-
- `, 'cccDdd')
- ).toMatchInlineSnapshot(`
- {
- "changes": {
- "file://\${testWorkspacePath}/tsconfigProject/fixture.vue": [
- {
- "newText": "cccDdd",
- "range": {
- "end": {
- "character": 24,
- "line": 6,
- },
- "start": {
- "character": 18,
- "line": 6,
- },
- },
- },
- {
- "newText": "cccDdd",
- "range": {
- "end": {
- "character": 14,
- "line": 2,
- },
- "start": {
- "character": 8,
- "line": 2,
- },
- },
- },
- ],
- "file://\${testWorkspacePath}/tsconfigProject/foo.vue": [
- {
- "newText": "cccDdd",
- "range": {
- "end": {
- "character": 17,
- "line": 3,
- },
- "start": {
- "character": 11,
- "line": 3,
- },
- },
- },
- {
- "newText": "ccc-ddd",
- "range": {
- "end": {
- "character": 18,
- "line": 2,
- },
- "start": {
- "character": 11,
- "line": 2,
- },
- },
- },
- ],
- },
- }
- `);
- });
- it('Component type props', async () => {
- await prepareDocument('tsconfigProject/foo.vue', 'vue', `
+
+
+
+ `)
+ ).toMatchInlineSnapshot(`
+ {
+ "info": {
+ "canRename": true,
+ "displayName": "foo",
+ "fullDisplayName": "foo",
+ "kind": "property",
+ "kindModifiers": "",
+ "triggerSpan": {
+ "end": {
+ "line": 8,
+ "offset": 28,
+ },
+ "start": {
+ "line": 8,
+ "offset": 25,
+ },
+ },
+ },
+ "locs": [
+ {
+ "file": "\${testWorkspacePath}/fixture.vue",
+ "locs": [
+ {
+ "contextEnd": {
+ "line": 3,
+ "offset": 18,
+ },
+ "contextStart": {
+ "line": 3,
+ "offset": 4,
+ },
+ "end": {
+ "line": 3,
+ "offset": 13,
+ },
+ "start": {
+ "line": 3,
+ "offset": 10,
+ },
+ },
+ {
+ "end": {
+ "line": 12,
+ "offset": 35,
+ },
+ "start": {
+ "line": 12,
+ "offset": 32,
+ },
+ },
+ {
+ "end": {
+ "line": 12,
+ "offset": 29,
+ },
+ "start": {
+ "line": 12,
+ "offset": 26,
+ },
+ },
+ {
+ "end": {
+ "line": 11,
+ "offset": 35,
+ },
+ "start": {
+ "line": 11,
+ "offset": 32,
+ },
+ },
+ {
+ "end": {
+ "line": 11,
+ "offset": 29,
+ },
+ "start": {
+ "line": 11,
+ "offset": 26,
+ },
+ },
+ {
+ "end": {
+ "line": 10,
+ "offset": 29,
+ },
+ "start": {
+ "line": 10,
+ "offset": 26,
+ },
+ },
+ {
+ "end": {
+ "line": 9,
+ "offset": 29,
+ },
+ "start": {
+ "line": 9,
+ "offset": 26,
+ },
+ },
+ {
+ "end": {
+ "line": 8,
+ "offset": 28,
+ },
+ "start": {
+ "line": 8,
+ "offset": 25,
+ },
+ },
+ ],
+ },
+ ],
+ }
+ `);
+});
+
+test('Component props', async () => {
+ await prepareDocument('tsconfigProject/foo.vue', 'vue', `
+
+
+
+
+
+
+ `);
+ expect(
+ await requestRenameToTsServer('tsconfigProject/fixture.vue', 'vue', `
-
-
+ {{ aaaBbb }}
- `);
- expect(
- await requestRename('tsconfigProject/fixture.vue', 'vue', `
-
- {{ aaaBbb }}
-
-
-
- `, 'cccDdd')
- ).toMatchInlineSnapshot(`
- {
- "changes": {
- "file://\${testWorkspacePath}/tsconfigProject/fixture.vue": [
- {
- "newText": "cccDdd",
- "range": {
- "end": {
- "character": 14,
- "line": 2,
- },
- "start": {
- "character": 8,
- "line": 2,
- },
- },
- },
- {
- "newText": "cccDdd",
- "range": {
- "end": {
- "character": 24,
- "line": 6,
- },
- "start": {
- "character": 18,
- "line": 6,
- },
- },
- },
- ],
- "file://\${testWorkspacePath}/tsconfigProject/foo.vue": [
- {
- "newText": "cccDdd",
- "range": {
- "end": {
- "character": 17,
- "line": 3,
- },
- "start": {
- "character": 11,
- "line": 3,
- },
- },
- },
- {
- "newText": "ccc-ddd",
- "range": {
- "end": {
- "character": 18,
- "line": 2,
- },
- "start": {
- "character": 11,
- "line": 2,
- },
- },
- },
- ],
- },
- }
- `);
- });
+ `)
+ ).toMatchInlineSnapshot(`
+ {
+ "info": {
+ "canRename": true,
+ "displayName": "aaaBbb",
+ "fullDisplayName": "__object.aaaBbb",
+ "kind": "property",
+ "kindModifiers": "",
+ "triggerSpan": {
+ "end": {
+ "line": 7,
+ "offset": 24,
+ },
+ "start": {
+ "line": 7,
+ "offset": 18,
+ },
+ },
+ },
+ "locs": [
+ {
+ "file": "\${testWorkspacePath}/tsconfigProject/fixture.vue",
+ "locs": [
+ {
+ "contextEnd": {
+ "line": 7,
+ "offset": 32,
+ },
+ "contextStart": {
+ "line": 7,
+ "offset": 18,
+ },
+ "end": {
+ "line": 7,
+ "offset": 24,
+ },
+ "start": {
+ "line": 7,
+ "offset": 18,
+ },
+ },
+ {
+ "end": {
+ "line": 3,
+ "offset": 14,
+ },
+ "start": {
+ "line": 3,
+ "offset": 8,
+ },
+ },
+ ],
+ },
+ {
+ "file": "\${testWorkspacePath}/tsconfigProject/foo.vue",
+ "locs": [
+ {
+ "end": {
+ "line": 4,
+ "offset": 17,
+ },
+ "start": {
+ "line": 4,
+ "offset": 11,
+ },
+ },
+ {
+ "end": {
+ "line": 3,
+ "offset": 18,
+ },
+ "start": {
+ "line": 3,
+ "offset": 11,
+ },
+ },
+ ],
+ },
+ ],
+ }
+ `);
+});
- it('Component dynamic props', async () => {
- expect(
- await requestRename('tsconfigProject/fixture.vue', 'vue', `
-
-
-
-
-
- `, 'bar')
- ).toMatchInlineSnapshot(`
- {
- "changes": {
- "file://\${testWorkspacePath}/tsconfigProject/fixture.vue": [
- {
- "newText": "bar",
- "range": {
- "end": {
- "character": 13,
- "line": 6,
- },
- "start": {
- "character": 10,
- "line": 6,
- },
- },
- },
- {
- "newText": "bar",
- "range": {
- "end": {
- "character": 15,
- "line": 2,
- },
- "start": {
- "character": 12,
- "line": 2,
- },
- },
- },
- ],
- },
- }
- `);
- });
+test('Component type props', async () => {
+ await prepareDocument('tsconfigProject/foo.vue', 'vue', `
+
+
+
+
- it('Component returns', async () => {
- expect(
- await requestRename('tsconfigProject/fixture.vue', 'vue', `
-
- {{ foo| }}
-
-
-
- `, 'bar')
- ).toMatchInlineSnapshot(`
- {
- "changes": {
- "file://\${testWorkspacePath}/tsconfigProject/fixture.vue": [
- {
- "newText": "bar",
- "range": {
- "end": {
- "character": 11,
- "line": 2,
- },
- "start": {
- "character": 8,
- "line": 2,
- },
- },
- },
- {
- "newText": "bar",
- "range": {
- "end": {
- "character": 10,
- "line": 11,
- },
- "start": {
- "character": 7,
- "line": 11,
- },
- },
- },
- ],
- },
- }
- `);
- });
+
+ `);
+ expect(
+ await requestRenameToTsServer('tsconfigProject/fixture.vue', 'vue', `
+
+ {{ aaaBbb }}
+
- it('
- `, 'bar')
- ).toMatchInlineSnapshot(`
- {
- "changes": {
- "file://\${testWorkspacePath}/tsconfigProject/fixture.vue": [
- {
- "newText": "bar",
- "range": {
- "end": {
- "character": 13,
- "line": 6,
- },
- "start": {
- "character": 10,
- "line": 6,
- },
- },
- },
- {
- "newText": "bar",
- "range": {
- "end": {
- "character": 11,
- "line": 2,
- },
- "start": {
- "character": 8,
- "line": 2,
- },
- },
- },
- ],
- },
- }
- `);
- });
+
+ `)
+ ).toMatchInlineSnapshot(`
+ {
+ "info": {
+ "canRename": true,
+ "displayName": "aaaBbb",
+ "fullDisplayName": "__type.aaaBbb",
+ "kind": "property",
+ "kindModifiers": "",
+ "triggerSpan": {
+ "end": {
+ "line": 7,
+ "offset": 24,
+ },
+ "start": {
+ "line": 7,
+ "offset": 18,
+ },
+ },
+ },
+ "locs": [
+ {
+ "file": "\${testWorkspacePath}/tsconfigProject/fixture.vue",
+ "locs": [
+ {
+ "end": {
+ "line": 3,
+ "offset": 14,
+ },
+ "start": {
+ "line": 3,
+ "offset": 8,
+ },
+ },
+ {
+ "contextEnd": {
+ "line": 7,
+ "offset": 32,
+ },
+ "contextStart": {
+ "line": 7,
+ "offset": 18,
+ },
+ "end": {
+ "line": 7,
+ "offset": 24,
+ },
+ "start": {
+ "line": 7,
+ "offset": 18,
+ },
+ },
+ ],
+ },
+ {
+ "file": "\${testWorkspacePath}/tsconfigProject/foo.vue",
+ "locs": [
+ {
+ "end": {
+ "line": 4,
+ "offset": 17,
+ },
+ "start": {
+ "line": 4,
+ "offset": 11,
+ },
+ },
+ {
+ "end": {
+ "line": 3,
+ "offset": 18,
+ },
+ "start": {
+ "line": 3,
+ "offset": 11,
+ },
+ },
+ ],
+ },
+ ],
+ }
+ `);
+});
- it('Component tags', async () => {
- expect(
- await requestRename('tsconfigProject/fixture.vue', 'vue', `
-
-
-
-
-
-
- `, 'CcDd')
- ).toMatchInlineSnapshot(`
- {
- "changes": {
- "file://\${testWorkspacePath}/tsconfigProject/fixture.vue": [
- {
- "newText": "cc-dd",
- "range": {
- "end": {
- "character": 19,
- "line": 3,
- },
- "start": {
- "character": 14,
- "line": 3,
- },
- },
- },
- {
- "newText": "cc-dd",
- "range": {
- "end": {
- "character": 11,
- "line": 3,
- },
- "start": {
- "character": 6,
- "line": 3,
- },
- },
- },
- {
- "newText": "CcDd",
- "range": {
- "end": {
- "character": 17,
- "line": 2,
- },
- "start": {
- "character": 13,
- "line": 2,
- },
- },
- },
- {
- "newText": "CcDd",
- "range": {
- "end": {
- "character": 10,
- "line": 2,
- },
- "start": {
- "character": 6,
- "line": 2,
- },
- },
- },
- {
- "newText": "CcDd",
- "range": {
- "end": {
- "character": 15,
- "line": 7,
- },
- "start": {
- "character": 11,
- "line": 7,
- },
- },
- },
- ],
- },
- }
- `);
- });
+test('Component dynamic props', async () => {
+ expect(
+ await requestRenameToTsServer('tsconfigProject/fixture.vue', 'vue', `
+
+
+
- it('#4673', async () => {
- expect(
- await requestRename('fixture.vue', 'vue', `
-
-
-
- {{ }}
-
-
-
-
-
- `, 'stylus')
- ).toMatchInlineSnapshot(`
- {
- "changes": {
- "file://\${testWorkspacePath}/fixture.vue": [
- {
- "newText": "stylus",
- "range": {
- "end": {
- "character": 22,
- "line": 8,
- },
- "start": {
- "character": 18,
- "line": 8,
- },
- },
- },
- {
- "newText": "stylus",
- "range": {
- "end": {
- "character": 23,
- "line": 15,
- },
- "start": {
- "character": 19,
- "line": 15,
- },
- },
- },
- {
- "newText": "stylus",
- "range": {
- "end": {
- "character": 40,
- "line": 4,
- },
- "start": {
- "character": 36,
- "line": 4,
- },
- },
- },
- ],
- },
- }
- `);
- });
+
+ `)
+ ).toMatchInlineSnapshot(`
+ {
+ "info": {
+ "canRename": true,
+ "displayName": "foo",
+ "fullDisplayName": "foo",
+ "kind": "property",
+ "kindModifiers": "",
+ "triggerSpan": {
+ "end": {
+ "line": 3,
+ "offset": 15,
+ },
+ "start": {
+ "line": 3,
+ "offset": 12,
+ },
+ },
+ },
+ "locs": [
+ {
+ "file": "\${testWorkspacePath}/tsconfigProject/fixture.vue",
+ "locs": [
+ {
+ "contextEnd": {
+ "line": 7,
+ "offset": 22,
+ },
+ "contextStart": {
+ "line": 7,
+ "offset": 4,
+ },
+ "end": {
+ "line": 7,
+ "offset": 13,
+ },
+ "start": {
+ "line": 7,
+ "offset": 10,
+ },
+ },
+ {
+ "end": {
+ "line": 3,
+ "offset": 15,
+ },
+ "start": {
+ "line": 3,
+ "offset": 12,
+ },
+ },
+ ],
+ },
+ ],
+ }
+ `);
+});
- it('Scoped Classes', async () => {
- expect(
- await requestRename('fixture.vue', 'vue', `
-
-
-
-
-
-
- `, 'bar')
- ).toMatchInlineSnapshot(`
- {
- "changes": {
- "file://\${testWorkspacePath}/fixture.vue": [
- {
- "newText": "bar",
- "range": {
- "end": {
- "character": 23,
- "line": 4,
- },
- "start": {
- "character": 20,
- "line": 4,
- },
- },
- },
- {
- "newText": "bar",
- "range": {
- "end": {
- "character": 32,
- "line": 3,
- },
- "start": {
- "character": 29,
- "line": 3,
- },
- },
- },
- {
- "newText": "bar",
- "range": {
- "end": {
- "character": 23,
- "line": 3,
- },
- "start": {
- "character": 20,
- "line": 3,
- },
- },
- },
- {
- "newText": "bar",
- "range": {
- "end": {
- "character": 22,
- "line": 2,
- },
- "start": {
- "character": 19,
- "line": 2,
- },
- },
- },
- {
- "newText": "bar",
- "range": {
- "end": {
- "character": 8,
- "line": 7,
- },
- "start": {
- "character": 5,
- "line": 7,
- },
- },
- },
- ],
- },
- }
- `);
- });
+test('Component returns', async () => {
+ expect(
+ await requestRenameToTsServer('tsconfigProject/fixture.vue', 'vue', `
+
+ {{ foo| }}
+
- it('Ref', async () => {
- expect(
- await requestRename('tsconfigProject/fixture.vue', 'vue', `
-
-
-
-
-
- `, 'bar')
- ).toMatchInlineSnapshot(`
- {
- "changes": {
- "file://\${testWorkspacePath}/tsconfigProject/fixture.vue": [
- {
- "newText": "bar",
- "range": {
- "end": {
- "character": 16,
- "line": 2,
- },
- "start": {
- "character": 13,
- "line": 2,
- },
- },
- },
- {
- "newText": "bar",
- "range": {
- "end": {
- "character": 13,
- "line": 7,
- },
- "start": {
- "character": 10,
- "line": 7,
- },
- },
- },
- ],
- },
- }
- `);
- });
+
- `, 'bar')
- ).toMatchInlineSnapshot(`
- {
- "changes": {
- "file://\${testWorkspacePath}/tsconfigProject/fixture.vue": [
- {
- "newText": "bar",
- "range": {
- "end": {
- "character": 16,
- "line": 2,
- },
- "start": {
- "character": 13,
- "line": 2,
- },
- },
- },
- {
- "newText": "bar",
- "range": {
- "end": {
- "character": 34,
- "line": 7,
- },
- "start": {
- "character": 31,
- "line": 7,
- },
- },
- },
- ],
- },
- }
- `);
- });
+ export default defineComponent({
+ setup() {
+ return {
+ foo: 1,
+ };
+ },
+ });
+
+ `)
+ ).toMatchInlineSnapshot(`
+ {
+ "info": {
+ "canRename": true,
+ "displayName": "foo",
+ "fullDisplayName": "foo",
+ "kind": "property",
+ "kindModifiers": "",
+ "triggerSpan": {
+ "end": {
+ "line": 3,
+ "offset": 11,
+ },
+ "start": {
+ "line": 3,
+ "offset": 8,
+ },
+ },
+ },
+ "locs": [
+ {
+ "file": "\${testWorkspacePath}/tsconfigProject/fixture.vue",
+ "locs": [
+ {
+ "end": {
+ "line": 3,
+ "offset": 11,
+ },
+ "start": {
+ "line": 3,
+ "offset": 8,
+ },
+ },
+ {
+ "contextEnd": {
+ "line": 12,
+ "offset": 13,
+ },
+ "contextStart": {
+ "line": 12,
+ "offset": 7,
+ },
+ "end": {
+ "line": 12,
+ "offset": 10,
+ },
+ "start": {
+ "line": 12,
+ "offset": 7,
+ },
+ },
+ ],
+ },
+ ],
+ }
+ `);
+});
- const openedDocuments: TextDocument[] = [];
+test('
+ `)
+ ).toMatchInlineSnapshot(`
+ {
+ "info": {
+ "canRename": true,
+ "displayName": "foo",
+ "fullDisplayName": "foo",
+ "kind": "property",
+ "kindModifiers": "",
+ "triggerSpan": {
+ "end": {
+ "line": 3,
+ "offset": 11,
+ },
+ "start": {
+ "line": 3,
+ "offset": 8,
+ },
+ },
+ },
+ "locs": [
+ {
+ "file": "\${testWorkspacePath}/tsconfigProject/fixture.vue",
+ "locs": [
+ {
+ "contextEnd": {
+ "line": 7,
+ "offset": 18,
+ },
+ "contextStart": {
+ "line": 7,
+ "offset": 4,
+ },
+ "end": {
+ "line": 7,
+ "offset": 13,
+ },
+ "start": {
+ "line": 7,
+ "offset": 10,
+ },
+ },
+ {
+ "end": {
+ "line": 3,
+ "offset": 11,
+ },
+ "start": {
+ "line": 3,
+ "offset": 8,
+ },
+ },
+ ],
+ },
+ ],
}
- openedDocuments.length = 0;
- });
+ `);
+});
+
+test('Component tags', async () => {
+ expect(
+ await requestRenameToTsServer('tsconfigProject/fixture.vue', 'vue', `
+
+
+
+
+
+
+ `)
+ ).toMatchInlineSnapshot(`
+ {
+ "info": {
+ "canRename": true,
+ "displayName": "AaBb",
+ "fullDisplayName": "AaBb",
+ "kind": "alias",
+ "kindModifiers": "export",
+ "triggerSpan": {
+ "end": {
+ "line": 8,
+ "offset": 15,
+ },
+ "start": {
+ "line": 8,
+ "offset": 11,
+ },
+ },
+ },
+ "locs": [
+ {
+ "file": "\${testWorkspacePath}/tsconfigProject/fixture.vue",
+ "locs": [
+ {
+ "end": {
+ "line": 4,
+ "offset": 19,
+ },
+ "start": {
+ "line": 4,
+ "offset": 14,
+ },
+ },
+ {
+ "end": {
+ "line": 4,
+ "offset": 11,
+ },
+ "start": {
+ "line": 4,
+ "offset": 6,
+ },
+ },
+ {
+ "end": {
+ "line": 3,
+ "offset": 17,
+ },
+ "start": {
+ "line": 3,
+ "offset": 13,
+ },
+ },
+ {
+ "end": {
+ "line": 3,
+ "offset": 10,
+ },
+ "start": {
+ "line": 3,
+ "offset": 6,
+ },
+ },
+ {
+ "contextEnd": {
+ "line": 8,
+ "offset": 35,
+ },
+ "contextStart": {
+ "line": 8,
+ "offset": 4,
+ },
+ "end": {
+ "line": 8,
+ "offset": 15,
+ },
+ "start": {
+ "line": 8,
+ "offset": 11,
+ },
+ },
+ ],
+ },
+ ],
+ }
+ `);
+});
- async function requestRename(fileName: string, languageId: string, _content: string, newName: string) {
- const offset = _content.indexOf('|');
- expect(offset).toBeGreaterThanOrEqual(0);
- const content = _content.slice(0, offset) + _content.slice(offset + 1);
+test('#4673', async () => {
+ expect(
+ await requestRenameToTsServer('fixture.vue', 'vue', `
+
- const server = await getLanguageServer();
- let document = await prepareDocument(fileName, languageId, content);
+
+ {{ }}
+
- const position = document.positionAt(offset);
- const edit = await server.sendRenameRequest(document.uri, position, newName);
- expect(edit?.changes).toBeDefined();
+
- for (const [uri, edits] of Object.entries(edit!.changes!)) {
- delete edit!.changes![uri];
- edit!.changes!['file://${testWorkspacePath}' + uri.slice(URI.file(testWorkspacePath).toString().length)] = edits;
+
+ `)
+ ).toMatchInlineSnapshot(`
+ {
+ "info": {
+ "canRename": true,
+ "displayName": "styl",
+ "fullDisplayName": "__type.styl",
+ "kind": "property",
+ "kindModifiers": "",
+ "triggerSpan": {
+ "end": {
+ "line": 9,
+ "offset": 22,
+ },
+ "start": {
+ "line": 9,
+ "offset": 18,
+ },
+ },
+ },
+ "locs": [
+ {
+ "file": "\${testWorkspacePath}/fixture.vue",
+ "locs": [
+ {
+ "end": {
+ "line": 9,
+ "offset": 22,
+ },
+ "start": {
+ "line": 9,
+ "offset": 18,
+ },
+ },
+ {
+ "end": {
+ "line": 16,
+ "offset": 23,
+ },
+ "start": {
+ "line": 16,
+ "offset": 19,
+ },
+ },
+ {
+ "end": {
+ "line": 5,
+ "offset": 40,
+ },
+ "start": {
+ "line": 5,
+ "offset": 36,
+ },
+ },
+ ],
+ },
+ ],
}
+ `);
+});
- return edit;
- }
+test('Scoped Classes', async () => {
+ expect(
+ await requestRenameToTsServer('fixture.vue', 'vue', `
+
+
+
+
+
+
+ `)
+ ).toMatchInlineSnapshot(`
+ {
+ "info": {
+ "canRename": true,
+ "displayName": "'foo'",
+ "fullDisplayName": "__type.'foo'",
+ "kind": "property",
+ "kindModifiers": "",
+ "triggerSpan": {
+ "end": {
+ "line": 3,
+ "offset": 22,
+ },
+ "start": {
+ "line": 3,
+ "offset": 19,
+ },
+ },
+ },
+ "locs": [
+ {
+ "file": "\${testWorkspacePath}/fixture.vue",
+ "locs": [
+ {
+ "end": {
+ "line": 5,
+ "offset": 23,
+ },
+ "start": {
+ "line": 5,
+ "offset": 20,
+ },
+ },
+ {
+ "end": {
+ "line": 4,
+ "offset": 32,
+ },
+ "start": {
+ "line": 4,
+ "offset": 29,
+ },
+ },
+ {
+ "end": {
+ "line": 4,
+ "offset": 23,
+ },
+ "start": {
+ "line": 4,
+ "offset": 20,
+ },
+ },
+ {
+ "end": {
+ "line": 3,
+ "offset": 22,
+ },
+ "start": {
+ "line": 3,
+ "offset": 19,
+ },
+ },
+ {
+ "end": {
+ "line": 8,
+ "offset": 8,
+ },
+ "start": {
+ "line": 8,
+ "offset": 5,
+ },
+ },
+ ],
+ },
+ ],
+ }
+ `);
+});
+
+test('Ref', async () => {
+ expect(
+ await requestRenameToTsServer('tsconfigProject/fixture.vue', 'vue', `
+
+
+
+
+
+ `)
+ ).toMatchInlineSnapshot(`
+ {
+ "info": {
+ "canRename": true,
+ "displayName": "foo",
+ "fullDisplayName": "foo",
+ "kind": "const",
+ "kindModifiers": "",
+ "triggerSpan": {
+ "end": {
+ "line": 8,
+ "offset": 13,
+ },
+ "start": {
+ "line": 8,
+ "offset": 10,
+ },
+ },
+ },
+ "locs": [
+ {
+ "file": "\${testWorkspacePath}/tsconfigProject/fixture.vue",
+ "locs": [
+ {
+ "end": {
+ "line": 3,
+ "offset": 16,
+ },
+ "start": {
+ "line": 3,
+ "offset": 13,
+ },
+ },
+ {
+ "contextEnd": {
+ "line": 8,
+ "offset": 22,
+ },
+ "contextStart": {
+ "line": 8,
+ "offset": 4,
+ },
+ "end": {
+ "line": 8,
+ "offset": 13,
+ },
+ "start": {
+ "line": 8,
+ "offset": 10,
+ },
+ },
+ ],
+ },
+ ],
+ }
+ `);
+});
- async function prepareDocument(fileName: string, languageId: string, content: string) {
- const server = await getLanguageServer();
- const uri = URI.file(`${testWorkspacePath}/${fileName}`);
- const document = await server.openInMemoryDocument(uri.toString(), languageId, content);
- if (openedDocuments.every(d => d.uri !== document.uri)) {
- openedDocuments.push(document);
+test('Template Ref', async () => {
+ expect(
+ await requestRenameToTsServer('tsconfigProject/fixture.vue', 'vue', `
+
+
+
+
+
+ `)
+ ).toMatchInlineSnapshot(`
+ {
+ "info": {
+ "canRename": true,
+ "displayName": "foo",
+ "fullDisplayName": "__type.foo",
+ "kind": "property",
+ "kindModifiers": "",
+ "triggerSpan": {
+ "end": {
+ "line": 8,
+ "offset": 34,
+ },
+ "start": {
+ "line": 8,
+ "offset": 31,
+ },
+ },
+ },
+ "locs": [
+ {
+ "file": "\${testWorkspacePath}/tsconfigProject/fixture.vue",
+ "locs": [
+ {
+ "end": {
+ "line": 3,
+ "offset": 16,
+ },
+ "start": {
+ "line": 3,
+ "offset": 13,
+ },
+ },
+ {
+ "end": {
+ "line": 8,
+ "offset": 34,
+ },
+ "start": {
+ "line": 8,
+ "offset": 31,
+ },
+ },
+ ],
+ },
+ ],
}
- return document;
+ `);
+});
+
+const openedDocuments: TextDocument[] = [];
+
+afterEach(async () => {
+ const server = await getLanguageServer();
+ for (const document of openedDocuments) {
+ await server.close(document.uri);
}
+ openedDocuments.length = 0;
});
+
+async function requestRenameToVueServer(fileName: string, languageId: string, _content: string, newName: string) {
+ const offset = _content.indexOf('|');
+ expect(offset).toBeGreaterThanOrEqual(0);
+ const content = _content.slice(0, offset) + _content.slice(offset + 1);
+
+ const server = await getLanguageServer();
+ let document = await prepareDocument(fileName, languageId, content);
+
+ const position = document.positionAt(offset);
+ const edit = await server.vueserver.sendRenameRequest(document.uri, position, newName);
+ expect(edit?.changes).toBeDefined();
+
+ for (const [uri, edits] of Object.entries(edit!.changes!)) {
+ delete edit!.changes![uri];
+ edit!.changes!['file://${testWorkspacePath}' + uri.slice(URI.file(testWorkspacePath).toString().length)] = edits;
+ }
+
+ return edit;
+}
+
+async function requestRenameToTsServer(fileName: string, languageId: string, _content: string) {
+ const offset = _content.indexOf('|');
+ expect(offset).toBeGreaterThanOrEqual(0);
+ const content = _content.slice(0, offset) + _content.slice(offset + 1);
+
+ const server = await getLanguageServer();
+ let document = await prepareDocument(fileName, languageId, content);
+
+ const res = await server.tsserver.message({
+ seq: server.nextSeq(),
+ command: 'rename',
+ arguments: {
+ file: URI.parse(document.uri).fsPath,
+ position: offset,
+ findInStrings: false,
+ findInComments: false,
+ },
+ });
+
+ expect(res!.success).toBe(true);
+
+ for (const loc of res!.body.locs) {
+ loc.file = '${testWorkspacePath}' + loc.file.slice(testWorkspacePath.length);
+ }
+
+ return res.body;
+}
+
+async function prepareDocument(fileName: string, languageId: string, content: string) {
+ const server = await getLanguageServer();
+ const uri = URI.file(`${testWorkspacePath}/${fileName}`);
+ const document = await server.open(uri.toString(), languageId, content);
+ if (openedDocuments.every(d => d.uri !== document.uri)) {
+ openedDocuments.push(document);
+ }
+ return document;
+}
diff --git a/packages/language-server/tests/server.ts b/packages/language-server/tests/server.ts
index fbce2d8992..8736b51adf 100644
--- a/packages/language-server/tests/server.ts
+++ b/packages/language-server/tests/server.ts
@@ -1,15 +1,38 @@
-import { ConfigurationRequest, PublishDiagnosticsNotification } from '@volar/language-server';
+import { launchServer } from '@typescript/server-harness';
+import { ConfigurationRequest, PublishDiagnosticsNotification, TextDocument } from '@volar/language-server';
import type { LanguageServerHandle } from '@volar/test-utils';
import { startLanguageServer } from '@volar/test-utils';
import * as path from 'node:path';
import { URI } from 'vscode-uri';
let serverHandle: LanguageServerHandle | undefined;
+let tsserver: import('@typescript/server-harness').Server;
+let seq = 1;
export const testWorkspacePath = path.resolve(__dirname, '../../../test-workspace');
-export async function getLanguageServer(): Promise {
+export async function getLanguageServer(): Promise<{
+ vueserver: LanguageServerHandle;
+ tsserver: import('@typescript/server-harness').Server;
+ nextSeq: () => number;
+ open: (uri: string, languageId: string, content: string) => Promise;
+ close: (uri: string) => Promise;
+}> {
if (!serverHandle) {
+ tsserver = launchServer(
+ path.join(__dirname, '..', '..', '..', 'node_modules', 'typescript', 'lib', 'tsserver.js'),
+ [
+ '--disableAutomaticTypingAcquisition',
+ '--globalPlugins', '@vue/typescript-plugin',
+ '--suppressDiagnosticEvents',
+ // '--logVerbosity', 'verbose',
+ // '--logFile', path.join(__dirname, 'tsserver.log'),
+ ]
+ );
+
+ tsserver.on('exit', code => console.log(code ? `Exited with code ${code}` : `Terminated`));
+ // tsserver.on('event', e => console.log(e));
+
serverHandle = startLanguageServer(require.resolve('../bin/vue-language-server.js'), testWorkspacePath);
serverHandle.connection.onNotification(PublishDiagnosticsNotification.type, () => { });
serverHandle.connection.onRequest(ConfigurationRequest.type, ({ items }) => {
@@ -26,10 +49,6 @@ export async function getLanguageServer(): Promise {
{
typescript: {
tsdk: path.dirname(require.resolve('typescript/lib/typescript.js')),
- disableAutoImportCache: true,
- },
- vue: {
- hybridMode: false,
},
},
{
@@ -39,5 +58,53 @@ export async function getLanguageServer(): Promise {
}
);
}
- return serverHandle;
+ return {
+ vueserver: serverHandle,
+ tsserver: tsserver,
+ nextSeq: () => seq++,
+ open: async (uri: string, languageId: string, content: string) => {
+ const res = await tsserver.message({
+ seq: seq++,
+ type: 'request',
+ command: 'updateOpen',
+ arguments: {
+ changedFiles: [],
+ closedFiles: [],
+ openFiles: [
+ {
+ file: URI.parse(uri).fsPath,
+ fileContent: content,
+ projectRootPath: path.resolve(testWorkspacePath, './tsconfigProject'),
+ plugins: ['@vue/typescript-plugin'],
+ }
+ ]
+ }
+ });
+ if (!res.success) {
+ throw new Error(res.body);
+ }
+
+ // Wait for the named pipe server ready
+ // TODO: remove this when named pipe logic is removed
+ await new Promise(resolve => setTimeout(resolve, 2000));
+
+ return await serverHandle!.openInMemoryDocument(uri, languageId, content);
+ },
+ close: async (uri: string) => {
+ const res = await tsserver.message({
+ seq: seq++,
+ type: 'request',
+ command: 'updateOpen',
+ arguments: {
+ changedFiles: [],
+ closedFiles: [URI.parse(uri).fsPath],
+ openFiles: []
+ }
+ });
+ if (!res.success) {
+ throw new Error(res.body);
+ }
+ await serverHandle!.closeTextDocument(uri);
+ },
+ };
}
diff --git a/packages/language-service/index.ts b/packages/language-service/index.ts
index 6c7925f83b..c50f8c61c7 100644
--- a/packages/language-service/index.ts
+++ b/packages/language-service/index.ts
@@ -50,16 +50,10 @@ declare module '@volar/language-service' {
}
}
-export function getFullLanguageServicePlugins(
- ts: typeof import('typescript'),
- { disableAutoImportCache }: { disableAutoImportCache?: boolean; } = {}
-) {
+export function getFullLanguageServicePlugins(ts: typeof import('typescript')) {
const plugins: LanguageServicePlugin[] = [
- ...createTypeScriptPlugins(ts, { disableAutoImportCache }),
- ...getCommonLanguageServicePlugins(
- ts,
- getTsPluginClientForLSP
- )
+ ...createTypeScriptPlugins(ts),
+ ...getCommonLanguageServicePlugins(ts, getTsPluginClientForLSP),
];
for (let i = 0; i < plugins.length; i++) {
const plugin = plugins[i];
diff --git a/packages/typescript-plugin/lib/utils.ts b/packages/typescript-plugin/lib/utils.ts
index 03a766fd22..f464c0ba85 100644
--- a/packages/typescript-plugin/lib/utils.ts
+++ b/packages/typescript-plugin/lib/utils.ts
@@ -28,7 +28,6 @@ class NamedPipeServer {
path: string;
connecting = false;
projectInfo?: ProjectInfo;
- containsFileCache = new Map>();
componentNamesAndProps = new FileMap<
Record
>(false);
@@ -39,17 +38,7 @@ class NamedPipeServer {
containsFile(fileName: string) {
if (this.projectInfo) {
- if (!this.containsFileCache.has(fileName)) {
- this.containsFileCache.set(fileName, (async () => {
- const res = await this.sendRequest('containsFile', fileName);
- if (typeof res !== 'boolean') {
- // If the request fails, delete the cache
- this.containsFileCache.delete(fileName);
- }
- return res;
- })());
- }
- return this.containsFileCache.get(fileName);
+ return this.sendRequest('containsFile', fileName);
}
}
@@ -82,7 +71,6 @@ class NamedPipeServer {
if (projectInfo) {
console.log('TSServer project ready:', projectInfo.name);
this.projectInfo = projectInfo;
- this.containsFileCache.clear();
onServerReady.forEach(cb => cb());
} else {
this.close();
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index d3891ba3c6..957e324b34 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -166,6 +166,9 @@ importers:
packages/language-server:
dependencies:
+ '@typescript/server-harness':
+ specifier: latest
+ version: 0.3.5
'@volar/language-core':
specifier: ~2.4.11
version: 2.4.11
@@ -1207,6 +1210,9 @@ packages:
resolution: {integrity: sha512-mCFtBbFBJDCNCWUl5y6sZSCHXw1DEFEk3c/M3nRK2a4XUB8StGFtmcEMizdjKuBzB6e/smJAAWYug3VrdLMr1w==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ '@typescript/server-harness@0.3.5':
+ resolution: {integrity: sha512-YT9oe27zm7HdGXYad5SZrdJzVe9eavG3F6YplsWvAraowGtuDeY7FHPVuQPtQj6GxG097Us4JDkA8n5I4iQovQ==}
+
'@vitest/expect@2.1.9':
resolution: {integrity: sha512-UJCIkTBenHeKT1TTlKMJWy1laZewsRIzYighyYiJKZreqtdxSos/S1t+ktRMQWu2CKqaarrkeszJx1cgC5tGZw==}
@@ -4740,6 +4746,8 @@ snapshots:
'@typescript-eslint/types': 8.19.0
eslint-visitor-keys: 4.2.0
+ '@typescript/server-harness@0.3.5': {}
+
'@vitest/expect@2.1.9':
dependencies:
'@vitest/spy': 2.1.9
diff --git a/test-workspace/tsconfigProject/fixture.vue b/test-workspace/tsconfigProject/fixture.vue
new file mode 100644
index 0000000000..e69de29bb2