pub struct Buffer { /* private fields */ }Expand description
Per-window view onto a Content.
Buffer is the type the rest of hjkl-buffer — and all consumers —
use directly. It owns exactly the state that is local to one editor
window:
cursor— the charwise caret for this window.
All document-level state (text rows, dirty generation, folds) lives on
the inner Content and is accessed via Arc<Mutex<Content>>.
Two Buffer instances that share the same Arc share text + folds
but carry independent cursors — the Helix Document+View model.
§Send + Sync
Arc<Mutex<Content>> is Send + Sync, so Buffer remains Send.
The engine trait surface requires Buffer: Send; this constraint
drove the choice of Mutex over RefCell. The mutex is never
contended in normal operation (single-threaded app loop), so the
lock cost is negligible (~5 ns uncontested).
§0.8.0 migration notes
The existing constructors (Buffer::new, Buffer::from_str,
Buffer::replace_all, etc.) keep the same external signatures.
Callers that do not need multi-window sharing see no behaviour change.
Use Buffer::new_view to create a second window onto the same
Content.
§Viewport
The lines invariant — at least one entry, never empty — is
preserved by every mutation. The viewport itself (top_row, top_col,
width, height, wrap, text_width) lives on the engine Host adapter;
methods that need it take a &Viewport / &mut Viewport parameter
so the rope-walking math stays here while runtime state lives there.
Implementations§
Source§impl Buffer
impl Buffer
Sourcepub fn from_str(text: &str) -> Self
pub fn from_str(text: &str) -> Self
Build a buffer from a flat string. Splits on \n; a trailing
\n produces a trailing empty line (matches every text
editor’s behaviour and keeps from_text(buf.as_string()) an
identity round-trip in the common case).
Sourcepub fn new_view(content: Arc<Mutex<Content>>) -> Self
pub fn new_view(content: Arc<Mutex<Content>>) -> Self
Create a second per-window view onto existing Content.
The new Buffer shares text + folds with every other view on the
same Arc. Its cursor starts at (0, 0) independently. This is
the primary entry point for split-window features.
let a = Buffer::from_str("hello\nworld");
let content = a.content_arc();
let mut b = Buffer::new_view(Arc::clone(&content));
// Cursors are independent.
let mut a = Buffer::new_view(Arc::clone(&content));
a.set_cursor(Position::new(1, 0));
assert_eq!(b.cursor(), Position::new(0, 0));Sourcepub fn content_arc(&self) -> Arc<Mutex<Content>>
pub fn content_arc(&self) -> Arc<Mutex<Content>>
Return a clone of the Arc<Mutex<Content>> so callers can
create additional views with Buffer::new_view.
Sourcepub fn lines(&self) -> Vec<String>
pub fn lines(&self) -> Vec<String>
Returns a snapshot of every line as an owned Vec<String>.
Owned rather than &[String] because a Buffer is a per-window
view onto a shared Content; another view could mutate the rope
between when this returns and when the caller reads the slice,
invalidating any borrowed reference.
Sourcepub fn line(&self, row: usize) -> Option<String>
pub fn line(&self, row: usize) -> Option<String>
Returns a clone of the line at row, or None if out of bounds.
Owned rather than Option<&str> for the same reason as Buffer::lines:
another view sharing the same Content could reallocate the backing Vec
between the lock release and the caller’s use of the reference.
pub fn cursor(&self) -> Position
pub fn dirty_gen(&self) -> u64
Sourcepub fn set_cursor(&mut self, pos: Position)
pub fn set_cursor(&mut self, pos: Position)
Set cursor without scrolling. Clamps to valid positions.
The optional sticky column for j/k motions is not reset
by this call — it survives set_cursor intentionally.
Sourcepub fn ensure_cursor_visible(&mut self, viewport: &mut Viewport)
pub fn ensure_cursor_visible(&mut self, viewport: &mut Viewport)
Bring the cursor into the visible Viewport, scrolling by the
minimum amount needed.
Sourcepub fn cursor_screen_row(&self, viewport: &Viewport) -> Option<usize>
pub fn cursor_screen_row(&self, viewport: &Viewport) -> Option<usize>
Cursor’s screen row offset (0-based) from viewport.top_row.
Sourcepub fn screen_rows_between(
&self,
viewport: &Viewport,
start: usize,
end: usize,
) -> usize
pub fn screen_rows_between( &self, viewport: &Viewport, start: usize, end: usize, ) -> usize
Number of screen rows the doc range start..=end occupies.
Sourcepub fn max_top_for_height(&self, viewport: &Viewport, height: usize) -> usize
pub fn max_top_for_height(&self, viewport: &Viewport, height: usize) -> usize
Earliest top_row such that screen_rows_between(top, last)
is at least height.
Sourcepub fn clamp_position(&self, pos: Position) -> Position
pub fn clamp_position(&self, pos: Position) -> Position
Clamp pos to the buffer’s content.
Sourcepub fn replace_all(&mut self, text: &str)
pub fn replace_all(&mut self, text: &str)
Replace the buffer’s full text in place. Cursor is clamped to the new content.
Source§impl Buffer
impl Buffer
Sourcepub fn apply_edit(&mut self, edit: Edit) -> Edit
pub fn apply_edit(&mut self, edit: Edit) -> Edit
Apply edit and return the inverse. Pushing the inverse back
through apply_edit restores the previous state, making it the
single hook for undo-stack integration.
apply_edit is the only way to mutate buffer text.
§Post-conditions
After any Edit variant:
Buffer::dirty_genis incremented exactly once.- The cursor is repositioned to a sensible place for the edit kind
(insert lands past the inserted content; delete lands at the
start). Callers that need to override the new cursor must call
Buffer::set_cursorimmediately after. - All
Positionvalues the caller held from before the edit may be invalid. Re-derive from row / col deltas; do not cache.
Source§impl Buffer
impl Buffer
Sourcepub fn folds(&self) -> Vec<Fold>
pub fn folds(&self) -> Vec<Fold>
Returns a snapshot of all folds as an owned Vec<Fold>.
Owned rather than &[Fold] because a Buffer is a per-window
view onto a shared Content; another view could mutate the folds vec
between when this returns and when the caller reads the slice.
Sourcepub fn add_fold(&mut self, start_row: usize, end_row: usize, closed: bool)
pub fn add_fold(&mut self, start_row: usize, end_row: usize, closed: bool)
Register a new fold. If an existing fold has the same
start_row, it’s replaced; otherwise the new one is inserted
in start-row order. Empty / inverted ranges are rejected.
Sourcepub fn remove_fold_at(&mut self, row: usize) -> bool
pub fn remove_fold_at(&mut self, row: usize) -> bool
Drop the fold whose range covers row. Returns true when a
fold was actually removed.
Sourcepub fn open_fold_at(&mut self, row: usize) -> bool
pub fn open_fold_at(&mut self, row: usize) -> bool
Open the fold at row (no-op if already open or no fold).
Sourcepub fn close_fold_at(&mut self, row: usize) -> bool
pub fn close_fold_at(&mut self, row: usize) -> bool
Close the fold at row (no-op if already closed or no fold).
Sourcepub fn toggle_fold_at(&mut self, row: usize) -> bool
pub fn toggle_fold_at(&mut self, row: usize) -> bool
Flip the closed/open state of the fold containing row.
Sourcepub fn open_all_folds(&mut self)
pub fn open_all_folds(&mut self)
zR — open every fold.
Sourcepub fn clear_all_folds(&mut self)
pub fn clear_all_folds(&mut self)
zE — eliminate every fold.
Sourcepub fn close_all_folds(&mut self)
pub fn close_all_folds(&mut self)
zM — close every fold.
Sourcepub fn fold_at_row(&self, row: usize) -> Option<Fold>
pub fn fold_at_row(&self, row: usize) -> Option<Fold>
First fold whose range contains row. Useful for the host’s
za/zo/zc handlers.
True iff row is hidden by a closed fold (any fold).
Sourcepub fn next_visible_row(&self, row: usize) -> Option<usize>
pub fn next_visible_row(&self, row: usize) -> Option<usize>
First visible row strictly after row, skipping any rows hidden
by closed folds. Returns None past the end of the buffer.
Sourcepub fn prev_visible_row(&self, row: usize) -> Option<usize>
pub fn prev_visible_row(&self, row: usize) -> Option<usize>
First visible row strictly before row, skipping hidden rows.
Sourcepub fn invalidate_folds_in_range(&mut self, start_row: usize, end_row: usize)
pub fn invalidate_folds_in_range(&mut self, start_row: usize, end_row: usize)
Drop every fold that touches [start_row, end_row].