Minga.Buffer (Minga v0.1.0)

Copy Markdown View Source

Domain facade for buffer operations.

This is the only valid entry point for code outside the buffer domain. All buffer operations go through this module: reading content, moving cursors, editing text, persisting files, and managing decorations.

Internally delegates to Minga.Buffer.Process (GenServer) and Minga.Buffer.Document (pure data structure). External callers should never reference those modules directly.

Summary

Functions

Accept content as the saved base revision and clear dirty state.

Acknowledge the current disk metadata after keeping local edits during a file-change conflict.

Add a block decoration (multi-line annotation) anchored to a line.

Add virtual text anchored to a line.

Append text to the end of the buffer.

Replace text in a range with new text (the general-purpose edit operation).

Apply a batch of edits atomically (for LSP workspace edits).

Apply a cursor motion inside the buffer process.

Apply a batch of decoration changes atomically.

Force the next edit to start a new undo group.

Logical name of the buffer (e.g., *Messages*), or nil for file buffers.

Buffer kind: :file, :scratch, or :special.

Byte offset of the start of a line (for tree-sitter integration).

Supervisor child spec for starting a buffer process.

Remove a face override for this buffer.

Clear the contents of a single line.

Commit a previously captured document snapshot.

Consume edit deltas accumulated since the given consumer's last read, or return :reset_required when the consumer must full-sync.

Full text content of the buffer.

Content and cursor position in a single call (avoids two round-trips).

Content of lines from start_line to end_line (inclusive, 0-indexed).

Grapheme count between two positions (end inclusive, includes the character at end_pos).

Current cursor position as {line, col}.

Current decoration state (highlights, virtual text, folds, etc.).

Version counter for decorations (for change detection).

Delete the character at the cursor.

Delete the character before the cursor.

Delete lines from start_line to end_line (inclusive).

Delete text between two positions.

Whether the buffer has unsaved changes.

Human-readable display name (file basename or buffer name).

Returns the pid for a buffer at path, starting one if it doesn't exist.

Current face override map.

File path of the buffer, or nil for scratch buffers.

Detected filetype (e.g., :elixir, :json).

Find and replace the first occurrence of old_text with new_text.

Find and replace multiple patterns atomically.

Save even if the buffer appears clean.

Read a per-buffer option (falls back to global config).

Insert a multi-character string at the cursor.

Number of lines in the buffer.

A range of lines starting at start (0-indexed), returning count lines.

Returns only options explicitly overridden on this buffer.

Returns all buffer option values currently cached on this buffer.

Move the cursor one step in a direction.

Move cursor if possible, returning the result position and whether a boundary was hit.

Move the cursor to an exact position.

Open a file into the buffer, replacing current content.

Whether the buffer survives tab close (e.g., *Messages*).

Look up a buffer process by its file path. Returns :not_found if no buffer has that file open.

Whether the buffer is read-only.

Redo the last undone edit.

Reload content from disk, discarding unsaved changes.

Override a face's attributes for this buffer.

Remove a block decoration by ID.

Remove a highlight by ID.

Remove all highlights in a group.

Remove virtual text by ID.

Render-ready snapshot of visible lines for the rendering pipeline.

Replace the entire buffer content.

Replace buffer content and apply decorations in one atomic operation.

Replace generated/internal content, bypassing user read-only restrictions.

Retargets the buffer to a new file path without writing content.

Save the buffer to disk.

Saves all dirty file-backed buffers to disk.

Save the buffer to a new file path.

Override the detected filetype.

Set a per-buffer option override.

Capture a snapshot of the document state (for undo boundaries and tab switching).

Start a new buffer process.

Text between two positions (end inclusive, includes the character at end_pos).

Undo the last edit.

Undo all consecutive agent-sourced entries from the top of the undo stack.

Whether the buffer is hidden from buffer lists.

Monotonic version counter (incremented on every content change).

Types

boundary()

@type boundary() :: Minga.Buffer.Process.boundary()

direction()

@type direction() :: :left | :right | :up | :down

document()

@type document() :: Minga.Buffer.Document.t()

motion_fun()

@type motion_fun() :: Minga.Buffer.Process.motion_fun()

position()

@type position() :: {line :: non_neg_integer(), col :: non_neg_integer()}

replace_edit()

@type replace_edit() :: Minga.Buffer.Process.replace_edit()

replace_result()

@type replace_result() :: Minga.Buffer.Process.replace_result()

server()

@type server() :: t()

t()

@type t() :: GenServer.server()

text_edit()

@type text_edit() :: Minga.Buffer.Process.text_edit()

Functions

accept_saved_content(server, new_content)

@spec accept_saved_content(t(), String.t()) :: :ok

Accept content as the saved base revision and clear dirty state.

acknowledge_disk_change(server)

@spec acknowledge_disk_change(t()) :: :ok

Acknowledge the current disk metadata after keeping local edits during a file-change conflict.

add_block_decoration(server, anchor_line, opts)

@spec add_block_decoration(t(), non_neg_integer(), keyword()) :: reference()

Add a block decoration (multi-line annotation) anchored to a line.

add_highlight(server, start_pos, end_pos, opts)

Add a highlight range.

add_virtual_text(server, anchor, opts)

@spec add_virtual_text(t(), Minga.Core.Decorations.highlight_range_pos(), keyword()) ::
  reference()

Add virtual text anchored to a line.

append(server, text)

@spec append(t(), String.t()) :: :ok

Append text to the end of the buffer.

apply_edit(server, start_line, start_col, end_line, end_col, new_text, source \\ Minga.Buffer.EditSource.user())

Replace text in a range with new text (the general-purpose edit operation).

apply_edits(server, edits, source \\ Minga.Buffer.EditSource.lsp(:unknown))

@spec apply_edits(t(), [text_edit()], Minga.Buffer.EditSource.t()) :: :ok

Apply a batch of edits atomically (for LSP workspace edits).

apply_motion(server, motion_fn)

@spec apply_motion(t(), motion_fun()) :: :ok

Apply a cursor motion inside the buffer process.

batch_decorations(server, fun)

@spec batch_decorations(t(), (Minga.Core.Decorations.t() ->
                          Minga.Core.Decorations.t())) :: :ok

Apply a batch of decoration changes atomically.

break_undo_coalescing(server)

@spec break_undo_coalescing(t()) :: :ok

Force the next edit to start a new undo group.

buffer_name(server)

@spec buffer_name(t()) :: String.t() | nil

Logical name of the buffer (e.g., *Messages*), or nil for file buffers.

buffer_type(server)

@spec buffer_type(t()) :: :file | :scratch | :special

Buffer kind: :file, :scratch, or :special.

byte_offset_for_line(server, line)

@spec byte_offset_for_line(t(), non_neg_integer()) :: non_neg_integer()

Byte offset of the start of a line (for tree-sitter integration).

child_spec(opts)

Supervisor child spec for starting a buffer process.

clear_face_override(server, face_name)

@spec clear_face_override(t(), String.t()) :: :ok

Remove a face override for this buffer.

clear_line(server, line)

@spec clear_line(t(), non_neg_integer()) :: {:ok, String.t()}

Clear the contents of a single line.

commit_snapshot(server, new_buf)

@spec commit_snapshot(t(), document()) :: :ok

Commit a previously captured document snapshot.

consume_edit_deltas(server, consumer_id)

@spec consume_edit_deltas(t(), atom()) :: Minga.Buffer.Process.edit_delta_update()

Consume edit deltas accumulated since the given consumer's last read, or return :reset_required when the consumer must full-sync.

content(server)

@spec content(t()) :: String.t()

Full text content of the buffer.

content_and_cursor(server)

@spec content_and_cursor(t()) :: {String.t(), position()}

Content and cursor position in a single call (avoids two round-trips).

content_on_lines(server, start_line, end_line)

@spec content_on_lines(t(), non_neg_integer(), non_neg_integer()) :: String.t()

Content of lines from start_line to end_line (inclusive, 0-indexed).

content_range_length(server, start_pos, end_pos)

@spec content_range_length(t(), position(), position()) :: non_neg_integer()

Grapheme count between two positions (end inclusive, includes the character at end_pos).

cursor(server)

@spec cursor(t()) :: position()

Current cursor position as {line, col}.

decorations(server)

@spec decorations(t()) :: Minga.Core.Decorations.t()

Current decoration state (highlights, virtual text, folds, etc.).

decorations_version(server)

@spec decorations_version(t()) :: non_neg_integer()

Version counter for decorations (for change detection).

delete_at(server)

@spec delete_at(t()) :: :ok

Delete the character at the cursor.

delete_before(server)

@spec delete_before(t()) :: :ok

Delete the character before the cursor.

delete_lines(server, start_line, end_line)

@spec delete_lines(t(), non_neg_integer(), non_neg_integer()) :: :ok

Delete lines from start_line to end_line (inclusive).

delete_range(server, from_pos, to_pos)

@spec delete_range(t(), position(), position()) :: :ok

Delete text between two positions.

dirty?(server)

@spec dirty?(t()) :: boolean()

Whether the buffer has unsaved changes.

display_name(server)

@spec display_name(t()) :: String.t()

Human-readable display name (file basename or buffer name).

ensure_for_path(path, events_registry \\ Minga.Events.default_registry(), opts \\ [])

@spec ensure_for_path(String.t(), Minga.Events.registry(), keyword()) ::
  {:ok, pid()} | {:error, term()}

Returns the pid for a buffer at path, starting one if it doesn't exist.

If a buffer is already registered for path, returns its pid immediately. Otherwise, starts a new buffer under Minga.Buffer.Supervisor and broadcasts a :buffer_opened event so the Editor and other subscribers can pick it up.

Used by agent tools to guarantee every edited file has a buffer with undo integration, without depending on the Editor (Layer 2).

face_overrides(server)

@spec face_overrides(t()) :: %{required(String.t()) => keyword()}

Current face override map.

file_path(server)

@spec file_path(t()) :: String.t() | nil

File path of the buffer, or nil for scratch buffers.

filetype(server)

@spec filetype(t()) :: atom()

Detected filetype (e.g., :elixir, :json).

find_and_replace(server, old_text, new_text, boundary \\ nil)

@spec find_and_replace(t(), String.t(), String.t(), boundary()) ::
  {:ok, String.t()} | {:error, String.t()}

Find and replace the first occurrence of old_text with new_text.

find_and_replace_batch(server, edits, boundary \\ nil)

@spec find_and_replace_batch(t(), [replace_edit()], boundary()) ::
  {:ok, [replace_result()]} | {:error, String.t()}

Find and replace multiple patterns atomically.

force_save(server)

@spec force_save(t()) :: :ok | {:error, term()}

Save even if the buffer appears clean.

get_option(server, name)

@spec get_option(t(), atom()) :: term()

Read a per-buffer option (falls back to global config).

insert_char(server, char, source \\ Minga.Buffer.EditSource.user())

@spec insert_char(t(), String.t(), Minga.Buffer.EditSource.t()) :: :ok

Insert a single character at the cursor.

insert_text(server, text, source \\ Minga.Buffer.EditSource.user())

@spec insert_text(t(), String.t(), Minga.Buffer.EditSource.t()) :: :ok

Insert a multi-character string at the cursor.

line_count(server)

@spec line_count(t()) :: pos_integer()

Number of lines in the buffer.

lines(server, start, count)

@spec lines(t(), non_neg_integer(), non_neg_integer()) :: [String.t()]

A range of lines starting at start (0-indexed), returning count lines.

local_option_overrides(server)

@spec local_option_overrides(t()) :: %{required(atom()) => term()}

Returns only options explicitly overridden on this buffer.

local_options(server)

@spec local_options(t()) :: %{required(atom()) => term()}

Returns all buffer option values currently cached on this buffer.

move(server, direction)

@spec move(t(), direction()) :: :ok

Move the cursor one step in a direction.

move_if_possible(server, direction)

@spec move_if_possible(t(), :left | :right) ::
  {:ok, position()} | {:at_boundary, position()}

Move cursor if possible, returning the result position and whether a boundary was hit.

move_to(server, pos)

@spec move_to(t(), position()) :: :ok

Move the cursor to an exact position.

open(server, file_path)

@spec open(t(), String.t()) :: :ok | {:error, term()}

Open a file into the buffer, replacing current content.

persistent?(server)

@spec persistent?(t()) :: boolean()

Whether the buffer survives tab close (e.g., *Messages*).

pid_for_path(path)

@spec pid_for_path(String.t()) :: {:ok, pid()} | :not_found

Look up a buffer process by its file path. Returns :not_found if no buffer has that file open.

read_only?(server)

@spec read_only?(t()) :: boolean()

Whether the buffer is read-only.

redo(server)

@spec redo(t()) :: :ok | :empty

Redo the last undone edit.

reload(server)

@spec reload(t()) :: :ok | {:error, term()}

Reload content from disk, discarding unsaved changes.

remap_face(server, face_name, attrs)

@spec remap_face(t(), String.t(), keyword()) :: :ok

Override a face's attributes for this buffer.

remove_block_decoration(server, id)

@spec remove_block_decoration(t(), reference()) :: :ok

Remove a block decoration by ID.

remove_highlight(server, id)

@spec remove_highlight(t(), reference()) :: :ok

Remove a highlight by ID.

remove_highlight_group(server, group)

@spec remove_highlight_group(t(), atom()) :: :ok

Remove all highlights in a group.

remove_virtual_text(server, id)

@spec remove_virtual_text(t(), reference()) :: :ok

Remove virtual text by ID.

render_snapshot(server, first_line, count)

@spec render_snapshot(t(), non_neg_integer(), non_neg_integer()) ::
  Minga.Buffer.RenderSnapshot.t()

Render-ready snapshot of visible lines for the rendering pipeline.

replace_content(server, new_content, source \\ :user)

@spec replace_content(t(), String.t(), Minga.Buffer.State.edit_source()) ::
  :ok | {:error, term()}

Replace the entire buffer content.

replace_content_with_decorations(server, content, decoration_fn, opts \\ [])

@spec replace_content_with_decorations(t(), String.t(), function(), keyword()) :: :ok

Replace buffer content and apply decorations in one atomic operation.

replace_generated_content(server, new_content)

@spec replace_generated_content(t(), String.t()) :: :ok

Replace generated/internal content, bypassing user read-only restrictions.

retarget_path(server, file_path)

@spec retarget_path(t(), String.t()) :: :ok | {:error, term()}

Retargets the buffer to a new file path without writing content.

save(server)

@spec save(t()) :: :ok | {:error, term()}

Save the buffer to disk.

save_all_dirty()

@spec save_all_dirty() :: {non_neg_integer(), [String.t()]}

Saves all dirty file-backed buffers to disk.

Enumerates the Buffer.Registry, filters for dirty buffers, and saves each one. Read-only buffers and save failures are logged as warnings but do not block other saves. Returns the count of successfully saved buffers and a list of any warnings (path + error reason).

save_as(server, file_path)

@spec save_as(t(), String.t()) :: :ok | {:error, term()}

Save the buffer to a new file path.

set_filetype(server, filetype)

@spec set_filetype(t(), atom()) :: :ok

Override the detected filetype.

set_option(server, name, value)

@spec set_option(t(), atom(), term()) :: {:ok, term()} | {:error, String.t()}

Set a per-buffer option override.

snapshot(server)

@spec snapshot(t()) :: document()

Capture a snapshot of the document state (for undo boundaries and tab switching).

start_link(opts \\ [])

Start a new buffer process.

storage(server)

@spec storage(t()) :: Minga.Buffer.State.storage()

See Minga.Buffer.Process.storage/1.

text_between_inclusive(server, start_pos, end_pos)

@spec text_between_inclusive(t(), position(), position()) :: String.t()

Text between two positions (end inclusive, includes the character at end_pos).

undo(server)

@spec undo(t()) :: :ok | :empty

Undo the last edit.

undo_agent_session(server)

@spec undo_agent_session(t()) :: {:ok, non_neg_integer()} | :empty

Undo all consecutive agent-sourced entries from the top of the undo stack.

unlisted?(server)

@spec unlisted?(t()) :: boolean()

Whether the buffer is hidden from buffer lists.

version(server)

@spec version(t()) :: non_neg_integer()

Monotonic version counter (incremented on every content change).