Skip to main content

editor_core/
lib.rs

1#![warn(missing_docs)]
2//! Editor Core - Industrial-Grade Headless Code Editor Kernel
3//!
4//! # Overview
5//!
6//! `editor-core` is a headless code editor kernel focused on state management, text metrics, and
7//! coordinate transformations.
8//!
9//! It does not render UI. Hosts render from snapshots (e.g. [`HeadlessGrid`]) and drive edits via
10//! the command/state APIs.
11//!
12//! # Core Features
13//!
14//! - **Efficient Text Storage**: Rope-backed text buffer with `char`-indexed access
15//! - **Fast Line Index**: Rope-based line access and coordinate conversion
16//! - **Soft Wrapping Support**: Headless layout engine, supporting arbitrary container widths
17//! - **Style Management**: Interval tree structure, O(log n + k) query complexity
18//! - **Code Folding**: Supports arbitrary levels of code folding
19//! - **State Tracking**: Version number mechanism and Change Notifications system
20//! - **Workspace model**: Multi-buffer + multi-view orchestration (`Workspace`, `BufferId`,
21//!   `ViewId`) for tabs and split panes
22//!
23//! # Architecture Layers
24//!
25//! ```text
26//! ┌─────────────────────────────────────────────┐
27//! │  Command Interface & State Management       │  ← Public API
28//! ├─────────────────────────────────────────────┤
29//! │  Snapshot API (HeadlessGrid)                │  ← Rendering Data
30//! ├─────────────────────────────────────────────┤
31//! │  Intervals & Visibility (Styles + Folding)  │  ← Visual Enhancement
32//! ├─────────────────────────────────────────────┤
33//! │  Layout Engine (Soft Wrapping)              │  ← Text Layout
34//! ├─────────────────────────────────────────────┤
35//! │  Line Index + TextBuffer (Rope-based)       │  ← Text Storage / Line Access
36//! └─────────────────────────────────────────────┘
37//! ```
38//!
39//! # Quick Start
40//!
41//! ## Using Command Interface
42//!
43//! ```rust
44//! use editor_core::{CommandExecutor, Command, EditCommand, CursorCommand, Position};
45//!
46//! let mut executor = CommandExecutor::empty(80);
47//!
48//! // Insert text
49//! executor.execute(Command::Edit(EditCommand::Insert {
50//!     offset: 0,
51//!     text: "fn main() {\n    println!(\"Hello\");\n}\n".to_string(),
52//! })).unwrap();
53//!
54//! // Move cursor
55//! executor.execute(Command::Cursor(CursorCommand::MoveTo {
56//!     line: 1,
57//!     column: 4,
58//! })).unwrap();
59//!
60//! assert_eq!(executor.editor().cursor_position(), Position::new(1, 4));
61//! ```
62//!
63//! ## Using State Management
64//!
65//! ```rust
66//! use editor_core::{EditorStateManager, StateChangeType};
67//!
68//! let mut manager = EditorStateManager::new("Initial text", 80);
69//!
70//! // Subscribe to state changes
71//! manager.subscribe(|change| {
72//!     println!("State changed: {:?}", change.change_type);
73//! });
74//!
75//! // Query state
76//! let doc_state = manager.get_document_state();
77//! println!("Line count: {}, Characters: {}", doc_state.line_count, doc_state.char_count);
78//! ```
79//!
80//! ## Using Workspace (multi-buffer / multi-view)
81//!
82//! ```rust
83//! use editor_core::{Command, CursorCommand, EditCommand, Workspace};
84//!
85//! let mut ws = Workspace::new();
86//! let opened = ws
87//!     .open_buffer(Some("file:///demo.txt".to_string()), "Hello\nWorld\n", 80)
88//!     .unwrap();
89//!
90//! // Commands always target a view.
91//! let view = opened.view_id;
92//! ws.execute(view, Command::Cursor(CursorCommand::MoveTo { line: 1, column: 0 }))
93//!     .unwrap();
94//! ws.execute(view, Command::Edit(EditCommand::InsertText { text: ">> ".into() }))
95//!     .unwrap();
96//!
97//! let grid = ws.get_viewport_content_styled(view, 0, 10).unwrap();
98//! assert!(grid.actual_line_count() > 0);
99//! ```
100//!
101//! # API Visibility
102//!
103//! `EditorCore` keeps its storage, layout, style, folding, and cursor fields private so hosts
104//! cannot bypass synchronization invariants. Use read-only getters such as
105//! [`EditorCore::line_index`], [`EditorCore::layout_engine`], [`EditorCore::folding_manager`], and
106//! [`EditorCore::viewport_width`] for inspection. Use [`CommandExecutor`], [`EditorStateManager`],
107//! [`Workspace`], or narrowly scoped public methods for mutations that must update text, layout,
108//! folding, styles, and notifications together. Layout and interval/folding types are exposed from
109//! the crate root as facade re-exports rather than through public `layout` or `intervals` modules.
110//!
111//! # Module Description
112//!
113//! - [`storage`] - deprecated Piece Table compatibility layer; it is not on the main editing path
114//! - [`line_index`] - Rope-based line index and canonical text access facade
115//! - [`LayoutEngine`], [`WrapMode`], and related root re-exports - soft wrapping layout facade
116//! - [`IntervalTree`], [`FoldingManager`], and related root re-exports - style intervals and code folding facade
117//! - [`snapshot`] - Headless snapshot API (HeadlessGrid)
118//! - [`commands`] - Unified command interface
119//! - [`state`] - State management and query interface
120//!
121//! # Performance Goals
122//!
123//! - **Loading**: 1000 line document < 100ms
124//! - **insertion**: 100 random insertions < 100ms
125//! - **line access**: 1000 line accesses < 10ms
126//! - **Memory**: bounded command history with rope-backed text storage on the main editing path
127//!
128//! # Unicode Support
129//!
130//! - UTF-8 internal encoding
131//! - Proper handling of CJK double-width characters
132//! - Public offsets and positions are `char`-indexed Unicode scalar coordinates
133//! - Layout columns and snapshot ranges use Unicode scalar positions plus rendered cell widths, not
134//!   grapheme-cluster indices
135//! - Dedicated grapheme cursor/delete commands use UAX #29 boundaries; dedicated word movement and
136//!   deletion commands use Unicode word boundaries
137//! - Editor-friendly word selection and expansion use configurable ASCII token boundaries, treating
138//!   non-ASCII scalars as single-character word units
139//! - `editor-core-lsp` provides UTF-16 code unit coordinate conversion for LSP integrations
140//! - `editor-core-sublime` provides optional `.sublime-syntax` syntax highlighting and folding
141
142pub mod anchors;
143pub mod commands;
144pub mod decorations;
145pub mod delta;
146pub mod diagnostics;
147pub mod intelligence;
148pub(crate) mod intervals;
149pub(crate) mod layout;
150pub mod line_ending;
151pub mod line_index;
152pub mod processing;
153pub mod search;
154mod selection_set;
155pub mod snapshot;
156pub mod snippets;
157pub mod state;
158pub mod storage;
159pub mod symbols;
160mod text;
161mod text_buffer;
162mod visual_rows;
163pub mod workspace;
164
165pub use anchors::{AnchorBias, TextAnchor};
166pub use commands::{
167    AutoPair, AutoPairsConfig, Command, CommandError, CommandExecutor, CommandResult,
168    CursorCommand, EditCommand, EditorCore, ExpandSelectionDirection, ExpandSelectionUnit,
169    Position, Selection, SelectionDirection, StyleCommand, TabKeyBehavior, TextEditSpec,
170    UndoHistoryRestoreError, UndoHistorySelectionSet, UndoHistorySnapshot, UndoHistoryStep,
171    UndoHistoryTextEdit, ViewCommand,
172};
173pub use decorations::{
174    Decoration, DecorationKind, DecorationLayerId, DecorationPlacement, DecorationRange,
175};
176pub use delta::{TextDelta, TextDeltaEdit};
177pub use diagnostics::{Diagnostic, DiagnosticRange, DiagnosticSeverity};
178pub use editor_core_lang::{CommentConfig, IndentStyle, IndentationConfig};
179pub use intelligence::{
180    CallHierarchyIncomingCall, CallHierarchyOutgoingCall, CallHierarchyResultSet, HierarchyItem,
181    IntelligenceResultSet, ReferencesResultSet, ResultSetId, ResultSetKind, TypeHierarchyResultSet,
182    WorkspaceIntelligence,
183};
184pub use intervals::{
185    CODE_LENS_STYLE_ID, DOCUMENT_HIGHLIGHT_READ_STYLE_ID, DOCUMENT_HIGHLIGHT_TEXT_STYLE_ID,
186    DOCUMENT_HIGHLIGHT_WRITE_STYLE_ID, DOCUMENT_LINK_STYLE_ID, FOLD_PLACEHOLDER_STYLE_ID,
187    FoldRegion, FoldingManager, IME_MARKED_TEXT_STYLE_ID, INLAY_HINT_STYLE_ID, Interval,
188    IntervalTextEdit, IntervalTree, MATCH_HIGHLIGHT_STYLE_ID, StyleId, StyleLayerId,
189};
190pub use layout::{
191    DEFAULT_TAB_WIDTH, LayoutEngine, VisualLineInfo, WrapIndent, WrapMode, WrapPoint,
192    calculate_wrap_points, calculate_wrap_points_with_tab_width,
193    calculate_wrap_points_with_tab_width_and_mode,
194    calculate_wrap_points_with_tab_width_mode_and_indent, cell_width_at, char_width, str_width,
195    str_width_with_tab_width, visual_x_for_column,
196};
197pub use line_ending::LineEnding;
198pub use line_index::LineIndex;
199pub use processing::{DocumentProcessor, ProcessingEdit};
200pub use search::{SearchError, SearchMatch, SearchOptions};
201pub use snapshot::{
202    Cell, ComposedCell, ComposedCellSource, ComposedGrid, ComposedLine, ComposedLineKind,
203    HeadlessGrid, HeadlessLine, MinimapGrid, MinimapLine, SnapshotGenerator,
204};
205pub use snippets::{SnippetRange, SnippetSession, SnippetTabstop, SnippetTemplate, parse_snippet};
206pub use state::{
207    CursorState, DecorationsState, DiagnosticsState, DocumentState, EditorState,
208    EditorStateManager, FoldingState, SmoothScrollState, StateChange, StateChangeCallback,
209    StateChangeType, StyleState, UndoRedoState, ViewportState,
210};
211#[deprecated(
212    note = "PieceTable is no longer on the main editing path; use EditorCore/LineIndex text APIs instead"
213)]
214pub use storage::PieceTable;
215pub use symbols::{
216    DocumentOutline, DocumentSymbol, SymbolKind, SymbolLocation, SymbolRange, Utf16Position,
217    Utf16Range, WorkspaceSymbol,
218};
219pub use workspace::{
220    BufferId, BufferMetadata, JumpTarget, OpenBufferResult, ViewId, ViewSmoothScrollState,
221    Workspace, WorkspaceError, WorkspaceSearchResult, WorkspaceUndoHistoryRestoreError,
222    WorkspaceViewportState,
223};