From 0aeed2f84eb564829ed608ae28a03668027b5ab1 Mon Sep 17 00:00:00 2001 From: Bevan Philip Date: Mon, 25 Nov 2024 16:39:25 +0000 Subject: [PATCH] lsp: migrate from tcp to stdio - Remove all networking artifacts. - Let the Language Client handle spawning the process (and thus, process cleanup). - Remove language server networking configuration options. - Remove all process cleanup code, and associated libraries. --- package-lock.json | 109 -------------------------------------- package.json | 12 ----- src/extension.ts | 63 +++++++--------------- src/utils/processUtils.ts | 23 -------- 4 files changed, 19 insertions(+), 188 deletions(-) delete mode 100644 src/utils/processUtils.ts diff --git a/package-lock.json b/package-lock.json index cd25431..029ef45 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,8 +16,6 @@ "glob": "^11.0.0", "semver": "^7.6.3", "shelljs": "^0.8.5", - "terminate": "^2.8.0", - "tree-kill": "^1.2.2", "vscode-languageclient": "^9.0.1", "vscode-languageserver": "^9.0.1", "xpath": "0.0.34" @@ -1799,12 +1797,6 @@ "node": ">=0.3.1" } }, - "node_modules/duplexer": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", - "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==", - "license": "MIT" - }, "node_modules/eastasianwidth": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", @@ -2114,21 +2106,6 @@ "node": ">=0.10.0" } }, - "node_modules/event-stream": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/event-stream/-/event-stream-3.3.4.tgz", - "integrity": "sha512-QHpkERcGsR0T7Qm3HNJSyXKEEj8AHNxkY3PK8TS2KJvQ7NiSHe3DDpwVKKtoYprL/AreyzFBeIkBIWChAqn60g==", - "license": "MIT", - "dependencies": { - "duplexer": "~0.1.1", - "from": "~0", - "map-stream": "~0.1.0", - "pause-stream": "0.0.11", - "split": "0.3", - "stream-combiner": "~0.0.4", - "through": "~2.3.1" - } - }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -2301,12 +2278,6 @@ "node": ">= 6" } }, - "node_modules/from": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/from/-/from-0.1.7.tgz", - "integrity": "sha512-twe20eF1OxVxp/ML/kq2p1uc6KvFK/+vs8WjEbeKmV2He22MKm7YF2ANIt+EOqhJ5L3K/SuuPhk0hWQDjOM23g==", - "license": "MIT" - }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -2935,11 +2906,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/map-stream": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.1.0.tgz", - "integrity": "sha512-CkYQrPYZfWnu/DAmVCpTSX/xHpKZ80eKh2lAkyA6AJTef6bW+6JpbQZN5rofum7da+SyN1bi5ctTm+lTfcCW3g==" - }, "node_modules/merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", @@ -3503,18 +3469,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/pause-stream": { - "version": "0.0.11", - "resolved": "https://registry.npmjs.org/pause-stream/-/pause-stream-0.0.11.tgz", - "integrity": "sha512-e3FBlXLmN/D1S+zHzanP4E/4Z60oFAa3O051qt1pxa7DEJWKAyil6upYVXCWadEnuoqa4Pkc9oUx9zsxYeRv8A==", - "license": [ - "MIT", - "Apache2" - ], - "dependencies": { - "through": "~2.3" - } - }, "node_modules/picomatch": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", @@ -3551,21 +3505,6 @@ "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", "license": "MIT" }, - "node_modules/ps-tree": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/ps-tree/-/ps-tree-1.2.0.tgz", - "integrity": "sha512-0VnamPPYHl4uaU/nSFeZZpR21QAWRz+sRv4iW9+v/GS/J5U5iZB5BNN6J0RMoOvdx2gWM2+ZFMIm58q24e4UYA==", - "license": "MIT", - "dependencies": { - "event-stream": "=3.3.4" - }, - "bin": { - "ps-tree": "bin/ps-tree.js" - }, - "engines": { - "node": ">= 0.10" - } - }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", @@ -3872,18 +3811,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/split": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/split/-/split-0.3.3.tgz", - "integrity": "sha512-wD2AeVmxXRBoX44wAycgjVpMhvbwdI2aZjCkvfNcH1YqHQvJVa1duWc73OyVGJUc05fhFaTZeQ/PYsrmyH0JVA==", - "license": "MIT", - "dependencies": { - "through": "2" - }, - "engines": { - "node": "*" - } - }, "node_modules/stdin-discarder": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/stdin-discarder/-/stdin-discarder-0.1.0.tgz", @@ -3900,15 +3827,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/stream-combiner": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.0.4.tgz", - "integrity": "sha512-rT00SPnTVyRsaSz5zgSPma/aHSOic5U1prhYdRy5HS2kTZviFpmDgzilbtsJsxiroqACmayynDN/9VzIbX5DOw==", - "license": "MIT", - "dependencies": { - "duplexer": "~0.1.1" - } - }, "node_modules/string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", @@ -4057,18 +3975,6 @@ "node": ">=6" } }, - "node_modules/terminate": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/terminate/-/terminate-2.8.0.tgz", - "integrity": "sha512-bcbjJEg0wY5nuJXvGxxHfmoEPkyHLCctUKO6suwtxy7jVSgGcgPeGwpbLDLELFhIaxCGRr3dPvyNg1yuz2V0eg==", - "license": "GPL-2.0", - "dependencies": { - "ps-tree": "^1.2.0" - }, - "engines": { - "node": ">=12" - } - }, "node_modules/test-exclude": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", @@ -4137,12 +4043,6 @@ "dev": true, "license": "MIT" }, - "node_modules/through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", - "license": "MIT" - }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -4162,15 +4062,6 @@ "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", "license": "MIT" }, - "node_modules/tree-kill": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", - "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", - "license": "MIT", - "bin": { - "tree-kill": "cli.js" - } - }, "node_modules/ts-api-utils": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz", diff --git a/package.json b/package.json index a15ce3d..a3257fd 100644 --- a/package.json +++ b/package.json @@ -48,16 +48,6 @@ "default": "local", "description": "Configure if the Language Server is enabled." }, - "apama.langServer.port": { - "type": "integer", - "default": "30030", - "description": "The Language Server port." - }, - "apama.langServer.maxErrors": { - "type": "integer", - "default": "100", - "description": "The maximum number of diagnostics that the Language Server will return." - }, "apama.c8y.url": { "type": "string", "default": "https://demos.cumulocity.com/", @@ -328,8 +318,6 @@ "glob": "^11.0.0", "semver": "^7.6.3", "shelljs": "^0.8.5", - "terminate": "^2.8.0", - "tree-kill": "^1.2.2", "vscode-languageclient": "^9.0.1", "vscode-languageserver": "^9.0.1", "xpath": "0.0.34" diff --git a/src/extension.ts b/src/extension.ts index dc8e3b9..91d44d8 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -1,13 +1,14 @@ "use_strict"; -import * as net from 'net'; import * as os from 'os'; import * as path from 'path'; import { ExtensionContext, Disposable, tasks, debug, workspace, WorkspaceConfiguration} from 'vscode'; import { - LanguageClient, LanguageClientOptions, ServerOptions + Executable, + LanguageClient, LanguageClientOptions, + TransportKind } from 'vscode-languageclient/node'; @@ -19,11 +20,8 @@ import { ApamaCommandProvider } from './apama_util/commands'; import { Logger } from './logger/logger'; import { ExecutableResolver } from './settings/ExecutableResolver'; -import { ChildProcessWithoutNullStreams, spawn } from 'child_process'; -import { killProcessTree } from './utils/processUtils'; let languageClient : LanguageClient; -let LanguageServerProcess: ChildProcessWithoutNullStreams | null = null; const logger = new Logger('ApamaCommunity.apama-extensions'); export async function activate(context: ExtensionContext): Promise { @@ -55,7 +53,7 @@ export async function activate(context: ExtensionContext): Promise { return Promise.resolve(); } - LanguageServerProcess = await createLangServerTCP(config, `${path.dirname(resolve.path)}/apama_env`); + createLangServerTCP(config, `${path.dirname(resolve.path)}/apama_env`); const apamaEnv: ApamaEnvironment = new ApamaEnvironment(); const taskprov = new ApamaTaskProvider(logger, apamaEnv); @@ -86,47 +84,25 @@ async function createLangServerTCP(config: WorkspaceConfiguration, apamaEnvPath: /** * Spawns a language server, and then proceeds to connect the language client up to it. */ - const logger = new Logger('Apama Language Server'); const lsType: string | undefined = config.get("type"); if (lsType === "disabled") { return Promise.reject("Apama Language Server disabled"); } - const serverOptions: ServerOptions = () => { - return new Promise((resolve) => { - if (os.platform() == "win32") { - LanguageServerProcess = spawn(`${path.dirname(apamaEnvPath)}/eplbuddy.exe`, ['-l']) - } else { - LanguageServerProcess = spawn(apamaEnvPath, [`eplbuddy`, "-l"], { "detached": true}); - } - - LanguageServerProcess.stdout.on('data', (data) => { - logger.info(`stdout: ${data}`); - - if (data.toString().startsWith("Listening on")) { - const clientSocket = new net.Socket(); - // Set an error handler - clientSocket.on('error', (error) => { - logger.error(`Socket connection error: ${error.message}`); - }); - - clientSocket.connect(30030, "127.0.0.1", () => { - logger.debug(`Connected to socket at: ${config.port}`) - resolve({ - reader: clientSocket, - writer: clientSocket, - }); - }); - } - }); - - LanguageServerProcess.stderr.on('data', (data) => { - logger.info(`stderr: ${data}`); - }); - LanguageServerProcess.on('error', (error) => { - logger.info(`Error: ${error}`); - }); - }); + let commandStr; + let args; + if (os.platform() == "win32") { + commandStr = `${path.dirname(apamaEnvPath)}/eplbuddy.exe` + args = ['-s'] + } else { + commandStr = apamaEnvPath + args = [`eplbuddy`, "-s"] + } + + const serverOptions: Executable = { + transport: TransportKind.stdio, + command: commandStr, + args: args }; // Options of the language client @@ -141,7 +117,7 @@ async function createLangServerTCP(config: WorkspaceConfiguration, apamaEnvPath: fileEvents: workspace.createFileSystemWatcher('**/.mon') } }; - languageClient = new LanguageClient(`Apama Language Client (host ${config.host} port ${config.port})`, serverOptions, clientOptions); + languageClient = new LanguageClient(`Apama Language Client`, serverOptions, clientOptions); await languageClient.start(); return null; } @@ -149,6 +125,5 @@ async function createLangServerTCP(config: WorkspaceConfiguration, apamaEnvPath: export function deactivate() { return Promise.all([ languageClient.stop(), - killProcessTree(LanguageServerProcess, logger), ]); } diff --git a/src/utils/processUtils.ts b/src/utils/processUtils.ts deleted file mode 100644 index dcf900f..0000000 --- a/src/utils/processUtils.ts +++ /dev/null @@ -1,23 +0,0 @@ -/*--------------------------------------------------------- - * Copyright 2020 The Go Authors. All rights reserved. - * Licensed under the MIT License. See LICENSE in the project root for license information. - *--------------------------------------------------------*/ - -import { ChildProcessWithoutNullStreams } from 'child_process'; -import { Logger } from '../logger/logger'; -import * as terminate from 'terminate'; - -// Kill a process and its children, returning a promise. -export function killProcessTree(p: ChildProcessWithoutNullStreams | null, logger: Logger): Promise { - if (!p || !p.pid || p.exitCode !== null) { - return Promise.resolve(); - } - return new Promise((resolve) => { - terminate.default(p.pid!, "SIGTERM", (err) => { - if (err) { - logger.error(`Error killing process ${p.pid}: ${err}`); - } - resolve(); - }); - }); -} \ No newline at end of file