LSP
Grove ships LSP coverage like Zed: you don't install language servers yourself. The first time you invoke go-to-definition, find-references, or hover in a supported language, Grove downloads and caches the server (and, for Node-based servers, a private Node runtime).
Why auto-install
- PATH-based is simpler to implement (Helix / Sublime LSP do this) but the first-run UX is bad: F12 silently does nothing if you forgot to
brew install rust-analyzer. - Auto-install moves the cost from you (every machine, every time) to Grove (write the adapters once).
Coverage matrix
| Language | Server | Distribution | Auto-install? |
|---|---|---|---|
| Rust | rust-analyzer | GitHub Releases binary | yes |
| TS/JS | typescript-language-server | npm package + Node | yes |
| Python | basedpyright | npm package + Node | yes |
| Go | gopls | requires Go toolchain | no — PATH |
gopls is the odd one out — the official channel needs a Go toolchain Grove would also have to bootstrap. It falls back to PATH; a missing binary surfaces as a clean error.
Python uses basedpyright, the pyright fork that supports semantic tokens over LSP (upstream pyright reserves them for Pylance) — without it, Python files would get no semantic highlighting.
Capabilities
- Go-to-definition
- Go-to-implementation
- Find-references
- Hover
- Semantic tokens — the LSP-powered layer of syntax highlighting, composed over the fast TextMate base
Each server's tokens are remapped onto one canonical legend, and Grove keeps servers in sync with your unsaved edits (incremental document sync), so highlighting tracks the buffer you're actually looking at — not the last saved copy.
The same engine backs the agent-facing navigation tools (goto_definition, find_references, hover, goto_implementation) — see MCP.
Out of scope: rename, diagnostics, inline peek, code actions.
Cache layout
Under the app data directory:
runtimes/
node-<version>/bin/node ← shared by every Node-based server
lsp/
rust-analyzer/<version>/...
typescript-language-server/<version>/...
basedpyright/<version>/...
Versions are pinned per Grove release; bumping a pin triggers a fresh install into a new directory.
Concurrency
- A per-language install mutex: two simultaneous file-opens never both download the same server.
- One server process per language, shared across all worktrees and workspaces — additional project roots are announced to the running server rather than spawning another process.
Progress feedback
The installer reports every state change — downloading (with MB / %), extracting, installing, ready, error — as a floating pill in the lower-right. First-run TS or Python is a ~30 MB Node download plus npm install; without the pill you'd think Grove hung.
Turning servers off
You can kill or disable a language's server from the process monitor — useful when rust-analyzer is eating a laptop. Disabled languages stay disabled across restarts.
Network and trust
- HTTPS only. No proxies, no mirrors.
- Checksums are not verified yet — URLs and versions are pinned in source; sha256 verification is a follow-up.
Out of scope for now
- User override of binary paths (PATH fallback only exists for languages Grove doesn't auto-install).
- Auto-update of pinned versions (a bump ships in a Grove release).
- Background pre-fetch — installs are lazy.
- Windows (the runtime download logic is Unix-only today).
Where this lives in the code
- LSP gateway / multiplexer, semantic-token remap:
src-tauri/src/lsp.rs. - Pinned versions and installer:
src-tauri/src/lsp_install.rs. - Monaco provider registration:
src/components/FileViewer.tsx.