Vim modal FSM behaviour and central dispatcher.
Defines the Mode behaviour that each mode module must implement,
and provides process/3 as the entry point for the editor. It:
- Delegates key events to the appropriate mode module.
- Handles count-prefix accumulation (e.g.
3j→ move down 3 times). - Returns
{new_mode, commands, new_fsm_state}for the editor to act on.
Count prefix
In Normal mode, pressing digit keys accumulates a repeat count stored in
the FSM state. The next :execute result's commands are repeated that many
times before the count is reset. Count does not multiply
:execute_then_transition commands, because mode-entry keys like a or i
do not sensibly repeat in Vim.
Result contract
Each mode module's handle_key/2 returns one of:
{:continue, state}— no-op; remain in the same mode.{:transition, mode, state}— switch tomode, no commands.{:execute, command | [command], state}— run commands, stay in same mode.{:execute_then_transition, [command], mode, state}— run commands, then switch mode.
Summary
Types
A command to execute. Either a bare atom (e.g. :move_left) or a
tagged tuple carrying an argument (e.g. {:insert_char, "x"}).
A key event: {codepoint, modifiers}.
Available editor modes.
Result returned by a mode's handle_key/2.
FSM-level state. The base Mode.State struct carries shared fields (count,
leader). Mode-specific structs (VisualState, CommandState, etc.) extend
this with their own fields.
Callbacks
Handle a key event for this mode.
Functions
Returns the status-line label for the given mode.
Returns the status-line label for a mode, using the FSM state for
additional context. Currently used to distinguish -- VISUAL -- from
-- VISUAL LINE -- based on :visual_type in the state.
Returns a fresh FSM state with no accumulated count and no leader sequence.
Processes a key event for the given mode.
Types
@type command() :: atom() | {atom(), term()} | {atom(), term(), term()} | {atom(), term(), term(), term()}
A command to execute. Either a bare atom (e.g. :move_left) or a
tagged tuple carrying an argument (e.g. {:insert_char, "x"}).
@type key() :: {non_neg_integer(), non_neg_integer()}
A key event: {codepoint, modifiers}.
@type mode() ::
:normal
| :insert
| :visual
| :visual_line
| :visual_block
| :operator_pending
| :command
| :eval
| :replace
| :search
| :search_prompt
| :substitute_confirm
| :extension_confirm
| :tool_confirm
| :delete_confirm
Available editor modes.
@type result() :: {:continue, state()} | {:transition, mode(), state()} | {:execute, command() | [command()], state()} | {:execute_then_transition, [command()], mode(), state()}
Result returned by a mode's handle_key/2.
{:continue, state}— no-op.{:transition, mode, state}— switch mode.{:execute, command | [command], state}— execute and stay.{:execute_then_transition, [command], mode, state}— execute then switch.
@type state() :: Minga.Mode.State.t() | Minga.Mode.OperatorPendingState.t() | Minga.Mode.VisualState.t() | Minga.Mode.CommandState.t() | Minga.Mode.EvalState.t() | Minga.Mode.ReplaceState.t() | Minga.Mode.SearchState.t() | Minga.Mode.SearchPromptState.t() | Minga.Mode.SubstituteConfirmState.t() | Minga.Mode.ExtensionConfirmState.t() | Minga.Mode.ToolConfirmState.t() | Minga.Mode.DeleteConfirmState.t()
FSM-level state. The base Mode.State struct carries shared fields (count,
leader). Mode-specific structs (VisualState, CommandState, etc.) extend
this with their own fields.
Callbacks
Functions
Returns the status-line label for the given mode.
Returns the status-line label for a mode, using the FSM state for
additional context. Currently used to distinguish -- VISUAL -- from
-- VISUAL LINE -- based on :visual_type in the state.
@spec initial_state() :: Minga.Mode.State.t()
Returns a fresh FSM state with no accumulated count and no leader sequence.
Processes a key event for the given mode.
Returns {new_mode, commands, new_state}. commands is the (possibly
empty, possibly repeated) list of commands the editor should execute.