helix-editor/helix
helix-core
The core editing primitives. Frontend-agnostic, mostly functional, ~19,600 lines of Rust.
Purpose
helix-core defines the fundamental data structures Helix manipulates: ropes, selections, transactions, and the wiring around tree-sitter syntax. Everything above it (helix-view, helix-term) builds on these primitives. Operations return new values rather than mutating in place; mutability is concentrated in the upper layers.
Directory layout
helix-core/src
├── lib.rs # public re-exports (Rope, Selection, Transaction, …)
├── transaction.rs # ChangeSet, Operation, Transaction (the OT-style edit)
├── selection.rs # Range, Selection
├── position.rs # char↔visual coordinate conversions, soft wrap math
├── movement.rs # word/line/grapheme motion (~2.2k LOC)
├── textobject.rs # tree-sitter-aware text objects
├── match_brackets.rs # bracket matching via tree-sitter
├── object.rs # selection extension over tree-sitter nodes
├── search.rs # wrap-around search helpers
├── surround.rs # `ms`/`md` surround commands
├── auto_pairs.rs # configurable auto-pairing of brackets/quotes
├── comment.rs # block & line comment toggling
├── indent.rs # indentation heuristics + tree-sitter `indents.scm`
├── editor_config.rs # `.editorconfig` support
├── line_ending.rs # CRLF/LF detection and normalization
├── chars.rs, graphemes.rs # Unicode-aware character classification
├── case_conversion.rs # camel/snake/title case
├── command_line.rs # parser for `:typed-commands` (flags, expansions)
├── completion.rs # CompletionItem type
├── diagnostic.rs # Diagnostic + Severity
├── diff.rs # rope diff helper
├── doc_formatter.rs # virtual line formatter (soft wrap, virtual text)
├── doc_formatter/ # supporting modules for the formatter
├── fuzzy.rs # nucleo configuration
├── history.rs # undo tree
├── increment/ # number/date/word incrementers (`Ctrl-a`/`Ctrl-x`)
├── snippets.rs, snippets/ # snippet parser, elaborator, renderer
├── syntax.rs, syntax/ # tree-sitter wrapper + per-language config
├── text_annotations.rs # virtual text overlays
├── uri.rs # `Uri` type used across LSP integration
├── wrap.rs # text-wrap helpers
├── macros.rs # `hashmap!` and friends
├── rope_reader.rs # `Read` adapter over a Rope
└── test.rs # selection-aware test helpersKey abstractions
| Type | File | Purpose |
|---|---|---|
Rope (re-export from ropey) |
lib.rs |
Persistent text buffer; cheap clone. |
Range |
selection.rs |
(anchor, head) pair using char-gap indexing. |
Selection |
selection.rs |
Non-empty list of Ranges + primary index. |
ChangeSet / Operation |
transaction.rs |
OT-style sequence of Retain/Delete/Insert. |
Transaction |
transaction.rs |
A ChangeSet plus an optional updated Selection. Reversible, composable, mappable. |
Assoc |
transaction.rs |
How a position should track edits (Before, After, AfterWord, BeforeSticky, …). |
Position |
position.rs |
Visual (row, col); helpers for soft wrap & virtual text. |
Syntax |
syntax.rs |
Tree-sitter parse tree + language data registry, wrapping tree-house. |
LanguageData / Configuration |
syntax/config.rs |
Deserialized languages.toml. |
History |
history.rs |
Undo tree of transactions with branching and persistence. |
IndentStyle, IndentQuery |
indent.rs |
Indent guessing + tree-sitter indents.scm evaluator. |
Tendril |
lib.rs |
SmartString<LazyCompact>; small-string optimised text fragment. |
How transactions work
A Transaction wraps a ChangeSet (a flat Vec<Operation>) and an optional new Selection. The change set's len and len_after fields are validated when applying so a stale transaction can't corrupt the rope.
let transaction = Transaction::change_by_selection(rope, selection, |range| {
let from = range.from();
let to = range.to();
(from, to, Some(replacement.into()))
});
// apply to the rope
transaction.apply(rope);
// invert for undo
let inverse = transaction.invert(rope_before);
// translate a selection through the edit
let new_selection = old_selection.map(transaction.changes());Two transactions can be composed (Transaction::compose), which is how undo coalescing works. The map operation on ChangeSet translates positions across edits — used by Selection::map, by LSP to update server state (helix-lsp::util), and by anchored marks like view positions and bookmarks.
Selections and cursors
A Range has an anchor (fixed) and head (moved when extending). Cursor convention: a block cursor sits one grapheme before the head when the range is forward, or at the head when zero-width. Range::cursor, Range::cursor_line, and Range::put_cursor help work in terms of the user's cursor without breaking the range invariants.
A Selection always has at least one range and a primary index. Operations like Selection::transform give every range to a closure that returns a new range, then the result is normalized (sorted, merged where adjacent or overlapping). See helix-core/src/selection.rs for merge_consecutive_ranges, union, and the main constructor Selection::new.
Tree-sitter integration
Helix talks to tree-sitter through the tree-house crate (a Helix-maintained wrapper). The migration to tree-house happened in the 25.07 release (CHANGELOG.md).
Syntax (syntax.rs) holds the parse tree per document and can incrementally re-parse on edits. Per-language settings (LSP servers, formatters, comment tokens, file-type detection) are loaded from languages.toml into Configuration and LanguageConfiguration (syntax/config.rs). LanguageData lazily compiles indent/textobject/tag/rainbow queries on first use.
graph LR
Toml[languages.toml] --> Config[Configuration]
Config --> LangData[LanguageData]
LangData -- compile_syntax_config --> SynCfg[tree_house LanguageConfig]
LangData -- compile_indent_query --> IndQ[IndentQuery]
LangData -- compile_textobject_query --> TxtQ[TextObjectQuery]
LangData -- compile_tag_query --> TagQ[TagQuery]
SynCfg --> Syntax
Syntax --> TreeHouse[tree-house highlighter]
TreeHouse --> Highlights[HighlightEvent stream]Movement and text objects
movement.rs (the second-largest core file at ~2,200 lines) implements grapheme-aware word, line, paragraph, and visual motions. textobject.rs implements tree-sitter-aware selection of functions, classes, parameters, comments, and tests via textobjects.scm queries; falls back to bracket-pair heuristics for languages without queries.
Snippets
The snippet system is split across snippets.rs and snippets/:
parser.rs— tokenizes the LSP snippet syntax.elaborate.rs— resolves variables and choices.render.rs— produces aTransactionplus anActiveSnippetdescribing tab-stops.active.rs— drives navigation between tab-stops as the user fills the snippet.
Indent
indent.rs is the largest "data" module (~1,650 lines). It implements both the legacy heuristic indent (looking at the previous lines and bracket nesting) and a tree-sitter-driven indent powered by indents.scm queries. The IndentationHeuristic enum lets users opt into either or a hybrid.
History
History (history.rs) stores undo state as a tree, not a linear stack. Each commit holds a transaction plus its inverse. Branches are reachable through :earlier/:later typed commands. The UndoKind enum lets callers ask for "1 step", "1 second", "to revision N", etc.
Re-exports
helix-core/src/lib.rs re-exports the most-used names: Rope, RopeSlice, Selection, Range, Transaction, ChangeSet, Position, Tendril, Syntax, Diagnostic, LineEnding, plus the regex, tree_sitter, and ropey crates. Downstream code typically imports from the crate root.
Integration points
helix-viewusesDocument::apply(helix-view/src/document.rs) to push transactions through the rope and update history.helix-term's commands (commands.rs) build transactions from the current selection then calldoc.apply(...).helix-lspmaps server-side text edits toChangeSets using helpers inhelix-lsp/src/lib.rs::util.
Entry points for modification
- Adding a movement (e.g. for a new motion): extend
movement.rsand add a key binding inhelix-term/src/keymap/default.rsand a command inhelix-term/src/commands.rs. - Tweaking auto-pair behaviour:
auto_pairs.rs— config struct inhelix-core/src/syntax/config.rs::AutoPairConfig. - Adding a tree-sitter-driven feature: extend
syntax.rsand add the corresponding*.scmquery underruntime/queries/<lang>/. - Adding a typable command flag/expansion: see
command_line.rs.
For details on the underlying types, see primitives/rope, primitives/selection, and primitives/transaction.
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.