Skip to content

Language servers

Optional installed stdio servers supply semantic completion through the native popup, canonical buffer and undo owner. LSP is disabled by default. The default Rust entry runs rust-analyzer with no arguments and uses Cargo.toml as its root marker. Registration reuses existing executables. Explicit managed Rust, Go, web, Markdown, Ruby, Python and Odin installation can acquire and verify servers without enabling analysis. Start with Set Up Language Servers, then use Use a language server for tasks and the configuration catalog for every setting.

Configuration example

The standalone TOML example contains installed Go and custom-language profiles, all launch fields, literal environment overrides, known/custom associations and explicit server/native-only preferences. Executable names are examples; admission does not install or verify them. Merge its leaves into the selected root instead of appending duplicate tables. The complete default sample keeps analysis disabled and preferences empty. Managed selection writes final owned executable paths into the same profile tables; installation facts do not replace those choices.

Activation and ownership

Command Effect
lsp-mode Toggle optional LSP for the session; disabling retires work and stops all owned servers.
lsp-status Observe the current file's transport state: disabled, excluded, unavailable, starting, ready, missing, failed, limited or stopping.
lsp-disconnect Exclude the canonical buffer across its views until restored or closed.
lsp-connect Restore buffer eligibility without enabling a disabled global mode.
lsp-restart Replace the eligible current root's connection; preserve other roots, exclusions and global mode.
lsp-setup Choose a language and installed profile, supported managed recipe, or native-only source; save and apply the admitted settings.
lsp-reload-settings Read the selected configuration root and includes, applying only LSP settings and file associations.
lsp-servers Open a read-only source/catalog snapshot without running helpers or checking remote versions.
lsp-select-server Choose a compatible configured profile, ready managed installation or native-only source.
lsp-install-server Acquire and verify a catalog recipe or reuse a ready installation; save its choice when configuration sources still match.
lsp-update-server Confirm a managed installation and its selected profiles; verify a replacement before one settings publication.
lsp-remove-server Confirm removal of an installer-owned, unreferenced, stopped payload.
lsp-cancel-installation Cancel the current management job; its slot remains owned until cleanup finishes.

Eligible file activation uses lsp.preferred when present: a compatible server name selects that profile; false selects native-only. Otherwise it selects the first matching server name in lexical order. Language identities and associations do not require a bundled grammar. One frozen server configuration and canonical root identify one owned connection. Split views and canonical file aliases share the document; other buffers under the same root share the process. Manual exclusions remain buffer-local.

Setup and explicit reload preserve the session's global LSP mode, exclusions, text, history, selections and startup interaction profile. Source changes retire old activations before new requests; unchanged connections remain usable. The writer preserves comments, includes and root symlinks, validates the complete candidate and refuses concurrent edits to any captured source. A reported post-publication durability warning means the new settings were published.

Root lookup chooses the nearest configured ancestor marker. An explicitly opened containing directory bounds the search and supplies its fallback. Files outside it, or individual files without a marker, use their containing directory. An empty marker array also selects the containing directory. Detection cannot enable a disabled client.

Healthy servers remain after the last buffer closes by default. shutdown_on_last_buffer = true requests shutdown instead. Missing/failed connections remain retained until explicit restart or disable/enable; normal activation does not create a crash restart loop. Shutdown sends shutdown/exit, then terminates and reaps owned children after its finite deadline. Healthy idle connections block on IO; disabled or idle completion creates no polling timer.

Catalog observations and setup presets

lsp-servers creates a read-only snapshot through the normal buffer/view owner. It lists at most 128 language rows, all admitted profiles (at most 32), and the finite Rust, Go, TypeScript/JavaScript/TSX, HTML/CSS/JSON, Markdown, Ruby, Python and Odin recipe metadata. The visible result is bounded to 64 KiB. Org and arbitrary custom languages retain generic registration without a managed recipe.

The catalog's managed acquisition target is Linux x86-64. Generic installed-server registration is independent of that target. Missing optional prerequisites and unsupported recipe platforms do not block external server selection. Local PATH inspection checks at most 128 entries without running them; relative launch/search paths report that a client working root is required. Environment values are never included in the snapshot. Executable prerequisites do not verify runtime features, transport readiness or semantic quality. Verified managed facts are listed separately with version, readiness, installation ID and selected configured profiles.

The recipe:<launch-profile> choices in lsp-setup reuse an observed installed executable. A configured profile with that ID remains authoritative. New presets use their recipe's command/arguments while preserving the selected profile's root markers, initialization options, settings and literal environment overrides. Arguments and markers remain editable before publication. Known associations are reused; no registry fact or recipe creates a language allowlist. Viewing the catalog and normal startup never acquire or update packages.

Managed Rust installation

lsp-install-server offers rust-analyzer acquisition on GNU/Linux x86-64. The equivalent setup choice is language rust, then install:rust-analyzer. An explicit operation resolves one exact official stable release, checks its asset URL and length, verifies the release SHA-256, and unpacks one executable. The digest comes from the same official release channel; it proves artifact integrity, not independent publisher identity or semantic quality.

Only this operation requires installed curl, gzip and sha256sum. Missing Rust toolchain or sources are reported separately; rev never installs or upgrades a compiler. The candidate must complete the native client's initialize/shutdown/ exit exchange in an owned empty root without user documents. Installation does not change global LSP enablement or buffer exclusions. Use lsp-mode separately when continuous analysis is desired.

Managed Go and web installation

lsp-install-server also offers gopls, typescript-language-server and vscode-langservers-extracted on Linux x86-64. Shared packages ask which language to select. The equivalent setup choice is a compatible language, then install:<recipe>. Generic registration remains available for every admitted language identity.

Recipe Optional tools Acquisition and verification
gopls Go Resolve an exact stable module version through the official Go proxy, verify checksum-database module provenance, build with owned GOBIN/module/build/temp caches and GOTOOLCHAIN=local, and compare the binary's module identity.
typescript-language-server Node and npm Resolve exact server and stable TypeScript 6 versions, install into an owned npm prefix with scripts disabled and retain the package lock. One --stdio profile serves TypeScript, JavaScript and TSX.
vscode-langservers-extracted Node and npm Resolve an exact package version into one owned npm prefix. Three independent --stdio profiles serve HTML, CSS and JSON; each must pass its own probe.

An insufficient Go compiler or incompatible Node/npm is an actionable failure; rev never downloads another compiler or upgrades the shared runtime. npm uses owned configuration and cache/temp roots, exact saved dependencies, strict engine checks and scripts disabled. Lock admission requires exact package identities, registry HTTPS sources and SHA-256/512 integrity. The lock, its SHA-256 and concrete runtime/package versions are retained. Go retains module sums, compiler identity and binary build information. Package provenance is distinct from native release artifact digests. Every launch completes the native empty-root probe without user documents before its installation becomes ready.

Selecting a shared TypeScript profile inherits the explicitly chosen language's current options. Independent web profiles retain each language's settings. Reusing a ready record for another language preserves that language's current settings and known associations. Acquisition and reuse preserve runtime analysis enablement and ordinary editing.

Managed Markdown, Ruby, Python and Odin

The remaining initial recipes are available through lsp-install-server and install:<recipe> in lsp-setup on Linux x86-64. Each uses the same bounded worker, final candidate directory, empty-root transport probe and independent settings publication as Rust/Go/web.

Recipe Acquisition and retained evidence
marksman Resolve one official stable release and its Linux x64 native asset. Require official HTTPS URL, bounded length and release SHA-256 before copying the executable. Retain the concrete release and --version output.
ruby-lsp Resolve one RubyGems version, verify the cached primary gem against its API SHA-256, install exact server/Bundler into owned gem and bin directories, and lock dependencies locally. Retain all resolved gem versions/platforms, Ruby identity and gem-lock:sha256:<digest>.
python-lsp-server Resolve one PyPI version, create a final-path venv with interpreter copies, and install base-only wheels through isolated pip. Admit exact dependency versions, official HTTPS wheel sources and SHA-256 values in the retained pip report. Record Python identity and pip-report:sha256:<digest>.
ols Resolve one full Git commit, fetch/check out that exact object, and run the pinned upstream build script with the installed Odin. Retain source/builtin files, toolchain/build provenance and git-revision:<commit>. Missing executable or builtin files fail verification even if the build script exits zero.

Ruby requires installed Ruby/RubyGems and loadable Bundler. Required native extensions additionally need compatible build tools; the managed build uses serial GNU Make with pipe jobservers (GNU Make 4.4+) so temporary FIFO files do not bypass owned-storage admission. The owned bundle is frozen for probe and launch, avoiding Ruby LSP's automatic project bundle installation. Python requires venv/pip for its selected interpreter. It requests no optional server extras, system site packages or runtime upgrades. Missing compatible wheels are an actionable failure.

Git acquisition ignores inherited Git redirections and global/system configuration. OLS follows upstream master once per requested operation, then uses only the resolved immutable revision. Upstream/compiler incompatibility remains an explicit failure; rev does not replace the compiler or try another revision implicitly. Managed OLS retains OLS_BUILTIN_FOLDER and forces enable_snippets = false and enable_fake_methods = false in initialization options. No recipe installs or upgrades a compiler/interpreter globally.

Selection and ready reuse retain unrelated user settings while restoring Ruby's owned gem/bundle paths and OLS's builtin path and snippet restrictions. Ready registry admission checks those required values. Package/revision evidence and a successful transport probe do not establish language semantic quality. Org and all other admitted language identities remain supported through generic custom registration, independently of this catalog and bundled grammars.

Managed storage and publication

Payloads live under the resolved data root at lsp/installations/<recipe>/<installation-id>/; a candidate is created at its final unique path and never moved. lsp/installations.toml stores schema-1 facts, with at most 32 records and 2 MiB. It records exact version, target, relative payload and launch paths, source, digest or package evidence, runtime identity, readiness and creation/verification timestamps. It contains no language preferences. The cache root's lsp/ holds disposable owned acquisition directories; state lsp/mutation.lock serializes settings changes. A shared data-directory lock also excludes concurrent managers using different state roots.

One native management worker owns acquisition, verification and publication; its phase appears in lsp-servers. Native artifacts are bounded to 256 MiB, candidate payloads to 1 GiB, disposable cache to 2 GiB, and captured helper output to 1 MiB with at most 64 KiB retained stderr. The total operation deadline is 900 seconds. Helpers use structured argv, owned directories and finite deadlines. They execute with user permissions and are not a security sandbox. Confirmed editor exit cancels the worker; owned groups receive a two-second stop grace before bounded forced stop/reap, with cleanup uncertainty reported separately.

Ready facts are published before the independent configuration transaction. It preserves known associations and selected profile options/environment, and creates another profile if a preset name already belongs to custom settings. A source conflict leaves the payload ready and unselected. Use lsp-servers to find its ID, then choose ready:<id> in lsp-install-server, lsp-setup or lsp-select-server. Reuse checks ready facts under the mutation locks and runs no acquisition helpers or probe. Configured commands remain authoritative; there is no implicit managed/PATH precedence.

A failed candidate remains incomplete and preserves earlier ready installations. Preparing records from an interrupted job are shown as incomplete and never resume automatically. Invalid, unknown-schema, duplicate, escaping-path or unverified-ready registry data blocks managed changes while ordinary editing, existing-server registration and configured external servers remain usable.

Update, removal and cancellation

lsp-update-server captures the complete settings graph and one ready supported installation. Its confirmation lists selected profiles whose command and arguments still match that installation's launch. Acquisition resolves one concrete version, creates another final candidate, and verifies it before retargeting any profile. A shared web update performs one acquisition and one atomic settings publication for all such selected profiles. User options, associations, analysis mode and editing state are retained. Custom commands/arguments and unselected profiles are not retargeted. The old payload is retained until explicitly removed.

The original installation and every captured source are checked again before publication. A failed acquisition preserves the previous ready version. A changed configuration leaves the verified replacement ready/unselected and preserves the user's new bytes. A cancelled operation cannot select a replacement; an effect already committed before late cancellation keeps its actual published outcome.

lsp-remove-server requires an admitted record and unchanged settings/registry. Any configured profile referencing the payload blocks removal, even when it is unselected or native-only is preferred. References include resolved executable aliases, server-specific PATH lookup, and absolute argument/environment paths. This check uses the selected root and its explicit include graph.

Active rev clients retain shared directory-inode leases for managed executable and literal payload paths through server reap. Removal requires an exclusive lease and a matching directory identity, so another rev instance can block it without sharing a state/configuration root. Aliased payload directories, escaping links, external executables and runtimes are never deletion targets. Cleanup marks the record incomplete before deleting its data, synchronizes the parent, and then forgets the record. Partial cleanup or durability uncertainty remains explicitly incomplete. An absent incomplete payload can be removed on retry.

Successful explicit removal also inspects the bounded acquisition-cache namespace. Only job directories with the native ownership marker and an available exclusive inode lease may be deleted. Active, unmarked and legacy directories are preserved. The existing 2 GiB/65,536-entry/64-level accounting bounds apply; cleanup runs in the worker and reports partial failure without misreporting a completed removal. There is no startup cleanup, remote version polling or automatic update.

lsp-cancel-installation requests cancellation of the current management worker. The slot and mutation locks stay owned through helper stop/reap and cache cleanup. A focused unanswered prompt can be cancelled with C-g or modal Escape. Confirmed exit requests cancellation before returning its exit action; aborted exit leaves the session's active job intact. Retained preparing/incomplete records do not become ready merely because some files exist.

Synchronization and completion

The client admits the LSP 3.17 stdio subset with typed JSON-RPC framing, negotiated UTF-8 or UTF-16 positions, and full or incremental document synchronization. A server must support document open/close and changes. Unsaved canonical text is sent before its completion request. Wire versions also carry connection, activation and source-revision identity. Save notifications follow only successful ordinary saves and the negotiated server capability.

Admitted semantic candidates take precedence. Valid-empty results, no supported items, missing capability, errors, EOF, timeouts and excluded/unavailable servers select current native fallback. Native support is detailed in Completion. Transport readiness alone does not imply semantic candidates or a fully loaded server workspace. A semantic source needs no bundled grammar; adding a server does not add syntax coloring.

The common popup retains its filtering, navigation and candidate limit. Semantic matching uses filterText or label, prefix before subsequence, then server sort keys/order. Supported primary forms are plain insertion, TextEdit, InsertReplaceEdit and admitted CompletionList defaults. Trigger characters can request before the ordinary prefix minimum; incomplete lists request again on changed input.

Only acceptance resolves an eligible selected item. The advertised mutable resolve field is additionalTextEdits; primary insertion and matching/order remain fixed. Additional edits must be valid, disjoint, current-document ranges. The whole primary/import batch prepares against one originating revision and publishes as one undo transaction. Failed resolution offers native fallback without a primary-only fragment. Read-only, changed or retired targets refuse the entire batch. Edits, motion, cancellation, exclusion, restart and disable retire pending work; late replies cannot reopen a cancelled popup.

Snippets, indentation adjustment, command-dependent items and cross-document edits are rejected. Server workspace/applyEdit is denied and server commands are not executed. Configuration sections, workspace folders and progress-token requests receive bounded replies. Unsupported requests return MethodNotFound. Diagnostics, logs and progress notifications add no diagnostic UI or edit authority.

Finite limits

Resource Limit
Frame header / JSON body 16 KiB / at most 8 MiB
Queue per direction and connection 64 messages and 16 MiB
Retained stderr tail 64 KiB per connection
Synchronization snapshots 8 MiB per document and 32 MiB total
Retained connections / configured server entries 8 / 32
Pending requests At most 16 per connection, one completion per buffer
Candidates / label / primary insertion 200 / 512 bytes / 64 KiB
Additional edits / total inserted text per item 32 / 256 KiB including the primary
Configuration request items / retained trigger characters 64 / 64
Default initialize / request / shutdown deadline 10000 / 2000 / 2000 ms

The configuration catalog gives configurable ranges. Overflow, malformed frames, EOF and deadlines make the connection unavailable; they do not silently drop document changes while claiming current synchronization. Oversized buffers stay editable without sending a truncated document. Completion and connection state remain session-local, without a second document store or background project index owned by rev.