From d4ff45c8a5c8aa37e824e63037874c46de877309 Mon Sep 17 00:00:00 2001 From: Adam Malczewski Date: Wed, 24 Jun 2026 19:24:05 +0900 Subject: fix(tool-edit-file): lazy LSP service lookup — diagnostics now actually work MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The previous fix (e03a96e) wrapped getService in try/catch to prevent the activation crash, but that wasn't enough: tool-edit-file activates at position 5 in CORE_EXTENSIONS while lsp activates at position 20. So getService ALWAYS threw at activation time, lspService was ALWAYS undefined, and the diagnostics hook was NEVER wired — edits succeeded but never showed LSP feedback. Fix: make the LSP service lookup LAZY — defer it to edit time (when the tool is actually called), not activation time. By then all extensions have activated. The diagnostics function tries getService on each edit call; if LSP isn't loaded, it returns a no-op (graceful degradation). --- packages/tool-edit-file/src/extension.ts | 43 +++++++++++++++++--------------- 1 file changed, 23 insertions(+), 20 deletions(-) diff --git a/packages/tool-edit-file/src/extension.ts b/packages/tool-edit-file/src/extension.ts index 9be53da..3929634 100644 --- a/packages/tool-edit-file/src/extension.ts +++ b/packages/tool-edit-file/src/extension.ts @@ -14,26 +14,29 @@ export const extension: Extension = { contributes: { tools: ["edit_file"] }, }, activate(host) { - // Optional LSP integration: if the lsp extension is loaded AND has - // activated before us, wire its getDiagnostics service as the post-edit - // diagnostics hook. If absent (not loaded, or activates after us), edits - // proceed without diagnostics (graceful degradation). - let lspService: LspService | undefined; - try { - lspService = host.getService(lspServiceHandle); - } catch { - lspService = undefined; - } - 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; + // Lazy LSP lookup: the LSP extension activates AFTER us in the + // CORE_EXTENSIONS array, so host.getService would throw at activation + // time. Instead, defer the lookup to edit time — by then all extensions + // have activated. If LSP isn't loaded, the try/catch returns a no-op + // (graceful degradation: edits proceed without diagnostics). + const diagnostics: DiagnosticsHook = async (opts) => { + let lspService: LspService | undefined; + try { + lspService = host.getService(lspServiceHandle); + } catch { + return { formatted: "", slow: false, timedOut: false }; + } + if (!lspService) { + return { formatted: "", slow: false, timedOut: false }; + } + return lspService.getDiagnostics({ + filePath: opts.filePath, + text: opts.text, + cwd: opts.cwd, + timeoutMs: 60_000, + minSeverity: 2, // errors + warnings only + }); + }; host.defineTool(createEditFileTool(process.cwd(), diagnostics)); }, -- cgit v1.2.3