record D-82: keymap sequence notation and matcher placement
Documents the decision behind T-205: keymap sequences are space- separated (list still alternates, + still joins a chord), repeat counts apply by firing N times, and sequence matching is a reusable stateless query + stateful matcher consumed at the interception point — because the global KeyboardListener can't swallow events, so the editor's Focus.onKeyEvent (T-206) is where normal-mode keys get consumed. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -123,6 +123,7 @@ You might also want, project-permitting:
|
|||||||
- [D-79: Workspace content search is a pure-Dart in-process engine, outside pql](decisions/architecture.md#d-79-workspace-content-search-is-a-pure-dart-in-process-engine-outside-pql) — _architecture_
|
- [D-79: Workspace content search is a pure-Dart in-process engine, outside pql](decisions/architecture.md#d-79-workspace-content-search-is-a-pure-dart-in-process-engine-outside-pql) — _architecture_
|
||||||
- [D-80: `files.read` allows trusted Claude config roots beyond the workspace](decisions/architecture.md#d-80-filesread-allows-trusted-claude-config-roots-beyond-the-workspace) — _architecture_
|
- [D-80: `files.read` allows trusted Claude config roots beyond the workspace](decisions/architecture.md#d-80-filesread-allows-trusted-claude-config-roots-beyond-the-workspace) — _architecture_
|
||||||
- [D-81: Right-pane reader load is driven by a retained `ReaderNav`, not per-view state or bus retention](decisions/architecture.md#d-81-right-pane-reader-load-is-driven-by-a-retained-readernav-not-per-view-state-or-bus-retention) — _architecture_
|
- [D-81: Right-pane reader load is driven by a retained `ReaderNav`, not per-view state or bus retention](decisions/architecture.md#d-81-right-pane-reader-load-is-driven-by-a-retained-readernav-not-per-view-state-or-bus-retention) — _architecture_
|
||||||
|
- [D-82: Keymap sequences are space-separated; matching is a reusable matcher consumed at the interception point](decisions/architecture.md#d-82-keymap-sequences-are-space-separated-matching-is-a-reusable-matcher-consumed-at-the-interception-point) — _architecture_
|
||||||
|
|
||||||
## Open questions
|
## Open questions
|
||||||
|
|
||||||
|
|||||||
@@ -379,3 +379,14 @@ Core, rendering, IPC, kernel, panel manager.
|
|||||||
- **Raised by:** 2026-06-01 — the user reported decisions opening only on the second click, diagnosed the lost-on-mount race, and explicitly chose a "right-pane nav history helper" over a MessageBus fix ("leaving them in the messagebus is the wrong shape").
|
- **Raised by:** 2026-06-01 — the user reported decisions opening only on the second click, diagnosed the lost-on-mount race, and explicitly chose a "right-pane nav history helper" over a MessageBus fix ("leaving them in the messagebus is the wrong shape").
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
### D-82: Keymap sequences are space-separated; matching is a reusable matcher consumed at the interception point
|
||||||
|
- **Date:** 2026-06-01
|
||||||
|
- **Status:** accepted
|
||||||
|
- **Decision:** A keymap binding's `keys:` may be a **multi-chord sequence** written as a **space-separated string** (`'d d'`, `'g g'`, `'ctrl+k ctrl+s'`): space means "then". The existing forms are unchanged — `+` joins modifiers within one chord, and a YAML **list** (`[ctrl+p, meta+p]`) still means **alternation** ("or"). A leading digit run in normal mode is captured as a **repeat count** and applied by firing the resolved intent N times (not threaded into the intent payload). Sequence *matching* is a reusable two-part facility: `Keymap` answers a **stateless** prefix/exact/none query over its bindings, and a small **stateful `SequenceMatcher`** (pending buffer + count + timeout) wraps it. **Interception lives at the consumer**, not in the global key handler.
|
||||||
|
- **Rationale:** Real Vim needs `dd`, `gg`, `dw`, `ciw`, `5j` — impossible under single-chord resolution (T-205). Of the candidate separators, every punctuation option (`,` `;` `>`) is *itself a bindable key* (Vim leader, repeat-find, indent), so each would force an escape rule (`\>`). A literal **space never appears as a key spec** — the space key is always spelled `space` — so it separates with zero collisions and no escaping, and matches Vim-doc / VS Code convention (`ctrl+k ctrl+s`). Count-by-repeat keeps intents `const` and payload-free. The interception split is forced by the host: the global dispatch is a passive `KeyboardListener` (returns void, **cannot swallow** events), so normal-mode keys can't be intercepted there before `EditableText` types them — the editor's `Focus.onKeyEvent` (returns `KeyEventResult`) is the only place that can consume them. Putting the matcher there (T-206) keeps T-205 pure, headless, and unit-testable, and avoids the global handler buffering keys it has no power to swallow.
|
||||||
|
- **Cost / alternatives:** Rejected a **full Vim grammar engine** (operator × motion × text-object × count combinatorics) for the first pass — common operator+motion combos are enumerated as explicit sequence bindings in `vim.yaml` instead, covering the demo surface without a parser. Rejected **comma/`>`/semicolon separators** (escape wart). Rejected **count-in-intent-payload** (would de-`const` every intent and bloat the bridge). Rejected **buffering in the global `resolveEvent`** (it can't swallow, so it would double-handle with the editor). Cost: `KeymapBinding` generalises `chord` → an ordered chord list; a new `SequenceMatcher`; the editor owns interception.
|
||||||
|
- **Cross-reference:** T-205 (matcher + notation), T-206 (editor interception + motions), T-65 (`vim.yaml`). Builds on the T-117 keymap layer; `vim.*` scope flags from [D-81](#d-81-right-pane-reader-load-is-driven-by-a-retained-readernav-not-per-view-state-or-bus-retention)-era work are set by the Vim mode service (T-207).
|
||||||
|
- **Raised by:** 2026-06-01 — scoping the Vim preset for a Vim-power-user demo; the user weighed `,`/`;`/`>` separators and flagged the escaping problem, which made space the collision-free choice.
|
||||||
|
|
||||||
|
---
|
||||||
|
|||||||
Reference in New Issue
Block a user