diff --git a/packages/language-core/lib/languagePlugin.ts b/packages/language-core/lib/languagePlugin.ts index c2b8d805b3..5b33fb1a25 100644 --- a/packages/language-core/lib/languagePlugin.ts +++ b/packages/language-core/lib/languagePlugin.ts @@ -60,7 +60,7 @@ export function createVueLanguagePlugin( createVirtualCode(scriptId, languageId, snapshot) { const fileName = asFileName(scriptId); if (plugins.some(plugin => plugin.isValidFile?.(fileName, languageId))) { - const code = fileRegistry.get(fileName); + const code = fileRegistry.get(String(scriptId)); if (code) { code.update(snapshot); return code; @@ -74,7 +74,7 @@ export function createVueLanguagePlugin( plugins, ts, ); - fileRegistry.set(fileName, code); + fileRegistry.set(String(scriptId), code); return code; } } @@ -84,8 +84,7 @@ export function createVueLanguagePlugin( return code; }, disposeVirtualCode(scriptId) { - const fileName = asFileName(scriptId); - fileRegistry.delete(fileName); + fileRegistry.delete(String(scriptId)); }, typescript: { extraFileExtensions: getAllExtensions(vueCompilerOptions) diff --git a/packages/language-server/tests/semanticTokens.spec.ts b/packages/language-server/tests/semanticTokens.spec.ts new file mode 100644 index 0000000000..8dc0c841cf --- /dev/null +++ b/packages/language-server/tests/semanticTokens.spec.ts @@ -0,0 +1,49 @@ +import type { TextDocument } from '@volar/language-server'; +import { afterEach, expect, test } from 'vitest'; +import { URI } from 'vscode-uri'; +import { getLanguageServer, testWorkspacePath } from './server.js'; + +const openedDocuments: TextDocument[] = []; + +afterEach(async () => { + const server = await getLanguageServer(); + for (const document of openedDocuments) { + await server.close(document.uri); + } + openedDocuments.length = 0; +}); + +test('#5572 semantic tokens stay in sync when a git view is opened', async () => { + const server = await getLanguageServer(); + const fileContent = ` + + `; + const fileUri = URI.file(`${testWorkspacePath}/semanticTokens.vue`); + + const document = await prepareDocument(fileUri, 'vue', fileContent); + const tokensBefore = (await server.vueserver.sendSemanticTokensRequest(document.uri))!.data; + + // simlulate open git diff view + const gitUri = URI.from({ scheme: 'git', path: fileUri.path }); + await prepareDocument(gitUri, 'vue', fileContent.replace('foo', 'fooooooo')); + + const tokensAfter = (await server.vueserver.sendSemanticTokensRequest(document.uri))!.data; + + expect(tokensAfter).toEqual(tokensBefore); +}); + +async function prepareDocument(uriOrFileName: string | URI, languageId: string, content: string) { + const server = await getLanguageServer(); + const uri = typeof uriOrFileName === 'string' + ? URI.file(`${testWorkspacePath}/${uriOrFileName}`) + : uriOrFileName; + 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 060c7b529d..e9f13ae0e9 100644 --- a/packages/language-server/tests/server.ts +++ b/packages/language-server/tests/server.ts @@ -69,40 +69,44 @@ export async function getLanguageServer(): Promise<{ 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, - }, - ], - }, - }); - if (!res.success) { - throw new Error(res.body); + open: async (uri, languageId, content) => { + if (uri.startsWith('file://')) { + const res = await tsserver.message({ + seq: seq++, + type: 'request', + command: 'updateOpen', + arguments: { + changedFiles: [], + closedFiles: [], + openFiles: [ + { + file: URI.parse(uri).fsPath, + fileContent: content, + }, + ], + }, + }); + if (!res.success) { + throw new Error(res.body); + } } 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); + close: async uri => { + if (uri.startsWith('file://')) { + 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); },