helix-editor/helix
Document
A Document is one open file. It bundles the text, the per-view selections, the tree-sitter syntax tree, the undo history, the LSP state, the diff handle, and everything else Helix knows about that file.
The implementation is in helix-view/src/document.rs (~2,700 lines, the second-largest file in helix-view).
Identity
pub struct DocumentId(NonZeroUsize);DocumentId is a slot key; Editor::documents: BTreeMap<DocumentId, Document> owns every open document. The NonZeroUsize choice makes Option<DocumentId> a single byte instead of two (helix-view/src/lib.rs).
State on a Document
A condensed view of the struct fields:
| Field | Purpose |
|---|---|
text: Rope |
The buffer contents. |
selections: HashMap<ViewId, Selection> |
One selection per view of this doc. |
view_data: HashMap<ViewId, ViewData> |
Per-view scroll/jumplist state. |
language: Option<Arc<LanguageConfiguration>> |
Detected language. |
syntax: Option<Syntax> |
Tree-sitter parse tree + state. |
history: History |
Undo tree. |
language_servers: HashSet<LanguageServerId> |
Active LSPs for this doc. |
diagnostics: Vec<Diagnostic> |
Aggregated diagnostics from all providers. |
diff_handle: Option<DiffHandle> |
git/VCS diff state. |
path: Option<PathBuf> |
None for scratch buffers. |
encoding: &'static Encoding |
Detected with chardetng. |
line_ending: LineEnding |
LF, CRLF, … |
indent_style: IndentStyle |
Tabs or N spaces. |
editor_config: Option<EditorConfig> |
Parsed from .editorconfig. |
version: i32 |
Monotonic; sent to LSP didChange. |
modified_since_accessed: bool |
Used for jumplist heuristics. |
restore_cursor: bool |
Whether to restore last cursor on reopen. |
view_offset: HashMap<ViewId, ViewPosition> |
Soft-wrap-aware scroll. |
savepoints: Vec<Weak<SavePoint>> |
Outstanding snapshots. |
Lifecycle
- Open —
Document::open(path)reads the file, detects encoding/line-ending/language, builds the rope, initializes syntax.Editor::openthen registers it and emitsDocumentDidOpen. - Apply —
Document::apply(transaction, view_id)is the hot path: history commit, selection map, syntax incremental parse, LSPdidChange, diff update,DocumentDidChangeevent. - Save —
Document::savereturns aDocumentSavedEventFuture; the editor consumes these viasave_queue(SelectAll<Flatten<…>>) so saves can run concurrently. Atomic save is enabled by default (editor.atomic-save = true) — write to a temp file, copy metadata viahelix_stdx::faccess::copy_metadata, rename over the original. - Reload — re-reads from disk and produces a transaction from the diff. Preserves selections and undo history.
- Close —
Editor::close(doc_id)removes fromdocuments, drops LSP file references, emitsDocumentDidClose.
Modes and history
Document::mode mirrors Editor::mode (the editor mode is global). The transition Insert -> Normal triggers:
History::commit_revision— coalesce per-character commits into one undoable revision.commit_undo_checkpoint— explicit checkpoint marker.- Hooks: auto-save handler, completion close, signature help close.
Document::undo / redo walk the history tree. :earlier/:later typable commands jump to the nearest revision before/after a time or N commits ago.
Detection logic
Several detectors run on document open:
- Language detection —
LanguageConfigurationis matched by file-type pattern, shebang, or first-line regex. - Encoding —
chardetngover the first 8 KiB; fall back to UTF-8. - Line ending —
auto_detect_line_ending(helix-core/src/line_ending.rs). - Indent —
auto_detect_indent_style(helix-core/src/indent.rs) inspects up to 1000 lines. - EditorConfig —
EditorConfig::find_for_pathloads.editorconfigsettings.
Users can override with :set indent, :set line-ending, :set language, and :set encoding.
LSP integration
When a document is opened with a language that has language servers, the document calls Registry::get_or_start for each one and registers itself via textDocument/didOpen. Subsequent applies emit didChange with the LSP-encoded edits computed from the ChangeSet. The Document::version counter is the LSP doc version.
Document::has_language_server_with_feature(feature) is the gate every command checks before issuing an LSP request.
VCS integration
Document::diff_handle is created lazily when a DiffProvider knows the file. The handle holds the base text and recomputes hunks on a debounce as the document changes. The diff gutter and goto_next_change/goto_prev_change consume the result.
Per-view state
A Document is shared across views. View-keyed maps:
selections— separate selection per view.view_data(jumplist) —:goto-jumplistis per-view.view_offset— distinct scroll positions.
When a view closes, its entries are pruned but the document stays alive until :bc.
Save events
save does not block. It returns a DocumentSavedEventFuture that the editor pushes onto a SelectAll. The async task writes to disk in the background; on completion, the editor receives a DocumentSavedEvent and runs post-save handlers (formatter run-on-save, LSP didSave, status message).
See also
- primitives/view — the per-viewport companion.
- primitives/transaction — what
Document::applyconsumes. - packages/helix-view — surrounding state model.
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.