fix(watcher): cap startup wait
This commit is contained in:
@@ -18,8 +18,8 @@ import {
|
||||
type LaunchContext,
|
||||
} from "./daemonProtocol.ts";
|
||||
|
||||
// Default Idle TTL - 5 minutes. Per-server overrides via ServerConfig.idleTtlMs.
|
||||
const DEFAULT_IDLE_TTL_MS = 5 * 60 * 1000;
|
||||
const WATCHER_READY_TIMEOUT_MS = 5000;
|
||||
|
||||
// Client Entry - One LspClient per (server.id, rootDir), plus the bookkeeping
|
||||
// needed to keep files in sync and evict on idleness.
|
||||
@@ -38,9 +38,6 @@ interface ClientEntry {
|
||||
idleTimer: NodeJS.Timeout | null;
|
||||
ttlMs: number;
|
||||
lastUsed: number;
|
||||
// watcher: Lazy - created on first registerCapability for watched files,
|
||||
// disposed on eviction. Null if the server never registered any watchers
|
||||
// (or if PI_LSP_DISABLE_WATCHERS is set).
|
||||
watcher: WorkspaceWatcher | null;
|
||||
unsubscribeWatchers: (() => void) | null;
|
||||
}
|
||||
@@ -109,10 +106,7 @@ async function getOrCreateEntry(
|
||||
return entry;
|
||||
}
|
||||
|
||||
// Attach Watcher - Subscribes to the client's watcher-registration events.
|
||||
// The first non-empty registration lazily creates the WorkspaceWatcher;
|
||||
// subsequent register/unregister calls update its pattern set in place.
|
||||
// Honors PI_LSP_DISABLE_WATCHERS for emergency rollback.
|
||||
// Attach Watcher - Registration can happen during initialize, before the daemon subscribes.
|
||||
async function attachWatcher(entry: ClientEntry): Promise<void> {
|
||||
if (process.env.PI_LSP_DISABLE_WATCHERS) return;
|
||||
const sync = async () => {
|
||||
@@ -128,17 +122,34 @@ async function attachWatcher(entry: ClientEntry): Promise<void> {
|
||||
log(`watcher patterns`, entry.server.id, JSON.stringify(patterns));
|
||||
}
|
||||
entry.watcher.setPatterns(patterns);
|
||||
if (patterns.length > 0) await entry.watcher.ready();
|
||||
if (patterns.length > 0) await waitForWatcherReady(entry);
|
||||
};
|
||||
entry.unsubscribeWatchers = entry.client.onWatchersChanged(() => void sync());
|
||||
// Initial Sync - Server may have already sent registerCapability during
|
||||
// initialize before we subscribed. Wait for chokidar's initial scan so
|
||||
// externally-created files are not swallowed as ignoreInitial events.
|
||||
await sync();
|
||||
}
|
||||
|
||||
// Forward Events - Sends a batched workspace/didChangeWatchedFiles to the
|
||||
// server. We catch errors so a downed transport doesn't crash the daemon.
|
||||
async function waitForWatcherReady(entry: ClientEntry): Promise<void> {
|
||||
if (!entry.watcher) return;
|
||||
let timeout: NodeJS.Timeout | null = null;
|
||||
let timedOut = false;
|
||||
try {
|
||||
await Promise.race([
|
||||
entry.watcher.ready(),
|
||||
new Promise<void>((resolve) => {
|
||||
timeout = setTimeout(() => {
|
||||
timedOut = true;
|
||||
resolve();
|
||||
}, WATCHER_READY_TIMEOUT_MS);
|
||||
}),
|
||||
]);
|
||||
} finally {
|
||||
if (timeout) clearTimeout(timeout);
|
||||
}
|
||||
if (timedOut) {
|
||||
log(`watcher ready timeout`, entry.server.id, entry.rootDir);
|
||||
}
|
||||
}
|
||||
|
||||
function forwardEvents(entry: ClientEntry, events: FileEvent[]): void {
|
||||
try {
|
||||
if (process.env.LSP_DEBUG) {
|
||||
|
||||
Reference in New Issue
Block a user