diff --git a/packages/language-server/tests/__snapshots__/completions.spec.ts.snap b/packages/language-server/tests/__snapshots__/completions.spec.ts.snap deleted file mode 100644 index f2b3dd1bf6..0000000000 --- a/packages/language-server/tests/__snapshots__/completions.spec.ts.snap +++ /dev/null @@ -1,124 +0,0 @@ -// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html - -exports[`Completions > #2454 1`] = ` -" - - - - " -`; - -exports[`Completions > #2511 1`] = ` -" - - " -`; - -exports[`Completions > #3658 1`] = ` -" - - " -`; - -exports[`Completions > #4639 1`] = ` -" - - " -`; - -exports[`Completions > $event argument 1`] = `""`; - -exports[`Completions > - " -`; - -exports[`Completions > Alias path 1`] = ` -" - - " -`; - -exports[`Completions > Component auto import 1`] = ` -" - - - - " -`; - -exports[`Completions > Directives 1`] = `""`; - -exports[`Completions > Directives 2`] = `""`; - -exports[`Completions > Directives 3`] = `""`; - -exports[`Completions > Directives 4`] = `""`; - -exports[`Completions > Relative path 1`] = ` -" - - " -`; - -exports[`Completions > Slot name 1`] = ` -" - - - - " -`; - -exports[`Completions > core#8811 1`] = ` -" - - - - " -`; diff --git a/packages/language-server/tests/__snapshots__/definitions.spec.ts.snap b/packages/language-server/tests/__snapshots__/definitions.spec.ts.snap new file mode 100644 index 0000000000..ff893647c0 --- /dev/null +++ b/packages/language-server/tests/__snapshots__/definitions.spec.ts.snap @@ -0,0 +1,61 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`Definitions > #2600 1`] = `"tsconfigProject/foo.vue"`; + +exports[`Definitions > #2600 2`] = ` +{ + "end": { + "character": 0, + "line": 0, + }, + "start": { + "character": 0, + "line": 0, + }, +} +`; + +exports[`Definitions > Alias path 1`] = `"tsconfigProject/foo.ts"`; + +exports[`Definitions > Alias path 2`] = ` +{ + "end": { + "character": 25, + "line": 0, + }, + "start": { + "character": 0, + "line": 0, + }, +} +`; + +exports[`Definitions > TS to vue 1`] = `"tsconfigProject/empty.vue"`; + +exports[`Definitions > TS to vue 2`] = ` +{ + "end": { + "character": 0, + "line": 0, + }, + "start": { + "character": 0, + "line": 0, + }, +} +`; + +exports[`Definitions > TS to vue 3`] = `"tsconfigProject/empty.vue"`; + +exports[`Definitions > TS to vue 4`] = ` +{ + "end": { + "character": 0, + "line": 0, + }, + "start": { + "character": 0, + "line": 0, + }, +} +`; diff --git a/packages/language-server/tests/definitions.spec.ts b/packages/language-server/tests/definitions.spec.ts new file mode 100644 index 0000000000..0121718366 --- /dev/null +++ b/packages/language-server/tests/definitions.spec.ts @@ -0,0 +1,89 @@ +import { Location, TextDocument } from '@volar/language-server'; +import * as path from 'path'; +import { afterEach, describe, expect, it } from 'vitest'; +import { URI } from 'vscode-uri'; +import { getLanguageServer, testWorkspacePath } from './server.js'; + +describe('Definitions', async () => { + + it('TS to vue', async () => { + await ensureGlobalTypesHolder('tsconfigProject'); + await assertDefinition('tsconfigProject/fixture1.ts', 'typescript', `import C|omponent from './empty.vue';`); + await assertDefinition('tsconfigProject/fixture2.ts', 'typescript', `import Component from '|./empty.vue';`); + }); + + it('Alias path', async () => { + await ensureGlobalTypesHolder('tsconfigProject'); + await openDocument('tsconfigProject/foo.ts', 'typescript', `export const foo = 'foo';`); + await assertDefinition('tsconfigProject/fixture.vue', 'vue', ` + + `); + }); + + it('#2600', async () => { + await ensureGlobalTypesHolder('tsconfigProject'); + await openDocument('tsconfigProject/foo.vue', 'vue', ` + + + + `); + await assertDefinition('tsconfigProject/fixture.vue', 'vue', ` + + `); + }); + + const openedDocuments: TextDocument[] = []; + + afterEach(async () => { + const server = await getLanguageServer(); + for (const document of openedDocuments) { + await server.closeTextDocument(document.uri); + } + openedDocuments.length = 0; + }); + + /** + * @deprecated Remove this when #4717 fixed. + */ + async function ensureGlobalTypesHolder(folderName: string) { + const document = await openDocument(`${folderName}/globalTypesHolder.vue`, 'vue', ''); + const server = await getLanguageServer(); + await server.sendDocumentDiagnosticRequest(document.uri); + } + + async function assertDefinition(fileName: string, languageId: string, content: string) { + const offset = content.indexOf('|'); + content = content.slice(0, offset) + content.slice(offset + 1); + + const server = await getLanguageServer(); + let document = await openDocument(fileName, languageId, content); + + const position = document.positionAt(offset); + const definition = await server.sendDefinitionRequest(document.uri, position) as Location[] | null; + expect(definition).toBeDefined(); + expect(definition!.length).greaterThan(0); + + for (const loc of definition!) { + expect(path.relative(testWorkspacePath, URI.parse(loc.uri).fsPath)).toMatchSnapshot(); + expect(loc.range).toMatchSnapshot(); + } + } + + async function openDocument(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; + } +}); diff --git a/packages/language-service/tests/complete.ts b/packages/language-service/tests/complete.ts deleted file mode 100644 index 582287949d..0000000000 --- a/packages/language-service/tests/complete.ts +++ /dev/null @@ -1,118 +0,0 @@ -import * as fs from 'fs'; -import * as path from 'path'; -import { describe, expect, it } from 'vitest'; -import type * as vscode from 'vscode-languageserver-protocol'; -import { TextDocument } from 'vscode-languageserver-textdocument'; -import { tester } from './utils/createTester'; -import { fileNameToUri } from './utils/mockEnv'; - -const baseDir = path.resolve(__dirname, '../../../test-workspace/language-service/complete'); -const testDirs = fs.readdirSync(baseDir); -const getLineText = (text: string, line: number) => text.replace(/\r\n/g, '\n').split('\n')[line]; - -for (const dirName of testDirs) { - - describe(`complete: ${dirName}`, async () => { - - const dir = path.join(baseDir, dirName); - const inputFiles = readFiles(path.join(dir, 'input')); - const outputFiles = readFiles(path.join(dir, 'output')); - - for (const file in inputFiles) { - - const filePath = path.join(dir, 'input', file); - const uri = fileNameToUri(filePath); - const fileText = inputFiles[file]; - const document = TextDocument.create('', '', 0, fileText); - const actions = findCompleteActions(fileText); - - const expectedFileText = outputFiles[file]; - - for (const action of actions) { - - const position = document.positionAt(action.offset); - - position.line--; - - const location = `${filePath}:${position.line + 1}:${position.character + 1}`; - - it(`${location} => ${action.label}`, async () => { - - expect(expectedFileText).toBeDefined(); - - let complete = await tester.languageService.getCompletionItems( - uri, - position, - { triggerKind: 1 satisfies typeof vscode.CompletionTriggerKind.Invoked } - ); - - if (!complete.items.length) { - // fix #2511 test case, it's a bug of TS 5.3 - complete = await tester.languageService.getCompletionItems( - uri, - position, - { triggerKind: 1 satisfies typeof vscode.CompletionTriggerKind.Invoked } - ); - } - - let item = complete.items.find(item => item.label === action.label)!; - - expect(item).toBeDefined(); - - item = await tester.languageService.resolveCompletionItem(item); - - let edits: vscode.TextEdit[] = []; - - if (item.textEdit) { - if ('replace' in item.textEdit) { - edits.push({ range: item.textEdit.replace, newText: item.textEdit.newText }); - } - else { - edits.push(item.textEdit); - } - } - else { - edits.push({ range: { start: position, end: position }, newText: item.insertText ?? item.label }); - } - - if (item.additionalTextEdits) { - edits = edits.concat(item.additionalTextEdits); - } - - let result = TextDocument.applyEdits(TextDocument.create('', '', 0, fileText), edits); - - result = result.replace(/\$0/g, '').replace(/\$1/g, ''); - - expect(getLineText(result, position.line)).toBe(getLineText(expectedFileText, position.line)); - }); - } - } - }); -} - -function readFiles(dir: string) { - - const filesText: Record = {}; - const files = fs.readdirSync(dir); - - for (const file of files) { - const filePath = path.join(dir, file); - filesText[file] = fs.readFileSync(filePath, 'utf8'); - } - - return filesText; -} - -function findCompleteActions(text: string) { - - return [...text.matchAll(/(\^*)complete:\s*([\S]*)/g)].map(flag => { - - const offset = flag.index; - const label = flag[2]; - - return { - offset, - label, - }; - }); -} diff --git a/packages/language-service/tests/findDefinition.ts b/packages/language-service/tests/findDefinition.ts deleted file mode 100644 index c359d44e3b..0000000000 --- a/packages/language-service/tests/findDefinition.ts +++ /dev/null @@ -1,94 +0,0 @@ -import * as fs from 'fs'; -import * as path from 'path'; -import { describe, expect, it } from 'vitest'; -import { TextDocument } from 'vscode-languageserver-textdocument'; -import { tester } from './utils/createTester'; -import { fileNameToUri } from './utils/mockEnv'; - -const baseDir = path.resolve(__dirname, '../../../test-workspace/language-service/find-definition'); -const testDirs = fs.readdirSync(baseDir); - -for (const dirName of testDirs) { - - describe(`find definition: ${dirName}`, async () => { - - const dir = path.join(baseDir, dirName); - const inputFiles = readFiles(dir); - - for (const file in inputFiles) { - - const filePath = path.join(dir, file); - const uri = fileNameToUri(filePath); - const fileText = inputFiles[file]; - const document = TextDocument.create('', '', 0, fileText); - const actions = findActions(fileText); - - for (const action of actions) { - - const position = document.positionAt(action.offset); - - position.line--; - - const targetFile = path.resolve(dir, action.targetFile); - const targetDocument = TextDocument.create('', '', 0, fs.readFileSync(targetFile, 'utf8')); - - it(`${filePath}:${position.line + 1}:${position.character + 1} => ${targetFile}:${action.targeRange.start}`, async () => { - - const locations = await tester.languageService.getDefinition( - uri, - position - ); - - expect(locations).toBeDefined(); - - const location = locations?.find(loc => - loc.targetUri === fileNameToUri(targetFile).toString() - && targetDocument.offsetAt(loc.targetSelectionRange.start) === action.targeRange.start - && targetDocument.offsetAt(loc.targetSelectionRange.end) === action.targeRange.end - ); - - if (!location) { - console.log(JSON.stringify(locations, null, 2)); - console.log(action.targeRange); - } - - expect(location).toBeDefined(); - }); - } - } - }); -} - -function readFiles(dir: string) { - - const filesText: Record = {}; - const files = fs.readdirSync(dir); - - for (const file of files) { - const filePath = path.join(dir, file); - filesText[file] = fs.readFileSync(filePath, 'utf8'); - } - - return filesText; -} - -const definitionReg = /(\^*)definition:\s*([\S]*),\s*([\S]*),\s*([\S]*)/g; - -function findActions(text: string) { - - return [...text.matchAll(definitionReg)].map(flag => { - - const offset = flag.index; - const targetFile = flag[2]; - const targeRange = { - start: Number(flag[3]), - end: Number(flag[4]), - }; - - return { - offset, - targetFile, - targeRange, - }; - }); -} diff --git a/test-workspace/language-service/find-definition/#2600/entry.vue b/test-workspace/language-service/find-definition/#2600/entry.vue deleted file mode 100644 index a39313dd73..0000000000 --- a/test-workspace/language-service/find-definition/#2600/entry.vue +++ /dev/null @@ -1,4 +0,0 @@ - diff --git a/test-workspace/language-service/find-definition/#2600/foo.vue b/test-workspace/language-service/find-definition/#2600/foo.vue deleted file mode 100644 index 35fd4030f5..0000000000 --- a/test-workspace/language-service/find-definition/#2600/foo.vue +++ /dev/null @@ -1,7 +0,0 @@ - - - diff --git a/test-workspace/language-service/find-definition/alias-path/entry.vue b/test-workspace/language-service/find-definition/alias-path/entry.vue deleted file mode 100644 index 0df2ffefbb..0000000000 --- a/test-workspace/language-service/find-definition/alias-path/entry.vue +++ /dev/null @@ -1,6 +0,0 @@ - diff --git a/test-workspace/language-service/find-definition/alias-path/foo.ts b/test-workspace/language-service/find-definition/alias-path/foo.ts deleted file mode 100644 index 3329a7d972..0000000000 --- a/test-workspace/language-service/find-definition/alias-path/foo.ts +++ /dev/null @@ -1 +0,0 @@ -export const foo = 'foo'; diff --git a/test-workspace/language-service/find-definition/ts-to-vue/component.vue b/test-workspace/language-service/find-definition/ts-to-vue/component.vue deleted file mode 100644 index 6b4c19a058..0000000000 --- a/test-workspace/language-service/find-definition/ts-to-vue/component.vue +++ /dev/null @@ -1 +0,0 @@ - diff --git a/test-workspace/language-service/find-definition/ts-to-vue/entry.ts b/test-workspace/language-service/find-definition/ts-to-vue/entry.ts deleted file mode 100644 index d3e3c899c0..0000000000 --- a/test-workspace/language-service/find-definition/ts-to-vue/entry.ts +++ /dev/null @@ -1,4 +0,0 @@ -import Component from './component.vue'; - // ^definition: ./component.vue, 0, 0 -import Component from './component.vue'; - // ^definition: ./component.vue, 0, 0