docs/plans/2026-10-05-keymaps.md
65 lines · 5091 bytes
7 symbols in this file
Keymaps and Command Palette Implementation Plan
For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (
- [ ]) syntax for tracking.
Goal: Keys run commands: TOML keymaps in layers (preset, then keymap.toml), key sequences of any length with an echo area and a which-key popup, context dispatch through when, Emacs and Mac presets, Option as Meta per side, and a command palette (M-x, ⇧⌘P) over every command.
Architecture: Platform-free in OrgCore/Keymap: Emacs key notation (KeyChord, KeySequence), the TOML subset keymaps use, Keymap (layered bindings, unbinding with none, prefixes, continuations), KeyContext (heading, table, item, region) and KeyDispatcher (the pending-prefix state machine). In OrgEditorAppKit, OrgTextView turns key events into chords before the text system sees them and the editor runs the winning binding: org.* text commands through perform, view commands (org.cycle, org.cycle-global), edit.* commands as text-system selectors with an Emacs-style active mark, and app.* commands handed to the app. The app loads the keymap, shows the echo area and which-key, and hosts the palette and an Org menu.
Tech Stack: Swift 6.2 tools, AppKit, SwiftUI, Swift Testing.
Spec: docs/design.md, "Commands and keymaps" (keymap format, presets).
Global Constraints
⌘shortcuts from the menus (save, undo, find, quick open) work in every preset.- While the text view has marked text (IME composition), keys go to the input method untouched.
modeis parsed and kept; bindings with amodeare inactive until the modal engine (Doom preset).- The user's keymap lives at
<data dir>/keymap.toml; problems in it are reported, and the entries that parse still apply.
Defaults chosen (user may change)
- Option as Meta: left Option is Meta, right Option types characters.
- Mac preset org keys: ⌃⌘T TODO, ⌃⌘↑/↓ priority, ⌃⌘←/→ promote/demote.
- An active region is replaced by typing (Cocoa), as
delete-selection-mode. - which-key appears 0.6 s after a prefix.
File structure
| File | Responsibility |
|---|---|
Sources/OrgCore/Keymap/Keys.swift |
KeyChord, KeySequence |
Sources/OrgCore/Keymap/TOML.swift |
TOML subset parser |
Sources/OrgCore/Keymap/Keymap.swift |
KeyBinding, Keymap, KeyContext, KeyDispatcher |
Sources/OrgCore/Keymap/Presets.swift |
KeymapPreset (Emacs, Mac) |
Sources/OrgEditorAppKit/Keys.swift |
OrgTextView, event-to-chord, OptionAsMeta |
Sources/OrgEditorAppKit/EditorCommands.swift |
Command registry: ids, titles, how each runs |
Sources/OrgEditorAppKit/OrgEditor.swift |
Dispatch, mark, callbacks for pending keys and app commands |
Sources/Orgstar/*.swift |
Keymap loading, echo area, which-key, palette, Org menu, Settings |
Task 1: Keymap core
KeyChord.parse, KeySequence.parse/format, TOML.parse, Keymap(toml:problems:), Keymap.layered, candidates(for:mode:), isPrefix, continuations(of:), keys(for:), KeyContext.holds, KeyDispatcher.feed returning .pending, .complete, .undefined, .unbound or .cancelled.
- Tests: notation round trips and shift folding; TOML tables, escapes and error lines; bad entries skipped with problems; later layers win and
noneunbinds; prefix dispatch, undefined sequences and C-g; continuations; contexts; presets parse. - Commit "Add keymaps".
Task 2: Keys in the editor
OrgTextView.keyDownandperformKeyEquivalentfeed the dispatcher (except during IME composition);.unboundgoes to the text system.- Context dispatch: the first candidate whose
whenholds and whose command applies runs; for a single key with none, the text system gets the key; for a longer sequence the first candidate runs and reports why it can't. edit.set-markstarts a region that movement extends; C-g, editing, kill and copy end it.- Option as Meta per side, from the device-dependent modifier bits.
onKeysPending(String?, [(key, title)]),onAppCommand(String), andrun(_ id:)for menus and the palette.- Tests with synthesized key events: C-c C-t cycles TODO; TAB folds on a heading and inserts a tab in a paragraph; C-SPC C-f C-w kills one character; M-f moves a word with left Option and types with right Option; C-x C-s reaches
onAppCommand; an undefined sequence reports a message; pending keys are reported. - Commit "Run commands from keys".
Task 3: App
- Settings: preset, Option as Meta;
keymap.tomlloaded at launch and with "Reload Keymap". - Echo area at the bottom of the editor: pending keys and command messages. which-key popup lists the continuations with command titles.
- Command palette (M-x, ⇧⌘P): fuzzy search over command titles, showing each command's keys; runs on the editor.
- Org menu with the org commands.
- Verify with the snapshot hook; commit "Keymaps, echo area and palette in the app".