helix-editor/helix
Language servers
Helix has built-in LSP support: per-language servers spawned on demand, multiple servers per language, and async hooks driving completion, signature help, document highlights, and diagnostics. The protocol layer lives in helix-lsp; the user-facing features are spread across helix-term.
Configuration
Servers are declared once at the top of languages.toml:
[language-server.rust-analyzer]
command = "rust-analyzer"
config = { check = { command = "clippy" } }Then attached to a language:
[[language]]
name = "rust"
language-servers = [ "rust-analyzer" ]A language can list multiple servers — each gets started independently and their responses are combined for goto, references, completions, and diagnostics. The server config struct is LanguageServerConfiguration in helix-core/src/syntax/config.rs.
Per-server feature flags (only-features, except-features) gate which capabilities Helix uses from that server. The full list lives in LanguageServerFeature in helix-core/src/syntax/config.rs.
Lifecycle
sequenceDiagram
participant Doc as Document::open
participant Ed as Editor
participant Reg as helix_lsp::Registry
participant Cli as Client
participant LS as Language Server
Doc->>Ed: detect language
Ed->>Reg: get_or_start("rust", "rust-analyzer", root)
Reg->>Cli: spawn process
Cli->>LS: initialize
LS-->>Cli: capabilities (offset_encoding, completion, hover, …)
Cli->>LS: initialized + workspace/didOpen documents
Doc->>Cli: textDocument/didOpen
Note over Doc,Cli: Document::apply now sends didChange on each edit
Doc->>Cli: textDocument/didClose (on Document drop)The registry restarts a crashed client transparently. :lsp-restart and :lsp-stop accept an optional language list to scope to specific servers.
Async handlers
Each LSP feature that fires in the background is an AsyncHook in helix-term/src/handlers/:
| Handler | Trigger | Behaviour |
|---|---|---|
completion |
PostInsertChar, SelectionDidChange |
Debounced completion request; resolves item details on demand. |
signature_help |
PostInsertChar, mode change to Insert |
Shows signature popup when in argument context. |
document_highlight |
SelectionDidChange (debounced) |
Highlights other references to the symbol under the cursor. |
diagnostics |
DocumentDidChange |
Pull-model: requests diagnostics for servers that don't push. |
pull_all_documents_diagnostics |
Workspace events | Workspace-wide pull diagnostics fetcher. |
document_colors |
Document open / change | Renders inline color swatches from textDocument/documentColor. |
document_links |
Document open / change | Resolves clickable links. |
All of these communicate back to the main loop via Jobs and helix_event::request_redraw.
Synchronous commands
LSP-driven user actions live in helix-term/src/commands/lsp.rs:
goto_definition,goto_declaration,goto_type_definition,goto_implementation,goto_referencehover— shows server hover content (cycles through multiple servers viaA-n/A-p).code_action— opens theMenuof available actions.rename_symbol,format_selections,signature_help(manual trigger).workspace_symbol_picker,document_symbol_picker,diagnostics_picker.lsp_workspace_command_picker— runs server-defined commands viaworkspace/executeCommand.
These commands are bound by default to keys under the g (goto) and space (workspace) prefixes — see helix-term/src/keymap/default.rs.
Diagnostics
Diagnostics are stored on Editor::diagnostics keyed by Uri and tagged with their DiagnosticProvider. Multiple providers can contribute to the same file (e.g. rust-analyzer plus a separate linter). Rendering happens via:
- The diagnostic gutter (
helix-view/src/gutter.rs). - Inline diagnostics — virtual-text overlays on the offending line (
helix-view/src/annotations/diagnostics.rs). - End-of-line diagnostics — short summary at the end of the line.
- The diagnostics picker (
space d/space D).
Completion
The completion pipeline is in helix-term/src/handlers/completion and the rendering popup in helix-term/src/ui/completion.rs. 25.07 added support for "incomplete" completions — when the server says the list isn't exhaustive, Helix re-requests on filter change.
editor.preview-completion-insert causes Helix to optimistically insert the completion text; cancelling reverts via the saved snippet snapshot.
Workspace edits, snippets, and code actions
util::apply_workspace_edit (helix-lsp/src/lib.rs) receives a WorkspaceEdit from a code action or rename and produces a sequence of transactions across documents (and file create/rename/delete operations). Snippet edits are turned into a Transaction plus an ActiveSnippet that drives tab-stop navigation in helix-core/src/snippets/active.rs.
Inlay hints, document colors, document links
Three "decorative" LSP features all follow the same pattern: an AsyncHook requests data from the server when the document or viewport changes; results are stored on Document (or Editor); a renderer consults the stored data and emits text annotations or virtual text. The decoration engine is in helix-term/src/ui/text_decorations.
Health check
hx --health <lang> (helix-term/src/health.rs) reports whether the configured language servers, formatter, and DAP adapter are on $PATH — the most common debugging step for "completion isn't working".
See also
- packages/helix-lsp — internals of the protocol layer.
- packages/helix-term — the application loop where LSP messages dispatch.
- reference/configuration — LSP-related editor config (debounce, snippet, signature help).
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.