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 selectionmarks— buffer-local marks (outer key is buffer pid, inner is mark name)last_jump_pos— cursor position before the last jumplast_find_char— last f/F/t/T char for;and,repeatchange_recorder— tracks changes for dot repeatmacro_recorder— tracks macro recording state
Summary
Types
Stored last find-char motion for ; and , repeat.
Buffer-local marks: outer key is buffer pid, inner key is mark name.
Functions
Returns a new VimState with default values.
Returns a vim state whose mode_state matches mode.
Sets the marks map for a specific buffer.
Updates the change recorder state.
Records the last f/F/t/T char for ; and , repeat.
Records the cursor position before a jump.
Updates the macro recorder state.
Replaces the entire marks map.
Updates mode_state without changing the mode.
Updates the register state.
Transitions to a new mode, returning an updated VimState.
Types
@type last_find_char() :: {Minga.Mode.State.find_direction(), String.t()} | nil
Stored last find-char motion for ; and , repeat.
@type marks() :: %{ required(pid()) => %{required(String.t()) => Minga.Buffer.position()} }
Buffer-local marks: outer key is buffer pid, inner key is mark name.
@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() }
Functions
@spec new() :: t()
Returns a new VimState with default values.
Returns a vim state whose mode_state matches mode.
The Mode FSM carries the leaving mode's state through Mode.apply_result/2
so commands that fire on :execute_then_transition can read the leaving
state (e.g. :yank_visual_selection reads %VisualState{} to find the
selection range). During that window workspace.editing.mode_state is
the leaving struct while mode is already the new mode.
Code paths that snapshot the workspace into long-lived storage (tab
contexts, session save) must not capture that in-flight pair, because
restoring it would put :normal next to a %CommandState{} (or similar)
and break the next leader keypress with a KeyError. Call this before
snapshotting.
Pass-through if mode_state is already the right struct for mode.
Otherwise rebuild via transition/3 with nil to use the default for
default-state modes; raises for context-required modes (which should
never be a snapshot's resting state).
@spec set_buffer_marks(t(), pid(), %{required(String.t()) => Minga.Buffer.position()}) :: t()
Sets the marks map for a specific buffer.
@spec set_change_recorder(t(), MingaEditor.ChangeRecorder.t()) :: t()
Updates the change recorder state.
@spec set_last_find_char(t(), last_find_char()) :: t()
Records the last f/F/t/T char for ; and , repeat.
@spec set_last_jump_pos(t(), Minga.Buffer.position() | nil) :: t()
Records the cursor position before a jump.
@spec set_macro_recorder(t(), MingaEditor.MacroRecorder.t()) :: t()
Updates the macro recorder state.
Replaces the entire marks map.
@spec set_mode_state(t(), Minga.Mode.state()) :: t()
Updates mode_state without changing the mode.
@spec set_registers(t(), MingaEditor.State.Registers.t()) :: t()
Updates the register state.
@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.