feat(lsp): add background daemon for language servers
This commit is contained in:
@@ -60,6 +60,10 @@ export class LspClient {
|
||||
// can await readiness.
|
||||
private progressTokens = new Set<string | number>();
|
||||
private progressListeners = new Set<() => void>();
|
||||
// Per-URI Version Counter - LSP requires monotonically increasing
|
||||
// version numbers in didOpen/didChange. We track them so the daemon
|
||||
// can resync files via notifyChange after on-disk edits.
|
||||
private versions = new Map<string, number>();
|
||||
|
||||
constructor(private readonly server: ServerConfig) {}
|
||||
|
||||
@@ -182,9 +186,12 @@ export class LspClient {
|
||||
|
||||
// Open Document - Reads the file from disk and sends didOpen. Most
|
||||
// servers require this before they'll answer hover/definition/etc.
|
||||
// Idempotent-ish: callers should track whether they've already opened
|
||||
// a URI and prefer notifyChange for subsequent syncs.
|
||||
openDocument(filePath: string): string {
|
||||
const uri = pathToUri(filePath);
|
||||
const text = fs.readFileSync(filePath, "utf8");
|
||||
this.versions.set(uri, 1);
|
||||
this.conn.sendNotification("textDocument/didOpen", {
|
||||
textDocument: {
|
||||
uri,
|
||||
@@ -196,11 +203,32 @@ export class LspClient {
|
||||
return uri;
|
||||
}
|
||||
|
||||
// Notify Change - Re-reads the file from disk and sends a full-text
|
||||
// didChange. Used by the daemon to keep the server in sync after the
|
||||
// agent's edit/write tools modify a file.
|
||||
notifyChange(filePath: string): string {
|
||||
const uri = pathToUri(filePath);
|
||||
const text = fs.readFileSync(filePath, "utf8");
|
||||
const version = (this.versions.get(uri) ?? 1) + 1;
|
||||
this.versions.set(uri, version);
|
||||
this.conn.sendNotification("textDocument/didChange", {
|
||||
textDocument: { uri, version },
|
||||
contentChanges: [{ text }],
|
||||
});
|
||||
return uri;
|
||||
}
|
||||
|
||||
// Send Raw LSP Request - Passthrough used by the command dispatcher.
|
||||
sendRequest<R = unknown>(method: string, params: unknown): Promise<R> {
|
||||
return this.conn.sendRequest(method, params) as Promise<R>;
|
||||
}
|
||||
|
||||
// Clear Diagnostics - Drops the cached diagnostics for a URI so callers
|
||||
// can force waitForDiagnostics to await a fresh publish after didChange.
|
||||
clearDiagnostics(uri: string): void {
|
||||
this.diagnostics.delete(uri);
|
||||
}
|
||||
|
||||
// Wait For Diagnostics - Resolves on the first publish for `uri` or
|
||||
// after `timeoutMs`. Returns whatever we have for that URI.
|
||||
async waitForDiagnostics(
|
||||
|
||||
Reference in New Issue
Block a user