editor-core
editor-core is a headless editor engine focused on state management, Unicode-aware text
measurement, and coordinate conversion. It is intentionally UI-agnostic: consumers render from
snapshots and drive edits through the command/state APIs.
Features
- Efficient text storage via a Piece Table (
PieceTable) for inserts/deletes. - Fast line indexing via a rope-backed
LineIndexfor line access and conversions. - Soft wrapping layout (
LayoutEngine) with Unicode-aware cell widths. - Style + folding metadata via interval trees (
IntervalTree) and fold regions (FoldingManager). - Headless snapshots (
SnapshotGenerator→HeadlessGrid) for building “text grid” UIs. - Command interface (
CommandExecutor) and state/query layer (EditorStateManager). - Search utilities (
find_next,find_prev,find_all) operating on character offsets.
Design overview
editor-core is organized as a set of small layers:
- Storage: Piece Table holds the document text.
- Indexing:
LineIndexprovides line access + offset/position conversions. - Layout:
LayoutEnginecomputes wrap points and logical↔visual mappings. - Intervals: styles/folding are represented as ranges and queried efficiently.
- Snapshots: a UI-facing “text grid” snapshot (
HeadlessGrid) can be rendered by any frontend. - State/commands: public APIs for edits, queries, versioning, and change notifications.
Offsets and coordinates
- Many public APIs use character offsets (not byte offsets) for robustness with Unicode.
- Rendering uses cell widths (
Cell.widthis typically 1 or 2) to support CJK and emoji. - There is a distinction between logical lines (document lines) and visual lines (after soft wrapping and/or folding).
Derived state pipeline
Higher-level integrations (like LSP semantic tokens or syntax highlighting) can compute derived editor metadata and apply it through:
DocumentProcessor(produce edits)ProcessingEdit(apply edits)EditorStateManager::apply_processing_edits(update state consistently)
Quick start
Command-driven editing
use ;
let mut executor = empty;
executor.execute.unwrap;
executor.execute.unwrap;
assert_eq!;
State queries + change notifications
use ;
let mut manager = new;
manager.subscribe;
manager.execute.unwrap;
assert!;
// Manual edits are possible, but callers must preserve invariants and call `mark_modified`.
manager.editor_mut.piece_table.insert;
manager.mark_modified;
Related crates
editor-core-lsp: LSP integration (UTF-16 conversions, semantic tokens helpers, stdio JSON-RPC).editor-core-sublime:.sublime-syntaxhighlighting + folding engine.