Skip to main content

Document

Struct Document 

Source
pub struct Document { /* private fields */ }
Expand description

A text document: buffer + undo history + selections + every derived view.

Implementations§

Source§

impl Document

Source

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.

Source

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.

Source

pub fn reveal_mode(&self) -> RevealMode

The RevealMode of the pending reveal request.

Source

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.

Source

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).

Source

pub fn line_comment(&self) -> Option<&str>

The injected line-comment prefix, if any.

Source

pub fn buffer(&self) -> &Buffer

Read-only access to the underlying buffer (text, lines, coordinates, snapshots).

Source

pub fn selections(&self) -> &SelectionSet

The current selection set (carets and ranges).

Source

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.

Source

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.

Source

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.

Source

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.

Source

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).

Source

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.

Source

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.

Source

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.)

Source

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.

Source

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.

Source

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.

Source

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.

Source

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.

Source

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.

Source

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.

Source

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.

Source

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.

Source

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.

Source

pub fn revision(&self) -> Revision

The current revision.

Source

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.

Source

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.

Source

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.

Source

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.

Source

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.

Source

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.

Source

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.

Source

pub fn is_dirty(&self) -> bool

Whether the document differs from the last Document::mark_saved.

Source

pub fn mark_saved(&mut self)

Record the current state as saved (clears the dirty flag).

Source

pub fn can_undo(&self) -> bool

Whether there is an edit to undo.

Source

pub fn can_redo(&self) -> bool

Whether there is an edit to redo along the current branch.

Source

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.

Source

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.

Source

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.

Source

pub fn undo_depth(&self) -> usize

How many undo steps are currently reachable — the depth of undo (bounded by set_undo_limit when set).

Source

pub fn serialize(&self, flavor: EolFlavor) -> String

Serialize for saving, re-expanding LF to the given flavor.

Source

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.

Source

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.

Source

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.

Source

pub fn decorations_mut(&mut self) -> &mut DecorationStore

Mutable decorations for a decoration owner.

Source

pub fn folds(&self) -> &FoldSet

The active code folds — read access for building a per-frame FoldMap.

Source

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).

Source

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.

Source

pub fn unfold(&mut self, header: BufferRow) -> bool

Unfold the fold whose header (opener row) is header. Returns whether one was removed.

Source

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).

Source

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).

Source

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).

Source

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.

Source

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.

Source

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).

Source

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.

Source

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.

Source

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).

Source

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.

Source

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.

Source

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).

Source

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.

Source

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).

Source

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.

Source

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).

Source

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.

Source

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_viewson_commit splices like every other derived fact — only in-flight results are dropped by the revision check.

Source

pub fn brackets(&self) -> &Brackets

The matched brackets — kept current on every edit. Drives bracket-pair colorization and the matching-bracket highlight.

Source

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.

Source

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.

Source

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.

Source

pub fn find_query(&self) -> Option<&FindQuery>

The active find query, or None if find is idle.

Source

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.

Source

pub fn find_scope(&self) -> Option<Range<u32>>

The live find-in-selection scope, if any — for the view to shade it.

Source

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.

Source

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.

Source

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.

Source

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.

Source

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.

Source

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.

Source

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.

Source

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.

Source

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.

Source

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.

Source

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.

Source

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.

Source

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

Source

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.

Source

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.

Source

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.

Source

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.

Source

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.

Source

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).

Source

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).

Source

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.

Source

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).

Source

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 extra indent_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).

Source

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.

Source

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.

Source

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.

Source

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.

Source

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.

Trait Implementations§

Source§

impl Debug for Document

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.