Minga.Mode behaviour (Minga v0.1.0)

Copy Markdown View Source

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:

  1. Delegates key events to the appropriate mode module.
  2. Handles count-prefix accumulation (e.g. 3j → move down 3 times).
  3. 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 to mode, 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

command()

@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"}).

key()

@type key() :: {non_neg_integer(), non_neg_integer()}

A key event: {codepoint, modifiers}.

mode()

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

result()

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

state()

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_key(key, state)

@callback handle_key(key(), state()) :: result()

Handle a key event for this mode.

key is a {codepoint, modifiers} tuple. state is the current FSM state. Returns a result/0 describing what the editor should do next.

Functions

display(atom)

@spec display(mode()) :: String.t()

Returns the status-line label for the given mode.

display(mode, s)

@spec display(mode(), state()) :: String.t()

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.

initial_state()

@spec initial_state() :: Minga.Mode.State.t()

Returns a fresh FSM state with no accumulated count and no leader sequence.

process(mode, key, state)

@spec process(mode(), key(), state()) :: {mode(), [command()], state()}

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.