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//! ## Features
10//!
11//! - `ratatui` (off by default): enables the `render` module with a direct
12//! cell-write `ratatui::widgets::Widget` impl for [`Buffer`] via
13//! [`BufferView`].
14//!
15//! ## Pre-1.0 stability
16//!
17//! Pre-1.0: signatures may shift between patch versions. The invariants
18//! documented on each type and function are the load-bearing semantics — they
19//! will not silently change without a CHANGELOG entry and a deliberate version
20//! bump.
21//!
22//! ## Why so many invariants?
23//!
24//! Most of them follow from one rule: **the engine layer treats
25//! [`Buffer`] as the source of truth for text content**. Any divergence
26//! between cached state (engine-side selections, undo stacks, search matches)
27//! and the buffer's `lines()` is a bug. The invariants documented on each type
28//! are the contract that lets the engine cache aggressively without risking
29//! that divergence.
30//!
31//! Open issues: <https://github.com/kryptic-sh/hjkl/issues>.
32//!
33//! ## Testing your `Buffer` use
34//!
35//! Property tests are encouraged for any non-trivial caller. The crate ships
36//! its own test suite; reuse [`Buffer::from_str`] to construct fixtures from
37//! inline strings.
38//!
39//! Things worth proving:
40//!
41//! - After any sequence of valid edits + their inverses, the buffer returns to
42//! its original `lines()`.
43//! - For any valid [`Position`] and motion call, the resulting cursor is itself
44//! valid.
45//! - [`Buffer::dirty_gen`] strictly increases across mutations and stays
46//! constant across read-only queries.
47
48#![deny(unsafe_op_in_unsafe_fn)]
49
50mod buffer;
51pub mod content;
52mod edit;
53mod folds;
54pub mod geom;
55mod motion;
56mod position;
57#[cfg(feature = "ratatui")]
58mod render;
59mod selection;
60mod span;
61mod viewport;
62pub mod wrap;
63
64pub use buffer::Buffer;
65pub use content::Content;
66pub use edit::{Edit, MotionKind};
67pub use folds::Fold;
68pub use geom::visual_col_to_char_col;
69pub use motion::is_keyword_char;
70pub use position::Position;
71#[cfg(feature = "ratatui")]
72pub use render::{BufferView, Conceal, DiagOverlay, Gutter, GutterNumbers, Sign, StyleResolver};
73pub use selection::{RowSpan, Selection};
74pub use span::Span;
75pub use viewport::{Viewport, is_big_viewport_jump, over_provisioned_range};
76pub use wrap::Wrap;