UNPKG

pi-lens

Version:

Real-time code feedback for pi — LSP, linters, formatters, type-checking, structural analysis & booboo

62 lines (61 loc) • 2.4 kB
/** * Debounced `workspace/didChangeWatchedFiles` batching (#271). * * `handleNotifyOpen` previously sent one `workspace/didChangeWatchedFiles` per * file as each was opened. Many servers (TypeScript, …) kick off a project-wide * re-analysis on every such notification, so a turn that edits N files in one * project triggered ~N full re-analyses. This per-client queue coalesces the * changes within a short window and flushes them as a SINGLE notification, so the * server re-indexes once per burst instead of once per file. * * One queue lives per LSP client (= per serverId+root), so batching is naturally * scoped to a single server/project. Worst-case added latency before the server * learns of a change is the debounce window — negligible against the multi-second * re-analysis it gates. */ export const WATCH_DEBOUNCE_MS = 100; export class WatchedFilesQueue { flushFn; debounceMs; // uri → latest FileChangeType. A Map collapses repeated events for the same // URI (last-type-wins) while preserving first-seen insertion order. pending = new Map(); timer = null; constructor(flushFn, debounceMs = WATCH_DEBOUNCE_MS) { this.flushFn = flushFn; this.debounceMs = debounceMs; } /** Queue a change; arms the debounce timer if not already pending. */ enqueue(uri, type) { this.pending.set(uri, type); if (this.timer) return; this.timer = setTimeout(() => this.flush(), this.debounceMs); // Never hold the event loop open for a pending watched-files flush. this.timer.unref?.(); } /** Emit all queued changes as one notification (no-op when empty). */ flush() { if (this.timer) { clearTimeout(this.timer); this.timer = null; } if (this.pending.size === 0) return; const changes = [...this.pending.entries()].map(([uri, type]) => ({ uri, type })); this.pending.clear(); this.flushFn(changes); } /** Drop the timer + any queued changes without emitting (client teardown). */ cancel() { if (this.timer) { clearTimeout(this.timer); this.timer = null; } this.pending.clear(); } /** Number of distinct URIs currently queued (for tests/introspection). */ get size() { return this.pending.size; } }