From f763cac5c93b8b2364809e76c98e4e5917454ebb Mon Sep 17 00:00:00 2001 From: David Senoner Date: Fri, 21 Nov 2025 00:30:52 +0100 Subject: [PATCH 1/2] feat: expose ls version to client --- packages/language-server/lib/server.ts | 222 +++++++++++++------------ 1 file changed, 114 insertions(+), 108 deletions(-) diff --git a/packages/language-server/lib/server.ts b/packages/language-server/lib/server.ts index 7edff3994a..9d0b74227a 100644 --- a/packages/language-server/lib/server.ts +++ b/packages/language-server/lib/server.ts @@ -47,118 +47,124 @@ export function startServer(ts: typeof import('typescript')) { let simpleLanguageService: LanguageService | undefined; - return server.initialize( - params, - { - setup() {}, - async getLanguageService(uri) { - if (uri.scheme === 'file') { - const fileName = uri.fsPath.replace(/\\/g, '/'); - let projectInfoPromise = file2ProjectInfo.get(fileName); - if (!projectInfoPromise) { - projectInfoPromise = sendTsServerRequest( - '_vue:' + ts.server.protocol.CommandTypes.ProjectInfo, - { - file: fileName, - needFileNameList: false, - } satisfies ts.server.protocol.ProjectInfoRequestArgs, - ); - file2ProjectInfo.set(fileName, projectInfoPromise); - } - const projectInfo = await projectInfoPromise; - if (projectInfo) { - const { configFileName } = projectInfo; - let languageService = tsconfigProjects.get(URI.file(configFileName)); - if (!languageService) { - languageService = createProjectLanguageService(server, configFileName); - tsconfigProjects.set(URI.file(configFileName), languageService); + return { + ...server.initialize( + params, + { + setup() {}, + async getLanguageService(uri) { + if (uri.scheme === 'file') { + const fileName = uri.fsPath.replace(/\\/g, '/'); + let projectInfoPromise = file2ProjectInfo.get(fileName); + if (!projectInfoPromise) { + projectInfoPromise = sendTsServerRequest( + '_vue:' + ts.server.protocol.CommandTypes.ProjectInfo, + { + file: fileName, + needFileNameList: false, + } satisfies ts.server.protocol.ProjectInfoRequestArgs, + ); + file2ProjectInfo.set(fileName, projectInfoPromise); + } + const projectInfo = await projectInfoPromise; + if (projectInfo) { + const { configFileName } = projectInfo; + let languageService = tsconfigProjects.get(URI.file(configFileName)); + if (!languageService) { + languageService = createProjectLanguageService(server, configFileName); + tsconfigProjects.set(URI.file(configFileName), languageService); + } + return languageService; } - return languageService; } - } - return simpleLanguageService ??= createProjectLanguageService(server, undefined); - }, - getExistingLanguageServices() { - return Promise.all([ - ...tsconfigProjects.values(), - simpleLanguageService, - ].filter(promise => !!promise)); - }, - reload() { - for (const languageService of tsconfigProjects.values()) { - languageService.dispose(); - } - tsconfigProjects.clear(); - if (simpleLanguageService) { - simpleLanguageService.dispose(); - simpleLanguageService = undefined; - } + return simpleLanguageService ??= createProjectLanguageService(server, undefined); + }, + getExistingLanguageServices() { + return Promise.all([ + ...tsconfigProjects.values(), + simpleLanguageService, + ].filter(promise => !!promise)); + }, + reload() { + for (const languageService of tsconfigProjects.values()) { + languageService.dispose(); + } + tsconfigProjects.clear(); + if (simpleLanguageService) { + simpleLanguageService.dispose(); + simpleLanguageService = undefined; + } + }, }, + createVueLanguageServicePlugins(ts, { + collectExtractProps(...args) { + return sendTsServerRequest('_vue:collectExtractProps', args); + }, + getComponentDirectives(...args) { + return sendTsServerRequest('_vue:getComponentDirectives', args); + }, + getComponentEvents(...args) { + return sendTsServerRequest('_vue:getComponentEvents', args); + }, + getComponentNames(...args) { + return sendTsServerRequest('_vue:getComponentNames', args); + }, + getComponentProps(...args) { + return sendTsServerRequest('_vue:getComponentProps', args); + }, + getComponentSlots(...args) { + return sendTsServerRequest('_vue:getComponentSlots', args); + }, + getElementAttrs(...args) { + return sendTsServerRequest('_vue:getElementAttrs', args); + }, + getElementNames(...args) { + return sendTsServerRequest('_vue:getElementNames', args); + }, + getImportPathForFile(...args) { + return sendTsServerRequest('_vue:getImportPathForFile', args); + }, + isRefAtPosition(...args) { + return sendTsServerRequest('_vue:isRefAtPosition', args); + }, + getDocumentHighlights(fileName, position) { + return sendTsServerRequest( + '_vue:documentHighlights-full', + { + file: fileName, + ...{ position } as unknown as { line: number; offset: number }, + filesToSearch: [fileName], + } satisfies ts.server.protocol.DocumentHighlightsRequestArgs, + ); + }, + getEncodedSemanticClassifications(fileName, span) { + return sendTsServerRequest( + '_vue:encodedSemanticClassifications-full', + { + file: fileName, + ...span, + format: ts.SemanticClassificationFormat.TwentyTwenty, + } satisfies ts.server.protocol.EncodedSemanticClassificationsRequestArgs, + ); + }, + async getQuickInfoAtPosition(fileName, { line, character }) { + const result = await sendTsServerRequest( + '_vue:' + ts.server.protocol.CommandTypes.Quickinfo, + { + file: fileName, + line: line + 1, + offset: character + 1, + } satisfies ts.server.protocol.FileLocationRequestArgs, + ); + return result?.displayString; + }, + }), + ), + serverInfo: { + name: require('../package.json').name as string, + version: require('../package.json').version as string, }, - createVueLanguageServicePlugins(ts, { - collectExtractProps(...args) { - return sendTsServerRequest('_vue:collectExtractProps', args); - }, - getComponentDirectives(...args) { - return sendTsServerRequest('_vue:getComponentDirectives', args); - }, - getComponentEvents(...args) { - return sendTsServerRequest('_vue:getComponentEvents', args); - }, - getComponentNames(...args) { - return sendTsServerRequest('_vue:getComponentNames', args); - }, - getComponentProps(...args) { - return sendTsServerRequest('_vue:getComponentProps', args); - }, - getComponentSlots(...args) { - return sendTsServerRequest('_vue:getComponentSlots', args); - }, - getElementAttrs(...args) { - return sendTsServerRequest('_vue:getElementAttrs', args); - }, - getElementNames(...args) { - return sendTsServerRequest('_vue:getElementNames', args); - }, - getImportPathForFile(...args) { - return sendTsServerRequest('_vue:getImportPathForFile', args); - }, - isRefAtPosition(...args) { - return sendTsServerRequest('_vue:isRefAtPosition', args); - }, - getDocumentHighlights(fileName, position) { - return sendTsServerRequest( - '_vue:documentHighlights-full', - { - file: fileName, - ...{ position } as unknown as { line: number; offset: number }, - filesToSearch: [fileName], - } satisfies ts.server.protocol.DocumentHighlightsRequestArgs, - ); - }, - getEncodedSemanticClassifications(fileName, span) { - return sendTsServerRequest( - '_vue:encodedSemanticClassifications-full', - { - file: fileName, - ...span, - format: ts.SemanticClassificationFormat.TwentyTwenty, - } satisfies ts.server.protocol.EncodedSemanticClassificationsRequestArgs, - ); - }, - async getQuickInfoAtPosition(fileName, { line, character }) { - const result = await sendTsServerRequest( - '_vue:' + ts.server.protocol.CommandTypes.Quickinfo, - { - file: fileName, - line: line + 1, - offset: character + 1, - } satisfies ts.server.protocol.FileLocationRequestArgs, - ); - return result?.displayString; - }, - }), - ); + }; async function sendTsServerRequest(command: string, args: any): Promise { return await new Promise(resolve => { From 04ec74177f80422dae92bb880137baa41e6bd09c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B1=B1=E5=90=B9=E8=89=B2=E5=BE=A1=E5=AE=88?= <85992002+KazariEX@users.noreply.github.com> Date: Fri, 21 Nov 2025 03:36:05 -0800 Subject: [PATCH 2/2] Update server.ts --- packages/language-server/lib/server.ts | 231 +++++++++++++------------ 1 file changed, 118 insertions(+), 113 deletions(-) diff --git a/packages/language-server/lib/server.ts b/packages/language-server/lib/server.ts index 9d0b74227a..66b0157d97 100644 --- a/packages/language-server/lib/server.ts +++ b/packages/language-server/lib/server.ts @@ -47,125 +47,130 @@ export function startServer(ts: typeof import('typescript')) { let simpleLanguageService: LanguageService | undefined; - return { - ...server.initialize( - params, - { - setup() {}, - async getLanguageService(uri) { - if (uri.scheme === 'file') { - const fileName = uri.fsPath.replace(/\\/g, '/'); - let projectInfoPromise = file2ProjectInfo.get(fileName); - if (!projectInfoPromise) { - projectInfoPromise = sendTsServerRequest( - '_vue:' + ts.server.protocol.CommandTypes.ProjectInfo, - { - file: fileName, - needFileNameList: false, - } satisfies ts.server.protocol.ProjectInfoRequestArgs, - ); - file2ProjectInfo.set(fileName, projectInfoPromise); - } - const projectInfo = await projectInfoPromise; - if (projectInfo) { - const { configFileName } = projectInfo; - let languageService = tsconfigProjects.get(URI.file(configFileName)); - if (!languageService) { - languageService = createProjectLanguageService(server, configFileName); - tsconfigProjects.set(URI.file(configFileName), languageService); - } - return languageService; - } - } - return simpleLanguageService ??= createProjectLanguageService(server, undefined); - }, - getExistingLanguageServices() { - return Promise.all([ - ...tsconfigProjects.values(), - simpleLanguageService, - ].filter(promise => !!promise)); - }, - reload() { - for (const languageService of tsconfigProjects.values()) { - languageService.dispose(); + const result = server.initialize( + params, + { + setup() {}, + async getLanguageService(uri) { + if (uri.scheme === 'file') { + const fileName = uri.fsPath.replace(/\\/g, '/'); + let projectInfoPromise = file2ProjectInfo.get(fileName); + if (!projectInfoPromise) { + projectInfoPromise = sendTsServerRequest( + '_vue:' + ts.server.protocol.CommandTypes.ProjectInfo, + { + file: fileName, + needFileNameList: false, + } satisfies ts.server.protocol.ProjectInfoRequestArgs, + ); + file2ProjectInfo.set(fileName, projectInfoPromise); } - tsconfigProjects.clear(); - if (simpleLanguageService) { - simpleLanguageService.dispose(); - simpleLanguageService = undefined; + const projectInfo = await projectInfoPromise; + if (projectInfo) { + const { configFileName } = projectInfo; + let languageService = tsconfigProjects.get(URI.file(configFileName)); + if (!languageService) { + languageService = createProjectLanguageService(server, configFileName); + tsconfigProjects.set(URI.file(configFileName), languageService); + } + return languageService; } - }, + } + return simpleLanguageService ??= createProjectLanguageService(server, undefined); + }, + getExistingLanguageServices() { + return Promise.all([ + ...tsconfigProjects.values(), + simpleLanguageService, + ].filter(promise => !!promise)); + }, + reload() { + for (const languageService of tsconfigProjects.values()) { + languageService.dispose(); + } + tsconfigProjects.clear(); + if (simpleLanguageService) { + simpleLanguageService.dispose(); + simpleLanguageService = undefined; + } }, - createVueLanguageServicePlugins(ts, { - collectExtractProps(...args) { - return sendTsServerRequest('_vue:collectExtractProps', args); - }, - getComponentDirectives(...args) { - return sendTsServerRequest('_vue:getComponentDirectives', args); - }, - getComponentEvents(...args) { - return sendTsServerRequest('_vue:getComponentEvents', args); - }, - getComponentNames(...args) { - return sendTsServerRequest('_vue:getComponentNames', args); - }, - getComponentProps(...args) { - return sendTsServerRequest('_vue:getComponentProps', args); - }, - getComponentSlots(...args) { - return sendTsServerRequest('_vue:getComponentSlots', args); - }, - getElementAttrs(...args) { - return sendTsServerRequest('_vue:getElementAttrs', args); - }, - getElementNames(...args) { - return sendTsServerRequest('_vue:getElementNames', args); - }, - getImportPathForFile(...args) { - return sendTsServerRequest('_vue:getImportPathForFile', args); - }, - isRefAtPosition(...args) { - return sendTsServerRequest('_vue:isRefAtPosition', args); - }, - getDocumentHighlights(fileName, position) { - return sendTsServerRequest( - '_vue:documentHighlights-full', - { - file: fileName, - ...{ position } as unknown as { line: number; offset: number }, - filesToSearch: [fileName], - } satisfies ts.server.protocol.DocumentHighlightsRequestArgs, - ); - }, - getEncodedSemanticClassifications(fileName, span) { - return sendTsServerRequest( - '_vue:encodedSemanticClassifications-full', - { - file: fileName, - ...span, - format: ts.SemanticClassificationFormat.TwentyTwenty, - } satisfies ts.server.protocol.EncodedSemanticClassificationsRequestArgs, - ); - }, - async getQuickInfoAtPosition(fileName, { line, character }) { - const result = await sendTsServerRequest( - '_vue:' + ts.server.protocol.CommandTypes.Quickinfo, - { - file: fileName, - line: line + 1, - offset: character + 1, - } satisfies ts.server.protocol.FileLocationRequestArgs, - ); - return result?.displayString; - }, - }), - ), - serverInfo: { - name: require('../package.json').name as string, - version: require('../package.json').version as string, }, + createVueLanguageServicePlugins(ts, { + collectExtractProps(...args) { + return sendTsServerRequest('_vue:collectExtractProps', args); + }, + getComponentDirectives(...args) { + return sendTsServerRequest('_vue:getComponentDirectives', args); + }, + getComponentEvents(...args) { + return sendTsServerRequest('_vue:getComponentEvents', args); + }, + getComponentNames(...args) { + return sendTsServerRequest('_vue:getComponentNames', args); + }, + getComponentProps(...args) { + return sendTsServerRequest('_vue:getComponentProps', args); + }, + getComponentSlots(...args) { + return sendTsServerRequest('_vue:getComponentSlots', args); + }, + getElementAttrs(...args) { + return sendTsServerRequest('_vue:getElementAttrs', args); + }, + getElementNames(...args) { + return sendTsServerRequest('_vue:getElementNames', args); + }, + getImportPathForFile(...args) { + return sendTsServerRequest('_vue:getImportPathForFile', args); + }, + isRefAtPosition(...args) { + return sendTsServerRequest('_vue:isRefAtPosition', args); + }, + resolveModuleName(...args) { + return sendTsServerRequest('_vue:resolveModuleName', args); + }, + getDocumentHighlights(fileName, position) { + return sendTsServerRequest( + '_vue:documentHighlights-full', + { + file: fileName, + ...{ position } as unknown as { line: number; offset: number }, + filesToSearch: [fileName], + } satisfies ts.server.protocol.DocumentHighlightsRequestArgs, + ); + }, + getEncodedSemanticClassifications(fileName, span) { + return sendTsServerRequest( + '_vue:encodedSemanticClassifications-full', + { + file: fileName, + ...span, + format: ts.SemanticClassificationFormat.TwentyTwenty, + } satisfies ts.server.protocol.EncodedSemanticClassificationsRequestArgs, + ); + }, + async getQuickInfoAtPosition(fileName, { line, character }) { + const result = await sendTsServerRequest( + '_vue:' + ts.server.protocol.CommandTypes.Quickinfo, + { + file: fileName, + line: line + 1, + offset: character + 1, + } satisfies ts.server.protocol.FileLocationRequestArgs, + ); + return result?.displayString; + }, + }), + ); + + const packageJson = require('../package.json'); + result.serverInfo = { + name: packageJson.name, + version: packageJson.version, }; + return result; + async function sendTsServerRequest(command: string, args: any): Promise { return await new Promise(resolve => { const requestId = ++tsserverRequestId;