diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index ceebdc4020315..5a78b203e5a4b 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -411,6 +411,7 @@ namespace ts.server { /** @deprecated use serverMode instead */ syntaxOnly?: boolean; serverMode?: LanguageServiceMode; + session: Session | undefined; } interface OriginalFileInfo { fileName: NormalizedPath; path: Path; } @@ -783,6 +784,9 @@ namespace ts.server { /*@internal*/ private packageJsonFilesMap: ESMap | undefined; + /*@internal*/ + readonly session: Session | undefined; + private performanceEventHandler?: PerformanceEventHandler; @@ -800,6 +804,8 @@ namespace ts.server { this.pluginProbeLocations = opts.pluginProbeLocations || emptyArray; this.allowLocalPluginLoads = !!opts.allowLocalPluginLoads; this.typesMapLocation = (opts.typesMapLocation === undefined) ? combinePaths(getDirectoryPath(this.getExecutingFilePath()), "typesMap.json") : opts.typesMapLocation; + this.session = opts.session; + if (opts.serverMode !== undefined) { this.serverMode = opts.serverMode; this.syntaxOnly = this.serverMode === LanguageServiceMode.Syntactic; diff --git a/src/server/project.ts b/src/server/project.ts index ef48ebf530bc2..a974a0d8497b2 100644 --- a/src/server/project.ts +++ b/src/server/project.ts @@ -84,6 +84,7 @@ namespace ts.server { languageService: LanguageService; languageServiceHost: LanguageServiceHost; serverHost: ServerHost; + session?: Session; config: any; } @@ -1603,7 +1604,8 @@ namespace ts.server { project: this, languageService: this.languageService, languageServiceHost: this, - serverHost: this.projectService.host + serverHost: this.projectService.host, + session: this.projectService.session }; const pluginModule = pluginModuleFactory({ typescript: ts }); @@ -2111,7 +2113,7 @@ namespace ts.server { /*compileOnSaveEnabled*/ false, /*watchOptions*/ undefined, cachedDirectoryStructureHost, - getDirectoryPath(configFileName), + getDirectoryPath(configFileName) ); } diff --git a/src/server/session.ts b/src/server/session.ts index ee2b7a243994c..999107910dc35 100644 --- a/src/server/session.ts +++ b/src/server/session.ts @@ -775,6 +775,7 @@ namespace ts.server { typesMapLocation: opts.typesMapLocation, syntaxOnly: opts.syntaxOnly, serverMode: opts.serverMode, + session: this }; this.projectService = new ProjectService(settings); this.projectService.setPerformanceEventHandler(this.performanceEventHandler.bind(this)); diff --git a/src/testRunner/unittests/tsserver/helpers.ts b/src/testRunner/unittests/tsserver/helpers.ts index b125667200c55..9e859efb279e8 100644 --- a/src/testRunner/unittests/tsserver/helpers.ts +++ b/src/testRunner/unittests/tsserver/helpers.ts @@ -389,6 +389,7 @@ namespace ts.projectSystem { super({ host, logger, + session: undefined, cancellationToken, useSingleInferredProject, useInferredProjectPerProjectRoot: false, diff --git a/src/testRunner/unittests/tsserver/plugins.ts b/src/testRunner/unittests/tsserver/plugins.ts index 68833409a4b6f..5d46dd203f9db 100644 --- a/src/testRunner/unittests/tsserver/plugins.ts +++ b/src/testRunner/unittests/tsserver/plugins.ts @@ -1,20 +1,31 @@ namespace ts.projectSystem { describe("unittests:: tsserver:: plugins loading", () => { + const testProtocolCommand = "testProtocolCommand"; + const testProtocolCommandRequest = "testProtocolCommandRequest"; + const testProtocolCommandResponse = "testProtocolCommandResponse"; + function createHostWithPlugin(files: readonly File[]) { const host = createServerHost(files); const pluginsLoaded: string[] = []; + const protocolHandlerRequests: [string, string][] = []; host.require = (_initialPath, moduleName) => { pluginsLoaded.push(moduleName); return { module: () => ({ create(info: server.PluginCreateInfo) { + info.session?.addProtocolHandler(testProtocolCommand, request => { + protocolHandlerRequests.push([request.command, request.arguments]); + return { + response: testProtocolCommandResponse + }; + }); return Harness.LanguageService.makeDefaultProxy(info); } }), error: undefined }; }; - return { host, pluginsLoaded }; + return { host, pluginsLoaded, protocolHandlerRequests }; } it("With local plugins", () => { @@ -51,5 +62,43 @@ namespace ts.projectSystem { service.openClientFile(aTs.path); assert.deepEqual(pluginsLoaded, expectedToLoad); }); + + it("With session and custom protocol message", () => { + const pluginName = "some-plugin"; + const expectedToLoad = [pluginName]; + const aTs: File = { path: "/a.ts", content: `class c { prop = "hello"; foo() { return this.prop; } }` }; + const tsconfig: File = { + path: "/tsconfig.json", + content: JSON.stringify({ + compilerOptions: { + plugins: [ + { name: pluginName } + ] + } + }) + }; + + const { host, pluginsLoaded, protocolHandlerRequests } = createHostWithPlugin([aTs, tsconfig, libFile]); + const session = createSession(host); + + const service = createProjectService(host, { session }); + service.openClientFile(aTs.path); + assert.deepEqual(pluginsLoaded, expectedToLoad); + + const resp = session.executeCommandSeq({ + command: testProtocolCommand, + arguments: testProtocolCommandRequest + }); + + assert.strictEqual(protocolHandlerRequests.length, 1); + const [command, args] = protocolHandlerRequests[0]; + assert.strictEqual(command, testProtocolCommand); + assert.strictEqual(args, testProtocolCommandRequest); + + const expectedResp: server.HandlerResponse = { + response: testProtocolCommandResponse + }; + assert.deepEqual(resp, expectedResp); + }); }); } \ No newline at end of file diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index dcbd016c31e3b..76c1e3c657278 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -9554,6 +9554,7 @@ declare namespace ts.server { languageService: LanguageService; languageServiceHost: LanguageServiceHost; serverHost: ServerHost; + session?: Session; config: any; } interface PluginModule { @@ -9947,6 +9948,7 @@ declare namespace ts.server { /** @deprecated use serverMode instead */ syntaxOnly?: boolean; serverMode?: LanguageServiceMode; + session: Session | undefined; } export interface WatchOptionsAndErrors { watchOptions: WatchOptions;