From 06c6babb1b4936a0dee0d80d9bb0d6da0ab6edc4 Mon Sep 17 00:00:00 2001 From: Kit Langton Date: Mon, 13 Apr 2026 13:57:17 -0400 Subject: [PATCH] refactor: use Effect debounce for LSP cleanup --- packages/opencode/src/lsp/index.ts | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/packages/opencode/src/lsp/index.ts b/packages/opencode/src/lsp/index.ts index 589aa7326b..8fe4de65be 100644 --- a/packages/opencode/src/lsp/index.ts +++ b/packages/opencode/src/lsp/index.ts @@ -12,7 +12,7 @@ import { Instance } from "../project/instance" import { Flag } from "@/flag/flag" import { Process } from "../util/process" import { spawn as lspspawn } from "./launch" -import { Effect, Layer, Context } from "effect" +import { Effect, Layer, Context, PubSub, Stream } from "effect" import { InstanceState } from "@/effect/instance-state" import { Filesystem } from "@/util/filesystem" @@ -139,10 +139,10 @@ export namespace LSP { clients: LSPClient.Info[] servers: Record broken: Set + pulse: PubSub.PubSub pruning: Promise | undefined spawning: Map> subs: Map }> - timer: ReturnType | undefined } export interface Interface { @@ -217,19 +217,25 @@ export namespace LSP { clients: [], servers, broken: new Set(), + pulse: yield* PubSub.unbounded(), pruning: undefined, spawning: new Map(), subs: new Map(), - timer: undefined, } + yield* Stream.fromPubSub(s.pulse).pipe( + Stream.debounce("50 millis"), + Stream.runForEach(() => Effect.promise(() => scan(s))), + Effect.forkScoped, + ) + yield* Effect.addFinalizer(() => - Effect.promise(async () => { - if (s.timer) clearTimeout(s.timer) + Effect.gen(function* () { + yield* PubSub.shutdown(s.pulse).pipe(Effect.ignore) for (const item of s.subs.values()) { item.sub.close() } - await Promise.all(s.clients.map((client) => client.shutdown())) + yield* Effect.promise(() => Promise.all(s.clients.map((client) => client.shutdown()))) }), ) @@ -363,7 +369,7 @@ export namespace LSP { const name = String(file) if (!s.subs.get(dir)?.names.has(name)) return } - kick(s) + fire(s) }), ) sub.on( @@ -372,7 +378,7 @@ export namespace LSP { if (s.subs.get(dir)?.sub !== sub) return s.subs.delete(dir) sub.close() - kick(s) + fire(s) }), ) s.subs.set(dir, { sub, names }) @@ -380,12 +386,8 @@ export namespace LSP { } } - function kick(s: State) { - if (s.timer) clearTimeout(s.timer) - s.timer = setTimeout(() => { - s.timer = undefined - void scan(s) - }, 50) + function fire(s: State) { + Effect.runFork(PubSub.publish(s.pulse, undefined).pipe(Effect.ignore)) } async function scan(s: State) {