diff --git a/packages/opencode/src/lsp/index.ts b/packages/opencode/src/lsp/index.ts index 2eb1ad93e9..41a650afd7 100644 --- a/packages/opencode/src/lsp/index.ts +++ b/packages/opencode/src/lsp/index.ts @@ -177,6 +177,12 @@ export namespace LSP { async function getClients(file: string) { const s = await state() + + // Only spawn LSP clients for files within the instance directory + if (!Instance.containsPath(file)) { + return [] + } + const extension = path.parse(file).ext || file const result: LSPClient.Info[] = [] diff --git a/packages/opencode/test/lsp/index.test.ts b/packages/opencode/test/lsp/index.test.ts new file mode 100644 index 0000000000..7e514e39b1 --- /dev/null +++ b/packages/opencode/test/lsp/index.test.ts @@ -0,0 +1,55 @@ +import { describe, expect, spyOn, test } from "bun:test" +import path from "path" +import * as Lsp from "../../src/lsp/index" +import { LSPServer } from "../../src/lsp/server" +import { Instance } from "../../src/project/instance" +import { tmpdir } from "../fixture/fixture" + +describe("lsp.spawn", () => { + test("does not spawn builtin LSP for files outside instance", async () => { + await using tmp = await tmpdir() + const spy = spyOn(LSPServer.Typescript, "spawn").mockResolvedValue(undefined) + + try { + await Instance.provide({ + directory: tmp.path, + fn: async () => { + await Lsp.LSP.touchFile(path.join(tmp.path, "..", "outside.ts")) + await Lsp.LSP.hover({ + file: path.join(tmp.path, "..", "hover.ts"), + line: 0, + character: 0, + }) + }, + }) + + expect(spy).toHaveBeenCalledTimes(0) + } finally { + spy.mockRestore() + await Instance.disposeAll() + } + }) + + test("would spawn builtin LSP for files inside instance", async () => { + await using tmp = await tmpdir() + const spy = spyOn(LSPServer.Typescript, "spawn").mockResolvedValue(undefined) + + try { + await Instance.provide({ + directory: tmp.path, + fn: async () => { + await Lsp.LSP.hover({ + file: path.join(tmp.path, "src", "inside.ts"), + line: 0, + character: 0, + }) + }, + }) + + expect(spy).toHaveBeenCalledTimes(1) + } finally { + spy.mockRestore() + await Instance.disposeAll() + } + }) +})