GenServer wrapping a stdio Port that speaks JSON-RPC 2.0 (LSP framing).
Lifecycle
start_link/1opens the OS process port and sends the LSPinitializerequest viahandle_continue/2. The GenServer is immediately callable — requests received before initialization completes are queued and replayed onceinitializedis confirmed.request/4sends a JSON-RPC request and awaits the reply (default 30 s). Multiple concurrent callers are safe — replies are correlated byid.notify/3sends a one-way notification (no reply expected).diagnostics/2returns cachedtextDocument/publishDiagnosticspayloads for a given URI (populated by push notifications from the server).stop/2sends the LSPshutdown+exitsequence, then waits for the port to close.
Telemetry
[:ex_athena, :lsp, :spawn]— discrete event with%{system_time: ...}measurements and%{language: atom, root: binary, binary: binary, pid: pid, phase: :started | :stopped | :crashed}metadata. Emitted exactly once per phase transition.:crashedis emitted only fromterminate/2; the port:exit_statushandler does not double-emit.[:ex_athena, :lsp, :request, :start | :stop]— span around each JSON-RPC request/response cycle, metadata%{method: binary, language: atom, root: binary}.
Assumptions
LSP servers must not interleave non-JSON-RPC bytes in stdout in normal
operation. Servers should use --log-file flags to avoid mixing stderr
with the JSON-RPC stream.
Summary
Functions
Returns a specification to start this module under a supervisor.
Return cached diagnostics for the given uri.
Send a JSON-RPC notification (no reply).
Send a JSON-RPC request and await the response.
Start a client for the given LSP server.
Initiate a graceful LSP shutdown and wait up to timeout ms.
Functions
Returns a specification to start this module under a supervisor.
See Supervisor.
Return cached diagnostics for the given uri.
Send a JSON-RPC notification (no reply).
Send a JSON-RPC request and await the response.
Returns {:ok, result} or {:error, reason}. On timeout returns
{:error, :timeout}.
@spec start_link(keyword()) :: GenServer.on_start()
Start a client for the given LSP server.
Options:
:binary(required) — absolute path to the server executable.:args(required) — list of additional CLI args.:root_uri(required) —file:///abs/pathworkspace root.:root(required) — plain filesystem root path (for telemetry/registry).:language(required) — language atom (for telemetry).:name(optional) — GenServer name (via-tuple for Registry).
@spec stop(pid(), non_neg_integer()) :: :ok
Initiate a graceful LSP shutdown and wait up to timeout ms.