# `MingaEditor.Session.State`
[🔗](https://github.com/jsmestad/minga/blob/main/lib/minga_editor/session/state.ex#L1)

Core editing context that exists regardless of presentation.

A workspace is the editing state that gets saved/restored when
switching tabs. It works identically whether rendered as a tab in
the traditional editor, an extension shell surface, or running headless
without any UI.

This struct formalizes the `@per_tab_fields` boundary from
`MingaEditor.State`: every field here is snapshotted per tab and
restored on tab switch.

# `document_highlight`

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

A document highlight range from the LSP server.

# `t`

```elixir
@type t() :: %MingaEditor.Session.State{
  agent_ui: MingaEditor.Agent.UIState.t(),
  buffers: MingaEditor.State.Buffers.t(),
  dired: MingaEditor.State.Dired.t(),
  document_highlights: [document_highlight()] | nil,
  editing: MingaEditor.VimState.t(),
  feature_state: MingaEditor.FeatureState.t(),
  file_tree: MingaEditor.State.FileTree.t(),
  highlight: MingaEditor.State.Highlighting.t(),
  injection_ranges: %{
    required(pid()) =&gt; [Minga.Language.Highlight.InjectionRange.t()]
  },
  keymap_scope: Minga.Keymap.Scope.scope_name(),
  lsp_pending: %{required(reference()) =&gt; atom() | tuple()},
  mouse: MingaEditor.State.Mouse.t(),
  search: MingaEditor.State.Search.t(),
  viewport: MingaEditor.Viewport.t(),
  windows: MingaEditor.State.Windows.t()
}
```

# `active_window_struct`

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

Returns the active window struct, or nil.

# `drop_extension_feature_state_sources`

```elixir
@spec drop_extension_feature_state_sources(t()) :: t()
```

Drops every extension-owned feature state entry.

# `drop_feature_state`

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

Drops one source-owned feature state entry. Missing state is treated as inactive.

# `drop_feature_state_source`

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

Drops all feature state owned by a source.

# `drop_file_tree`

```elixir
@spec drop_file_tree(t()) :: t()
```

Resets FileTree UI state.

# `field_names`

```elixir
@spec field_names() :: [MingaEditor.State.Tab.Context.field_name()]
```

Returns the list of field names (for snapshot/restore compatibility).

# `file_tree_state`

```elixir
@spec file_tree_state(t()) :: MingaEditor.State.FileTree.t()
```

Returns FileTree UI state.

# `get_feature_state`

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

Returns source-owned feature state, or nil when inactive.

# `get_feature_state`

```elixir
@spec get_feature_state(
  t(),
  MingaEditor.FeatureState.source(),
  MingaEditor.FeatureState.feature_id(),
  default
) :: term() | default
when default: var
```

Returns source-owned feature state, or a caller-provided default when inactive.

# `invalidate_all_windows`

```elixir
@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.

# `mark_frontend_reset_pending`

```elixir
@spec mark_frontend_reset_pending(t()) :: t()
```

Marks all window retained-GUI render caches reset-pending after frontend state loss.

# `put_feature_state`

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

Stores source-owned feature state.

# `restore_tab_context`

```elixir
@spec restore_tab_context(
  t(),
  MingaEditor.State.Tab.Context.t() | MingaEditor.State.Tab.Context.legacy()
) :: t()
```

Restores a tab context into a workspace. Empty contexts are ignored by this pure helper; EditorState handles brand-new tab defaults because those need editor dimensions. Dead buffer pids in the restored context are scrubbed to prevent activating a dead process.

# `scope_for_active_window`

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

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

# `scope_for_content`

```elixir
@spec scope_for_content(
  MingaEditor.Window.Content.t(),
  Minga.Keymap.Scope.scope_name()
) ::
  Minga.Keymap.Scope.scope_name()
```

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.

# `set_agent_ui`

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

Updates the agent UI state.

# `set_buffers`

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

Replaces the buffers sub-struct.

# `set_dired`

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

Replaces the dired sub-struct.

# `set_document_highlights`

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

Updates the document highlights from LSP.

# `set_editing`

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

Replaces the editing (VimState) sub-struct.

# `set_feature_state`

```elixir
@spec set_feature_state(t(), MingaEditor.FeatureState.t()) :: t()
```

Replaces the feature-state registry.

# `set_file_tree`

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

Replaces the FileTree UI state.

# `set_highlight`

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

Updates the highlighting sub-struct.

# `set_injection_ranges`

```elixir
@spec set_injection_ranges(t(), %{
  required(pid()) =&gt; [Minga.Language.Highlight.InjectionRange.t()]
}) ::
  t()
```

Updates the injection ranges map.

# `set_keymap_scope`

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

Sets the keymap scope.

# `set_lsp_pending`

```elixir
@spec set_lsp_pending(t(), %{required(reference()) =&gt; atom() | tuple()}) :: t()
```

Updates the LSP pending requests map.

# `set_mouse`

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

Updates the mouse sub-struct.

# `set_search`

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

Updates the search sub-struct.

# `set_viewport`

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

Sets the viewport dimensions.

# `set_windows`

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

Replaces the windows sub-struct.

# `split?`

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

Returns true if the workspace has more than one window.

# `switch_buffer`

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

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

Pure workspace operation: updates Buffers and syncs the active window.

# `sync_active_window_buffer`

```elixir
@spec sync_active_window_buffer(t()) :: t()
```

Syncs the active window's buffer reference with `buffers.active`.

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

# `to_tab_context`

```elixir
@spec to_tab_context(t()) :: MingaEditor.State.Tab.Context.t()
```

Converts a workspace into a typed tab context suitable for storing on a `MingaEditor.State.Tab` and later restoring via `restore_tab_context/2`.

The single chokepoint for snapshots. Delegates to `TabContext.from_workspace/1` which constructs the context struct directly from the session struct (no intermediate map). The vim state is normalised so the snapshotted editing state is a valid resting state, not a transient mid-transition pair where `mode_state` belongs to the leaving mode (see `VimState.normalize/1`). Use this everywhere the editor captures `state.workspace` into a tab context.

# `transition_mode`

```elixir
@spec transition_mode(t(), atom(), term()) :: t()
```

Transitions the editing model to a new mode.

# `update_editing`

```elixir
@spec update_editing(t(), (MingaEditor.VimState.t() -&gt; MingaEditor.VimState.t())) ::
  t()
```

Updates the editing (VimState) sub-struct.

# `update_feature_state`

```elixir
@spec update_feature_state(
  t(),
  MingaEditor.FeatureState.source(),
  MingaEditor.FeatureState.feature_id(),
  term(),
  (term() -&gt; term())
) :: t()
```

Updates source-owned feature state. Missing values are initialized with `default`.

# `update_file_tree`

```elixir
@spec update_file_tree(t(), (MingaEditor.State.FileTree.t() -&gt;
                         MingaEditor.State.FileTree.t())) :: t()
```

Updates the FileTree UI state.

# `update_highlight`

```elixir
@spec update_highlight(t(), (MingaEditor.State.Highlighting.t() -&gt;
                         MingaEditor.State.Highlighting.t())) ::
  t()
```

Updates the highlighting sub-struct via a mapper function.

# `update_search`

```elixir
@spec update_search(t(), (MingaEditor.State.Search.t() -&gt;
                      MingaEditor.State.Search.t())) :: t()
```

Updates the search sub-struct via a mapper function.

# `update_window`

```elixir
@spec update_window(t(), MingaEditor.Window.id(), (MingaEditor.Window.t() -&gt;
                                               MingaEditor.Window.t())) ::
  t()
```

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

# `update_windows_for_buffer`

```elixir
@spec update_windows_for_buffer(t(), pid(), (MingaEditor.Window.t() -&gt;
                                         MingaEditor.Window.t())) :: t()
```

Updates every window that shows the given buffer via a mapper function.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
