Open-Source Wikis

/

Helix

/

Packages

/

helix-view

helix-editor/helix

helix-view

The mutable, frontend-aware editor state. Owns documents, view splits, registers, themes, and the integrations with LSP, DAP, and VCS. ~14,500 lines of Rust.

Purpose

Where helix-core is functional and immutable, helix-view is the imperative shell. It defines the Editor (the global state container), Document (one open file), and View (a viewport with its own selection). Frontends — currently only helix-term — drive the editor through this crate.

The crate was originally intended to be frontend-agnostic. In practice it has terminal-shaped types like Rect, Style, CursorKind, but the Editor/Document/View API itself is the seam any future frontend would target.

Directory layout

helix-view/src
├── lib.rs              # DocumentId/ViewId types, align_view helper, public re-exports
├── editor.rs           # Editor struct, Config, Action, EditorEvent (~2.5k LOC)
├── document.rs         # Document, Mode, History integration (~2.7k LOC)
├── view.rs             # View, ViewPosition, gutter assignments, scroll math
├── tree.rs             # Tree (split layout) and Layout enum
├── theme.rs            # TOML theme loader, Style/Color resolution
├── gutter.rs           # Gutter renderers (line numbers, diagnostics, diff signs, breakpoints)
├── register.rs         # Named registers (`"x`)
├── clipboard.rs        # System clipboard providers (osc52, wayland, x11, win, mac, none)
├── input.rs            # KeyEvent + KeyCode parsing/serialization (~1.4k LOC)
├── keyboard.rs         # KittyEnhancedKeyboardProtocol detection
├── graphics.rs         # Rect, Color, Modifier, Style, CursorKind
├── info.rs             # Auto-info popup data
├── annotations/        # InlineDiagnostics + virtual text plumbing
├── annotations.rs
├── handlers/, handlers.rs   # Cross-subsystem handler registry (LSP, DAP, completion)
├── events.rs           # DocumentDidChange/Open/Close/FocusLost events
├── expansion.rs        # `%{}` variable expansion in commands and external args
├── macros.rs           # log macros & `view!`/`current!` shortcuts

Key abstractions

Type File Purpose
Editor editor.rs Global state: documents map, view tree, registers, language servers, diagnostics, debug adapters, theme loader, syn loader.
Document document.rs Open file: rope + selections + syntax + history + diagnostics + diff handle + per-language config.
View view.rs Open viewport: DocumentId, ViewPosition (scroll), gutter offsets, jumplist.
Tree tree.rs Hierarchical Container/Node representation of view splits, with Layout::Horizontal/Vertical.
Mode document.rs Normal / Select / Insert.
Action editor.rs What to do when opening a document: Replace, HorizontalSplit, VerticalSplit, Load.
Config editor.rs The [editor] section of the user TOML; ~80 fields covering autocompletion, gutters, soft wrap, LSP, search, statusline.
Theme theme.rs Named scopes → Style. Loader merges parent themes.
Registers register.rs Named registers including special ones (", _, *, +, #).
EditorEvent editor.rs The async event type the main loop selects over.

Editor: the central data structure

pub struct Editor {
    pub mode: Mode,
    pub tree: Tree,
    pub documents: BTreeMap<DocumentId, Document>,
    pub registers: Registers,
    pub language_servers: helix_lsp::Registry,
    pub debug_adapters: helix_dap::registry::Registry,
    pub diff_providers: DiffProviderRegistry,
    pub diagnostics: BTreeMap<Uri, Vec<(lsp::Diagnostic, DiagnosticProvider)>>,
    pub theme: Theme,
    pub syn_loader: Arc<ArcSwap<syntax::Loader>>,
    pub config: Arc<dyn DynAccess<Config>>,
    pub macro_recording: Option<(char, Vec<KeyEvent>)>,
    /* … */
}

Anything cross-document (a register, a language server registry) lives on Editor. Anything per-file lives on Document. Anything per-viewport lives on View.

The current! and view! macros from macros.rs are the canonical way to grab the focused view + document inside a command.

Document lifecycle

graph LR
    Open[Editor::open<br/>or Document::open] --> NewDoc[Document]
    NewDoc -- emit --> DDO[DocumentDidOpen event]
    NewDoc --> Apply[doc.apply Transaction]
    Apply --> History[History stores commit]
    Apply -- emit --> DDC[DocumentDidChange event]
    Apply --> ReParse[Syntax incremental parse]
    Apply --> LspNotify[LSP didChange]
    Apply --> Diff[VCS DiffHandle update]
    Save[doc.save] -- emit --> DDS[DocumentSaved future]
    Close[Editor::close] -- emit --> Closed[DocumentDidClose event]

The events fire through helix_event::dispatch. Hooks declared elsewhere (in helix-term/src/handlers/) react to these to drive completion, document highlights, inlay hints, and so on.

Views and the tree

A View knows the DocumentId it shows, its scroll position (ViewPosition), the area Helix gave it from the layout tree, and a jumplist. Multiple views can show the same document; selections are stored on the Document keyed by ViewId. Closing the last view of a document does not close the document — :bc (buffer-close) does that explicitly.

The view tree is a binary Tree of horizontal and vertical splits (tree.rs). Tree::split and Tree::remove perform layout updates; Tree::traverse iterates leaves in display order.

Gutters

Gutters are configurable in TOML (editor.gutters) and rendered in gutter.rs. Built-in gutters: line numbers, diagnostics, diff signs (from helix-vcs), spacer, and breakpoints. Each gutter declares a width and a per-line render closure.

Themes

theme.rs loads *.toml files from runtime/themes/ (and the user's config dir). A theme is a scope-to-Style map; scopes are dotted paths like function.method. Theme::find_scope_index walks from most specific to least specific to find a matching style — the same fallback rule tree-sitter uses.

There are 200+ bundled themes in runtime/themes/, validated by cargo xtask theme-check.

Registers and macros

register.rs implements the named registers users access via "x syntax. Special registers integrate with the system clipboard (*, +), the search history (/), the command history (:), and the black-hole register (_). Macro recording (Q/q) writes a sequence of KeyEvents into a register; replaying (@x) feeds them back through the keymap.

Configuration

editor::Config (editor.rs) is the largest config struct in the workspace. New fields require:

  1. Adding the field with a Default value.
  2. Documenting it in book/src/editor.md.
  3. (If the user can toggle it via :set or :toggle) ensuring it resolves correctly through the TOML deserializer (#[serde(deny_unknown_fields)] on the parent).

Integration points

  • helix-coreSelection, Transaction, Syntax are central; Document owns one of each.
  • helix-event — the events! macro declares the document/selection events; hooks live in helix-term/src/handlers/.
  • helix-lspEditor::language_servers is helix_lsp::Registry; per-document language server IDs live on Document::language_servers.
  • helix-dapEditor::debug_adapters and Editor::breakpoints.
  • helix-vcsEditor::diff_providers; per-document DiffHandle lives on Document.
  • helix-tui — only at the type level (Rect, Style); rendering happens in helix-term/src/ui/.

Entry points for modification

  • Adding an editor config field: see "Configuration" above.
  • Adding a new event the editor emits: declare it with events! in events.rs and dispatch via helix_event::dispatch in the spot that mutates state.
  • Adding a new gutter: implement the gutter closure in gutter.rs and add it to GutterType.
  • Tweaking selection rendering or virtual text: see text_decorations which renders annotations declared in helix-view.

For a deeper dive into Document, View, and Tree, see primitives/document and primitives/view.

Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.

helix-view – Helix wiki | Factory