diff options
| author | Adam Malczewski <[email protected]> | 2026-06-24 16:48:46 +0900 |
|---|---|---|
| committer | Adam Malczewski <[email protected]> | 2026-06-24 16:48:46 +0900 |
| commit | 8f6114be790016bd954fcfccbe80a88bd0cb758e (patch) | |
| tree | 6be223628e35ce83759314f6fcce2161daa370ba /packages/tool-edit-file | |
| parent | 4935c268dd53592ec264c1b3eaa9805b3e069df5 (diff) | |
| download | dispatch-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/tool-edit-file')
| -rw-r--r-- | packages/tool-edit-file/package.json | 3 | ||||
| -rw-r--r-- | packages/tool-edit-file/src/edit-file.ts | 55 | ||||
| -rw-r--r-- | packages/tool-edit-file/src/extension.ts | 20 | ||||
| -rw-r--r-- | packages/tool-edit-file/tsconfig.json | 2 |
4 files changed, 74 insertions, 6 deletions
diff --git a/packages/tool-edit-file/package.json b/packages/tool-edit-file/package.json index f71ad80..7194cce 100644 --- a/packages/tool-edit-file/package.json +++ b/packages/tool-edit-file/package.json @@ -6,6 +6,7 @@ "main": "dist/index.js", "types": "dist/index.d.ts", "dependencies": { - "@dispatch/kernel": "workspace:*" + "@dispatch/kernel": "workspace:*", + "@dispatch/lsp": "workspace:*" } } diff --git a/packages/tool-edit-file/src/edit-file.ts b/packages/tool-edit-file/src/edit-file.ts index 36f99e0..1719ea3 100644 --- a/packages/tool-edit-file/src/edit-file.ts +++ b/packages/tool-edit-file/src/edit-file.ts @@ -103,13 +103,35 @@ export function computeReplacement( }; } +// --- Diagnostics hook --- + +/** + * Optional post-edit diagnostics hook. Returns formatted diagnostics string + * (empty if none) + timing metadata. Injected by the extension from the LSP + * service; absent when no LSP is available (graceful degradation). + */ +export type DiagnosticsHook = (opts: { + readonly filePath: string; + readonly text: string; + readonly cwd: string; +}) => Promise<{ + readonly formatted: string; + readonly slow: boolean; + readonly timedOut: boolean; +}>; + // --- Shell / edge --- /** * Factory: create an edit_file ToolContract bound to a working directory. * The working directory is injected so the tool is testable. + * `diagnostics` is optional — when provided, errors+warnings from LSP servers + * are appended to successful edit results (only when errors exist). */ -export function createEditFileTool(workingDirectory: string): ToolContract { +export function createEditFileTool( + workingDirectory: string, + diagnostics?: DiagnosticsHook, +): ToolContract { const workdir = resolve(workingDirectory); return { @@ -202,7 +224,36 @@ export function createEditFileTool(workingDirectory: string): ToolContract { } const plural = result.count === 1 ? "" : "s"; - return { content: `Replaced ${result.count} occurrence${plural} in "${relPath}".` }; + let baseContent = `Replaced ${result.count} occurrence${plural} in "${relPath}".`; + + // After a successful edit, query LSP diagnostics (if available). + // Only append if there are actual errors/warnings (no noise on clean edits). + if (diagnostics) { + try { + const cwd = ctx.cwd ?? process.cwd(); + const diag = await diagnostics({ + filePath: resolvedPath, + text: result.content, + cwd, + }); + const suffix: string[] = []; + if (diag.slow) { + suffix.push( + "⚠️ LSP is taking unusually long. If this happens more than once, raise it to the user.", + ); + } + if (diag.formatted) { + suffix.push(diag.formatted); + } + if (suffix.length > 0) { + baseContent += `\n\n${suffix.join("\n\n")}`; + } + } catch { + // LSP diagnostics failure is non-fatal — the edit already succeeded. + } + } + + return { content: baseContent }; }, }; } diff --git a/packages/tool-edit-file/src/extension.ts b/packages/tool-edit-file/src/extension.ts index a4bb19e..bbd8256 100644 --- a/packages/tool-edit-file/src/extension.ts +++ b/packages/tool-edit-file/src/extension.ts @@ -1,5 +1,6 @@ import type { Extension } from "@dispatch/kernel"; -import { createEditFileTool } from "./edit-file.js"; +import { lspServiceHandle } from "@dispatch/lsp"; +import { createEditFileTool, type DiagnosticsHook } from "./edit-file.js"; export const extension: Extension = { manifest: { @@ -13,6 +14,21 @@ export const extension: Extension = { contributes: { tools: ["edit_file"] }, }, activate(host) { - host.defineTool(createEditFileTool(process.cwd())); + // Optional LSP integration: if the lsp extension is loaded, wire its + // getDiagnostics service as the post-edit diagnostics hook. If absent, + // edits proceed without diagnostics (graceful degradation). + const lspService = host.getService(lspServiceHandle); + const diagnostics: DiagnosticsHook | undefined = lspService + ? async (opts) => + lspService.getDiagnostics({ + filePath: opts.filePath, + text: opts.text, + cwd: opts.cwd, + timeoutMs: 60_000, + minSeverity: 2, // errors + warnings only + }) + : undefined; + + host.defineTool(createEditFileTool(process.cwd(), diagnostics)); }, }; diff --git a/packages/tool-edit-file/tsconfig.json b/packages/tool-edit-file/tsconfig.json index ff99a43..38a7610 100644 --- a/packages/tool-edit-file/tsconfig.json +++ b/packages/tool-edit-file/tsconfig.json @@ -2,5 +2,5 @@ "extends": "../../tsconfig.base.json", "compilerOptions": { "rootDir": "src", "outDir": "dist", "composite": true }, "include": ["src/**/*.ts"], - "references": [{ "path": "../kernel" }] + "references": [{ "path": "../kernel" }, { "path": "../lsp" }] } |
