Open-Source Wikis

/

Helix

/

Primitives

/

Document

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

  • OpenDocument::open(path) reads the file, detects encoding/line-ending/language, builds the rope, initializes syntax. Editor::open then registers it and emits DocumentDidOpen.
  • ApplyDocument::apply(transaction, view_id) is the hot path: history commit, selection map, syntax incremental parse, LSP didChange, diff update, DocumentDidChange event.
  • SaveDocument::save returns a DocumentSavedEventFuture; the editor consumes these via save_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 via helix_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.
  • CloseEditor::close(doc_id) removes from documents, drops LSP file references, emits DocumentDidClose.

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 detectionLanguageConfiguration is matched by file-type pattern, shebang, or first-line regex.
  • Encodingchardetng over the first 8 KiB; fall back to UTF-8.
  • Line endingauto_detect_line_ending (helix-core/src/line_ending.rs).
  • Indentauto_detect_indent_style (helix-core/src/indent.rs) inspects up to 1000 lines.
  • EditorConfigEditorConfig::find_for_path loads .editorconfig settings.

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-jumplist is 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

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

Document – Helix wiki | Factory