helix-editor/helix
Modal editing
Helix is modal. The editor has three primary modes — Normal, Select, and Insert — plus transient sub-modes triggered by sticky keys (Goto, Match, Window, View, Space). The keymap and the editing model are intentionally Kakoune-style: a key first selects (or moves a selection) and then an action operates on the selection.
Modes
Mode is defined in helix-view/src/document.rs:
pub enum Mode {
Normal,
Select,
Insert,
}| Mode | Meaning | Default keys |
|---|---|---|
| Normal | Movement and command mode. Cursor selects one grapheme. | Default at startup. Esc from any other mode. |
| Select | "Visual" mode — every motion extends the selection. | v toggles select; Esc returns to normal. |
| Insert | Free-form character insertion. | i, a, o, O, I, A. |
The current mode lives on Editor::mode. A few invariants:
- Insert-mode commands enforce single-grapheme cursors (the rope position is always on a grapheme boundary).
- Switching from Insert to Normal collapses the selection to a one-grapheme block cursor and runs registered hooks (auto-format, auto-save, completion close).
- Macro recording and replaying serialize key events through the active mode's keymap.
The keymap pipeline
graph LR
KeyEvent --> EditorView[EditorView::handle_event]
EditorView -->|mode| Keymaps
Keymaps -->|walk| KeyTrie
KeyTrie -->|leaf| Mappable[MappableCommand]
KeyTrie -->|inner| Pending[Pending sequence + Info popup]
Mappable -->|Static| Cmd[fn ctx]
Mappable -->|Typable| TypedCmd[:typed-command]
Cmd --> Doc[Document::apply]Keymaps maintains a pending stack of KeyEvents. Each new key extends the stack; on a leaf match the command runs and the stack clears; on a non-leaf the Info popup appears showing what keys are valid next.
The default keymap is built in helix-term/src/keymap/default.rs using the keymap! macro:
let normal = keymap!({ "Normal mode"
"h" | "left" => move_char_left,
"g" => { "Goto"
"g" => goto_file_start,
"d" => goto_definition,
// …
},
// …
});User TOML keymaps are merged on top via keymap::merge_keys (helix-term/src/keymap.rs).
Selection-first commands
Every editing command takes the current Selection and produces either a new Selection (for movements) or a Transaction plus a new selection (for edits). The pattern is illustrated in commands.rs:
fn delete_selection(cx: &mut Context) {
let (view, doc) = current!(cx.editor);
let selection = doc.selection(view.id);
let transaction = Transaction::change_by_selection(doc.text(), selection, |range| {
(range.from(), range.to(), None)
});
doc.apply(&transaction, view.id);
}Movements compose the same way: a movement function returns a new Range and the command lifts it across all ranges in the selection.
The "selection -> action" idea is documented in the project vision and is the most opinionated decision in the editor.
Sticky modes (Goto, Match, etc.)
Some keys (e.g. g, m, space, z, Z, [, ]) open a sub-mode. The compositor surfaces this as an info popup listing the valid follow-ups. Sticky modes are flagged in the KeyTrieNode::is_sticky field — when true, after running a leaf the prefix stays on the stack so the user can repeat without retyping.
Counts and registers
Every command in normal mode can be prefixed with:
- A digit count (
5jmoves down 5 lines) — stored onEditor::countand consumed by the next command. - A register selector (
"xselects registerxfor the next yank/paste/macro).
Both are captured in EditorView::handle_event before keymap lookup. See Context::count and Context::register in commands.rs.
Mode-related events
Mode changes go through helix_event::dispatch so handlers can react:
- Auto-save runs
:writeonModetransitioning toNormalfromInsert. - The completion popup closes on the same transition.
- Macro recording serializes the key event into the active register.
Customizing
User keymaps in TOML use the same trie structure:
[keys.normal]
C-s = ":write"
"space" = { "f" = "file_picker" }The TOML parser in helix-term/src/keymap.rs deserializes into a KeyTrie and merges with the defaults at config load time.
See also
- packages/helix-term — commands, keymap macros, application loop.
- primitives/selection —
Range/Selectionmechanics. - reference/keymap — pointers to the canonical keymap reference.
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.