Internal state for the Buffer GenServer.
Holds the gap buffer, file path, dirty flag, and undo/redo stacks.
Undo coalescing
Rapid edits (e.g., AI-driven or fast typing) that arrive within
@undo_coalesce_ms of each other are grouped into a single undo entry.
Instead of pushing a new snapshot for every mutation, the top of the undo
stack is kept and only the document is replaced. This bounds memory usage
under burst editing while preserving correct undo for human-speed edits.
Summary
Types
Buffer type controlling behavior
The source of an edit for undo/redo attribution.
An undo/redo stack entry: the document snapshot, version, and edit source.
Functions
Resets the coalescing timer so the next push_undo/2 will always
create a new undo entry. Call this at undo boundaries (e.g., mode
transitions like leaving insert mode).
Marks the buffer as having unsaved changes (bumps version).
Records the current version as the save point for dirty tracking.
Pushes the current document onto the undo stack and replaces it with
new_buf. Clears the redo stack. The undo stack is capped at
1000 entries.
Pushes an undo entry unconditionally, bypassing time-based coalescing.
Sets the dirty flag based on whether the given version matches the saved version. Used by undo/redo which restore a version from the stack rather than incrementing.
The coalescing window in milliseconds. Edits arriving within this window of the previous undo push are merged into the same undo entry.
Types
@type buffer_type() :: :file | :nofile | :nowrite | :prompt | :terminal
Buffer type controlling behavior:
:file— (default) normal file buffer, supports save/dirty/undo:nofile— no file association, implicitly read-only, no save:nowrite— has a file path for display but cannot save:prompt— like:nofilebut the last line is editable (for agent input, command input):terminal— backed by an external process writing into the buffer
@type edit_source() :: :user | :agent | :lsp | :recovery
The source of an edit for undo/redo attribution.
@type stack_entry() :: {version :: non_neg_integer(), document :: Minga.Buffer.Document.t(), source :: edit_source()}
An undo/redo stack entry: the document snapshot, version, and edit source.
@type t() :: %Minga.Buffer.State{ buffer_type: buffer_type(), consumer_cursors: %{required(atom()) => non_neg_integer()}, decorations: Minga.Core.Decorations.t(), dirty: boolean(), document: Minga.Buffer.Document.t(), edit_log: [{non_neg_integer(), Minga.Buffer.EditDelta.t()}], edit_seq: non_neg_integer(), explicit_options: MapSet.t(atom()), face_overrides: %{required(String.t()) => keyword()}, file_path: String.t() | nil, file_size: non_neg_integer() | nil, filetype: atom(), last_undo_at: integer(), mtime: integer() | nil, name: String.t() | nil, options: %{required(atom()) => term()}, pending_edits: [Minga.Buffer.EditDelta.t()], persistent: boolean(), read_only: boolean(), redo_stack: [stack_entry()], saved_version: non_neg_integer(), swap_dir: String.t() | nil, swap_timer: reference() | nil, undo_stack: [stack_entry()], unlisted: boolean(), version: non_neg_integer() }
Functions
Resets the coalescing timer so the next push_undo/2 will always
create a new undo entry. Call this at undo boundaries (e.g., mode
transitions like leaving insert mode).
Marks the buffer as having unsaved changes (bumps version).
Records the current version as the save point for dirty tracking.
@spec push_undo(t(), Minga.Buffer.Document.t(), edit_source()) :: t()
Pushes the current document onto the undo stack and replaces it with
new_buf. Clears the redo stack. The undo stack is capped at
1000 entries.
If the previous undo push happened within 300ms, the new document replaces the current one without pushing another snapshot onto the stack. This coalesces rapid edits (AI, fast typing) into a single undo step while preserving distinct undo entries for edits separated by a pause.
@spec push_undo_force(t(), Minga.Buffer.Document.t(), edit_source()) :: t()
Pushes an undo entry unconditionally, bypassing time-based coalescing.
Use this for explicit actions (like :replace_content, agent batch edits)
where each invocation should always be a separate undo step regardless of
timing.
Sets the dirty flag based on whether the given version matches the saved version. Used by undo/redo which restore a version from the stack rather than incrementing.
@spec undo_coalesce_ms() :: pos_integer()
The coalescing window in milliseconds. Edits arriving within this window of the previous undo push are merged into the same undo entry.