412 lines
18 KiB
Markdown
412 lines
18 KiB
Markdown
# 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 and `Tab` inserts spaces by default
|
|
(expandtab, matching the dotfiles Neovim baseline). 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.
|
|
Vertical movement preserves a preferred cursor column across ragged lines,
|
|
clamping only to the current line length until horizontal/editing motion resets
|
|
that preference. Semantic object selections live behind the match/object rail
|
|
(`m w`, `m l`, `m i`, `m p`, etc.) so bare `h/j/k/l`, word motions, arrows, and
|
|
mobile line aliases keep behaving as selection-extending motions. Line object
|
|
selection includes the line's newline boundary when present, matching Vim-like
|
|
linewise edit semantics. 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 |
|
|
| `gg` `G` | document start / bottom line |
|
|
| `Ctrl-u` `Ctrl-d` | half-page up/down when the SSH client exposes Ctrl; `g u` / `g d` are mobile fallback aliases |
|
|
| `H` `L` | mobile-reachable line start/end aliases |
|
|
| `0` `^` `$` | attached/Vim-style line start/first-nonblank/end aliases; `Space g` `l` opens line targets |
|
|
| `.` | repeat the last repeatable local edit (insert/change/delete/put/open-line subset) |
|
|
| `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.
|