- Encapsulate a minimal LSP client stack so the CLI can ask language servers for definitions, references, diagnostics, and renames without managing subprocesses itself.
- Provide thin, documented tools (
lsp_goto_definition,lsp_find_references,lsp_diagnostics,lsp_rename) that composewithLspClienthelpers with exported plugin schema definitions (seesrc/tools/lsp/tools.ts).
LSPServerManagerinsrc/tools/lsp/client.tsis a singleton connection pool that keys clients by<workspace-root>::<server-id>, tracks reference counts, and evicts idle or dead processes.LSPClientwears responsibility for spawning the underlying language server (bun.spawn), wiring vscode-jsonrpc streams, maintaining opened documents, collecting diagnostics notifications, and gracefully shutting down/ restarting when needed.- Utility helpers in
src/tools/lsp/utils.tskeep formatting, severity mapping, diagnostic filtering, workspace root discovery, URI translation, and workspace-edit application consolidated; they also hostwithLspClient, which orchestrates server lookup, client acquisition/release, and retry messaging when initialization is still in progress. - Constants/configuration (
src/tools/lsp/constants.ts,src/tools/lsp/config.ts) define the supported servers, extension-to-language mappings, install hints, and runtime checks for whether the configured binaries exist, so the tools never start a missing server. - Shared types from
src/tools/lsp/types.tsmirror the vscode-languageserver-protocol definitions that both the client and tools consume.
- Tool execution (e.g.,
lsp_find_references) callswithLspClientwith a file path;withLspClientresolves the extension usingfindServerForExtensionand either throws an install/configuration error or proceeds. withLspClientaskslspManagerfor a client tied to the workspace root; the manager either reuses an existingLSPClientor starts/initializes a new one, waiting on the init promise before handing it back, and increments the reference count.LSPClientensures the file is open viatextDocument/didOpen(loading the text, waiting for the server) before sending requests such astextDocument/definition,references,diagnostic, orrename; diagnostics request also waits for pendingpublishDiagnosticsnotifications when the server cannot answer directly.- Responses flow back through
vscode-jsonrpcand are formatted/filtered byutils(formatLocation,filterDiagnosticsBySeverity,formatDiagnostic, etc.), and in the rename path the returnedWorkspaceEditis applied locally, with results reported viaformatApplyResult. - After the tool receives its answer,
withLspClientreleases the client reference; the manager later tears down idle clients or when the process exits.
src/tools/lsp/index.tsre-exports the manager and the defined tools/types so other pieces of the CLI can import the LSP surface without touching implementation details.- Tools are wired into the plugin layer via
@opencode-ai/plugin/tool; the exportedToolDefinitioninstances declare arguments, descriptions, and error formatting using helpers fromsrc/tools/lsp/utils.ts. client.tsdepends onconfig.tsandconstants.tsfor language IDs, server configuration, and install hints;utils.tsdepends on the same modules for severity maps,findServerForExtension, andlspManager.- External callers (e.g., command handlers) simply feed absolute paths and cursor positions into the exported tools; the module reports installation errors, server initialization delays, or successful results back through formatted strings so higher layers can relay them to the user.