# `MingaEditor.VimState`
[🔗](https://github.com/jsmestad/minga/blob/main/lib/minga_editor/vim_state.ex#L1)

Vim-specific editing model state.

Groups the modal FSM state, registers, marks, and recording state
that are specific to vim-style editing. This substruct on EditorState
creates the swap boundary for alternative editing models (CUA, #306):
replace `state.workspace.editing` with a different struct to change the editing model.

## Fields

* `mode` — current vim mode (:normal, :insert, :visual, etc.)
* `mode_state` — mode-specific state (pending operator, search input, etc.)
* `reg` — named registers and active register selection
* `marks` — buffer-local marks (outer key is buffer pid, inner is mark name)
* `last_jump_pos` — cursor position before the last jump
* `last_find_char` — last f/F/t/T char for `;` and `,` repeat
* `change_recorder` — tracks changes for dot repeat
* `macro_recorder` — tracks macro recording state

# `last_find_char`

```elixir
@type last_find_char() :: {Minga.Mode.State.find_direction(), String.t()} | nil
```

Stored last find-char motion for ; and , repeat.

# `marks`

```elixir
@type marks() :: %{
  required(pid()) =&gt; %{required(String.t()) =&gt; Minga.Buffer.position()}
}
```

Buffer-local marks: outer key is buffer pid, inner key is mark name.

# `t`

```elixir
@type t() :: %MingaEditor.VimState{
  change_recorder: MingaEditor.ChangeRecorder.t(),
  last_find_char: last_find_char(),
  last_jump_pos: Minga.Buffer.position() | nil,
  macro_recorder: MingaEditor.MacroRecorder.t(),
  marks: marks(),
  mode: Minga.Mode.mode(),
  mode_state: Minga.Mode.state(),
  reg: MingaEditor.State.Registers.t()
}
```

# `new`

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

Returns a new VimState with default values.

# `set_buffer_marks`

```elixir
@spec set_buffer_marks(t(), pid(), %{required(String.t()) =&gt; Minga.Buffer.position()}) ::
  t()
```

Sets the marks map for a specific buffer.

# `set_change_recorder`

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

Updates the change recorder state.

# `set_last_find_char`

```elixir
@spec set_last_find_char(t(), last_find_char()) :: t()
```

Records the last f/F/t/T char for ; and , repeat.

# `set_last_jump_pos`

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

Records the cursor position before a jump.

# `set_macro_recorder`

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

Updates the macro recorder state.

# `set_marks`

```elixir
@spec set_marks(t(), marks()) :: t()
```

Replaces the entire marks map.

# `set_mode_state`

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

Updates mode_state without changing the mode.

# `set_registers`

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

Updates the register state.

# `transition`

```elixir
@spec transition(t(), Minga.Mode.mode(), Minga.Mode.state() | nil) :: t()
```

Transitions to a new mode, returning an updated VimState.

This is the single gate function for all mode changes. Every mode
transition in the codebase must go through this function (or the
`EditorState.transition_mode/3` convenience wrapper). A custom Credo
check enforces this by flagging raw `mode:` writes on the vim struct.

When `mode_state` is nil, sensible defaults are used:
- `:normal`, `:insert` → `Mode.initial_state()`
- `:command` → `%CommandState{}`
- `:eval` → `%EvalState{}`
- `:replace` → `%ReplaceState{}`

Modes that require context (`:visual`, `:search`, `:search_prompt`,
`:substitute_confirm`, `:extension_confirm`, `:operator_pending`)
must be given an explicit `mode_state`.

---

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