ADR: Use pygls JSON-RPC client for LSP context extraction¶
Context¶
YazSes v0.4 (cap-002) needs to query the running language server in the user's editor to obtain code context (file language, enclosing scope, recent identifiers) before each voice transcription. This context is injected into the Whisper initial_prompt to improve identifier recognition accuracy.
Language Server Protocol (LSP) uses JSON-RPC 2.0 over stdio or a socket connection. The two main approaches are: (1) use the pygls library, which provides a complete Python LSP client and server framework with JSON-RPC transport; or (2) implement the JSON-RPC transport directly using the standard library (asyncio, json, socket/pipe IO).
The LspContextProvider has a strict latency budget (≤ 50 ms per context read) and must be non-blocking — a timeout must return None cleanly without blocking the audio pipeline. The implementation must support at minimum Neovim (communicates over a Unix socket using Neovim's own msgpack-RPC, not standard LSP JSON-RPC) and VS Code (communicates over a TCP socket to a VS Code extension).
Neovim's communication protocol is msgpack-RPC, not JSON-RPC — this is a significant complication. The Neovim bridge will not use LSP directly but will query nvim_buf_get_var and nvim_lsp Lua APIs via the Neovim API, which uses msgpack-RPC. The pygls library covers JSON-RPC (for VS Code and generic LSP servers); a separate pynvim (msgpack-RPC) client is needed for Neovim.
Decision¶
We will use pygls >= 1.3.0 as the JSON-RPC transport layer for LSP-speaking editors (VS Code and language-server-direct connections), and pynvim >= 0.5.0 as the msgpack-RPC transport layer for Neovim. The LspContextProvider wraps both transports behind a common EditorBridge protocol: connect() -> bool, get_context() -> CodeContext | None.
In practice: VsCodeBridge uses pygls to connect to the VS Code extension socket; NeovimBridge uses pynvim to connect to the Neovim socket at $NVIM (the standard Neovim socket environment variable). The LspContextProvider selects the appropriate bridge via CommandsConfig.lsp_editor = "auto" (auto-detect by probing $NVIM and checking for VS Code process) or via explicit editor name in config. Both bridges enforce the 50 ms timeout using asyncio.wait_for with fallback to None. [HYPOTHESIS]
Consequences¶
Positive¶
pyglsis the reference Python LSP library; it is used by the official Python Language Server (pylsp) and has active maintenance. [HYPOTHESIS]pynvimis the official Neovim Python client library (maintained by the Neovim project), providing stable msgpack-RPC bindings. [HYPOTHESIS]- The
EditorBridgeprotocol abstraction means future editor support (Emacs, Helix) requires only a new bridge class implementingconnect()andget_context(), with no changes to theLspContextProvidercore logic. [HYPOTHESIS] - [EVIDENCE src-010] The asymmetric design pattern (voice = primary, context = optional refinement) from the ICMI systematic review is directly implemented: if any bridge returns
None, the pipeline continues without context. The voice path is never blocked.
Negative¶
- Two separate transport libraries (
pyglsandpynvim) are required to support the two most common editors. This adds two new transitive dependencies and two separate connection lifecycle management paths. [HYPOTHESIS] pynvimrequires the Neovim socket to be present at the$NVIMenvironment variable, which is only set when Neovim is launched in a way that exports the socket path. Users who launch Neovim from a GUI application or a tmux session that doesn't inherit the variable may not be detected. [HYPOTHESIS]- The VS Code bridge requires a companion extension to expose the LSP context over a socket — this is additional shipping artefact complexity not present for Neovim. [HYPOTHESIS]
Neutral¶
- If
$NVIMis not set and VS Code is not detected, theLspContextProviderreturnsNoneon every call and the LSP feature is effectively disabled without error. The daemon logs this at INFO level once per startup. - The
pyglslibrary is also a server framework; we use only its client transport. The server-side API is not imported and adds no code overhead beyond the library installation.
Alternatives Considered¶
| Alternative | Reason Rejected |
|---|---|
| Direct JSON-RPC implementation (stdlib only) | Reduces dependencies but requires implementing JSON-RPC framing, request/response correlation, and async timeout handling from scratch. pygls solves these correctly; reimplementing them increases maintenance burden with no user-visible benefit. [HYPOTHESIS] |
| VS Code Language Server direct connection (bypass extension) | VS Code language servers run as child processes of VS Code; there is no public socket interface to connect to them directly without modifying VS Code internals. The extension API is the only supported path. [HYPOTHESIS] |
| Atom/Helix/Zed bridge first | Neovim and VS Code collectively represent ~65% of the developer editor market. Prioritising other editors first would serve a smaller population at higher development cost. Bridge-first architecture makes adding other editors straightforward later. [HYPOTHESIS] |
References¶
- [EVIDENCE src-010]: ICMI systematic review — asymmetric multimodal design (voice primary, context secondary/optional) is the most successful pattern across 50 surveyed systems. This ADR implements that pattern for LSP context.
- [EVIDENCE src-001]: XR multimodal survey — context-awareness improves command disambiguation; the same principle applies to LSP context in coding contexts.
Output Quality Note¶
- All placeholders replaced.
- First sentence of Decision is "We will use..."
- Every claim tagged.
-
deciderspopulated. - Alternatives table has ≥ 2 rows.
- Negative consequences section is honest.
Study: [[yazses-future-voice-hci/input/research_scope|yazses-future-voice-hci]]