Jesse A. Green / notebook

Helix Admin & Architecture Guide

Generated June 2026 from helix-editor/helix at d3ec5bc

AI-generated documentation. The library is checked by sampling (three repositories so far); this guide was not individually reviewed. Verify against the project before relying on it.

This guide is the comprehensive technical reference for Helix — the modal terminal text editor written in Rust. It is written for administrators, packagers, power users, and developers who need to understand how Helix is structured, deploy and configure it precisely, harden it, and extend or contribute to it.

Where the Helix User Guide explains how to use the editor, this guide explains how it works: the multi-crate workspace architecture, the complete configuration surface, the runtime-file model, the workspace-trust security boundary, and the internals a contributor needs.


Table of Contents

  1. Executive Summary
  2. Architecture Overview
  3. Crate Reference
  4. Core Data Model
  5. Rendering and UI Layer
  6. Event System and Hooks
  7. LSP and DAP Integration
  8. System Requirements
  9. Build and Installation for Operators
  10. Runtime File Resolution
  11. Configuration Mastery
  12. Language and Grammar Configuration
  13. Theming Reference
  14. Security Analysis
  15. Operations
  16. Code Quality and Conventions
  17. Testing Strategy
  18. Potential Improvements
  19. Contributing
  20. Quick Reference

1. Executive Summary

Helix is a single self-contained binary (hx) plus a runtime directory of tree-sitter grammars and queries. It has no plugin runtime, no embedded scripting language, and no network services. All "smart" behavior — syntax highlighting, code navigation, completion, diagnostics, debugging — is delivered either by compiled-in tree-sitter grammars or by out-of-process language servers and debug adapters that Helix speaks to over stdio.

The codebase is a Rust Cargo workspace of 14 crates organized as a clean dependency stack: functional core editing primitives at the bottom, an imperative editor/view layer in the middle, and a terminal frontend at the top, with protocol clients (LSP, DAP) and support crates alongside. This layering is the single most important fact for understanding, packaging, or extending Helix.

Operationally, Helix is low-risk: it runs entirely as the invoking user, opens no sockets, and gates its only code-execution surfaces (project-local config and language-server startup) behind a workspace-trust prompt that is off by default for untrusted workspaces.

Attribute Value
Distribution unit hx binary + runtime/ directory
Process model Single process, runs as the user; spawns LSP/DAP child processes
Network surface None (LSP/DAP communicate over stdio by default)
Persistence Config files, log file, jumplist/trust lists on disk
Configuration TOML (config.toml, languages.toml), with project-local merge
Extensibility (today) Language servers, debug adapters, tree-sitter grammars, themes
Code-execution gates Workspace trust (LSP + local config)

2. Architecture Overview

Helix is built as a layered Cargo workspace. The dependency direction flows upward: lower crates know nothing about higher ones.

                         ┌───────────────────────────────────────────┐
   User ── terminal ───▶ │ helix-term  (terminal frontend, event loop)│
                         │   commands.rs · keymap.rs · ui/* · main()  │
                         └───────────────┬───────────────────────────┘
                                         │ uses
              ┌──────────────────────────┴──────────────────────────┐
              ▼                                                       ▼
   ┌────────────────────┐                              ┌──────────────────────────┐
   │ helix-view         │  imperative shell:           │ helix-tui                │
   │ Editor · Document  │  open docs, view tree,        │ TUI primitives (Surface, │
   │ View · registers   │  config, LSP registry         │ Rect, widgets, backend)  │
   └─────────┬──────────┘                              └──────────────────────────┘
             │ uses                         uses ▲                ▲
             ▼                                   │                │
   ┌────────────────────┐   ┌──────────────┐  ┌─┴────────┐  ┌────┴───────┐
   │ helix-core         │   │ helix-lsp    │  │ helix-dap│  │ helix-vcs  │
   │ Rope · Selection   │   │ LSP client   │  │ DAP clnt │  │ git/diff   │
   │ Transaction·Syntax │   └──────┬───────┘  └────┬─────┘  └────────────┘
   └─────────┬──────────┘          │               │
             │              ┌──────┴──────┐  ┌──────┴───────┐
             │              │helix-lsp-   │  │helix-dap-    │
             │              │types        │  │types         │
             │              └─────────────┘  └──────────────┘
             ▼
   ┌────────────────────────────────────────────────────────────────┐
   │ Foundations: helix-stdx (std extensions) · helix-event (events) │
   │ helix-loader (fetch/build/load runtime) · helix-parsec (parser  │
   │ combinators) · xtask (dev task runner)                          │
   └────────────────────────────────────────────────────────────────┘

The design intent (documented in the project's own architecture.md) is that helix-core is purely functional — operations return new copies rather than mutating in place — while helix-view is the imperative shell that ties documents, views, syntax, history, and language servers together. helix-term is the concrete terminal frontend and event loop. The core is "heavily based on CodeMirror 6."

Note

The helix-view layer was originally intended to be frontend-agnostic; today it is tied to the terminal UI. A future custom GPU renderer (mentioned by the maintainers) would build on the same view/core split.


3. Crate Reference

The workspace members, in dependency order from foundation to frontend. The default-members is helix-term (so a bare cargo build builds the editor).

Crate Role
helix-stdx Extensions to the Rust standard library (path canonicalization, etc.), in the spirit of rust-analyzer's stdx.
helix-core Core editing primitives, functional: Rope, Selection/Range, Transaction, Syntax, history, snippets, increment/decrement, indentation.
helix-event Primitives for defining and dispatching editor events and registering hooks (sync hooks and debounced AsyncHooks).
helix-loader Fetching, building, and loading external resources — tree-sitter grammars and runtime files; runtime-directory resolution.
helix-parsec Small parser-combinator library used internally (e.g. for command-line and snippet parsing).
helix-lsp-types Language Server Protocol type definitions.
helix-lsp The LSP client — talks to language servers over stdio.
helix-dap-types Debug Adapter Protocol type definitions.
helix-dap The DAP client — talks to debug adapters.
helix-vcs Version-control integration: git diff gutters and change tracking (worker-based diffing).
helix-tui TUI primitives — Surface, Rect, widgets, terminal backends. Forked from tui-rs, inspired by Cursive.
helix-view The imperative editor shell: Editor, Document, View, registers, the view tree, config, and the language-server registry.
helix-term The terminal frontend: main(), the Application event loop, commands.rs (every action), keymap.rs (key→command), and the ui/ components.
xtask Ad-hoc dev task runner (e.g. cargo xtask docgen regenerates the command/language docs).

Tip

When orienting yourself in the source, helix-term/src/commands.rs is the highest-value file — it defines every command tied to a keybinding. helix-term/src/keymap.rs maps keys to those commands, and helix-term/src/ui/ holds the rendered components.

Workspace build profiles

Defined in the root Cargo.toml:

Profile Inherits Notable settings Use
release — lto = "thin" Standard release
opt release lto = "fat", codegen-units = 1, strip, opt-level = 3 Maximum-performance distribution build
integration test opt-level = 2 for helix-core/tui/term Faster integration-test runs

4. Core Data Model

These are the primitives in helix-core that everything else is built on. Understanding them explains many of Helix's user-visible behaviors.

Rope

Buffer text is represented as a Rope (re-exported from the ropey library). Ropes are cheap to clone, which lets Helix take inexpensive snapshots of document state — the basis for undo history and concurrent views.

Selection and Range

Multiple selections are a core primitive, not an add-on. A document's selection is a Selection containing one or more Ranges. Each Range has a moving head and an immovable anchor. A plain cursor is a single-width range where head and anchor coincide. This is why every Helix command operates naturally on N selections at once.

Transaction

Edits are expressed as an OT-like Transaction — a single coherent change applied to a rope. Transactions:

  • can be inverted to produce an undo;
  • allow selections and marks to be mapped through them, translating positions into the post-edit text state.

Transaction::change / Transaction::change_by_selection is the primary interface for generating text edits.

Syntax

Syntax is the interface to tree-sitter ASTs, powering incremental syntax highlighting, structural textobjects, indentation heuristics, and the ]f/[t-style navigation. Grammar loading and the highlight engine are provided via the tree-house dependency (a tree-sitter integration) plus the per-language query files in the runtime directory.

Important

The functional core means most operations return new values rather than mutating. This is what makes snapshots, undo, and multi-view consistency tractable — but it also means contributors must thread new state through rather than editing in place.


5. Rendering and UI Layer

The UI is composed of Components (widgets), found mostly in helix-term/src/ui. Key abstractions (from helix-view and helix-tui):

Abstraction Responsibility
Document Ties together the Rope, per-view Selections, Syntax, History, language server state — the full representation of an open file.
View An open split: holds the displayed document ID plus gutter, status line, diagnostics, and the inner text area. Multiple views can show the same document (hence selections are stored per ViewId).
Editor Global state: all open documents, the tree of view splits, configuration, and the language-server registry.
Surface A buffer that widgets draw into; rendered to the screen each cycle.
Rect An x/y/width/height region of a Surface constraining where a Component may render.
Component A widget. Some (e.g. Popup, Overlay) take child components.
Layer A Vec<Component> managed by the Compositor.
Compositor Renders each layer in push order, so components stack — this is how a picker draws over the editor, a popup over a picker, and so on.
Info The autoinfo box that shows available bindings while you wait on a prefix key like g or m.

The render loop lives in helix-term: main() constructs an Application that runs the event loop; on each top-level render call the Compositor paints the layer stack onto the Surface.


6. Event System and Hooks

The helix-event crate defines the editor's internal event mechanism. Events cover document open/change/close, language-server start/stop, and more.

  • Define events with the events! macro.
  • Register synchronous hooks with register_hook!.
  • Dispatch editor-wide events to hooks with helix_event::dispatch.
  • Use AsyncHook for cancellable, debounced work that should run a short time after an event settles (the basis for idle-triggered behaviors like completion and diagnostics refresh, governed by editor.idle-timeout).

This event/hook system is the closest thing Helix has to an internal extension point today, and is expected to underpin a future plugin system.


7. LSP and DAP Integration

Language servers (LSP)

helix-lsp is a client that launches each configured language server as a child process and communicates over stdio using the types in helix-lsp-types. The Editor holds a registry of running servers. Multiple languages can share one server instance (e.g. typescript-language-server serves JavaScript, JSX, TSX, and TypeScript by default).

Per-language and per-server behavior is configured in languages.toml (see Language and Grammar Configuration). Feature routing (only-features / except-features) lets operators compose several servers — for example a dedicated formatter server plus a primary analysis server.

Debug adapters (DAP)

helix-dap is a Debug Adapter Protocol client (types in helix-dap-types). Debugging is driven from the Space G minor mode or the :debug-start / :debug-remote commands and is marked experimental.

Note

LSP and DAP are external programs that Helix executes. They are a code-execution surface and are therefore gated by workspace trust. Operators packaging Helix do not bundle language servers; users install them separately and place them on $PATH.


8. System Requirements

Runtime

Requirement Detail
OS Linux, macOS, Windows (and other Unix-likes)
Terminal Any reasonably modern terminal. For full keybinding fidelity (Super key, Shift-Tab, some Alt combos), a terminal supporting the enhanced/Kitty keyboard protocol is recommended.
Runtime files The runtime/ directory (grammars + queries + themes + tutor) must be discoverable (see Runtime File Resolution).
Language servers Optional, per language; must be on $PATH.
Truecolor Auto-detected; can be forced with editor.true-color = true.

Build

Requirement Detail
Rust The Rust toolchain; MSRV 1.90. Helix follows Firefox's MSRV policy and keeps it intentionally low for downstream packaging.
Git Required.
C++ compiler C++14-compatible (GCC or Clang) to build tree-sitter grammars.
musl note With musl-libc, set RUSTFLAGS="-C target-feature=-crt-static" so grammars load.

Important

When bumping the MSRV, three places must change together: workspace.package.rust-version in the root Cargo.toml, env.MSRV in .github/workflows/build.yml, and toolchain.channel in rust-toolchain.toml.


9. Build and Installation for Operators

Reproducible vs. optimized builds

# Reproducible (locked dependencies)
cargo install --path helix-term --locked

# Optimized for the local CPU
cargo install \
   --profile opt \
   --config 'build.rustflags=["-C", "target-cpu=native"]' \
   --path helix-term \
   --locked

Both produce hx and build the grammars into the local runtime/ folder.

Grammar control at build time

Mechanism Effect
HELIX_DISABLE_AUTO_GRAMMAR_BUILD Skip fetching/building grammars during the build.
hx --grammar fetch / --grammar build Fetch/build grammars later, into the user runtime dir.
use-grammars = { only = [...] } in languages.toml Restrict which grammars are processed.

Packaging guidance

For distribution packages, set HELIX_DEFAULT_RUNTIME at build time to the final install location of the runtime files so the shipped hx finds them out of the box:

export HELIX_DEFAULT_RUNTIME=/usr/lib/helix/runtime
cargo build --profile opt --locked
cp -r runtime "$BUILD_DIR/usr/lib/helix/"
cp target/opt/hx "$BUILD_DIR/usr/bin/hx"

Building the Debian package

cargo install cargo-deb
cargo deb -- --locked      # output in target/debian/

The resulting .deb bundles shell completions (bash/fish/zsh), the .desktop file, the icon, and a launcher that wires up the runtime.

Warning

A cargo deb run may print warning: Failed to find dependency specification for some grammar files — this is expected and harmless.


10. Runtime File Resolution

Helix searches multiple runtime directories in priority order; the first match for a given filename wins:

  1. runtime/ sibling of $CARGO_MANIFEST_DIR (development/testing only).
  2. runtime/ under the OS-dependent Helix user config directory.
  3. $HELIX_RUNTIME.
  4. The distribution fallback set at compile time via HELIX_DEFAULT_RUNTIME.
  5. runtime/ next to the hx executable.
Platform Config / runtime base
Linux / macOS ~/.config/helix/ (runtime at ~/.config/helix/runtime/)
Windows %AppData%\helix\

Validate resolution with hx --health. For source builds, either export HELIX_RUNTIME or symlink the source runtime/ into the config dir; on Windows use a directory junction (New-Item -ItemType Junction or mklink /D).

Caution

If syntax highlighting or indentation is missing, the cause is almost always an unresolved runtime directory or unbuilt grammar. hx --health <language> pinpoints which.


11. Configuration Mastery

Configuration is TOML. The user file is ~/.config/helix/config.toml (%AppData%\helix\config.toml on Windows). A project-local .helix/config.toml merges on top of the user file and built-in defaults. Apply changes at runtime with :config-reload or, on Unix, pkill -USR1 hx. A one-off file can be passed with hx -c.

Note

The theme key and the [keys.*] remapping tables are top-level (not under [editor]). Key remapping is documented in the User Guide; this section is the exhaustive option reference.

[editor]

Key Description Default
scrolloff Lines of padding kept around the screen edge when scrolling 5
mouse Enable mouse mode true
mouse-yank-register Register used for mouse yanks *
middle-click-paste Middle-click paste support true
default-yank-register Default register for yank/paste "
scroll-lines Lines scrolled per wheel step 3
shell Shell for external commands Unix ["sh","-c"] / Win ["cmd","/C"]
line-number absolute or relative "absolute"
cursorline Highlight cursor line(s) false
cursorcolumn Highlight cursor column(s) false
continue-comments Continue comment token on new line inside a comment true
gutters Gutters to show (diagnostics, diff, line-numbers, spacer) ["diagnostics","spacer","line-numbers","spacer","diff"]
auto-completion Auto popup of completion true
path-completion Filepath completion true
auto-format Format on save (also needs language auto-format) true
idle-timeout ms after last keypress before idle timers fire 250
completion-timeout ms after typing before completions show (5 = instant) 250
preview-completion-insert Apply completion instantly when selected true
completion-trigger-len Min word length to trigger autocompletion 2
completion-replace Completions replace the whole word false
auto-info Show info boxes true
true-color Force truecolor false
undercurl Force undercurl support false
rulers Columns to draw rulers at []
bufferline always, never, or multiple "never"
color-modes Color mode indicator per mode false
text-width Max line length (for :reflow, soft-wrap) 80
workspace-lsp-roots Dirs treated as LSP roots (set in .helix/config.toml) []
default-line-ending native, lf, crlf, ff, cr, nel "native"
insert-final-newline Insert trailing newline on write if missing true
atomic-save Atomic file writes (prevents data loss; may confuse watchers) true
trim-final-newlines Remove extra trailing newlines on write false
trim-trailing-whitespace Trim trailing whitespace on write false
popup-border popup, menu, all, or none "none"
indent-heuristic simple, tree-sitter, or hybrid "hybrid"
jump-label-alphabet Characters used for two-char jump labels "abc…xyz"
end-of-line-diagnostics Min severity rendered at end of line (disable to off) "hint"
clipboard-provider Clipboard backend (see below) platform-specific
editor-config Read .editorconfig files true
rainbow-brackets Rainbow matching brackets (needs rainbows.scm) false
kitty-keyboard-protocol enabled, disabled, or auto "auto"
insecure Disable workspace-trust gating (see Security) false

[editor.clipboard-provider]

Either a built-in name — pasteboard (macOS), wayland, x-clip, x-sel, win32-yank, termux, tmux, windows, termcode, none — or a custom command set:

[editor.clipboard-provider.custom]
yank  = { command = "cat", args = ["test.txt"] }
paste = { command = "tee", args = ["test.txt"] }
# optional: primary-yank / primary-paste

Custom commands communicate the clipboard contents over stdin/stdout.

[editor.statusline]

Three regions (left / center / right). Sub-keys:

Key Default
left ["mode","spinner","file-name","read-only-indicator","file-modification-indicator"]
center []
right ["diagnostics","selections","register","position","file-encoding"]
separator "│"
mode.normal / mode.insert / mode.select "NOR" / "INS" / "SEL"
diagnostics ["warning","error"]
workspace-diagnostics ["warning","error"]

Available elements: mode, spinner, file-name, file-absolute-path, file-base-name, current-working-directory, file-modification-indicator, file-encoding, file-line-ending, file-indent-style, read-only-indicator, total-line-numbers, file-type, diagnostics, workspace-diagnostics, selections, primary-selection-length, position, position-percentage, separator, spacer, version-control, register.

[editor.lsp]

Key Description Default
enable Master switch for LSP true
display-messages Show window/showMessage below statusline true
display-progress-messages Show LSP progress messages false
auto-signature-help Auto popup signature help true
auto-document-highlight Highlight symbol references at cursor false
display-inlay-hints Show inlay hints false
inlay-hints-length-limit Max displayed inlay-hint length unset
display-color-swatches Show color swatches true
display-signature-help-docs Docs under signature help true
snippets Snippet completions (needs :lsp-restart to toggle) true
goto-reference-include-declaration Include declaration in references true

[editor.cursor-shape]

Per-mode shape — block, bar, underline, or hidden. Defaults: normal/insert/select all "block". (Only the primary cursor can change shape, a terminal limitation.)

[editor.file-picker]

Key Default
hidden true
follow-symlinks true
deduplicate-links true
parents true
ignore true
git-ignore true
git-global true
git-exclude true
max-depth unset

Helix also reads .ignore, ~/.ignore, a workspace .helix/ignore, and a global ~/.config/helix/ignore.

[editor.file-explorer]

Same key set as the file picker but with defaults tuned to show most files (hidden, ignore, git-* default to false), plus flatten-dirs (default true).

[editor.buffer-picker]

Key Description Default
start-position Initially selected buffer (current / previous) current

[editor.auto-pairs]

true/false, or a table mapping open→close characters. Default pairs: () {} [] '' "" `` `` ``. Can also be set per language in languages.toml`.

[editor.auto-save]

Key Description Default
focus-lost Save when focus leaves Helix (needs terminal focus events) false
after-delay.enable Save after a delay since last edit false
after-delay.timeout Delay in ms 3000

[editor.search]

Key Description Default
smart-case Case-insensitive unless pattern has uppercase true
wrap-around Wrap after the last match true

[editor.whitespace]

render is all, none, or a per-character table (space, nbsp, nnbsp, tab, newline). characters overrides the glyphs used. Toggle live with :set whitespace.render all.

[editor.indent-guides]

Key Description Default
render Draw indent guides false
character Guide glyph "│"
skip-levels Indent levels to skip 0

[editor.gutters]

layout is a vector of gutter components, default ["diagnostics","spacer","line-numbers","spacer","diff"]. Sub-sections: gutters.line-numbers (min-width, default 3), gutters.diagnostics (unused), gutters.diff (colors from theme diff.plus/minus/delta; no options), gutters.spacer (unused).

[editor.soft-wrap]

Key Description Default
enable Enable soft wrapping false
max-wrap Max free space at line end 20
max-indent-retain Max indentation carried to wrapped lines 40
wrap-indicator Text before wrapped lines "↪ "
wrap-at-text-width Wrap at text-width instead of viewport false

[editor.smart-tab]

Key Description Default
enable Tab runs move_parent_node_end when non-whitespace is to the left true
supersede-menu smart-tab takes precedence over menu cycling false

[editor.inline-diagnostics]

Key Description Default
cursor-line Min severity shown inline on the cursor line (disable to off) "warning"
other-lines Min severity shown inline on other lines "disable"
prefix-len Horizontal bars before the text 1
max-wrap As soft-wrap.max-wrap for diagnostics 20
max-diagnostics Max diagnostics rendered inline per line 10

Severity values: error, warning, info, hint.

[editor.word-completion]

Key Description Default
enable Complete words from open buffers true
trigger-length Word chars before triggering 7

12. Language and Grammar Configuration

Language behavior lives in languages.toml, merged across three locations (built-in → user config dir → project .helix/).

[[language]] keys

Key Description
name Language name
language-id LSP language id
scope TextMate-style scope, e.g. source.rust
injection-regex Pattern for language-injection matching
file-types Extensions and/or glob tables
shebangs Interpreters from the shebang line
roots Marker files for LSP root selection
auto-format Format on save
diagnostic-severity Minimum severity to display
comment-tokens / block-comment-tokens Line / block comment tokens
indent { unit, tab-width }
language-servers Servers for this language (with optional feature filters)
grammar Tree-sitter grammar name (defaults to name)
formatter External formatter (stdin→stdout)
soft-wrap / text-width / rulers Per-language overrides
path-completion / word-completion / rainbow-brackets Per-language overrides
workspace-lsp-roots Stop the LSP root search early
persistent-diagnostic-sources Sources whose positions Helix tracks internally

Project and LSP root selection

  • The workspace root is found once by walking up from the working directory to the first directory containing .git, .svn, .jj, or .helix (else the cwd).
  • Root markers (roots) are searched starting at the file, using the topmost marker up to the workspace root.
  • workspace-lsp-roots (set in $PROJECT/.helix/config.toml) stops the search early for nested-root projects.
  • A server's required-root-patterns is validation after root selection — if none match, the server is not started.

File-type detection

file-types accepts strings (extension match) and glob tables (full-path match, always /-separated). Globs take priority over extensions.

[language-server.<name>] keys

Key Description
command Binary name/path (must be in $PATH)
args Arguments
config Initialization options
timeout Max request time in seconds (default 20)
environment Env vars for the server
required-root-patterns Globs that must exist for the server to start

A language may list multiple servers; only-features / except-features route the supported features. Feature priority follows the array order, except diagnostics, code-action, completion, document-symbols, and workspace-symbols, which merge across all enabled servers. The full feature list: format, goto-declaration, goto-definition, goto-type-definition, goto-reference, goto-implementation, signature-help, hover, document-highlight, completion, code-action, document-links, workspace-command, document-symbols, workspace-symbols, diagnostics, pull-diagnostics, rename-symbol, inlay-hints, document-colors, call-hierarchy.

[[grammar]] and grammar selection

[[grammar]]
name = "mylang"
source = { git = "https://github.com/example/mylang", rev = "<commit>", subpath = "optional" }

Fetch/build with hx --grammar fetch / hx --grammar build. A top-level use-grammars = { only = [...] } or { except = [...] } (declared before any [[language]]/[[grammar]]) limits which grammars are processed.

Tip

Helix ships query files for several hundred languages (the runtime/queries/<lang>/ directories), each potentially providing highlights.scm, indents.scm, textobjects.scm, injections.scm, locals.scm, and rainbows.scm. Structural textobjects and tree-sitter navigation only work for languages whose textobjects.scm exists.


13. Theming Reference

Themes are TOML files in ~/.config/helix/themes/ (or %AppData%\helix\themes\). Set with theme = "<name>", or split light/dark:

[theme]
dark = "catppuccin_frappe"
light = "catppuccin_latte"
# fallback = "catppuccin_frappe"

Style syntax

key = { fg = "#ffffff", bg = "#000000", underline = { color = "#ff0000", style = "curl" }, modifiers = ["bold","italic"] }
  • Colors: CSS hex (#RGB/#RRGGBB) or a [palette] name. The default palette is the terminal's 16 colors (black, red, …, white, plus light-*). No alpha/transparency.
  • Modifiers: bold, dim, italic, underlined (deprecated), slow_blink, rapid_blink, reversed, hidden, crossed_out.
  • Underline styles: line, curl, dashed, dotted, double_line.
  • Inheritance: inherits = "<theme>".
  • Rainbow brackets: a rainbow = [...] list of styles.

Note

When matching a syntax highlight, the longest matching key wins (e.g. function.builtin beats function). default and base16_default are reserved built-in names.

Scope families

Syntax scopes mirror tree-sitter highlight names and follow Sublime/TextMate naming — top-level families include type (builtin/parameter/enum.variant), constant (numeric.integer/float, character.escape, builtin.boolean), string (regexp, special.path/url/symbol), comment (line.documentation, block.documentation, unused), variable (mutable, builtin, parameter, other.member.private), keyword (control.conditional/repeat/import/return/exception, operator, directive, function, storage.type/modifier), function (builtin/method/macro/special), tag, namespace, label, punctuation (delimiter/bracket/special), operator, special, markup (heading.1..6, list, bold, italic, link.*, quote, raw.inline/block), and diff (plus/minus/delta with gutter variants).

Interface scopes theme the editor chrome — ui.background, ui.cursor.*, ui.gutter, ui.linenr(.selected), ui.statusline(.normal/insert/select/inactive/separator), ui.bufferline.*, ui.popup(.info), ui.picker.header.*, ui.window, ui.help, ui.text(.focus/inactive/info/directory/symlink), ui.virtual.* (ruler/whitespace/indent-guide/inlay-hint/wrap/jump-label), ui.menu(.selected/scroll), ui.selection(.primary), ui.cursorline.*, ui.cursorcolumn.*, plus diagnostic scopes (warning, error, info, hint, their .diagnostic.inline variants, and diagnostic.*), and tabstop for snippet placeholders.


14. Security Analysis

Helix's threat surface is small and well-defined. It runs as the invoking user, opens no network sockets, and has no embedded scripting runtime. The notable surfaces are below.

Code execution via workspace features (the primary boundary)

Two features can lead to running code defined by a repository:

  1. Project-local configuration — .helix/config.toml and .helix/languages.toml can specify formatters, language-server commands, and other behavior.
  2. Language servers — starting a server executes an external binary against project files.

Helix mitigates this with Workspace Trust:

  • No workspace is trusted by default.
  • Opening a file in an undecided workspace prompts the user.
  • Never excludes the workspace permanently from prompts.
  • :workspace-trust / :workspace-untrust manage the current workspace.
  • Trust/exclude lists live in ~/.local/share/helix/trusted_workspaces and .../excluded_workspaces.

Caution

Setting editor.insecure = true disables this protection — every .helix/ config is loaded and language servers start without prompting. Do not enable it on machines that open untrusted repositories.

Shell expansions and command execution

The command line supports %sh{...} expansions and shell commands (:sh, :pipe, !, |). These run with the user's editor.shell. They execute only when the user invokes them, but keybindings and project config could in principle pre-stage such commands — another reason the trust boundary matters.

File writing

atomic-save (default true) writes files atomically to prevent corruption on interruption. Operators relying on file watchers/hot-reload should be aware atomic writes (rename-in-place) can confuse some watchers; disable per the trade-off if needed.

Clipboard providers

Custom clipboard providers run external commands for yank/paste. Treat them as trusted configuration; a malicious clipboard-provider.custom command would run on every clipboard operation.

Defense-in-depth recommendations

  • Keep editor.insecure unset.
  • Review .helix/ directories before trusting unfamiliar repositories.
  • Install language servers from trusted sources and pin versions where possible.
  • For debugging untrusted code, run inside a container or VM (DAP executes the target).

15. Operations

Day-to-day operator commands

Task How
Health check hx --health (or hx --health <language>)
Open log :log-open, or launch with hx --log /path and tail -f
Increase log verbosity hx -v (repeatable: -vv, -vvv); HELIX_LOG_LEVEL for integration tests
Reload config :config-reload or pkill -USR1 hx (Unix)
Restart a language server :lsp-restart (or :lsp-stop)
Set/inspect options live :set <key> <value>, :toggle <key>, :get <key>
Change working directory :cd <path> (:pwd to show)

Log file locations

The log file path is reported by hx --health (typically under the OS cache directory, e.g. ~/.cache/helix/helix.log). Override with --log.

Upgrades

Helix is a single binary plus a runtime/ directory. Upgrade via your package manager, or rebuild from source. After upgrading a source build, rebuild grammars if the grammar set changed (hx --grammar fetch && hx --grammar build). Many crash fixes ship per release, so staying current is the main reliability lever.

Backup and state

User-relevant state on disk:

Path Contents
~/.config/helix/config.toml, languages.toml User configuration
~/.config/helix/themes/ User themes
~/.config/helix/runtime/ User runtime (grammars/queries/themes), if used
~/.local/share/helix/trusted_workspaces, excluded_workspaces Workspace-trust decisions
~/.cache/helix/helix.log (typical) Log file

16. Code Quality and Conventions

Aspect Convention
Language Rust, edition 2021, MSRV 1.90
Formatting rustfmt (rustfmt.toml at repo root)
Workspace 14-crate Cargo workspace; layered dependency stack (core → view → term)
Core style Functional — operations return new values rather than mutating in place
Errors anyhow::Result at boundaries (e.g. arg parsing), thiserror for typed errors
Concurrency tokio async runtime; parking_lot locks; arc-swap for hot-swappable config
Key dependencies ropey (rope), nucleo (fuzzy matcher for pickers), tree-house/tree-sitter (syntax), slotmap (arena IDs), toml (config), termina (terminal), globset (ignore globs), etcetera (config dirs)
Generated docs Command and language tables are generated via cargo xtask docgen
Logging log crate macros (log::info!, warn!, error!); verbosity via -v

Tip

Some book pages (the :command list and language-support matrix) are generated from the code with cargo xtask docgen. When adding commands or languages, regenerate them rather than hand-editing.


17. Testing Strategy

Test type Command Notes
Unit + doc tests cargo test --workspace All packages
Integration tests (helix-term) cargo integration-test Helpers in helix-term/tests/test/helpers.rs; set HELIX_LOG_LEVEL=debug for logs
Build/test speed integration profile (opt-level 2 for core/tui/term) Faster integration runs

Integration tests exercise editor commands end-to-end (see helix-term/tests/test/commands). Contributors are strongly encouraged to add integration tests for new behavior, using existing tests as templates.

Warning

On macOS, integration tests may fail with Too many open files (os error 24). Raise the limit first: ulimit -n 10240.


18. Potential Improvements

Observations drawn from the codebase, the project's own documentation, and the community issue tracker. These are opportunities, not defects in normal use.

  • Plugin system. Helix has no end-user plugin runtime today; extensibility is limited to language servers, debug adapters, grammars, and themes. The helix-event hook system and documented intentions point toward a future plugin architecture. Diff providers beyond git are explicitly noted as "eventually supported by a future plugin system."
  • Frontend-agnostic view layer. helix-view was meant to be frontend-agnostic but is currently coupled to the terminal UI; decoupling would enable the mooted custom GPU renderer.
  • Crash hardening. The issue tracker shows a recurring class of panics around changeset/ position math and selection edge cases (e.g. #12582, #9830, #13325). Many are fixed per release; continued fuzzing/property testing of Transaction and Selection mapping would reduce this class further.
  • Terminal key-protocol fragmentation. Several reported "binding doesn't work" issues (e.g. macOS Alt/Opt #2280) stem from inconsistent terminal support for modifier keys. Broader enhanced-keyboard-protocol adoption upstream and clearer in-app diagnostics would help.
  • Window resizing. Manual split resizing is a long-standing requested feature (#1176).
  • Documentation gaps. The project notes that "not all functionality is fully documented"; the auto-generated keymap/command tables are the most reliable reference, and from-vim.md carries TODOs (textobjects/surround/registers coverage).
  • Grammar/textobject coverage. Structural textobjects and tree-sitter navigation depend on per-language textobjects.scm files, which exist for only a subset of the supported languages — a steady contribution opportunity.

19. Contributing

  • Guidelines: docs/CONTRIBUTING.md. Good first issues are labeled E-easy.
  • Internals overview: docs/architecture.md (summarized in this guide).
  • Dev build: cargo run (debug; faster to compile). For faster compiles try the mold linker. With rustup, you may need rustup component add rust-analyzer because rust-toolchain.toml pins the MSRV.
  • Print debugging: use log::info!/warn!/error! and run with -v (more vs = higher verbosity), or cargo run -- --log foo.log and tail -f foo.log.
  • Generated docs: run cargo xtask docgen after changing commands or languages. Preview the book with mdbook serve book.
  • API docs: cargo doc --open.
  • High-value files: helix-term/src/commands.rs (all commands), helix-term/src/keymap.rs (key bindings), helix-term/src/ui/ (components), helix-core/ (editing primitives).
  • MSRV bumps: update Cargo.toml, .github/workflows/build.yml, and rust-toolchain.toml together; follow Firefox's MSRV policy.

20. Quick Reference

Environment variables

Variable Purpose
HELIX_RUNTIME Override the runtime directory location
HELIX_DEFAULT_RUNTIME Compile-time distribution fallback runtime path (packagers)
HELIX_DISABLE_AUTO_GRAMMAR_BUILD Skip grammar fetch/build during the build
HELIX_LOG_LEVEL Log level for integration tests
RUSTFLAGS="-C target-feature=-crt-static" Required for musl builds

CLI flags

hx [FLAGS] [files] — -h/--help, -V/--version, --tutor, --health [LANG], -g/--grammar fetch|build, -c/--config <path>, --log <path>, -w/--working-dir <path>, --vsplit, --hsplit, --strict, -v (repeatable), -- (end of flags). File positions: file:row:col, +N file, + file.

Key paths

Path Purpose
~/.config/helix/config.toml (%AppData%\helix\config.toml) User config
~/.config/helix/languages.toml Language config
~/.config/helix/themes/ User themes
~/.config/helix/runtime/ User runtime files
<project>/.helix/{config,languages}.toml Project-local overrides (trust-gated)
~/.local/share/helix/trusted_workspaces / excluded_workspaces Trust decisions

Crate map (one line each)

helix-stdx std extensions · helix-core functional editing primitives · helix-event events/hooks · helix-loader runtime/grammar loading · helix-parsec parser combinators · helix-lsp-types/helix-lsp LSP types/client · helix-dap-types/helix-dap DAP types/client · helix-vcs git diff · helix-tui TUI primitives · helix-view editor shell · helix-term terminal frontend · xtask dev tasks.

Operator checklist

  • [ ] hx --health reports config, language, log, and runtime paths correctly.
  • [ ] Runtime directory resolves (grammars built; syntax highlighting works).
  • [ ] Required language servers are installed and on $PATH.
  • [ ] editor.insecure is unset; workspace trust prompts behave as expected.
  • [ ] Themes/config in .helix/ reviewed before trusting unfamiliar repos.
  • [ ] Backups cover ~/.config/helix/ and ~/.local/share/helix/ trust lists.