Skip to main content

Document

Struct Document 

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

An editable document: a Buffer plus the bookkeeping every edit needs — a process-unique [DocumentId], an optional file path, version counters, the [SelectionSet], the UndoStack and dirty tracking.

All text mutation goes through Document::apply_edit / Document::apply_edit_batch (or Document::undo / Document::redo); each one records its inverse for undo, carries the selections across the change, and bumps both Document::version and Document::text_version. Positions are the protocol’s (line, byte-within-line) pairs, both 0-based — see Buffer.

A Document has no interior mutability and no locking; the editor keeps it behind a single writer (the core actor). It knows nothing about syntax, modes, LSP or rendering.

§Examples

Edit, undo, redo and dirty tracking:

use lattice_core::Document;
use lattice_core::protocol::edit::Edit;
use lattice_core::protocol::position::Position;

let mut doc = Document::from_text("hello");
assert!(!doc.dirty());

doc.apply_edit(Edit::insert(Position::new(0, 5), " world"))?;
assert_eq!(doc.text(), "hello world");
assert!(doc.dirty());

doc.undo()?;
assert_eq!(doc.text(), "hello");
assert!(!doc.dirty()); // back at the loaded state

doc.redo()?;
assert_eq!(doc.text(), "hello world");

Coalescing a whole insert session into one undo step:

use lattice_core::Document;
use lattice_core::protocol::edit::Edit;
use lattice_core::protocol::position::Position;

let mut doc = Document::empty();
doc.begin_undo_group(); // `i`
doc.apply_edit(Edit::insert(Position::new(0, 0), "a"))?;
doc.apply_edit(Edit::insert(Position::new(0, 1), "b"))?;
doc.apply_edit(Edit::insert(Position::new(0, 2), "c"))?;
doc.end_undo_group(); // `<Esc>`

doc.undo()?; // one `u` removes the whole session
assert_eq!(doc.text(), "");

Implementations§

Source§

impl Document

Source

pub fn empty() -> Self

An empty, pathless, clean document.

Source

pub fn from_text(text: impl Into<String>) -> Self

A pathless document holding text. It starts clean: text is treated as the saved state.

Source

pub fn from_buffer(buffer: Buffer) -> Self

Construct a Document around a pre-built Buffer. Sister of Self::from_text for callers that already hold a Rope-backed Buffer and want to avoid the as_string() → from_text(&str) round-trip. See DocumentBuilder::with_buffer for the rationale + the K.4.11 consumer.

Slice: K.4.11.perf-fix (2026-06-02).

Source

pub fn open(path: impl AsRef<Path>) -> CoreResult<Self>

Read path from disk into a new clean document whose path is set.

Blocking I/O — never call it on the UI thread.

§Errors

CoreError::Io if the file cannot be read, including when it is not valid UTF-8. A missing file is an error here; use Self::open_or_new for vim’s :e newfile behaviour.

Source

pub fn open_or_new(path: impl AsRef<Path>) -> CoreResult<(Self, bool)>

Open path, or — when nothing is there yet — start an empty, unmodified document that will create it on the first save.

vim’s :e newfile (checked in 9.2: an empty buffer, &modified 0, and :w creates the file). Only NotFound becomes a new document; any other read failure — permissions, a non-UTF-8 file — is still an error, because opening an empty buffer over an unreadable file would let the first save overwrite it.

Returns whether the document is new, so a caller can tell a fresh file from an existing one without a second stat.

Source

pub fn id(&self) -> DocumentId

The process-unique id assigned at construction. Never reused within a process; not stable across restarts.

Source

pub fn path(&self) -> Option<&Path>

The file this document is backed by, or None for a scratch / synthetic document that has never been saved.

Source

pub fn set_path_shared(&mut self, path: Arc<PathBuf>)

Adopt a path that is already shared. Does NOT touch the filesystem — Document::save_as is the one that writes.

For a throwaway document assembled around an existing buffer, where the path came from the snapshot that buffer was cloned from (the host’s plugin-action gate, OM.6b). An Arc bump, so it costs the same as not carrying the path at all.

Source

pub fn path_shared(&self) -> Option<Arc<PathBuf>>

The path as a shared handle, for readers that must carry it out of the borrow — a published DocumentSnapshot, a grammar dispatch context. An Arc bump, never an allocation. Prefer Document::path when a borrow will do.

Source

pub fn version(&self) -> u64

Monotonic change counter: bumps on every edit, batch, undo, redo and Self::set_selections. Use it to detect “anything changed”; use Self::text_version when only text matters.

Source

pub fn text_version(&self) -> u64

Monotonic counter that bumps only when the text changes (edits, batches, undo, redo) — not on selection changes or saves. The syntax cache keys reparses on it.

Source

pub fn dirty(&self) -> bool

Whether the text differs from the last saved (or initially loaded) state, as judged by undo depth.

Undoing back to the saved state makes the document clean again. Once an edit discards the redo entries that led back to the saved state, no clean state is reachable and the document stays dirty until the next Self::save / Self::save_as.

Source

pub fn buffer(&self) -> &Buffer

The underlying text. Read-only: mutation must go through the document so undo, versions and selections stay consistent.

Source

pub fn selections(&self) -> &SelectionSet

The current selections (a single cursor at the origin for a fresh document). Edits transform them automatically.

Source

pub fn set_selections(&mut self, selections: SelectionSet)

Replace the selections. Bumps Self::version but not Self::text_version. Positions are not validated against the buffer.

Source

pub fn text(&self) -> String

The whole text as a String — an O(n) allocation; see Buffer::as_string for the cheaper alternatives.

Source

pub fn apply_edit(&mut self, edit: Edit) -> CoreResult<AppliedEdit>

Apply an edit, push its inverse onto the undo stack, bump the version. Returns a structural description of what changed (suitable for Event::DocumentChanged). Transforms selections across the edit so the caret survives owner writes (§4 of owner-write-caret.md).

Inside an open Self::begin_undo_group the inverse folds into the group’s entry instead of pushing a new one. Pushing a new entry clears the redo stack.

§Errors

Those of Buffer::apply_edit; the document is unchanged on error.

Source

pub fn apply_edit_batch( &mut self, edits: Vec<Edit>, ) -> CoreResult<Vec<AppliedEdit>>

Apply a batch of edits as a single undoable unit. Edits are applied in order; undo reverts them all. Transforms selections across each edit so the caret survives owner writes (§4 of owner-write-caret.md).

Each edit’s range is in the coordinates produced by the edits before it, not the pre-batch text. Versions bump once for the whole batch.

§Errors

Those of Buffer::apply_edit. Not atomic: edits before the failing one stay applied, and — because the undo entry is recorded only after the loop — they are not on the undo stack and versions do not bump. Callers must hand in a batch that is valid in sequence.

§Examples
use lattice_core::Document;
use lattice_core::protocol::edit::Edit;
use lattice_core::protocol::position::{Position, Range};

let mut doc = Document::from_text("one two");
doc.apply_edit_batch(vec![
    // Delete "one " …
    Edit::delete(Range::new(Position::new(0, 0), Position::new(0, 4))),
    // … so "two" now starts at byte 0.
    Edit::insert(Position::new(0, 3), "!"),
])?;
assert_eq!(doc.text(), "two!");

doc.undo()?; // the batch is one undo unit
assert_eq!(doc.text(), "one two");
Source

pub fn begin_undo_group(&mut self)

Open an undo-coalescing group: every edit applied until Self::end_undo_group folds into a single undo entry rather than pushing its own. This is how a vim insert session (i/a/o/cw .. <Esc>) becomes one u step – typed characters, in-session backspaces, and completion inserts all collapse together. Re-opening an already-open group starts a fresh coalescing run (the next edit pushes a new entry).

Source

pub fn end_undo_group(&mut self)

Close the group opened by Self::begin_undo_group. Subsequent edits push their own entries again. Idempotent when no group is open.

Source

pub fn undo(&mut self) -> CoreResult<Vec<AppliedEdit>>

Revert the most recent undo entry and move it onto the redo stack. Returns the edits as applied to the buffer, in application order, so callers can emit change events. Bumps both versions.

§Errors

CoreError::NothingToUndo if the undo stack is empty.

Source

pub fn redo(&mut self) -> CoreResult<Vec<AppliedEdit>>

Re-apply the most recently undone entry and move it back onto the undo stack. The mirror of Self::undo.

§Errors

CoreError::NothingToRedo if nothing has been undone since the last new edit.

Source

pub fn save(&mut self) -> CoreResult<&Path>

Persist to the document’s path. Errors if no path is set.

Writes the text verbatim (blocking I/O) and marks the current undo depth as clean. Returns the path written.

§Errors

CoreError::NoPath for a pathless document; CoreError::Io if the write fails (the document stays dirty).

Source

pub fn save_as(&mut self, path: impl Into<PathBuf>) -> CoreResult<()>

Write the text to path, then adopt path as the document’s path and mark it clean. On a write error neither the path nor the clean state changes.

§Errors

CoreError::Io if the write fails.

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.