summaryrefslogtreecommitdiffhomepage
path: root/packages/lsp/src/diagnostics.ts
diff options
context:
space:
mode:
authorAdam Malczewski <[email protected]>2026-06-24 16:48:46 +0900
committerAdam Malczewski <[email protected]>2026-06-24 16:48:46 +0900
commit8f6114be790016bd954fcfccbe80a88bd0cb758e (patch)
tree6be223628e35ce83759314f6fcce2161daa370ba /packages/lsp/src/diagnostics.ts
parent4935c268dd53592ec264c1b3eaa9805b3e069df5 (diff)
downloaddispatch-8f6114be790016bd954fcfccbe80a88bd0cb758e.tar.gz
dispatch-8f6114be790016bd954fcfccbe80a88bd0cb758e.zip
feat(lsp+tool-edit-file): multi-server diagnostics + per-edit auto-append
LSP extension: - Multi-server aggregation: query ALL connected servers matching the file's extension (not just the first), merge diagnostics tagged by source - Incremental sync: capture each server's textDocumentSync.change during initialize; compute prefix/suffix diff ranges for change:2 servers; full content for change:1 (generic, works for any LSP) - New diff.ts: pure computeChangeRange + offsetToPosition (O(n), tested) - Buffer sync: change(filePath, newText) sends didChange with post-edit in-memory content; openWithText for first open; tracks open doc text - languageId mapping: extended with .rb/.rbs/.c/.cpp/etc. (was 'unknown') - waitForDiagnostics: accepts text override + timeoutMs; returns { formatted, slow, timedOut }; polls for publishDiagnostics push - DiagnosticsStore: hasReceivedPush/clearReceived tracking; formatFiltered with minSeverity (1=Error, 2=Warning) for edit_file integration - LspService.getDiagnostics: service method for cross-extension use tool-edit-file: - After successful edit, calls LSP getDiagnostics with post-edit buffer - Only appends diagnostics with severity ≤ 2 (errors+warnings, no noise) - Appends slow warning (>10s): 'LSP is taking unusually long...' - 60s timeout; graceful degradation when no LSP available - Optional dep on @dispatch/lsp (getService pattern, not manifest depOn) 1468 vitest pass (was 1453, +15 new diff tests).
Diffstat (limited to 'packages/lsp/src/diagnostics.ts')
-rw-r--r--packages/lsp/src/diagnostics.ts28
1 files changed, 26 insertions, 2 deletions
diff --git a/packages/lsp/src/diagnostics.ts b/packages/lsp/src/diagnostics.ts
index ea18811..bc7ac0a 100644
--- a/packages/lsp/src/diagnostics.ts
+++ b/packages/lsp/src/diagnostics.ts
@@ -34,9 +34,11 @@ const severityNames: Record<number, string> = {
export class DiagnosticsStore {
private pushDiagnostics = new Map<string, readonly Diagnostic[]>();
private pullDiagnostics = new Map<string, readonly Diagnostic[]>();
+ private pushReceived = new Set<string>();
setPushDiagnostics(params: PublishDiagnosticsParams): void {
this.pushDiagnostics.set(params.uri, params.diagnostics);
+ this.pushReceived.add(params.uri);
}
setPullDiagnostics(uri: string, report: DocumentDiagnosticReport): void {
@@ -45,14 +47,32 @@ export class DiagnosticsStore {
}
}
+ /** True if the server has pushed at least one publishDiagnostics for this URI. */
+ hasReceivedPush(uri: string): boolean {
+ return this.pushReceived.has(uri);
+ }
+
+ /** Clear the "received" flag so the next waitForDiagnostics poll detects fresh pushes. */
+ clearReceived(uri: string): void {
+ this.pushReceived.delete(uri);
+ }
+
getMerged(uri: string): readonly Diagnostic[] {
const push = this.pushDiagnostics.get(uri) ?? [];
const pull = this.pullDiagnostics.get(uri) ?? [];
return dedupeDiagnostics([...push, ...pull]);
}
- format(uri: string): string {
- const diags = this.getMerged(uri);
+ /**
+ * Format diagnostics for a URI, optionally filtering by minimum severity.
+ * `minSeverity` includes only diagnostics with severity ≤ the given value
+ * (1=Error, 2=Warning, 3=Info, 4=Hint). Omit to include all.
+ */
+ formatFiltered(uri: string, minSeverity?: number): string {
+ let diags = this.getMerged(uri);
+ if (minSeverity !== undefined) {
+ diags = diags.filter((d) => (d.severity ?? 0) <= minSeverity);
+ }
if (diags.length === 0) return "";
const lines: string[] = [];
for (const d of diags) {
@@ -65,6 +85,10 @@ export class DiagnosticsStore {
}
return lines.join("\n");
}
+
+ format(uri: string): string {
+ return this.formatFiltered(uri);
+ }
}
function diagnosticKey(d: Diagnostic): string {