diff --git a/vscode-extension/package.json b/vscode-extension/package.json index 536b78d7b..ee73b229c 100644 --- a/vscode-extension/package.json +++ b/vscode-extension/package.json @@ -29,6 +29,7 @@ "color": "#1a1a2e", "theme": "dark" }, + "extensionKind": ["workspace"], "engines": { "vscode": "^1.95.0" }, diff --git a/vscode-extension/src/gsd-client.ts b/vscode-extension/src/gsd-client.ts index 20db6d327..29237dc24 100644 --- a/vscode-extension/src/gsd-client.ts +++ b/vscode-extension/src/gsd-client.ts @@ -118,28 +118,78 @@ export class GsdClient implements vscode.Disposable { return; } - this.process = spawn(this.binaryPath, ["--mode", "rpc", "--no-session"], { + const proc = spawn(this.binaryPath, ["--mode", "rpc", "--no-session"], { cwd: this.cwd, stdio: ["pipe", "pipe", "pipe"], env: { ...process.env }, }); + this.process = proc; this.buffer = ""; - this.process.stdout?.on("data", (chunk: Buffer) => { + proc.stdout?.on("data", (chunk: Buffer) => { this.buffer += chunk.toString("utf8"); this.drainBuffer(); }); - this.process.stderr?.on("data", (chunk: Buffer) => { + proc.stderr?.on("data", (chunk: Buffer) => { const text = chunk.toString("utf8").trim(); if (text) { this._onError.fire(text); } }); - this.process.on("exit", (code, signal) => { - this.process = null; + let startupSettled = false; + const startupResult = new Promise((resolve, reject) => { + const cleanup = () => { + proc.off("spawn", handleSpawn); + proc.off("error", handleStartupError); + }; + const handleSpawn = () => { + if (startupSettled) return; + startupSettled = true; + cleanup(); + this._onConnectionChange.fire(true); + this.restartCount = 0; + resolve(); + }; + const handleStartupError = (err: NodeJS.ErrnoException) => { + if (startupSettled) return; + startupSettled = true; + cleanup(); + if (this.process === proc) { + this.process = null; + } + const hint = err.code === "ENOENT" + ? ` Make sure GSD is installed ("npm install -g gsd-pi") and set "gsd.binaryPath" to the absolute path if it is not on PATH.` + : ""; + const message = `Failed to start GSD process: ${err.message}.${hint}`; + this._onError.fire(message); + reject(new Error(message)); + }; + + proc.once("spawn", handleSpawn); + proc.once("error", handleStartupError); + }); + + proc.on("error", (err: NodeJS.ErrnoException) => { + if (!startupSettled) { + return; + } + if (this.process === proc) { + this.process = null; + } + this._onConnectionChange.fire(false); + const hint = err.code === "ENOENT" + ? ` Make sure GSD is installed ("npm install -g gsd-pi") and set "gsd.binaryPath" to the absolute path if it is not on PATH.` + : ""; + this._onError.fire(`GSD process error: ${err.message}.${hint}`); + }); + + proc.on("exit", (code, signal) => { + if (this.process === proc) { + this.process = null; + } this.rejectAllPending(`GSD process exited (code=${code}, signal=${signal})`); this._onConnectionChange.fire(false); @@ -161,8 +211,7 @@ export class GsdClient implements vscode.Disposable { } }); - this._onConnectionChange.fire(true); - this.restartCount = 0; + await startupResult; } /**