diff --git a/packages/desktop/src/features/editor-targets.test.ts b/packages/desktop/src/features/editor-targets.test.ts index d31a51057..857cbb138 100644 --- a/packages/desktop/src/features/editor-targets.test.ts +++ b/packages/desktop/src/features/editor-targets.test.ts @@ -203,6 +203,54 @@ describe("desktop editor targets", () => { }); }); + it("prefers Windows command shims over extensionless shell launchers", async () => { + const recorder = createSpawnRecorder(); + const vscodeBin = "C:/Users/me/AppData/Local/Programs/Microsoft VS Code/bin"; + + await openEditorTarget( + { + editorId: "vscode", + path: "C:/repo", + }, + { + platform: "win32", + env: { PATH: vscodeBin }, + existsSync: createExistsSync(["C:/repo", `${vscodeBin}/code`, `${vscodeBin}/code.cmd`]), + spawn: recorder.spawn, + }, + ); + + expect(recorder.calls[0]).toMatchObject({ + command: `"${vscodeBin}/code.cmd"`, + args: ["C:/repo"], + options: { shell: true }, + }); + }); + + it("keeps the Windows extensionless fallback when no command shim exists", async () => { + const recorder = createSpawnRecorder(); + const vscodeBin = "C:/Portable/VS Code/bin"; + + await openEditorTarget( + { + editorId: "vscode", + path: "C:/repo", + }, + { + platform: "win32", + env: { PATH: vscodeBin }, + existsSync: createExistsSync(["C:/repo", `${vscodeBin}/code`]), + spawn: recorder.spawn, + }, + ); + + expect(recorder.calls[0]).toMatchObject({ + command: `${vscodeBin}/code`, + args: ["C:/repo"], + options: { shell: false }, + }); + }); + it("quotes Windows command-script paths without corrupting metacharacters", async () => { const recorder = createSpawnRecorder(); diff --git a/packages/desktop/src/features/editor-targets.ts b/packages/desktop/src/features/editor-targets.ts index ea70a1ab4..175d87c56 100644 --- a/packages/desktop/src/features/editor-targets.ts +++ b/packages/desktop/src/features/editor-targets.ts @@ -147,16 +147,19 @@ function resolveExecutable( continue; } const candidate = `${directory}/${command}`; - if (input.existsSync(candidate)) { - return candidate; - } if (input.platform === "win32") { - for (const extension of [".exe", ".cmd"]) { + const hasExtension = Boolean(win32.extname(command)); + const extensions = hasExtension ? [""] : [".exe", ".cmd", ".bat", ".com", ""]; + for (const extension of extensions) { const windowsCandidate = `${candidate}${extension}`; if (input.existsSync(windowsCandidate)) { return windowsCandidate; } } + continue; + } + if (input.existsSync(candidate)) { + return candidate; } } return null;