Skip to main content

hjkl_buffer/
lib.rs

1//! # hjkl-buffer
2//!
3//! Rope-backed text buffer with vim-shaped semantics: charwise/linewise/
4//! blockwise selection, motions matching vim edge cases (no `h` wrap, `$`
5//! clamp, sticky col on `j`/`k`), folds, viewport, and search.
6//!
7//! Extracted from `sqeel-buffer` with full git history.
8//!
9//! ## Pre-1.0 stability
10//!
11//! Pre-1.0: signatures may shift between patch versions. The invariants
12//! documented on each type and function are the load-bearing semantics — they
13//! will not silently change without a CHANGELOG entry and a deliberate version
14//! bump.
15//!
16//! ## Why so many invariants?
17//!
18//! Most of them follow from one rule: **the engine layer treats
19//! [`View`] as the source of truth for text content**. Any divergence
20//! between cached state (engine-side selections, undo stacks, search matches)
21//! and the buffer's `lines()` is a bug. The invariants documented on each type
22//! are the contract that lets the engine cache aggressively without risking
23//! that divergence.
24//!
25//! Open issues: <https://github.com/kryptic-sh/hjkl/issues>.
26//!
27//! ## Testing your `View` use
28//!
29//! Property tests are encouraged for any non-trivial caller. The crate ships
30//! its own test suite; reuse [`View::from_str`] to construct fixtures from
31//! inline strings.
32//!
33//! Things worth proving:
34//!
35//! - After any sequence of valid edits + their inverses, the buffer returns to
36//!   its original `lines()`.
37//! - For any valid [`Position`] and motion call, the resulting cursor is itself
38//!   valid.
39//! - [`View::dirty_gen`] strictly increases across mutations and stays
40//!   constant across read-only queries.
41
42#![deny(unsafe_op_in_unsafe_fn)]
43
44mod buffer;
45pub mod content;
46mod edit;
47mod engine_types;
48mod folds;
49pub mod geom;
50pub mod listchars;
51mod motion;
52mod position;
53pub mod search;
54mod selection;
55mod span;
56mod undo;
57mod viewport;
58pub mod wrap;
59
60pub use buffer::View;
61pub use buffer::{floor_char_boundary, rope_line_bytes, rope_line_str};
62pub use content::Buffer;
63pub use edit::{Edit, MotionKind};
64pub use engine_types::{ContentEdit, EngineEdit, FoldOp, Pos};
65pub use folds::{Fold, FoldIndex, invalidate_folds, shift_fold, shift_folds_after_edit};
66pub use geom::{
67    char_col_to_visual_col, cursor_visual_col, leading_visual_width, visual_col_to_char_col,
68};
69pub use listchars::{ListChars, apply_listchars};
70pub use motion::{KeywordSpec, is_keyword_char};
71pub use position::Position;
72pub use search::search_match_ranges;
73pub use selection::{RowSpan, Selection};
74pub use span::Span;
75pub(crate) use undo::UndoTree;
76pub use undo::{Delta, MarkSnapshot, SerNode, SerTree, UndoEntry};
77pub use viewport::{Viewport, is_big_viewport_jump};
78pub use wrap::{Wrap, char_col_for_visual_offset, visual_offset_for_char_col, wrap_segments};
79
80/// Stable per-buffer identifier carried through async pipelines
81/// (syntax, git-signs, format-worker) so workers can multiplex per-buffer
82/// state without holding buffer references.
83///
84/// Assigned by the application layer; 0 is a valid test sentinel.
85///
86/// # Example
87///
88/// ```
89/// use hjkl_buffer::BufferId;
90/// let id: BufferId = 42;
91/// assert_eq!(id, 42);
92/// ```
93pub type BufferId = u64;