# `Minga.Keymap.Scope`
[🔗](https://github.com/jsmestad/minga/blob/main/lib/minga/keymap/scope.ex#L1)

Behaviour and resolution logic for buffer-type-specific keybindings.

A keymap scope determines which keybindings are active based on the type of
view the user is interacting with. Think of scopes as Neovim's which-key
groups: flat, explicit declarations of what keys do in a given context.

## Design rules

The keymap follows Neovim's flat model, not Emacs's composed hierarchy.
See AGENTS.md § "Keymap Architecture" for the full rationale. Three rules:

1. **Keymap is the single authority.** If a key resolves through a scope
   trie, the command runs. Commands never re-check context internally.
   Don't bind a command in a scope where it shouldn't run.

2. **Scopes are flat.** No implicit inheritance or minor-mode stacking.
   Shared bindings come in through bulk registration helpers that merge
   named binding groups at compile time. See #1278.

3. **Derived scope.** The active scope should follow from what's on screen,
   not from a manually managed field. (Target architecture; today
   `workspace.keymap_scope` is still a field.)

## Built-in scopes

* `:editor` — normal text editing (default)
* `:agent` — agent chat view (Board zoom or side panel)
* `:file_tree` — file tree panel
* `:git_status` — git status panel

## Resolution layers

Each scope module implements this behaviour and declares its own keybindings
as trie data. Keymap resolution walks layers in priority order:

1. User overrides for the active scope + vim state
2. Vim-state-specific bindings from the scope module
3. Shared bindings that apply across all vim states for the scope
4. `:not_found` (caller decides what to do: self-insert, passthrough, etc.)

Global bindings (leader sequences, Ctrl+S) and the Mode FSM fallback are
handled by the caller, not by this module.

## Context parameter

The `keymap/2` callback receives a `context` keyword list. Phase 1 always
passes `[]`. Phase 2 (#215) will pass `[filetype: :elixir]` so the editor
scope can return filetype-specific bindings. Agent and file_tree scopes
ignore context.

# `context`

```elixir
@type context() :: keyword()
```

Extra context for scope keymap resolution (e.g., filetype).

# `help_binding`

```elixir
@type help_binding() :: {String.t(), String.t()}
```

A single help binding: `{key_string, description}`.

# `help_group`

```elixir
@type help_group() :: {String.t(), [help_binding()]}
```

A group of help bindings with a category label.

# `resolve_result`

```elixir
@type resolve_result() ::
  {:command, atom()} | {:prefix, Minga.Keymap.Bindings.node_t()} | :not_found
```

Result of resolving a key through the scope system.

* `{:command, atom()}` — execute this named command
* `{:prefix, Bindings.node_t()}` — key is a prefix; more keys needed
* `:not_found` — scope has no binding for this key

# `scope_name`

```elixir
@type scope_name() :: :editor | :agent | :file_tree | :git_status
```

A scope name atom.

# `vim_state`

```elixir
@type vim_state() :: :normal | :insert | :input_normal | :cua
```

Vim state relevant to scope resolution.

# `display_name`

```elixir
@callback display_name() :: String.t()
```

Returns a human-readable name for display (e.g., "Agent").

# `help_groups`

```elixir
@callback help_groups(focus :: atom()) :: [help_group()]
```

Returns categorized help groups for the `?` help overlay.

Each group is a `{category_label, [{key_string, description}]}` tuple.
The `focus` parameter lets scopes return different help content depending
on the current UI context (e.g., `:chat` vs `:file_viewer` in the agent
scope).

Return `[]` to indicate no help overlay is available for this scope.

# `included_groups`
*optional* 

```elixir
@callback included_groups() :: [atom() | {atom(), keyword()}]
```

Returns the shared binding groups this scope includes.

Each entry is either a group name atom or a `{group_name, opts}` tuple
with exclusion options. Used for introspection and documentation; the
actual merge happens in the scope's trie-building functions.

Optional callback. Returns `[]` by default.

# `keymap`

```elixir
@callback keymap(vim_state(), context()) :: Minga.Keymap.Bindings.node_t()
```

Returns the keybinding trie for a specific vim state.

The `context` parameter is a keyword list that phase 1 passes as `[]`.
Phase 2 will pass `[filetype: :elixir]` for filetype-specific bindings.

# `name`

```elixir
@callback name() :: scope_name()
```

Returns the atom name of this scope (e.g., `:agent`).

# `on_enter`

```elixir
@callback on_enter(state :: term()) :: term()
```

Called when this scope becomes active. Initialize scope-specific state.

# `on_exit`

```elixir
@callback on_exit(state :: term()) :: term()
```

Called when this scope is deactivated. Clean up scope-specific state.

# `shared_keymap`

```elixir
@callback shared_keymap() :: Minga.Keymap.Bindings.node_t()
```

Returns bindings that apply regardless of vim state.

These are checked after vim-state-specific bindings but before global
bindings. Useful for keys like Ctrl+C (abort) that work the same in
both normal and insert mode within a scope.

# `all_scopes`

```elixir
@spec all_scopes() :: [scope_name()]
```

Returns all registered scope names.

# `help_groups`

```elixir
@spec help_groups(scope_name(), atom()) :: [help_group()]
```

Returns help groups for the given scope and focus context.

Delegates to the scope module's `help_groups/1` callback.
Returns `[]` if the scope is not found.

# `module_for`

```elixir
@spec module_for(scope_name()) :: module() | nil
```

Returns the scope module for a given scope name.

# `resolve_key`

```elixir
@spec resolve_key(scope_name(), vim_state(), Minga.Keymap.Bindings.key(), context()) ::
  resolve_result()
```

Resolves a key through the scope's keybinding layers.

Walks layers in priority order:
1. User overrides for the scope + vim state (from `Keymap.Active`)
2. Vim-state-specific bindings for the active scope
3. Shared bindings for the active scope
4. Returns `:not_found` if no scope binding matches

Global bindings (leader sequences, Ctrl+S) and Mode.process fallback are
handled by the caller, not by this function.

# `resolve_key_in_node`

```elixir
@spec resolve_key_in_node(Minga.Keymap.Bindings.node_t(), Minga.Keymap.Bindings.key()) ::
  resolve_result()
```

Resolves a key against a specific trie node (for multi-key sequences).

Used when continuing a prefix sequence within a scope. The caller tracks
which trie node to continue from.

---

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