User guide / Language support

Language servers

Development reference · View source

Setting up a server

rust-analyzer is configured out of the box. For another language:

  1. Install the server so it is on PATH.
  2. Add it under lsp in the configuration file.
  3. Run :config-reload.
  4. Make sure the workspace allows language servers: answer the prompt shown when it first opened, or run :lsp-trust (see Permission to run servers ). A reload does not ask again.
lsp:
  enable: true
  markdown:
    command: marksman
    args: ["server"]
FieldMeaning
commandExecutable name or absolute path
argsOptional list of arguments. The server is started directly, not through a shell.
initialization_optionsOptional; passed verbatim as initializationOptions

Language keys are Runyte’s language names, so a buffer’s language is the same question for highlighting and for LSP: rust, python, swift, c, cpp, javascript, typescript, tsx, html, css, go, bash, java, kotlin, json, jsonc, sql, lua, c-sharp, zig, cmake, proto, make, ini, toml, yaml, and markdown. Other keys under lsp are rejected, apart from enable and the servers wrapper.

  • lsp.servers.<language> is an accepted compatibility spelling of lsp.<language>, which is preferred.
  • Copy-ready examples for the servers in Runyte’s compatibility tests are in docs/lsp/ .
  • Server definitions are YAML-only. Space o o can only turn LSP on or off as a whole.
  • :help lsp repeats this setup inside the editor.

Reloading and restarting:

CommandEffect
:config-reloadAdopt new definitions; restart only languages whose definition changed
:lsp-restart [language]Restart a stopped server from the configuration already loaded. It does not re-read YAML, so it is the wrong command after editing the file.
:lsp-status (Space l ?)Report servers that started or failed. The report replaces the command’s echo while that echo is still current; :not keeps the full report.
:service-healthShow whether the active document has a configured and attached server

lsp.enable is decided at startup. To turn LSP on or off, reopen an ide-mode Runyte, or restart a persistent session with runyte --session-restart [WORKSPACE] (passing the same --config PATH if the host used a non-default one).

Failures cost only the language features. A server that is missing, crashes, or answers slowly never makes the editor wait. Diagnostics from a stopped server are dropped rather than left as stale claims. A launch error appears in :lsp-status after the first start attempt and in the notification center.

Permission to run servers

The first time a workspace opens with LSP enabled, Runyte asks whether language servers may run there.

ChoiceLasts
Keep LSP disabled (default)—
Allow LSP onceUntil the ide-mode editor or persistent host stops
Always allow LSPRemembered for this exact workspace
EscapeDismisses without changing permission; on a first visit LSP stays off

Editing, search, and highlighting work either way.

Why it asks. Language servers can run project code — build scripts, plugins — with your permissions. Approval covers every configured server, custom ones included, and future code changes in the workspace. It is permission to run those tools, not a security review or a sandbox.

Changing your mind. :lsp-trust reopens the choices, including revocation.

  • Disabling LSP there stops this host’s servers and clears their editor state. It cannot undo code already run.
  • A persistent host owns the permission, so detaching and reattaching cannot bypass it. Other running editors keep their own decision until changed or restarted.
  • Setting lsp.enable: false suppresses the question and keeps approval from starting LSP.

Where decisions are stored. Private lsp-trust/ storage under Runyte’s per-user platform cache, separate from .runyte/.

  • Clearing that cache makes Runyte ask again.
  • A workspace rooted at your home directory may use Runyte’s standard cache under it; other project-local cache overrides are rejected.
  • Records use the canonical workspace root: symlinked aliases share a decision, but nested workspaces, other worktrees, and clones elsewhere do not.

When storage fails:

  • An unreadable decision keeps LSP disabled and asks again.
  • If storage is unavailable, the prompt explains why and offers only Keep LSP disabled for now and Allow LSP once.
  • A failed save keeps the prompt open with the error and those temporary choices.
  • Keep LSP disabled for now stops this editor’s servers without changing any remembered decision.
  • Allow LSP once needs no new storage, but first removes an older remembered decision; if that fails, the choice stays pending. The next launch asks again.
  • Reopening :lsp-trust retries the store once it is repaired.

What a server is asked

Runyte only sends requests for capabilities the server advertised in its handshake. Hover, completion, signature help, goto (definition, declaration, type definition, implementation), references, document and workspace symbols, rename, code actions, and formatting are gated independently.

  • Asking for one the server never advertised reports it as unavailable on the interaction line. It is not kept as a notification, since it is expected rather than a fault. Typing near a trigger character a server does not support costs no round trip.
  • A Method not found from a server that did advertise the capability is a protocol violation, reported and kept as an ERROR.

Trigger characters come from the server too:

ServerCompletion afterSignature help
clangd/, "Seven delimiters
Pyright[, "Among its characters, names ) but answers nothing there, closing the popup
rust-analyzer'
goplsTwo delimiters
sourcekit-lsp, MarksmanNone
A server advertising the capability but naming no characters., :(, ,
  • The table names only the characters discussed here, not each server’s full list; a blank cell says nothing.
  • Some servers name the closing ), so the inner ) of f(g(a), b) asks again instead of dismissing the popup; clangd and typescript-language-server answer with the enclosing signature.
  • A server may name retrigger characters, active only while a popup shows. Runyte advertises the contextSupport that allows them and tells the server which character asked and whether a popup was open.
  • A server naming ) neither way has the popup closed locally.
  • tests/lsp_real_servers.rs records what each server in the compatibility matrix does.

Edits from a server

  • Servers may not create, rename, or delete files. Such operations in a workspace edit are reported and skipped, and files a rename touches are opened as buffers rather than written behind your back.
  • Text edits must name absolute local files inside the project and exact, forward-ordered character boundaries. A malformed, remote, out-of-range, or overlapping edit rejects the whole multi-file change.
  • Open target buffers are checked against the revisions the request was made from, including targets other than the source buffer.
  • Versioned diagnostics are ignored once their document has moved on.

Compatibility tests

An opt-in Docker matrix tests real servers: Python (Pyright), Swift (SourceKit-LSP), C and C++ (clangd), JavaScript (typescript-language-server), Go (gopls), Rust (rust-analyzer), and Markdown (Marksman).

tests/lsp/run.sh
  • The image pins every server toolchain. Each test uses Runyte’s production stdio transport for the full handshake, in a disposable project.
  • After initial symbol and definition checks, each sends an unsaved incremental change with non-ASCII text and resolves a definition that exists only in the changed document.
  • Fixtures then exercise completion, hover, signature help, references, rename, formatting, code actions, and diagnostics, as far as each pinned server meaningfully implements them. tests/lsp_real_servers.rs marks each feature as tested, advertised-only, or unsupported per server.
  • Rust, Go, and JavaScript also resolve definitions across files.
  • Diagnostic fixtures check the reported range and the publication that clears it after a fix.
  • Ordinary cargo test skips the matrix: building its image downloads several large toolchains, and running it starts eight real servers.