# KEYMAP.md This is the durable v1 keymap and operation grammar for `mim`. It is design authority, not a claim that every operation is implemented today. Implementation slices should cite the relevant requirements and keep the grammar source-patched, replayable, and terminal-trace-backed. ## Product stance `mim` is mobile-first, not phone-only. The same command grammar must work when SSHing from an iPhone software keyboard, a QWERTZ thumb keyboard, or an attached physical keyboard. Physical keyboards may add faster direct keys, but they must not become the only way to perform core editing, navigation, tool, or recovery actions. The keymap is optimized for: - narrow terminal viewports; - mnemonic commands that can be discovered from the screen; - common operations as a mode plus one key, or at most a two-key sequence; - an orthogonal grammar where verbs, objects, modes, panels, and tool operations compose instead of becoming one-off bindings; - avoiding required Esc/Ctrl/Alt/function-key chords for core actions; - replayable terminal events separated from layout profiles and editor intents; - code editing workflows, including diagnostics, LSP, lint, format, build/test, and local trusted tools. ## Input layers The input path has three distinct layers: 1. **Terminal event**: bytes or key events observed from the terminal. 2. **Layout profile**: source-patched keyboard tables backed by recorded traces such as iOS QWERTZ and physical keyboard traces. 3. **Editor intent**: semantic operations such as `delete line`, `select indent`, `format buffer`, or `next diagnostic`. No command should inspect layout quirks directly. A new keyboard layout is a new source-patched profile and trace fixture, not an ad-hoc branch in command code. ## Orthogonality rules The keymap should feel small because its parts compose: - modes decide how raw input is interpreted; - verbs decide the operation: delete, change, yank, select, format, explain; - objects decide the range: word, line, indent block, delimiter pair, parameter, diagnostic, function, panel item; - counts/repetition decide how often a movement or previous operation applies; - providers decide the source of language/tool data, not the user-facing command; - panels decide presentation and filtering, not the operation semantics. A new operation is suspect if it needs a bespoke binding that cannot reuse this shape. Prefer adding one verb, one object, or one provider rule over adding many special-case bindings. Reuse is not allowed to hide ambiguity: if the same key can mean two things, the current mode or visible rail label must make the meaning obvious. Examples: - `d` + object deletes; `c` + object changes; `y` + object copies; `s` + object selects. The object grammar is shared. - `Space l` operates on language features; `Space t` operates on jobs/tools; both can target buffer, selection, project, or provider without inventing a separate mode. - Formatting a selection, buffer, or project should be one format command plus a scope/provider choice, not unrelated shortcuts. ## Counts and repetition Counts and repetition are orthogonal modifiers, not separate commands for every operation. - A numeric prefix in Normal, Select, or Panel mode applies to the next movement, object, or panel navigation where that operation is repeatable. - Physical keyboards can type digits directly: `3 j`, `2 w`, `4 d w`. - Mobile users can open the repeat rail with `Space r`, enter a small count in a prompt, then choose the movement/object/operation. This avoids requiring a comfortable number row for common editing. - `.` repeats the last mutating edit when the key is available; `Space r .` is the mobile canonical repeat-last path. - Counts do not apply to unsafe broad tool operations such as format project, fix-all, build, test, or external terminal commands unless a visible prompt confirms the affected scope. - A count is shown in the status/rail while pending and is cleared after one operation, cancel, or mode switch. ## Modes ### Normal mode Default navigation and command mode. Text input does not happen here except via explicit symbol/punctuation insertion commands. Normal mode owns movement, selection entry, leader rails, tool commands, save/quit, and recoverability. ### Insert mode Text-entry mode for ordinary typing. `mim` starts in Normal mode; Insert mode is entered explicitly with `i`, `a`, `o`, or another visible command. In Insert mode, `Space` commits a literal space immediately. Physical keyboards may use conventional direct keys such as Escape as aliases back to Normal, but those aliases are optional accelerators, not required controls. ### Select mode Selection-building mode. Movement extends the active selection by default. Selection commands are semantic: select word, line, current indent block, matching delimiter pair, enclosing text object, parameter, diagnostic range, and visible panel item. Select mode can apply operations such as delete, replace, copy, format, code action, or explain to the selection. ### Panel mode Transient-panel mode for file picker, project search, diagnostics, git, LSP, build/test output, terminal escape hatch, and Pi/tool context. Panels share one navigation grammar: move, open, preview, filter, act, close, and return. ### Prompt mode Short text prompt mode for current-file search, project search, rename, command filtering, and tool arguments. Prompt mode is not an ex-command language; it is a small typed argument surface entered from a visible command. ## Leader keys and rails `Space` is the primary leader in Normal mode. In Insert mode, Space inserts a literal space immediately; terminals do not give `mim` reliable key-up/hold semantics, so hold-to-leader is intentionally not a v1 path. The rail shows mnemonic groups and available next keys. A command either executes after one key or enters a second-level group; common paths should not go deeper than two keys after the mode/leader. `m` is the match/object prefix in Normal and Select modes. It is deliberately not Vim `%`: `%` remains a physical-keyboard alias where available, while `m` is the mobile mnemonic path. `g` is the go/navigation prefix. It is used for jumps whose target is elsewhere: definition, references, next/previous diagnostic, file symbol, project symbol, and matching syntax target when a direct object prefix would be ambiguous. `Space Space` reopens the last command rail/panel context. It is the universal "where was I?" recovery path. ## Mnemonic command grammar The grammar is verb-first for editing and noun/group-first for panels: - `i`: insert; - `a`: append/after; - `o`: open/new line or open item depending on context; - `s`: select/search depending on mode/rail group; - `d`: delete; - `c`: change; - `r`: replace; - `y`: yank/copy; - `p`: paste/put; - `u`: undo; - `R`: redo where uppercase is available, otherwise `Space u` then `r`; - `f`: find/format/file depending on rail group label; - `l`: lint/LSP language tools depending on rail group label; - `t`: tools/tasks/tests; - `b`: buffer/build depending on rail group label; - `q`: quit/close; - `w`: write/save, shown as "write/save" in rails for mnemonic continuity. Letter reuse is allowed only when the current mode or visible rail label makes the meaning obvious. Invisible overloading is not allowed. ## Core Normal bindings These are the mobile canonical paths. Physical keyboards may add direct aliases such as arrow keys, Home/End, PageUp/PageDown, Escape, or `%`. | Keys | Intent | | --- | --- | | `i` | enter Insert before cursor | | `a` | enter Insert after cursor | | `o` | open line below and enter Insert | | `O` or `Space o` `a` | open line above and enter Insert | | `h` `j` `k` `l` | left/down/up/right where comfortable; arrows are aliases | | `w` `b` `e` | word forward/back/end | | `0` `$` | line start/end when available; `Space g` `l` opens line targets | | `u` | undo | | `Space u` `r` | redo | | `Space w` | write/save current buffer | | `Space q` | close/quit current surface, with dirty-buffer protection | | `Space ?` | show contextual help/command rail | | Normal `i` / `a` / `o` | enter Insert explicitly; `mim` starts in Normal mode | ## Editing operations | Keys | Intent | | --- | --- | | `d` `d` | delete current line | | `d` then motion/object | delete range | | `c` `c` | change current line | | `c` then motion/object | change range and enter Insert | | `r` key/symbol | replace character or active selection | | `s` | enter Select mode | | `y` then motion/object | yank/copy range | | `p` | paste/put after cursor or replace active selection | | `Space i` | symbol insertion rail for coding punctuation | | `Space i` `p` | insert paired delimiter, then place cursor inside | | `Space i` `q` | insert paired quotes, then place cursor inside | Delete/change/yank all accept the same object grammar. This keeps operations orthogonal: verbs decide what happens; objects decide what range is affected. ## Object and match grammar Objects are shared by Normal and Select mode. | Keys | Object or jump | | --- | --- | | `m` `m` | jump to matching delimiter or quote | | `m` `s` | select inside matching delimiter/string pair | | `m` `a` | select around matching delimiter/string pair | | `m` `(` | jump/select `()` pair, depending on mode | | `m` `{` | jump/select `{}` pair, depending on mode | | `m` `[` | jump/select `[]` pair, depending on mode | | `m` `"` / `m` `'` / `m` `` ` `` | jump/select quoted string where parseable | | `s` `w` | select word | | `s` `l` | select line | | `s` `i` | select current indent block | | `s` `p` | select current parameter/argument | | `s` `f` | select current function or syntactic enclosing form where available | | `s` `d` | select current diagnostic range | Physical keyboards may expose `%` as an alias for `m m`. The mobile canonical path stays `m m` because `%` is awkward on software keyboards and QWERTZ. ## Search and navigation | Keys | Intent | | --- | --- | | `/` or `Space s` `f` | search in current file | | `n` / `N` | next/previous search result; rail aliases exist if uppercase is awkward | | `Space f` | file finder | | `Space s` `p` | project text search | | `Space s` `s` | symbol search in current file | | `Space g` `d` | go to definition | | `Space g` `r` | references | | `Space g` `i` | implementation | | `Space g` `t` | type definition | | `Space g` `p` | previous cursor location | | `Space g` `n` | next cursor location | | `Space g` `e` | next diagnostic/error | | `Space g` `E` or `Space g` `p` from diagnostics rail | previous diagnostic/error | | `Space g` `a` | next parameter/argument in call | | `Space g` `A` | previous parameter/argument in call | Search results and navigation targets use transient panels on narrow viewports. Opening a result returns to the editor without leaving behind permanent splits. ## Panels `Space` plus a group opens a panel or rail: | Keys | Surface | | --- | --- | | `Space f` | files: file tree/finder/recent | | `Space s` | search: file/project/symbol | | `Space d` | diagnostics | | `Space g` | go/navigation | | `Space l` | language/LSP tools | | `Space t` | tasks: build/test/check/lint jobs | | `Space b` | buffers/build output depending on rail label | | `Space v` | version control/git workbench | | `Space !` | terminal escape hatch/job command | | `Space p` | Pi/local trusted tool context | Panel controls are consistent: | Key | Intent | | --- | --- | | `j`/`k` or arrows | move item | | `o` or Enter | open/accept item | | `p` | preview item | | `/` | filter/search within panel | | `a` | act on item; opens contextual action rail | | `q` | close panel | | `Space Space` | restore last panel/rail | ## Coding tools and language operations Tool operations are first-class commands, not hidden side effects. | Keys | Intent | | --- | --- | | `Space l` `h` | hover: compact card first, expandable panel for full text | | `Space l` `s` | signature help: active parameter first, expandable panel for overloads/docs | | `Space l` `a` | code actions for cursor/selection/diagnostic | | `Space l` `r` | rename | | `Space l` `o` | organize imports | | `Space l` `f` | format current buffer | | `Space l` `F` or `Space l` `w` | toggle/show format-on-save policy for this source build | | `Space l` `d` | show provider details for symbol/diagnostic under cursor | | `Space t` `l` | lint current file | | `Space t` `L` | lint project/workspace | | `Space t` `b` | build | | `Space t` `t` | test | | `Space t` `c` | check/typecheck | | `Space d` | diagnostics panel | | `Space d` `n` | next diagnostic | | `Space d` `p` | previous diagnostic | | `Space d` `s` | filter diagnostics by source/provider | Format-on-save is a policy command, not a runtime config file. Defaults live in source profiles. The rail must show whether save will format, which formatter or LSP provider will run, and how to perform a one-shot save without formatting when needed. If no formatter/linter/server exists, `mim` reports a clear missing-tool or unsupported-capability diagnostic instead of installing anything. ### Hover, signature help, and long content Hover responses are often too large for an iPhone terminal. The default answer is not to dump all text inline. `Space l h` shows a compact hover card first: - symbol/type/signature headline; - shortest useful documentation excerpt that fits the viewport; - provider/source label when more than one provider could answer; - `more`, `open`, `search`, `copy`, and `pin` actions in the rail. Expanding hover opens a transient scrollable panel. The panel supports search, line wrapping by terminal cell width, source/provider switching, copy/yank, and closing back to the original cursor location. Long hovers never permanently steal editor space, and they should not make diagnostics or signature context unreachable. Signature help follows the same rule: show the active overload and parameter first; move between parameters with the parameter navigation grammar; expand only when the user asks to read all overloads or full docs. ### Multiple providers Several LSPs, linters, formatters, or build tools may apply to one buffer. `mim` must keep provider identity visible when ambiguity matters: - diagnostics carry source/provider and severity; - hover and signature help show provider when multiple providers answer; - formatting chooses a source-profile default and offers provider selection when multiple formatters are available; - code actions show provider/source in the action list; - lint/build/test jobs show command/provider, status, exit code, and captured diagnostics; - provider conflicts are surfaced in the language rail instead of silently choosing surprising behavior. Safe default: use the source-profile-preferred provider when one exists; otherwise ask through a transient provider picker for mutating operations such as format, organize imports, rename, or fix-all. Non-mutating operations may merge results while preserving source labels. ## Discoverability and feedback - Pausing after `Space`, `m`, `g`, `d`, `c`, `y`, or `s` shows the valid next keys for the current mode and selection. - Rails use verbs and nouns, not internal names: "format buffer", "lint file", "select indent", "matching brace". - Dangerous or broad operations show the affected scope before execution. - Dirty-buffer, missing-tool, provider-conflict, and unsupported-profile states appear as diagnostics/panel messages, not silent failures. - Help is contextual: `Space ?` in Insert, Select, Normal, and Panel mode shows different high-value commands. ## Implementation gap as of this design Backed by current code/tests: - source-patched symbol insertion and keyboard-layout acceptance fixtures; - leader rail seed behavior and command-intent separation; - transient panel/rendering foundations; - protocol/session/job/LSP/diagnostic skeletons; - `Space t` lint/build/test/check job rail backed by source-patched command profiles, captured output panels, missing-tool/cancel/timeout rows, and jump/yank actions; - physical-keyboard parity aliases for arrows, Home/End, PageUp/PageDown, Escape, direct digit counts, `%` match jumping, and panel page keys while preserving mobile Space paths; - basic trace commands for open, insert, save, panels, diagnostics, repo files, physical key aliases, and local context. Design-only and needing future implementation slices: - explicit modal state machine for Normal/Insert/Select/Panel/Prompt; - orthogonal command parser for shared verb + object + scope/provider grammar; - object grammar shared by delete/change/yank/select; - matching delimiter and quote object behavior; - select current indent block and syntactic enclosing form; - current-file search prompt and result navigation; - parameter navigation in function calls; - compact-first hover/signature cards with expandable scroll/search panels; - full provider arbitration for multiple LSP/tool sources; - source-profile-backed format, organize-imports, code-action edit application; - format-on-save policy and one-shot save-without-format; - contextual rails for all prefixes listed in this document; - explicit mobile-safe Normal/Insert transitions without required Esc/Ctrl/Alt. Future slices should implement one vertical behavior at a time with replay or headless tests: for example, `m m` delimiter jump, `s i` indent selection, current-file search, or `Space l f` format buffer with provider diagnostics.