MingaEditor.State (Minga v0.1.0)

Copy Markdown View Source

Internal state for the Editor GenServer.

Field categories

EditorState fields fall into three categories:

Workspace fields live in state.workspace (MingaEditor.Session.State) and are saved/restored when switching tabs. Each tab carries a snapshot of the workspace so switching tabs restores the full editing context.

Shell fields live in state.shell_state and hold presentation concerns: chrome, overlays, transient UI state. The active shell id is state.shell_id; state.shell is the registered module cached for hot-path compatibility. See MingaEditor.Shell for the behaviour definition.

Global fields are shared across all tabs and never snapshotted: port_manager, theme, render_timer, focus_stack, capabilities.

Composed sub-structs

Summary

Types

Per-lane serial gate for MingaEditor.AsyncAction: the in-flight operation's token (or nil when idle) and a FIFO queue of pending zero-arity work funcs.

Display metadata derived from the active window's content type.

Metadata for an open diff view buffer.

A document highlight range from the LSP server.

Event bus registry used by this editor instance.

The git_remote_op tracking tuple, or nil when no operation is in flight.

Re-export of Minga.Keymap.server/0 for editor-state callers.

Line number display style.

Re-export of Minga.Config.Options.server/0 for editor-state callers.

t()

Functions

Returns the active buffer index.

Returns display metadata for the active window's content.

Returns the active shell id, preserving compatibility with tests that still set only :shell.

Returns the active shell module, refreshing from the registry when possible.

Returns the active window struct, or nil if windows aren't initialized.

Adds a new buffer and makes it the active buffer for the current window.

Pure variant of add_buffer/2. Returns {state, effects} instead of performing side effects directly.

Returns the agent stream-ingest coalescer pid, or nil if not started.

Applies render pipeline mutations back to the editor state.

Applies asynchronous renderer writeback without overwriting editor-owned state.

Switches the active editor theme and re-colors existing syntax highlights.

Returns the active buffer pid.

Returns the buffer list.

Builds a complete per-tab context for an agent tab.

Builds a fresh agent-shaped workspace context for shell-owned agent surfaces.

Pure variant of remove_dead_buffer/2. Returns {state, effects} instead of performing side effects directly.

Returns the viewport for the user's current focus: the active window's viewport when a window is active, otherwise a derived terminal-size viewport (the no-window fallback case). Use this for scroll commands that read/write the focused window's viewport.

Removes the async-action gate state for lane, marking it idle.

Deletes an active workspace LSP pending request.

Dismisses a GUI notification by id.

Dismisses a GUI notification only when the auto-dismiss ref still matches.

Drops extension-owned feature state from live and snapshotted workspaces.

Drops one source-owned feature state entry from the active workspace.

Drops all feature state owned by a source from live and snapshotted workspaces.

Drops the active workspace FileTree feature state.

Ensures the active shell still exists, falling back to the registry default if it was unregistered.

Returns the event bus registry used by this editor instance.

Returns the active workspace FileTree feature state.

Finds the agent chat window in the windows map.

Returns the index of the buffer whose file path matches file_path, or nil.

Switches focus to the given window, saving the current cursor to the outgoing window and restoring the target window's stored cursor.

Returns the async-action gate state for lane (idle default when absent).

Returns source-owned feature state from the active workspace, or nil when inactive.

Returns the cached native settings snapshot emitted in-frame (#2119).

Invalidates render caches for all windows.

Returns the keymap context keyword list passed to scoped key resolution.

Returns the keymap server used for scope and binding lookups.

Returns the set of buffer pids known to the live workspace and tab snapshots.

Monitors a buffer pid so the Editor receives :DOWN when it dies.

Monitors a list of buffer pids. Convenience wrapper around monitor_buffer/2.

Looks up an inline notification action.

Returns the options server used for typed option lookups.

Stores the async-action gate state for lane.

Stores source-owned feature state on the active workspace.

Stores the cached native settings snapshot emitted in-frame (#2119).

Adds or replaces an active workspace LSP pending request.

Rebinds the logical file identity for matching tabs and their workspaces.

Rebuilds the agent rendering cache from the Session process when switching to an agent tab. The Session is the source of truth for status, pending approval, and error; the cache lives on state.shell_state.agent and is repopulated from the Tab's session pid on every tab switch.

Removes a dead buffer pid from all state locations.

Clears frontend-retained render state after a frontend ready/recovery event.

Writes a tab context back into the live editor state.

Returns the appropriate keymap scope for the active window's content type.

Derives the keymap scope from a window's content type.

Returns the screen rect for layout computation, excluding the global minibuffer row and reserving space for the file tree panel when open.

Scrolls the agent chat window's viewport by delta lines and updates pinned state. Delegates to Window.scroll_viewport/3.

Stores the agent stream-ingest coalescer pid (see MingaEditor.Agent.Ingest).

Replaces the active workspace agent UI state.

Sets the context for the next add_buffer call.

Replaces the active workspace buffer state.

Replaces the vim change recorder.

Replaces the active workspace dired state.

Replaces the active workspace document highlights.

Replaces the active workspace editing state.

Replaces the active workspace FileTree feature state.

Replaces the active workspace highlighting state.

Replaces the active workspace injection ranges map.

Sets the active workspace keymap scope.

Records the last find-char motion.

Records the cursor position before a jump.

Replaces the active workspace LSP pending request map.

Replaces the vim macro recorder.

Replaces the vim marks map.

Replaces the current vim mode state without changing the mode.

Replaces the active workspace mouse state.

Replaces the vim register state.

Updates the current frontend-reported resource pressure.

Replaces the active workspace search state.

Stores a new terminal viewport. Called by the editor's resize handler when the frontend reports a new screen size.

Sets the active workspace viewport.

Replaces the active workspace window state.

Replaces the active workspace.

Returns the active sidebar registry table for this state.

Returns true if the given tool should NOT be prompted for installation.

Captures the current per-tab fields into a context struct.

Returns true if the editor has more than one window.

Starts a new buffer under the buffer supervisor for the given file path.

Switches to the buffer at idx, making it active for the current window.

Switches to a registered shell by id, stashing the current shell state by shell id.

Switches to the tab with target_id.

Pure variant of switch_tab/2. Returns {state, effects} instead of performing side effects directly.

Syncs the active window's buffer reference with state.workspace.buffers.active.

Snapshots the active buffer's cursor into the active window struct.

Syncs the live workspace agent UI mirror from the active workspace.

Returns the terminal-level viewport: total screen rows/cols reported by the frontend on resize. Used for screen-spanning chrome (picker, popups, completion menu placement) and for layout computation that needs the editor's full canvas.

Transitions the editor to a new vim mode.

Returns the screen rect for the file tree panel, or nil if closed.

Updates the active workspace buffer state.

Updates the active window's viewport. No-op when no window is active (the no-window fallback case has no per-window viewport to write).

Updates the active workspace dired state.

Updates the active workspace editing state.

Updates source-owned feature state on the active workspace.

Updates the active workspace FileTree feature state.

Updates the active workspace highlighting state.

Updates the active workspace injection ranges map.

Updates the current vim mode state without changing the mode.

Updates the active workspace mouse state.

Applies a function to remote session state.

Updates the active workspace search state.

Applies a function to the shell state and returns the updated state.

Updates the window struct for the given window id via a mapper function.

Updates the active workspace window state.

Updates every window showing a buffer via a mapper function.

Applies a function to the workspace and returns the updated state.

Adds or updates a GUI notification.

Types

async_lane()

@type async_lane() :: %{running: reference() | nil, queue: [(-> term())]}

Per-lane serial gate for MingaEditor.AsyncAction: the in-flight operation's token (or nil when idle) and a FIFO queue of pending zero-arity work funcs.

backend()

@type backend() :: :tui | :gui | :native_gui | :headless

content_context()

@type content_context() :: %{
  type: :buffer | :agent,
  display_name: String.t(),
  directory: String.t(),
  dirty: boolean(),
  filetype: atom()
}

Display metadata derived from the active window's content type.

Used by title, modeline, and any other subsystem that needs to answer "what is the user looking at?" without assuming a buffer is active.

diff_view_info()

@type diff_view_info() :: %{
  :source_buf => pid() | nil,
  :git_root => String.t(),
  :rel_path => String.t(),
  :staged => boolean(),
  :line_metadata => [Minga.Core.DiffView.line_meta()],
  :hunk_lines => [non_neg_integer()],
  optional(:view_mode) => :unified | :side_by_side,
  optional(:pane_width) => pos_integer()
}

Metadata for an open diff view buffer.

document_highlight()

@type document_highlight() :: Minga.LSP.DocumentHighlight.t()

A document highlight range from the LSP server.

events_registry()

@type events_registry() :: Minga.Events.registry()

Event bus registry used by this editor instance.

git_remote_op()

@type git_remote_op() ::
  {msg_ref :: reference(), task_monitor :: reference(),
   {git_root :: String.t(), success_msg :: String.t(),
    error_prefix :: String.t()}}
  | nil

The git_remote_op tracking tuple, or nil when no operation is in flight.

keymap_server()

@type keymap_server() :: Minga.Keymap.server()

Re-export of Minga.Keymap.server/0 for editor-state callers.

line_number_style()

@type line_number_style() :: :hybrid | :absolute | :relative | :none

Line number display style.

options_server()

@type options_server() :: Minga.Config.Options.server()

Re-export of Minga.Config.Options.server/0 for editor-state callers.

shell_id()

@type shell_id() :: atom()

shell_identity()

@type shell_identity() :: MingaEditor.Shell.Identity.t() | nil

shell_state()

@type shell_state() :: term()

shell_state_stash()

@type shell_state_stash() :: %{
  required(shell_id()) => MingaEditor.Shell.StateStash.t()
}

t()

@type t() :: %MingaEditor.State{
  agent_ingest: pid() | nil,
  async_actions: %{optional(atom()) => async_lane()},
  backend: backend(),
  buffer_add_context: MingaEditor.Shell.buffer_add_context(),
  buffer_monitors: %{required(pid()) => reference()},
  caches: MingaEditor.Renderer.Caches.t(),
  capabilities: MingaEditor.Frontend.Capabilities.t(),
  diff_views: %{required(pid()) => diff_view_info()},
  editing_model: :vim | :cua,
  events_registry: events_registry(),
  face_override_registries: %{
    required(pid()) => MingaEditor.UI.Face.Registry.t()
  },
  focus_stack: [module()],
  focus_tree: MingaEditor.FocusTree.t() | nil,
  font_size_override: pos_integer() | nil,
  git_commit_gen_ref: reference() | nil,
  git_remote_op: git_remote_op(),
  gui_config_state: Minga.RenderModel.UI.ConfigState.t() | nil,
  keyframe_pending?: boolean(),
  keymap_server: keymap_server(),
  keystroke_history: MingaEditor.KeystrokeHistory.t(),
  last_cursor_line: non_neg_integer() | nil,
  last_input_seq: non_neg_integer(),
  last_test_command: {String.t(), String.t()} | nil,
  layout: MingaEditor.Layout.t() | nil,
  lsp: MingaEditor.State.LSP.t(),
  message_store: MingaEditor.UI.Panel.MessageStore.t(),
  notifications: MingaEditor.UI.NotificationCenter.t(),
  options_server: options_server(),
  parser_status: MingaEditor.Shell.Traditional.Modeline.parser_status(),
  pending_quit: :quit | :quit_all | nil,
  port_manager: GenServer.server() | nil,
  remote: MingaEditor.State.Remote.t(),
  render_timer: reference() | nil,
  renderer: pid() | nil,
  resource_pressure: MingaEditor.State.ResourcePressure.t(),
  session: MingaEditor.State.Session.t(),
  shell: module(),
  shell_id: shell_id(),
  shell_identity: shell_identity(),
  shell_state: shell_state(),
  shell_state_stash: shell_state_stash(),
  sidebar_registry: MingaEditor.Extension.Sidebar.table(),
  terminal_viewport: MingaEditor.Viewport.t(),
  theme: MingaEditor.UI.Theme.t(),
  workspace: MingaEditor.Session.State.t()
}

Functions

active_buffer(state)

@spec active_buffer(t()) :: non_neg_integer()

Returns the active buffer index.

active_content_context(state)

@spec active_content_context(t()) :: content_context()

Returns display metadata for the active window's content.

Buffer windows return file/buffer metadata. Agent chat windows return agent-specific display info. Falls back to buffer metadata when the active window is nil or unrecognized.

active_shell_id(arg1)

@spec active_shell_id(t() | map()) :: shell_id()

Returns the active shell id, preserving compatibility with tests that still set only :shell.

active_shell_module(state)

@spec active_shell_module(t() | map()) :: module()

Returns the active shell module, refreshing from the registry when possible.

active_tab(state)

@spec active_tab(t()) :: MingaEditor.State.Tab.t() | nil

active_tab_kind(state)

@spec active_tab_kind(t()) :: MingaEditor.State.Tab.kind()

active_window_struct(state)

@spec active_window_struct(t()) :: MingaEditor.Window.t() | nil

Returns the active window struct, or nil if windows aren't initialized.

add_buffer(state, pid, opts \\ [])

@spec add_buffer(t(), pid(), keyword()) :: t()

Adds a new buffer and makes it the active buffer for the current window.

Thin wrapper around add_buffer_pure/3 that applies effects inline.

add_buffer_pure(state, pid, opts \\ [])

@spec add_buffer_pure(t(), pid(), keyword()) :: {t(), [MingaEditor.effect()]}

Pure variant of add_buffer/2. Returns {state, effects} instead of performing side effects directly.

Generic concerns (buffer pool) are handled here. Shell-specific presentation logic (tab bar, card routing) is dispatched through shell.on_buffer_added/5. The only effect returned is {:monitor, pid}.

The buffer-add context is read from state.buffer_add_context (set by picker preview) or overridden via opts[:context]. After dispatch the field is reset to :open.

agent(map)

@spec agent(t()) :: MingaEditor.State.Agent.t()

agent_ingest(state)

@spec agent_ingest(t()) :: pid() | nil

Returns the agent stream-ingest coalescer pid, or nil if not started.

apply_render_output(state, render_output)

@spec apply_render_output(t(), MingaEditor.RenderPipeline.Input.t()) :: t()

Applies render pipeline mutations back to the editor state.

The render pipeline updates window caches (invalidation tracking, context fingerprints), click regions, and layout during rendering. This function writes those mutations back after the pipeline completes.

The render_output is a RenderPipeline.Input struct with the mutated fields. Only windows, shell_state, layout, and caches carry meaningful changes; other fields are unchanged.

apply_renderer_writeback(state, wb)

@spec apply_renderer_writeback(t(), map()) :: t()

Applies asynchronous renderer writeback without overwriting editor-owned state.

Async rendering runs from an older RenderPipeline.Input snapshot while the Editor process continues handling input. The renderer may return stale copies of windows and shell state, so this function only merges fields owned by the renderer: global render caches, layout, per-window render caches, and chrome click regions.

apply_theme(state, theme)

@spec apply_theme(t(), MingaEditor.UI.Theme.t()) :: t()

Switches the active editor theme and re-colors existing syntax highlights.

Sets state.theme and rebuilds each buffer's highlight face_registry from the new theme so tree-sitter colors update immediately. Buffers with a syntax override keep their custom palette.

bottom_panel(map)

@spec bottom_panel(t()) :: MingaEditor.BottomPanel.t()

buffer(state)

@spec buffer(t()) :: pid() | nil

Returns the active buffer pid.

buffers(state)

@spec buffers(t()) :: [pid()]

Returns the buffer list.

build_agent_tab_defaults(state, windows, agent_buf)

@spec build_agent_tab_defaults(t(), MingaEditor.State.Windows.t(), pid() | nil) ::
  MingaEditor.State.Tab.context()

Builds a complete per-tab context for an agent tab.

Used by agent tab creation paths to ensure all @per_tab_fields are populated. Accepts a pre-built Windows struct for the agent chat window and the agent buffer pid.

build_agent_workspace_context(state, agent_buf)

@spec build_agent_workspace_context(t(), pid() | nil) ::
  MingaEditor.State.Tab.context()

Builds a fresh agent-shaped workspace context for shell-owned agent surfaces.

Returns a Tab.context() carrying a single agent-chat window sized to the current viewport, with the agent keymap scope. The caller restores it via restore_tab_context/2 and then activates the relevant shell-owned session pid against the window content.

Falls back to an empty Windows map when no agent buffer is available; the caller's activation step then becomes a no-op.

cancel_nav_flash(s)

@spec cancel_nav_flash(t()) :: t()

cancel_yank_flash(s)

@spec cancel_yank_flash(t()) :: t()

clear_git_remote_op(state)

@spec clear_git_remote_op(t()) :: t()

clear_git_toast(s)

@spec clear_git_toast(t()) :: t()

clear_git_toast(s, dismiss_ref)

@spec clear_git_toast(t(), reference()) :: t()

clear_pending_quit(state)

@spec clear_pending_quit(t()) :: t()

clear_status(s)

@spec clear_status(t()) :: t()

close_buffer_pure(state, pid)

@spec close_buffer_pure(t(), pid()) :: {t(), [MingaEditor.effect()]}

Pure variant of remove_dead_buffer/2. Returns {state, effects} instead of performing side effects directly.

Removes the pid from the buffer list, clears it from special buffer slots, switches to another buffer if the active one died, and cleans up the monitor ref. This function is already pure (no process calls), so the effects list is always empty.

close_git_status_panel(s)

@spec close_git_status_panel(t()) :: t()

close_observatory(s)

@spec close_observatory(t()) :: t()

current_viewport(state)

@spec current_viewport(t()) :: MingaEditor.Viewport.t()

Returns the viewport for the user's current focus: the active window's viewport when a window is active, otherwise a derived terminal-size viewport (the no-window fallback case). Use this for scroll commands that read/write the focused window's viewport.

Replaces the older active_window_viewport/1 (renamed for symmetry with terminal_viewport/1).

delete_async_lane(state, lane)

@spec delete_async_lane(t(), atom()) :: t()

Removes the async-action gate state for lane, marking it idle.

delete_lsp_pending(state, ref)

@spec delete_lsp_pending(t(), reference()) :: t()

Deletes an active workspace LSP pending request.

diff_view_for_source(state, source_buf)

@spec diff_view_for_source(t(), pid()) :: {pid(), diff_view_info()} | nil

diff_view_info(state, diff_buf)

@spec diff_view_info(t(), pid() | nil) :: diff_view_info() | nil

diff_views_for_source(state, source_buf)

@spec diff_views_for_source(t(), pid()) :: [{pid(), diff_view_info()}]

dismiss_hover_popup(s)

@spec dismiss_hover_popup(t()) :: t()

dismiss_notification(state, id)

@spec dismiss_notification(t(), String.t()) :: t()

Dismisses a GUI notification by id.

dismiss_notification(state, id, dismiss_ref)

@spec dismiss_notification(t(), String.t(), reference()) :: t()

Dismisses a GUI notification only when the auto-dismiss ref still matches.

drop_extension_feature_state_sources(state)

@spec drop_extension_feature_state_sources(t()) :: t()

Drops extension-owned feature state from live and snapshotted workspaces.

drop_feature_state(state, source, feature_id)

@spec drop_feature_state(
  t(),
  MingaEditor.FeatureState.source(),
  MingaEditor.FeatureState.feature_id()
) ::
  t()

Drops one source-owned feature state entry from the active workspace.

drop_feature_state_source(state, source)

@spec drop_feature_state_source(t(), MingaEditor.FeatureState.source()) :: t()

Drops all feature state owned by a source from live and snapshotted workspaces.

drop_file_tree(state)

@spec drop_file_tree(t()) :: t()

Drops the active workspace FileTree feature state.

ensure_shell_available(state)

@spec ensure_shell_available(t()) :: t()

Ensures the active shell still exists, falling back to the registry default if it was unregistered.

events_registry(state)

@spec events_registry(t()) :: events_registry()

Returns the event bus registry used by this editor instance.

file_tree_state(input)

@spec file_tree_state(t() | map()) :: MingaEditor.State.FileTree.t()

Returns the active workspace FileTree feature state.

find_agent_chat_window(state)

@spec find_agent_chat_window(t()) ::
  {MingaEditor.Window.id(), MingaEditor.Window.t()} | nil

Finds the agent chat window in the windows map.

Returns {win_id, window} or nil if no agent chat window exists.

find_buffer_by_path(map, file_path)

@spec find_buffer_by_path(t() | map(), String.t()) :: non_neg_integer() | nil

Returns the index of the buffer whose file path matches file_path, or nil.

Catches :exit for each buffer in case a process has died but not yet been removed from the buffer list.

find_tab_by_buffer(state, pid)

@spec find_tab_by_buffer(t(), pid()) :: MingaEditor.State.Tab.t() | nil

focus_window(state, target_id)

@spec focus_window(t(), MingaEditor.Window.id()) :: t()

Switches focus to the given window, saving the current cursor to the outgoing window and restoring the target window's stored cursor.

No-op if target_id is already the active window or windows aren't set up.

get_async_lane(state, lane)

@spec get_async_lane(t(), atom()) :: async_lane()

Returns the async-action gate state for lane (idle default when absent).

get_feature_state(state, source, feature_id)

@spec get_feature_state(
  t(),
  MingaEditor.FeatureState.source(),
  MingaEditor.FeatureState.feature_id()
) ::
  term() | nil

Returns source-owned feature state from the active workspace, or nil when inactive.

git_status_panel(map)

@spec git_status_panel(t()) ::
  MingaEditor.Shell.Traditional.State.git_status_panel() | nil

gui_config_state(state)

@spec gui_config_state(t()) :: Minga.RenderModel.UI.ConfigState.t() | nil

Returns the cached native settings snapshot emitted in-frame (#2119).

hover_popup(map)

@spec hover_popup(t()) :: MingaEditor.HoverPopup.t() | nil

inline_asks(map)

@spec inline_asks(t()) :: MingaEditor.State.InlineAsk.store()

inline_edits(map)

@spec inline_edits(t()) :: MingaEditor.State.InlineEdit.store()

invalidate_all_windows(state)

@spec invalidate_all_windows(t()) :: t()

Invalidates render caches for all windows.

Call when the screen layout changes (file tree toggle, agent panel toggle) because cached draws contain baked-in absolute coordinates that become wrong when column offsets shift.

keymap_context(state)

@spec keymap_context(t()) :: [{:keymap_server, keymap_server()}]

Returns the keymap context keyword list passed to scoped key resolution.

keymap_server(state)

@spec keymap_server(t()) :: keymap_server()

Returns the keymap server used for scope and binding lookups.

known_open_buffer_pids(state)

@spec known_open_buffer_pids(t()) :: [pid()]

Returns the set of buffer pids known to the live workspace and tab snapshots.

modal(map)

@spec modal(t()) :: MingaEditor.State.ModalOverlay.t()

monitor_buffer(state, pid)

@spec monitor_buffer(t(), pid()) :: t()

Monitors a buffer pid so the Editor receives :DOWN when it dies.

Idempotent: if the pid is already monitored, returns state unchanged.

monitor_buffers(state, pids)

@spec monitor_buffers(t(), [pid()]) :: t()

Monitors a list of buffer pids. Convenience wrapper around monitor_buffer/2.

notification_action(state, notification_id, action_id)

@spec notification_action(t(), String.t(), String.t()) ::
  MingaEditor.UI.Notification.Action.t() | nil

Looks up an inline notification action.

observatory_visible?(map)

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

open_observatory(s, timer)

@spec open_observatory(t(), {reference(), reference()} | nil) :: t()

options_server(state)

@spec options_server(t()) :: options_server()

Returns the options server used for typed option lookups.

put_async_lane(state, lane, lane_state)

@spec put_async_lane(t(), atom(), async_lane()) :: t()

Stores the async-action gate state for lane.

put_feature_state(state, source, feature_id, value)

@spec put_feature_state(
  t(),
  MingaEditor.FeatureState.source(),
  MingaEditor.FeatureState.feature_id(),
  term()
) :: t()

Stores source-owned feature state on the active workspace.

put_gui_config_state(state, snapshot)

@spec put_gui_config_state(t(), Minga.RenderModel.UI.ConfigState.t() | nil) :: t()

Stores the cached native settings snapshot emitted in-frame (#2119).

put_lsp_pending(state, ref, kind)

@spec put_lsp_pending(t(), reference(), atom() | tuple()) :: t()

Adds or replaces an active workspace LSP pending request.

rebind_buffer_file_identity(state, buffer_pid)

@spec rebind_buffer_file_identity(t(), pid()) :: t()

Rebinds the logical file identity for matching tabs and their workspaces.

Use this after a buffer save, save-as, or path retarget so the live tab and its workspace stop pointing at stale buffer refs and start pointing at the new path ref.

rebuild_agent_from_session(state, tab)

@spec rebuild_agent_from_session(t(), MingaEditor.State.Tab.t()) :: t()

Rebuilds the agent rendering cache from the Session process when switching to an agent tab. The Session is the source of truth for status, pending approval, and error; the cache lives on state.shell_state.agent and is repopulated from the Tab's session pid on every tab switch.

The session pid itself lives on Tab.session (see set_tab_session/3), not on the agent cache. AgentAccess.session/1 reads it through the shell behaviour.

register_diff_view(state, diff_buf, info)

@spec register_diff_view(t(), pid(), diff_view_info()) :: t()

remove_dead_buffer(state, pid)

@spec remove_dead_buffer(t(), pid()) :: t()

Removes a dead buffer pid from all state locations.

Called from the Editor's :DOWN handler. Removes the pid from the buffer list, clears it from special buffer slots (messages, warnings, help), and switches to another buffer if the active one died. Also cleans up the monitor ref.

Thin wrapper around close_buffer_pure/2 that applies effects inline.

reset_frontend_render_state(state)

@spec reset_frontend_render_state(t()) :: t()

Clears frontend-retained render state after a frontend ready/recovery event.

restore_tab_context(state, context)

@spec restore_tab_context(
  t(),
  MingaEditor.State.Tab.context() | MingaEditor.State.Tab.legacy_context()
) ::
  t()

Writes a tab context back into the live editor state.

The context carries workspace fields as an explicit struct. Empty context means a brand-new tab; we build defaults with the current active buffer and viewport dimensions.

scope_for_active_window(map)

@spec scope_for_active_window(t()) :: atom()

Returns the appropriate keymap scope for the active window's content type.

Used when leaving the file tree (toggle, close, navigate right) to restore the correct scope. Returns :agent for agent chat windows, :editor otherwise.

scope_for_content(content, current_scope)

Derives the keymap scope from a window's content type.

Agent chat windows always use :agent scope. Buffer windows use :editor when coming from :agent scope, and preserve the current scope otherwise (e.g., :file_tree stays as :file_tree).

screen_rect(state)

@spec screen_rect(t()) :: MingaEditor.WindowTree.rect()

Returns the screen rect for layout computation, excluding the global minibuffer row and reserving space for the file tree panel when open.

scroll_agent_chat_window(state, delta)

@spec scroll_agent_chat_window(t(), integer()) :: t()

Scrolls the agent chat window's viewport by delta lines and updates pinned state. Delegates to Window.scroll_viewport/3.

Returns the state unchanged if no agent chat window exists.

set_agent(s, agent)

@spec set_agent(t(), MingaEditor.State.Agent.t()) :: t()

set_agent_ingest(state, pid)

@spec set_agent_ingest(t(), pid() | nil) :: t()

Stores the agent stream-ingest coalescer pid (see MingaEditor.Agent.Ingest).

set_agent_ui(state, agent_ui)

@spec set_agent_ui(t(), MingaEditor.Agent.UIState.t()) :: t()

Replaces the active workspace agent UI state.

set_bottom_panel(s, panel)

@spec set_bottom_panel(t(), MingaEditor.BottomPanel.t()) :: t()

set_buffer_add_context(state, context)

@spec set_buffer_add_context(t(), MingaEditor.Shell.buffer_add_context()) :: t()

Sets the context for the next add_buffer call.

Used by picker preview to mark buffer additions as transient previews rather than permanent opens. The context is consumed and reset to :open by add_buffer_pure/3.

set_buffers(state, buffers)

@spec set_buffers(t(), MingaEditor.State.Buffers.t()) :: t()

Replaces the active workspace buffer state.

set_change_recorder(state, recorder)

@spec set_change_recorder(t(), MingaEditor.ChangeRecorder.t()) :: t()

Replaces the vim change recorder.

set_dired(state, dired)

@spec set_dired(t(), MingaEditor.State.Dired.t()) :: t()

Replaces the active workspace dired state.

set_document_highlights(state, highlights)

@spec set_document_highlights(t(), [document_highlight()] | nil) :: t()

Replaces the active workspace document highlights.

set_editing(state, editing)

@spec set_editing(t(), MingaEditor.VimState.t()) :: t()

Replaces the active workspace editing state.

set_file_tree(state, file_tree)

@spec set_file_tree(t(), MingaEditor.State.FileTree.t()) :: t()

Replaces the active workspace FileTree feature state.

set_git_remote_op(state, op)

@spec set_git_remote_op(t(), git_remote_op()) :: t()

set_git_status_panel(s, data)

@spec set_git_status_panel(
  t(),
  MingaEditor.Shell.Traditional.State.git_status_panel() | nil
) :: t()

set_git_toast(s, toast)

@spec set_git_toast(t(), MingaEditor.Shell.Traditional.State.git_toast()) :: t()

set_highlight(state, highlight)

@spec set_highlight(t(), MingaEditor.State.Highlighting.t()) :: t()

Replaces the active workspace highlighting state.

set_hover_popup(s, popup)

@spec set_hover_popup(t(), MingaEditor.HoverPopup.t()) :: t()

set_injection_ranges(state, ranges)

@spec set_injection_ranges(t(), %{
  required(pid()) => [Minga.Language.Highlight.InjectionRange.t()]
}) ::
  t()

Replaces the active workspace injection ranges map.

set_inline_asks(s, asks)

@spec set_inline_asks(t(), MingaEditor.State.InlineAsk.store()) :: t()

set_inline_edits(s, edits)

@spec set_inline_edits(t(), MingaEditor.State.InlineEdit.store()) :: t()

set_keymap_scope(state, scope)

@spec set_keymap_scope(t(), Minga.Keymap.Scope.scope_name()) :: t()

Sets the active workspace keymap scope.

set_last_find_char(state, find_char)

@spec set_last_find_char(t(), MingaEditor.VimState.last_find_char()) :: t()

Records the last find-char motion.

set_last_jump_pos(state, pos)

@spec set_last_jump_pos(t(), Minga.Buffer.position() | nil) :: t()

Records the cursor position before a jump.

set_last_test_command(state, val)

@spec set_last_test_command(
  t(),
  {String.t(), String.t()}
) :: t()

set_lsp_pending(state, pending)

@spec set_lsp_pending(t(), %{required(reference()) => atom() | tuple()}) :: t()

Replaces the active workspace LSP pending request map.

set_macro_recorder(state, recorder)

@spec set_macro_recorder(t(), MingaEditor.MacroRecorder.t()) :: t()

Replaces the vim macro recorder.

set_marks(state, marks)

@spec set_marks(t(), MingaEditor.VimState.marks()) :: t()

Replaces the vim marks map.

set_modal(s, modal)

@spec set_modal(t(), MingaEditor.State.ModalOverlay.t()) :: t()

set_mode_state(state, mode_state)

@spec set_mode_state(t(), Minga.Mode.state()) :: t()

Replaces the current vim mode state without changing the mode.

Use this only for same-mode state updates where the replacement has the shape expected by the current mode. Use transition_mode/3 when changing modes so VimState can keep mode and mode_state aligned.

set_mouse(state, mouse)

@spec set_mouse(t(), MingaEditor.State.Mouse.t()) :: t()

Replaces the active workspace mouse state.

set_nav_flash(s, flash)

@spec set_nav_flash(t(), MingaEditor.NavFlash.t()) :: t()

set_observatory_data(s, data)

@spec set_observatory_data(t(), MingaEditor.Observatory.Data.t() | nil) :: t()

set_observatory_inspection(s, inspection)

@spec set_observatory_inspection(t(), MingaEditor.Observatory.Inspection.t() | nil) ::
  t()

set_observatory_timer(s, timer)

@spec set_observatory_timer(t(), {reference(), reference()} | nil) :: t()

set_pending_quit(state, kind)

@spec set_pending_quit(t(), :quit | :quit_all) :: t()

set_registers(state, registers)

@spec set_registers(t(), MingaEditor.State.Registers.t()) :: t()

Replaces the vim register state.

set_renderer(state, pid)

@spec set_renderer(t(), pid() | nil) :: t()

set_resource_pressure(state, low_power?, thermal_state)

@spec set_resource_pressure(
  t(),
  boolean(),
  MingaEditor.State.ResourcePressure.thermal_state()
) :: t()

Updates the current frontend-reported resource pressure.

set_search(state, search)

@spec set_search(t(), MingaEditor.State.Search.t()) :: t()

Replaces the active workspace search state.

set_sidebar_active_id(s, id)

@spec set_sidebar_active_id(t(), String.t() | nil) :: t()

set_status(s, msg)

@spec set_status(t(), String.t()) :: t()

set_tab_bar(s, tb)

@spec set_tab_bar(t(), MingaEditor.State.TabBar.t() | nil) :: t()

set_tab_session(state, tab_id, session_pid)

@spec set_tab_session(t(), MingaEditor.State.Tab.id(), pid() | nil) :: t()

set_terminal_viewport(state, vp)

@spec set_terminal_viewport(t(), MingaEditor.Viewport.t()) :: t()

Stores a new terminal viewport. Called by the editor's resize handler when the frontend reports a new screen size.

set_viewport(state, viewport)

@spec set_viewport(t(), MingaEditor.Viewport.t()) :: t()

Sets the active workspace viewport.

set_whichkey(s, wk)

@spec set_whichkey(t(), MingaEditor.State.WhichKey.t()) :: t()

set_windows(state, windows)

@spec set_windows(t(), MingaEditor.State.Windows.t()) :: t()

Replaces the active workspace window state.

set_workspace(state, workspace)

@spec set_workspace(t(), MingaEditor.Session.State.t()) :: t()

Replaces the active workspace.

set_yank_flash(s, flash)

@spec set_yank_flash(t(), MingaEditor.YankFlash.t()) :: t()

skip_tool_prompt?(state, tool_name)

@spec skip_tool_prompt?(t(), atom()) :: boolean()

Returns true if the given tool should NOT be prompted for installation.

A tool is skipped when it's already declined this session, already installed, currently being installed, or already in the prompt queue.

snapshot_tab_context(state)

@spec snapshot_tab_context(t()) :: MingaEditor.State.Tab.context()

Captures the current per-tab fields into a context struct.

The returned struct is stored in the outgoing tab so it can be restored when the user switches back.

split?(state)

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

Returns true if the editor has more than one window.

start_buffer(file_path, options_server \\ Minga.Config.Options.default_server())

@spec start_buffer(String.t(), Minga.Config.Options.server() | nil) ::
  {:ok, pid()} | {:error, term()}

Starts a new buffer under the buffer supervisor for the given file path.

status_msg(map)

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

switch_buffer(state, idx)

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

Switches to the buffer at idx, making it active for the current window.

Centralizes Buffers.switch_to + window sync so callers don't need to remember to call sync_active_window_buffer/1. Shell-specific presentation logic (tab label updates, etc.) is dispatched through shell.on_buffer_switched/2.

switch_shell(state, shell_id)

@spec switch_shell(t(), shell_id()) :: t()

Switches to a registered shell by id, stashing the current shell state by shell id.

switch_tab(state, target_id)

@spec switch_tab(t(), MingaEditor.State.Tab.id()) :: t()

Switches to the tab with target_id.

Snapshots the current tab's context, stores it, updates the tab bar's active pointer, and restores the target tab's saved context into the live editor state. Invalidates layout and window caches since the entire visual context changes.

Thin wrapper around switch_tab_pure/2 that applies effects inline.

switch_tab_pure(state, target_id)

@spec switch_tab_pure(t(), MingaEditor.State.Tab.id()) ::
  {t(), [MingaEditor.effect()]}

Pure variant of switch_tab/2. Returns {state, effects} instead of performing side effects directly.

Snapshots the current tab's context, updates the tab bar pointer, restores the target tab's context, invalidates layout. Side effects (spinner stop/start, agent session rebuild) are returned as effects.

The returned effects list may include:

  • :stop_spinner — cancel the outgoing agent's spinner timer
  • {:rebuild_agent_session, tab} — rebuild agent state from session process
  • :start_spinner — conditionally restart spinner for incoming agent

sync_active_window_buffer(state)

@spec sync_active_window_buffer(t()) :: t()

Syncs the active window's buffer reference with state.workspace.buffers.active.

Call this after any operation that changes state.workspace.buffers.active to keep the window tree consistent. No-op when windows aren't initialized.

sync_active_window_cursor(state)

@spec sync_active_window_cursor(t()) :: t()

Snapshots the active buffer's cursor into the active window struct.

Call this before rendering split views so inactive windows have a fresh cursor position for the active window when it becomes inactive later.

sync_agent_ui_from_active_workspace(state)

@spec sync_agent_ui_from_active_workspace(t()) :: t()

Syncs the live workspace agent UI mirror from the active workspace.

tab_bar(map)

@spec tab_bar(t()) :: MingaEditor.State.TabBar.t() | nil

terminal_viewport(state)

@spec terminal_viewport(t()) :: MingaEditor.Viewport.t()

Returns the terminal-level viewport: total screen rows/cols reported by the frontend on resize. Used for screen-spanning chrome (picker, popups, completion menu placement) and for layout computation that needs the editor's full canvas.

This is distinct from current_viewport/1, which scopes to the active window's viewport.

transition_mode(state, mode, mode_state \\ nil)

@spec transition_mode(t(), Minga.Mode.mode(), Minga.Mode.state() | nil) :: t()

Transitions the editor to a new vim mode.

Convenience wrapper around VimState.transition/3 that operates on the full EditorState. This is the preferred API for call sites that already have an EditorState.

Examples

# Simple transition (uses default mode_state):
EditorState.transition_mode(state, :normal)
EditorState.transition_mode(state, :insert)

# With explicit mode_state (required for visual, search, etc.):
EditorState.transition_mode(state, :visual, %VisualState{...})

tree_rect(state)

@spec tree_rect(t()) :: MingaEditor.WindowTree.rect() | nil

Returns the screen rect for the file tree panel, or nil if closed.

unregister_diff_view(state, diff_buf)

@spec unregister_diff_view(t(), pid()) :: t()

update_buffers(state, fun)

@spec update_buffers(t(), (MingaEditor.State.Buffers.t() ->
                       MingaEditor.State.Buffers.t())) :: t()

Updates the active workspace buffer state.

update_current_viewport(state, new_vp)

@spec update_current_viewport(t(), MingaEditor.Viewport.t()) :: t()

Updates the active window's viewport. No-op when no window is active (the no-window fallback case has no per-window viewport to write).

Replaces the older put_active_window_viewport/2.

update_dired(state, fun)

@spec update_dired(t(), (MingaEditor.State.Dired.t() -> MingaEditor.State.Dired.t())) ::
  t()

Updates the active workspace dired state.

update_editing(state, fun)

@spec update_editing(t(), (MingaEditor.VimState.t() -> MingaEditor.VimState.t())) ::
  t()

Updates the active workspace editing state.

update_feature_state(state, source, feature_id, default, fun)

@spec update_feature_state(
  t(),
  MingaEditor.FeatureState.source(),
  MingaEditor.FeatureState.feature_id(),
  term(),
  (term() -> term())
) :: t()

Updates source-owned feature state on the active workspace.

update_file_tree(state, fun)

@spec update_file_tree(t(), (MingaEditor.State.FileTree.t() ->
                         MingaEditor.State.FileTree.t())) :: t()

Updates the active workspace FileTree feature state.

update_highlight(state, fun)

@spec update_highlight(t(), (MingaEditor.State.Highlighting.t() ->
                         MingaEditor.State.Highlighting.t())) ::
  t()

Updates the active workspace highlighting state.

update_injection_ranges(state, fun)

@spec update_injection_ranges(t(), (%{
                                required(pid()) => [
                                  Minga.Language.Highlight.InjectionRange.t()
                                ]
                              } ->
                                %{
                                  required(pid()) => [
                                    Minga.Language.Highlight.InjectionRange.t()
                                  ]
                                })) :: t()

Updates the active workspace injection ranges map.

update_lsp(state, fun)

@spec update_lsp(t(), (MingaEditor.State.LSP.t() -> MingaEditor.State.LSP.t())) :: t()

update_mode_state(state, fun)

@spec update_mode_state(t(), (Minga.Mode.state() -> Minga.Mode.state())) :: t()

Updates the current vim mode state without changing the mode.

Use this only for same-mode state updates where the mapper preserves the state shape expected by the current mode. Use transition_mode/3 when changing modes so VimState can keep mode and mode_state aligned.

update_mouse(state, fun)

@spec update_mouse(t(), (MingaEditor.State.Mouse.t() -> MingaEditor.State.Mouse.t())) ::
  t()

Updates the active workspace mouse state.

update_remote(state, fun)

@spec update_remote(t(), (MingaEditor.State.Remote.t() ->
                      MingaEditor.State.Remote.t())) :: t()

Applies a function to remote session state.

update_search(state, fun)

@spec update_search(t(), (MingaEditor.State.Search.t() ->
                      MingaEditor.State.Search.t())) :: t()

Updates the active workspace search state.

update_shell_state(state, fun)

@spec update_shell_state(t() | %{shell_state: shell_state()}, (shell_state() ->
                                                           shell_state())) ::
  t() | %{shell_state: shell_state()}

Applies a function to the shell state and returns the updated state.

update_window(state, id, fun)

@spec update_window(t(), MingaEditor.Window.id(), (MingaEditor.Window.t() ->
                                               MingaEditor.Window.t())) ::
  t()

Updates the window struct for the given window id via a mapper function.

update_windows(state, fun)

@spec update_windows(t(), (MingaEditor.State.Windows.t() ->
                       MingaEditor.State.Windows.t())) :: t()

Updates the active workspace window state.

update_windows_for_buffer(state, buffer, fun)

@spec update_windows_for_buffer(t(), pid(), (MingaEditor.Window.t() ->
                                         MingaEditor.Window.t())) :: t()

Updates every window showing a buffer via a mapper function.

update_workspace(state, fun)

@spec update_workspace(t(), (MingaEditor.Session.State.t() ->
                         MingaEditor.Session.State.t())) :: t()

Applies a function to the workspace and returns the updated state.

upsert_notification(state, notification)

@spec upsert_notification(t(), MingaEditor.UI.Notification.t()) :: t()

Adds or updates a GUI notification.

whichkey(map)

@spec whichkey(t()) :: MingaEditor.State.WhichKey.t()

yank_flash(map)

@spec yank_flash(t()) :: MingaEditor.YankFlash.t() | nil