pub struct Document { /* private fields */ }Expand description
A text document: buffer + undo history + selections + every derived view.
Implementations§
Source§impl Document
impl Document
Sourcepub fn new(text: &str) -> Result<Self, LoadError>
pub fn new(text: &str) -> Result<Self, LoadError>
Load text as a new document (see Buffer::new for the load
contract: size limit, CRLF normalization, EOL-flavor detection). Starts
with a single caret at the top.
Sourcepub fn fold_map(&self) -> Ref<'_, FoldMap>
pub fn fold_map(&self) -> Ref<'_, FoldMap>
The memoized fold projection — buffer rows ↔ display rows, hidden
interiors, hit-testing. Rebuilt from scratch only when the buffer revision
or the FoldSet generation changed since the cached build; otherwise the
cached map is returned untouched, so the render path’s ~20 per-frame calls
cost O(1) instead of O(folds) each. The one owner every fold query flows
through.
Sourcepub fn reveal_mode(&self) -> RevealMode
pub fn reveal_mode(&self) -> RevealMode
The RevealMode of the pending reveal request.
Sourcepub fn set_line_comment(&mut self, prefix: Option<&str>)
pub fn set_line_comment(&mut self, prefix: Option<&str>)
Inject the language’s line-comment prefix (e.g. "//") — app-supplied
configuration, like Document::set_syntax; None disables
toggle-comment.
Sourcepub fn set_bracket_lexing(
&mut self,
string_delims: Vec<u8>,
char_delim: Option<u8>,
)
pub fn set_bracket_lexing( &mut self, string_delims: Vec<u8>, char_delim: Option<u8>, )
Configure comment/string-aware bracket matching: string_delims (e.g.
b'"') open strings and char_delim (e.g. b'\'') opens a char literal,
so brackets inside them are not matched (colored, folded, indent-guided).
Line comments come from set_line_comment.
All constructs are line-local; this rebuilds the bracket set (O(n), for a
setup-time config change, never per edit).
Sourcepub fn line_comment(&self) -> Option<&str>
pub fn line_comment(&self) -> Option<&str>
The injected line-comment prefix, if any.
Sourcepub fn buffer(&self) -> &Buffer
pub fn buffer(&self) -> &Buffer
Read-only access to the underlying buffer (text, lines, coordinates, snapshots).
Sourcepub fn selections(&self) -> &SelectionSet
pub fn selections(&self) -> &SelectionSet
The current selection set (carets and ranges).
Sourcepub fn set_selections(&mut self, selections: SelectionSet)
pub fn set_selections(&mut self, selections: SelectionSet)
Replace the selection set (e.g. a mouse click placing a caret). Offsets are clamped to the buffer.
Sourcepub fn move_carets(&mut self, motion: Motion, extend: bool)
pub fn move_carets(&mut self, motion: Motion, extend: bool)
Move (or, with extend, drag) every caret by motion. Vertical
motion is fold-aware: the caret steps display rows, skipping folds.
Sourcepub fn add_caret(&mut self, offset: u32)
pub fn add_caret(&mut self, offset: u32)
Add a bare caret at offset — the Alt+Click add-caret gesture.
Merges into any selection it touches via the set’s merge rule.
Sourcepub fn add_next_occurrence(&mut self)
pub fn add_next_occurrence(&mut self)
Ctrl+D. If the newest selection is an empty caret, select the word surrounding it; otherwise add the next literal occurrence of the newest selection’s text as a new (and now newest) selection — scanning forward from its end, wrapping to the document start, skipping already-selected ranges. No word / no further match ⇒ no change. Selection-only, no edit.
Sourcepub fn jump_to_bracket(&mut self)
pub fn jump_to_bracket(&mut self)
Ctrl+Shift+: move each caret to its matching bracket. Adjacent to
a matched bracket (the Brackets::active_pair rule — left of the
caret preferred), the caret crosses to the SAME side of the partner, so
a second press returns it. With no adjacent bracket it jumps to the
innermost enclosing pair’s closer. No bracket in reach ⇒ the
caret stays. Collapses selections to carets (a plain motion).
Sourcepub fn expand_selection(&mut self)
pub fn expand_selection(&mut self)
Shift+Alt+Right: grow every selection one structural step — caret
→ word → bracket contents → bracket pair → outward, ending at the whole
document — pushing the previous set so Document::shrink_selection
can walk back down. Fully expanded is a no-op (nothing pushed). The
ladder clears on any other gesture or edit. Selection-only.
Sourcepub fn shrink_selection(&mut self)
pub fn shrink_selection(&mut self)
Shift+Alt+Left: step back down the expansion ladder — restore the set exactly as it was before the matching expand. Empty ladder ⇒ no-op.
Sourcepub fn add_caret_vertical(&mut self, down: bool)
pub fn add_caret_vertical(&mut self, down: bool)
Ctrl+Alt+↑/↓: add a caret one display row above/below EVERY
existing caret, keeping the current set — the stack-a-column gesture.
Each landing resolves like a click at that caret’s visual column (the
movement::caret_one_display_row rule → hit_row), so tabs, chips,
and collapsed folds behave exactly as plain vertical movement; a caret
already on the first/last display row adds nothing, and a landing on an
existing caret merges via the set’s rule. Selection-only. (Landings on
short lines clamp; the visual goal column is not carried across
presses — a deliberate simplification.)
Sourcepub fn select_all_occurrences(&mut self)
pub fn select_all_occurrences(&mut self)
Ctrl+Shift+L: select EVERY occurrence of the newest selection’s
text as a multi-cursor set, seeding from the word under a bare caret
(like Ctrl+D) — the multi-cursor rename: select all, type once.
Single-pass: ONE scan collects every non-overlapping occurrence (the
same match_indices rule find_next_occurrence uses), skips
already-taken selection ranges, and adds the rest in cyclic order —
forward from the seed, wrapping — so the final newest (the reveal
target) is the last occurrence in that order. Selection-only, no edit.
Sourcepub fn select_find_matches(&mut self) -> bool
pub fn select_find_matches(&mut self) -> bool
Every live find match becomes a selection (the find bar’s
select-all-matches, Alt+Enter) — the multi-cursor “replace all”: select
every match, type once. The active match
(or the first) becomes the newest selection, so the caret stays where
find navigation left it. No live matches ⇒ false, nothing changes.
Sourcepub fn collapse_selections(&mut self)
pub fn collapse_selections(&mut self)
Collapse a multi-cursor set (or a single range) to one caret — the primary (oldest) cursor. The Escape gesture.
Sourcepub fn select_all(&mut self)
pub fn select_all(&mut self)
Select the whole document — one selection from the start to the end, head at the end (Ctrl+A). Selection-only, no edit; an empty buffer stays a single caret at 0.
Sourcepub fn drag_select(&mut self, granularity: Granularity, origin: u32, head: u32)
pub fn drag_select(&mut self, granularity: Granularity, origin: u32, head: u32)
Drag-select from origin to head at the given granularity. Char
is a plain range; Word/Line extend by whole units and keep the origin unit
fully selected even when the drag reverses past it (the tail flips to the
far edge of the origin unit). head == origin selects just the origin
unit — the double/triple-click initial selection. Replaces the set.
Sourcepub fn column_select(&mut self, dir: ColumnDir)
pub fn column_select(&mut self, dir: ColumnDir)
Column (box) selection — Ctrl+Shift+Alt+Arrow. The first press anchors
at the primary caret; each press steps the active corner by dir and
rebuilds one selection per spanned row, from the anchor cell to the active
cell. Stepping the active corner back toward the anchor shrinks the box.
Display-space end to end: corners are display CELLS, so the box stays
visually rectangular across tabs and collapsed inline folds; vertical
steps walk display rows (a collapsed fold is one step, not one per hidden
row); and cells are virtual (unbounded right), so a box can reach past
short lines and still select the full width of longer ones. Selection-only;
any other action exits the mode, leaving the box as a multi-cursor set.
Sourcepub fn column_drag(&mut self, anchor: (u32, u32), active: (u32, u32))
pub fn column_drag(&mut self, anchor: (u32, u32), active: (u32, u32))
Mouse box (column) selection — Shift+Alt+drag. Sets the box from an
anchor and active corner, each (visible buffer row, display cell);
the widget derives the row from the one row inversion and the cell from
the one virtual-cell rounding rule (crate::row_layout::virtual_cell).
Cells are virtual (unclamped by line): the box clamps each row to its
own content, so a drag past short lines still selects the full width of
longer ones. Same selection-only semantics as column_select; any other
action exits the mode. Shares Document.column, so a keyboard box then
continues it.
Sourcepub fn move_line(&mut self, down: bool)
pub fn move_line(&mut self, down: bool)
Move the selected line-block up or down one line (Alt+↑/↓). Swaps the block with its neighbour; the selection rides the moved text. A block at the document edge — or, moving down, against the trailing empty line — is a no-op. Single selection. Own undo step; never re-indents.
Sourcepub fn copy_line(&mut self, down: bool)
pub fn copy_line(&mut self, down: bool)
Duplicate the selected line-block above (down = false) or below
(down = true) — Shift+Alt+↑/↓. The caret lands on the copy. Single
selection. Own undo step.
Sourcepub fn text(&self) -> Cow<'_, str>
pub fn text(&self) -> Cow<'_, str>
The full document text (LF-only). Shorthand for self.buffer().text()
— a cold-path whole-text read: O(document), never call it per-frame
or per-keystroke.
Sourcepub fn snapshot(&self) -> Snapshot
pub fn snapshot(&self) -> Snapshot
An immutable snapshot for a background consumer (the compile thread). O(1) — a rope clone; the consumer pays for what it reads.
Sourcepub fn edit(&mut self, ops: Vec<EditOp>) -> Result<Committed, TransactionError>
pub fn edit(&mut self, ops: Vec<EditOp>) -> Result<Committed, TransactionError>
Apply a batch of edits as one discrete transaction (its own undo step).
This is the common programmatic path. For keystroke-level edits that
should merge into one undo unit, use Document::edit_grouped.
Returns the Committed result (forward patch + change records); an
empty/no-op batch changes nothing and records no undo step.
Sourcepub fn edit_grouped(
&mut self,
ops: Vec<EditOp>,
hint: GroupingHint,
) -> Result<Committed, TransactionError>
pub fn edit_grouped( &mut self, ops: Vec<EditOp>, hint: GroupingHint, ) -> Result<Committed, TransactionError>
Apply a batch of edits with an explicit GroupingHint: the
verbs layer uses this so a typing run collapses to one undo step.
Sourcepub fn observe_changes(&mut self, on: bool)
pub fn observe_changes(&mut self, on: bool)
Enable or disable the incremental-change log. Off by default so the common
path pays nothing; a host mirroring edits to a language server
(textDocument/didChange) turns it on and drains
drain_changes after each edit. Turning it off
clears any pending log. For full-document sync, leave this off and re-read
snapshot instead.
Sourcepub fn drain_changes(&mut self) -> Vec<EditOp>
pub fn drain_changes(&mut self) -> Vec<EditOp>
Drain the incremental-change log: every applied edit since the last drain,
in commit order (offset-stable within each commit), as EditOp deltas
whose range is in the coordinates of the document just before that edit —
ready to translate into language-server content changes. Empty unless
observe_changes is on.
Sourcepub fn undo(&mut self) -> bool
pub fn undo(&mut self) -> bool
Undo the most recent undo unit. Returns false if there is nothing to
undo.
Each reverted step threads its patch through rebase_views — the exact
mover forward edits use — so highlight, brackets, and every decoration
(diagnostics, find matches, snippet stops) stay consistent with no
undo-specific resync per feature. The fields are destructured so the
per-step callback can borrow the views while history borrows the buffer.
Sourcepub fn redo(&mut self) -> bool
pub fn redo(&mut self) -> bool
Redo the most recently undone unit — the undo mirror,
through the same rebase_views mover. Returns false if nothing to redo.
Sourcepub fn is_dirty(&self) -> bool
pub fn is_dirty(&self) -> bool
Whether the document differs from the last Document::mark_saved.
Sourcepub fn mark_saved(&mut self)
pub fn mark_saved(&mut self)
Record the current state as saved (clears the dirty flag).
Sourcepub fn redo_branch_count(&self) -> usize
pub fn redo_branch_count(&self) -> usize
How many redo branches diverge from the current position: 1 in the classic single-line case, ≥2 at a fork where an undo was followed by a divergent edit — the branching undo tree retains the branch you undid out of rather than discarding it, so it stays reachable.
Sourcepub fn select_redo_branch(&mut self, index: usize) -> bool
pub fn select_redo_branch(&mut self, index: usize) -> bool
Steer the next redo to the index-th redo branch
(0 = oldest), returning false if out of range. The chosen branch also
becomes the default thereafter. Plain redo (without selecting) always
follows the most-recent branch, so single-line undo is unchanged.
Sourcepub fn set_undo_limit(&mut self, limit: Option<usize>)
pub fn set_undo_limit(&mut self, limit: Option<usize>)
Cap undo history at limit units on the current line (None = the
default, unbounded), dropping older units and any branch that diverged
before the kept window. Opt-in: a host that wants bounded undo memory
calls this; by default nothing is ever pruned.
Sourcepub fn undo_depth(&self) -> usize
pub fn undo_depth(&self) -> usize
How many undo steps are currently reachable — the depth of undo
(bounded by set_undo_limit when set).
Sourcepub fn serialize(&self, flavor: EolFlavor) -> String
pub fn serialize(&self, flavor: EolFlavor) -> String
Serialize for saving, re-expanding LF to the given flavor.
Sourcepub fn set_syntax(&mut self, syntax: SyntaxDef, theme: TokenTheme)
pub fn set_syntax(&mut self, syntax: SyntaxDef, theme: TokenTheme)
Attach a syntax highlighter. The grammar and theme are app-supplied
(scrive-core is language-agnostic). The cache starts sized to the current
line count, every line dirty; call Document::tokenize_highlight before
reading spans, and it re-splices on every edit. A mid-session grammar
swap carries the previous retention-window aim forward, so the currently
visible rows are re-highlighted immediately even though nothing visible
moved to re-trigger the widget’s viewport report.
Sourcepub fn set_theme(&mut self, theme: TokenTheme)
pub fn set_theme(&mut self, theme: TokenTheme)
Swap the highlight theme, keeping the grammar and cache sizing. The
whole cache invalidates; colors repaint on the next tokenize_highlight
(old colors show meanwhile — see HighlightCache::set_theme). No-op
without a highlighter.
Sourcepub fn decorations(&self) -> &DecorationStore
pub fn decorations(&self) -> &DecorationStore
The tracked-range decoration store. Exposed so an app-level owner — e.g. a snippet session registering one range per tab stop — can drive it through the store’s handle API. The store rides edits automatically: every transaction patches it before change events fire.
Sourcepub fn decorations_mut(&mut self) -> &mut DecorationStore
pub fn decorations_mut(&mut self) -> &mut DecorationStore
Mutable decorations for a decoration owner.
Sourcepub fn folds(&self) -> &FoldSet
pub fn folds(&self) -> &FoldSet
The active code folds — read access for building a per-frame
FoldMap.
Sourcepub fn folds_mut(&mut self) -> &mut FoldSet
pub fn folds_mut(&mut self) -> &mut FoldSet
Mutable access to the fold set (e.g. clear). Folds are view state:
mutating them records nothing on the undo stack. Row-based fold/unfold go
through the convenience methods below (they need the buffer too).
Sourcepub fn fold(&mut self, header: BufferRow, last: BufferRow) -> bool
pub fn fold(&mut self, header: BufferRow, last: BufferRow) -> bool
Fold the multi-line bracket pair opening on header and closing on
last. A fold is keyed by that pair’s opening-bracket offset. Nesting is
allowed (folds may sit inside or around others). Returns false if no such
foldable pair exists or it is already folded. Records nothing on the undo
stack — folds are view state.
Sourcepub fn unfold(&mut self, header: BufferRow) -> bool
pub fn unfold(&mut self, header: BufferRow) -> bool
Unfold the fold whose header (opener row) is header. Returns whether one
was removed.
Sourcepub fn toggle_fold(&mut self, header: BufferRow, last: BufferRow) -> bool
pub fn toggle_fold(&mut self, header: BufferRow, last: BufferRow) -> bool
Fold if header has no active fold, else unfold it (the gutter-chevron /
Ctrl+Shift+[,] action).
Sourcepub fn foldable_pairs_in_rows(
&self,
rows: Range<u32>,
) -> Vec<(u32, u32, u32, u32)>
pub fn foldable_pairs_in_rows( &self, rows: Range<u32>, ) -> Vec<(u32, u32, u32, u32)>
Document::foldable_pairs restricted to pairs whose opening bracket
lies on a buffer row in rows — the windowed twin for the fold mouse
hot paths (gutter hover/click, Ctrl+hover boxes), so they cost
O(brackets in the window) instead of a whole-document scan per
mouse-move on a large document. Since a pair’s header is
its opener’s row, this returns exactly the pairs headed in rows (their
closer may lie far below — that offset is carried in the bracket, no
second scan). A pair enclosing rows from above is intentionally not
returned (its opener is elsewhere); callers that only test header == row (block_opener_on_row, the gutter chevron) are exact, and the
Ctrl+hover affordance widens rows with a slack for near-enclosing
blocks.
Public because the widget’s plain-hover chip hit-test
(collapsed_chip_at) needs exactly this: every foldable pair — inline
[ … ] and block { … } alike — headed on the pointer’s single row, so
it can test that row’s chips instead of scanning every fold in the
document (an O(folds) HeaderLayout build per frame at scale).
Sourcepub fn toggle_fold_opener(&mut self, opener: u32) -> bool
pub fn toggle_fold_opener(&mut self, opener: u32) -> bool
Toggle the fold whose opening bracket is at byte offset opener (a
gutter-chevron click or Ctrl+Shift+[/]). Returns whether it changed.
When it folds, any caret the fold would hide is ejected to the gap’s
entry edge so it never sits on collapsed text: an inline pair
pulls it to opener+1 (just after [), a block to the header line’s
end (just before the … placeholder).
Sourcepub fn tab_size(&self) -> u32
pub fn tab_size(&self) -> u32
The document’s tab-stop width — the ONE owner every consumer (core
projections and the widget’s pixel layer alike) consults. Fixed at the
crate::display_map::default_tab_size for now; when it becomes
configurable, the change is one whole-document edit and no
caller-side constant exists to drift.
Sourcepub fn block_opener_on_row(&self, row: u32) -> Option<u32>
pub fn block_opener_on_row(&self, row: u32) -> Option<u32>
The opener offset of the widest multi-line pair whose header is buffer
row row — the block a gutter chevron on that row folds.
Sourcepub fn fold_opener_at_caret(&self, want_folded: bool) -> Option<u32>
pub fn fold_opener_at_caret(&self, want_folded: bool) -> Option<u32>
The opener of the innermost foldable pair enclosing or touching the
primary caret whose collapsed state matches want_folded — the
Ctrl+Shift+[ (fold, false) / Ctrl+Shift+] (unfold, true) target.
A caret directly before the opening bracket or after the closing one
counts (the bracket-highlight adjacency). Includes single-line inline
pairs, disambiguated by the caret’s byte offset (rows can’t tell two
[..] apart).
Sourcepub fn fold_at_carets(&mut self, unfold: bool) -> bool
pub fn fold_at_carets(&mut self, unfold: bool) -> bool
Fold (unfold == false) or unfold (unfold == true) the innermost
foldable block enclosing or touching EACH caret — the multi-cursor
Ctrl+Shift+[ / Ctrl+Shift+]. Distinct target blocks act once (two
carets in one block don’t cancel each other), and every opener is resolved
BEFORE any toggle — safe because folding moves no byte offset. Returns
whether anything changed.
Sourcepub fn collapsible_pairs(&self) -> Vec<(u32, u32, u32, u32)>
pub fn collapsible_pairs(&self) -> Vec<(u32, u32, u32, u32)>
Every collapsible bracket pair — foldable (non-empty interior) and not
already collapsed — as (open, close, header, last), document order. The
candidate set for the Ctrl+hover affordance: a pair already folded is
excluded (there is nothing left to collapse). Includes both multi-line
blocks and single-line inline pairs, so one gesture reaches either.
Sourcepub fn collapsible_pairs_in_rows(
&self,
rows: Range<u32>,
) -> Vec<(u32, u32, u32, u32)>
pub fn collapsible_pairs_in_rows( &self, rows: Range<u32>, ) -> Vec<(u32, u32, u32, u32)>
Document::collapsible_pairs restricted to pairs headed on buffer rows
in rows — the windowed query the Ctrl+hover affordance uses per
mouse-move instead of a whole-document scan on a large document.
The caller widens rows with a slack so a pair whose header sits just
above the viewport still arms; a block taller than that slack enclosing
the pointer is the accepted miss (fold it from its header chevron or the
Ctrl+Shift+[ chord instead).
Sourcepub fn collapsible_at(&self, offset: u32) -> Option<u32>
pub fn collapsible_at(&self, offset: u32) -> Option<u32>
The opener of the innermost collapsible pair whose interior contains byte
offset (open < offset < close) — the Ctrl+hover / Ctrl+Click target.
Innermost = smallest byte span, so a pointer inside nested
collapsibles resolves to the tightest one (an inline array over its
enclosing block). None when the offset sits in no collapsible interior.
Sourcepub fn foldable_ranges(&self) -> Vec<(BufferRow, BufferRow)>
pub fn foldable_ranges(&self) -> Vec<(BufferRow, BufferRow)>
Every foldable (header, last) row range — the source for gutter chevrons
and fold commands. Derived from the matched-bracket pass: each multi-line
matched pair ({}/()/[]) becomes a range from its opener row to its
closer row. At most one range per header row (the widest), sorted by header.
Sourcepub fn foldable_ranges_in_rows(
&self,
rows: Range<u32>,
) -> Vec<(BufferRow, BufferRow)>
pub fn foldable_ranges_in_rows( &self, rows: Range<u32>, ) -> Vec<(BufferRow, BufferRow)>
Document::foldable_ranges restricted to headers on buffer rows in
rows — the gutter chevrons’ per-frame query: two partition points over
the sorted bracket Vec (Brackets::in_range) instead of a
whole-document scan per frame.
Same widest-per-header dedup; hidden in-window headers are the
caller’s to cull (its visible_y already does).
Sourcepub fn tokenize_highlight(&mut self, target: u32)
pub fn tokenize_highlight(&mut self, target: u32)
Bring the highlight cache current up to buffer line target (a viewport’s
last line). Lazy and incremental — end-state convergence bounds the work
to the edited lines. No-op without a highlighter.
Sourcepub fn highlight_frontier(&self) -> Option<u32>
pub fn highlight_frontier(&self) -> Option<u32>
The next row highlight work would touch — a dirty row, or a window
row awaiting a refill after Document::set_highlight_window moved
the retention window (highlight virtualization). None when there is
nothing to do — or when no grammar is injected. The app’s idle sweep
polls this to drive budgeted Document::tokenize_highlight calls
and to stop when idle (an idle document does zero highlight work).
Sourcepub fn set_highlight_window(&mut self, rows: Range<u32>)
pub fn set_highlight_window(&mut self, rows: Range<u32>)
Aim the highlight retention window at rows (the visible buffer rows;
the cache pads by crate::highlight::HIGHLIGHT_WINDOW_SLACK and
evicts outside it). Spans and per-line states are retained only there;
elsewhere sparse checkpoints keep every row re-derivable, so a fully
swept document holds memory proportional to the window, not the line
count. Call on viewport change, then drive
Document::tokenize_highlight as usual. No-op without a grammar.
Sourcepub fn highlight_line_spans(&self, row: u32) -> Option<&[HighlightSpan]>
pub fn highlight_line_spans(&self, row: u32) -> Option<&[HighlightSpan]>
Cached highlight spans for a buffer row — None if untokenized or no
highlighter is attached (the renderer falls back to the plain text color).
Sourcepub fn highlight_engine(&self) -> Option<HighlightEngine>
pub fn highlight_engine(&self) -> Option<HighlightEngine>
A Send + Sync handle to the highlighter’s grammar + theme, for the
app’s off-thread parallel/speculative sweep. None without a grammar.
Pair with Document::snapshot (an O(1) rope clone) and
crate::tokenize_segment on a worker, then feed results back through
Document::absorb_highlight.
Sourcepub fn absorb_highlight(
&mut self,
revision: Revision,
seg: SegmentTokens,
verified: bool,
) -> bool
pub fn absorb_highlight( &mut self, revision: Revision, seg: SegmentTokens, verified: bool, ) -> bool
Ingest a segment tokenized off-thread (the parallel/speculative sweep).
Returns false — absorbing nothing — if revision no longer
matches the document (an edit landed since the snapshot the segment was
computed from; the app drops the stale result and re-dispatches). On a
match, forwards to HighlightCache::absorb:
verified— the coordinator chained this segment from row 0, so its start state is TRUE: its checkpoints merge, its window spans/states are written, and its rows leave the dirty frontier (replacing the foreground walk).!verified— viewport speculation from a guessed fresh start: only still-dirty window rows get its spans, no checkpoints are planted, and the frontier still re-verifies those rows per-line (converging in O(1) if the guess was right).
Already-absorbed work is never lost to a later edit: it lives in the
cache and rides rebase_views’ on_commit splices like every other
derived fact — only in-flight results are dropped by the revision
check.
Sourcepub fn brackets(&self) -> &Brackets
pub fn brackets(&self) -> &Brackets
The matched brackets — kept current on every edit. Drives bracket-pair colorization and the matching-bracket highlight.
Sourcepub fn set_diagnostics(
&mut self,
revision: Revision,
diags: Vec<Diagnostic>,
) -> DiagnosticsOutcome
pub fn set_diagnostics( &mut self, revision: Revision, diags: Vec<Diagnostic>, ) -> DiagnosticsOutcome
Publish a diagnostic set from the app’s debounced compile loop.
The compiler ran against the snapshot at revision; if the buffer has
since moved on this returns DiagnosticsOutcome::Stale having installed
nothing — the previous squiggles keep riding edits via stickiness until
the next publish (no stale set is placed). On a current revision the whole
diagnostic set is replaced wholesale; spans are first clipped to the buffer
length. Zero-width spans survive — the squiggle enforces its one-cell
minimum at draw time.
Sourcepub fn diagnostics_in(
&self,
range: Range<u32>,
) -> impl Iterator<Item = (Range<u32>, Severity, Arc<str>)> + '_
pub fn diagnostics_in( &self, range: Range<u32>, ) -> impl Iterator<Item = (Range<u32>, Severity, Arc<str>)> + '_
Diagnostics overlapping the byte range, for squiggle rendering:
(span, severity, message), position and content reunited from the store,
in ascending (start, id) order. The renderer clips to its visible rows.
Sourcepub fn set_find_query(&mut self, query: Option<FindQuery>, now_ms: u64)
pub fn set_find_query(&mut self, query: Option<FindQuery>, now_ms: u64)
Set (or clear with None) the find query, scanning synchronously.
Never scrolls and drops the active match; the app calls find_next after
if it wants reveal-as-you-type. now_ms is the injected clock. This is the
one whole-document find op — every subsequent edit repairs the set
incrementally through the commit mover.
Sourcepub fn find_query(&self) -> Option<&FindQuery>
pub fn find_query(&self) -> Option<&FindQuery>
The active find query, or None if find is idle.
Sourcepub fn set_find_scope(&mut self, scope: Option<Range<u32>>, now_ms: u64)
pub fn set_find_scope(&mut self, scope: Option<Range<u32>>, now_ms: u64)
Restrict find to a byte range — find in selection — or clear the
restriction with None. Re-scans synchronously: the scope is part of
what matches, not a display filter, so every consumer (navigation, the
N-of-M count, replace_all) honors it without
knowing it exists.
The scope then rides every edit’s patch like any other derived position, growing when text is typed at its edges and dropping if it collapses. An empty range clears it rather than pinning zero matches.
Sourcepub fn find_scope(&self) -> Option<Range<u32>>
pub fn find_scope(&self) -> Option<Range<u32>>
The live find-in-selection scope, if any — for the view to shade it.
Sourcepub fn find_pattern_error(&self) -> Option<&str>
pub fn find_pattern_error(&self) -> Option<&str>
Why the current find pattern does not parse, if it does not — for the find bar to show.
This is not an error path: with the regex option on, every prefix of a
pattern being typed ((, [a-) is invalid, so find simply reports zero
matches and carries the reason until the pattern parses again.
Sourcepub fn caret_word_occurrences(&self, within: Range<u32>) -> Vec<Range<u32>>
pub fn caret_word_occurrences(&self, within: Range<u32>) -> Vec<Range<u32>>
Whole-word occurrences of the word under the newest caret WITHIN the
byte range within — the passive occurrence wash (the reading aid for
tracing a PID/label through a script). The window keeps this per-frame
query O(viewport): the widget passes its visible byte range; tests pass
0..u32::MAX (the scan clamps). Every
match must be a WHOLE word, judged by the same
movement::surrounding_word rule that seeds Ctrl+D — checked against
the full buffer, so a window edge can never fake a word boundary. Empty
when the newest selection is non-empty or the caret is off-word. A pure
per-frame query (the FoldMap precedent): no tracked state, nothing to
rebase on the undo path.
Sourcepub fn find_match_count(&self) -> usize
pub fn find_match_count(&self) -> usize
How many matches the current query has — the live count. The store IS
the match set: there is no shadow handle list to disagree with it, so this
is the O(1) root-summary find_count, not an O(matches) walk. Empty
matches never persist (FindMatch is EmptyPolicy::Drop), so the count
equals the rendered start < end set.
Sourcepub fn active_find_match(&self) -> Option<u32>
pub fn active_find_match(&self) -> Option<u32>
The active match’s position among the live matches, in document order, if
one is active and still present — consistent with
find_match_count for an “N of M” display.
O(log M) from the active handle’s tracked start: its rank is the number of
finds starting before it, not an O(M) position scan.
Sourcepub fn find_matches_in(
&self,
range: Range<u32>,
) -> impl Iterator<Item = (Range<u32>, bool)> + '_
pub fn find_matches_in( &self, range: Range<u32>, ) -> impl Iterator<Item = (Range<u32>, bool)> + '_
Find matches overlapping the byte range, for highlight rendering:
(span, is_active), in ascending order. Collapsed (empty) matches are
skipped — an edit may zero one before the next re-scan purges it.
Sourcepub fn overview_marks(
&self,
bounds: &[u32],
sev_out: &mut Vec<(u8, u32)>,
find_out: &mut Vec<Option<u32>>,
)
pub fn overview_marks( &self, bounds: &[u32], sev_out: &mut Vec<(u8, u32)>, find_out: &mut Vec<Option<u32>>, )
Fill the scrollbar-overview lanes for the ascending byte-offset bucket
bounds. The widget passes the P + 1 boundaries it gets by inverting its
own round(y) pixel map, so bucket b holds exactly the decorations whose
mark lands on track pixel b. Per bucket b: sev_out[b] = (encoded max Diagnostic severity, byte offset of the first severest one) — (0, 0)
when empty; find_out[b] = the start offset of the first
DecorationKind::FindMatch, or None. Both lanes are the O(P + log M)
summary reduce, never a per-frame whole-store walk.
overview_reduce_equals_linear_scan (decorations.rs) is the correctness
authority.
Sourcepub fn maybe_rescan_find(&mut self, now_ms: u64) -> bool
pub fn maybe_rescan_find(&mut self, now_ms: u64) -> bool
Refill a capped find set, debounced — driven from the widget’s
update() each event. Returns whether it scanned.
The match set is repaired eagerly at every commit, so there is no stale set to rescan; the only remaining job is re-growing a capped set’s coverage after matches inside it died. Anything else is a no-op — idle documents do zero work here.
Sourcepub fn find_next(&mut self, now_ms: u64) -> Option<Range<u32>>
pub fn find_next(&mut self, now_ms: u64) -> Option<Range<u32>>
Select the next find match from the caret, wrapping: sets the selection to
the match (head at end), seals the undo group, and returns the match range
for the widget to reveal (autoscroll). None when there are no matches.
Sourcepub fn find_prev(&mut self, now_ms: u64) -> Option<Range<u32>>
pub fn find_prev(&mut self, now_ms: u64) -> Option<Range<u32>>
Select the previous find match from the caret, wrapping — the
find_next mirror.
Sourcepub fn replace_next(
&mut self,
replacement: &str,
preserve_case: bool,
now_ms: u64,
) -> Option<Range<u32>>
pub fn replace_next( &mut self, replacement: &str, preserve_case: bool, now_ms: u64, ) -> Option<Range<u32>>
Replace the active find match with replacement, then advance to the
next match, wrapping — the Replace verb. Returns the match left selected,
or None when there is nowhere to go.
Replaces only when the newest selection IS the active match — the state
find_next leaves behind. Otherwise it merely
navigates, so the first press selects and the second replaces: text is
never overwritten before it has been shown. One transaction ⇒ one undo
step, and the match set needs no resync — it rides the commit through the
shared view-rebase mover like any other edit.
preserve_case (the replace bar’s AB toggle) re-cases the replacement
to the match it lands on — an ALL-CAPS match takes an upper-cased
replacement, a Capitalized match a capitalized one.
Sourcepub fn replace_all(&mut self, replacement: &str, preserve_case: bool) -> usize
pub fn replace_all(&mut self, replacement: &str, preserve_case: bool) -> usize
Replace every match of the current query with replacement in one
transaction — the Replace All verb. Returns how many were replaced.
One transaction ⇒ one undo step. Matches are disjoint by
construction, so the batch satisfies the engine’s no-overlap rule for
free — TransactionError::Overlap cannot fire here.
Scans the query’s range itself (the whole document, or the
find-in-selection scope) rather than reading the live match set: that set
is capped at FIND_MATCH_CAP and is only a prefix of that range, so
an “all” built from it would silently stop at the cap and leave the tail
untouched. That scan is whole-range — the same class as
set_find_query, and legitimate for the same
reason: a discrete user action, never a keystroke.
Cost, honestly: one EditOp per match, each owning its own copy of
replacement, so a query with millions of hits allocates proportionally
before it commits. Bounded by the match count and paid once per press.
preserve_case (the replace bar’s AB toggle) re-cases each replacement
to the match it lands on — an ALL-CAPS match takes an upper-cased
replacement, a Capitalized match a capitalized one.
Sourcepub fn next_diagnostic(&mut self, forward: bool) -> Option<Range<u32>>
pub fn next_diagnostic(&mut self, forward: bool) -> Option<Range<u32>>
F8 / Shift+F8: select the next/previous diagnostic from the
newest selection’s start, wrapping — the compile-loop navigation:
jump, read the message (hover), fix, F8 again. Selects the
diagnostic’s span, expands any fold hiding it, and bumps the reveal
generation (a jump-class action, like find navigation). None — and
no movement — when the document has no live diagnostics.
Sourcepub fn reveal_seq(&self) -> u64
pub fn reveal_seq(&self) -> u64
The reveal-request generation. The view autoscrolls to the newest selection whenever this changes — the bridge that lets app-driven find navigation scroll a match into view without the widget’s input path.
Source§impl Document
impl Document
Sourcepub fn type_char(&mut self, ch: char)
pub fn type_char(&mut self, ch: char)
Insert ch at every caret, replacing any non-empty selection. Merges
into the current typing run for undo.
Sourcepub fn insert_text(&mut self, text: &str)
pub fn insert_text(&mut self, text: &str)
Insert text (e.g. a paste) at every caret, replacing selections. A
discrete undo step; \r\n|\r is normalized to LF.
Sourcepub fn clipboard_payload(&self) -> (String, bool)
pub fn clipboard_payload(&self) -> (String, bool)
The text Copy/Cut should put on the clipboard, with the is_entire_line
flag the paste side honors. Each non-empty selection contributes its
text, joined by newlines. When every selection is an empty caret,
each caret contributes its ENTIRE line — the one Document::line_unit
rule — including the newline (a final line without a terminator still
exports one so the paste splices cleanly), duplicate lines deduplicated,
and the flag is true. LF-only; the OS-flavor re-expansion and the side
table live in the widget.
Sourcepub fn cut(&mut self)
pub fn cut(&mut self)
Cut’s edit half: cut is copy-then-delete, so this deletes exactly what
Document::clipboard_payload exported — non-empty selections; or,
when EVERY selection is an empty caret (the payload’s whole-line mode,
same gate), each caret’s line_block. In a
mixed set the empty carets reached the clipboard as nothing, so they
delete nothing — cut never destroys text that was not copied. The
final-line block takes its PRECEDING newline while the payload
synthesizes a trailing one: the paste splices a whole line either way,
and no empty tail line is left behind. Overlapping ranges (two carets on
one line) merge; one discrete transaction, caret after each deletion.
Sourcepub fn paste(&mut self, text: &str, entire_line: bool)
pub fn paste(&mut self, text: &str, entire_line: bool)
Paste. Plain: replace each selection, caret after (a discrete step).
entire_line at all-empty carets: insert before each caret’s line start
with no caret placement — the carets rebase past the insertion and
stay put on their own lines, matching mainstream editors.
Sourcepub fn backspace(&mut self)
pub fn backspace(&mut self)
Backspace: delete the selection, or the character before an empty caret (to the previous tab stop when the caret sits in leading whitespace).
Sourcepub fn delete_forward(&mut self)
pub fn delete_forward(&mut self)
Forward delete: delete the selection, or the character after an empty caret (merging the next line when at end of line).
Sourcepub fn delete_word_back(&mut self)
pub fn delete_word_back(&mut self)
Delete the word before each caret — Ctrl+Backspace. A non-empty selection deletes as-is; an empty caret deletes back to the previous word start, or only the newline at column 0 (never newline + previous word). A discrete undo step (word-delete seals the run on both sides).
Two carets inside one word yield overlapping delete ranges, which the transaction engine rejects — so the ranges are merged first (the shared span is deleted once and the carets collapse via the selection merge rule), the one verb that can produce overlap.
Sourcepub fn delete_word_forward(&mut self)
pub fn delete_word_forward(&mut self)
Delete the word after each caret — Ctrl+Delete, the
delete_word_back mirror (overlapping ranges
merged the same way).
Sourcepub fn enter(&mut self)
pub fn enter(&mut self)
Enter: split the line at every caret, carrying the current line’s leading indentation (truncated to the caret column — never duplicated), plus the two brace rules (both computed from raw text at insert time — existing lines are never re-indented, so autoindent cannot fight the user):
- (a) the current line’s text left of the caret, right-trimmed,
ends with
{→ the new line gets one extraindent_unit; - (b) the caret sits exactly between a pair (
{|}) → insert two lines (\n indent+unit \n indent), closer dedented onto its own line, caret at the end of the middle line (one line up from the inserted text’s end, indented one unit deeper than the closer).
Its own undo step (seals before; typing after merges into the run).
Sourcepub fn tab(&mut self)
pub fn tab(&mut self)
Tab: at an empty caret insert spaces to the next indent stop; over a non-empty selection indent every spanned line one level.
Sourcepub fn outdent(&mut self)
pub fn outdent(&mut self)
Shift+Tab: outdent every spanned line by one indent level. Unlike Tab (which types over a single-line selection), Shift+Tab always outdents — carets, single-line selections, and multi-line selections alike; the selection is preserved.
Sourcepub fn delete_line(&mut self)
pub fn delete_line(&mut self)
Delete each selection’s whole-line block (Ctrl+Shift+K) — the one
line_block rule per selection. Overlapping
blocks (two carets on one line) merge; one discrete step, caret after.
Sourcepub fn insert_line(&mut self, down: bool)
pub fn insert_line(&mut self, down: bool)
Open a fresh, indent-carrying line below (down, Ctrl+Enter) or above
(Ctrl+Shift+Enter) each caret’s line — without splitting the line the
caret sat on — and land the caret at the new line’s end. A line below
a block-opening { line gains one indent unit, like Enter at the line’s
end. One discrete transaction.
Sourcepub fn toggle_line_comment(&mut self)
pub fn toggle_line_comment(&mut self)
Toggle the line comment on every line the selections span (Ctrl+/).
If ANY spanned non-blank line is uncommented, comment them all —
prefix + " " inserted at the block’s minimum indent column,
so the markers align — otherwise strip each line’s prefix (and one
following space). Blank lines are skipped, unless EVERY spanned line is
blank (the start-a-comment-here case, which appends the prefix). One
transaction; the selections rebase through it and survive. A no-op until
the app injects a prefix (Document::set_line_comment) — the core
knows no language.