krz/orgstar

A native macOS editor for org-mode files. editor org-mode swift

docs/plans/2026-10-05-keymaps.md

2fa201330f61c801f777baa3f2943d49d8758cda
orgstar/docs/plans/2026-10-05-keymaps.md rendered · source · history · blame · raw

65 lines · 5091 bytes

 1# Keymaps and Command Palette Implementation Plan
 2
 3> **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.
 4
 5**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.
 6
 7**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.
 8
 9**Tech Stack:** Swift 6.2 tools, AppKit, SwiftUI, Swift Testing.
10
11**Spec:** `docs/design.md`, "Commands and keymaps" (keymap format, presets).
12
13## Global Constraints
14
15- `⌘` shortcuts from the menus (save, undo, find, quick open) work in every preset.
16- While the text view has marked text (IME composition), keys go to the input method untouched.
17- `mode` is parsed and kept; bindings with a `mode` are inactive until the modal engine (Doom preset).
18- The user's keymap lives at `<data dir>/keymap.toml`; problems in it are reported, and the entries that parse still apply.
19
20## Defaults chosen (user may change)
21
22- Option as Meta: left Option is Meta, right Option types characters.
23- Mac preset org keys: ⌃⌘T TODO, ⌃⌘↑/↓ priority, ⌃⌘←/→ promote/demote.
24- An active region is replaced by typing (Cocoa), as `delete-selection-mode`.
25- which-key appears 0.6 s after a prefix.
26
27## File structure
28
29| File | Responsibility |
30| --- | --- |
31| `Sources/OrgCore/Keymap/Keys.swift` | `KeyChord`, `KeySequence` |
32| `Sources/OrgCore/Keymap/TOML.swift` | TOML subset parser |
33| `Sources/OrgCore/Keymap/Keymap.swift` | `KeyBinding`, `Keymap`, `KeyContext`, `KeyDispatcher` |
34| `Sources/OrgCore/Keymap/Presets.swift` | `KeymapPreset` (Emacs, Mac) |
35| `Sources/OrgEditorAppKit/Keys.swift` | `OrgTextView`, event-to-chord, `OptionAsMeta` |
36| `Sources/OrgEditorAppKit/EditorCommands.swift` | Command registry: ids, titles, how each runs |
37| `Sources/OrgEditorAppKit/OrgEditor.swift` | Dispatch, mark, callbacks for pending keys and app commands |
38| `Sources/Orgstar/*.swift` | Keymap loading, echo area, which-key, palette, Org menu, Settings |
39
40---
41
42### Task 1: Keymap core
43
44`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`.
45
46- [ ] Tests: notation round trips and shift folding; TOML tables, escapes and error lines; bad entries skipped with problems; later layers win and `none` unbinds; prefix dispatch, undefined sequences and C-g; continuations; contexts; presets parse.
47- [ ] Commit "Add keymaps".
48
49### Task 2: Keys in the editor
50
51- `OrgTextView.keyDown` and `performKeyEquivalent` feed the dispatcher (except during IME composition); `.unbound` goes to the text system.
52- Context dispatch: the first candidate whose `when` holds 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.
53- `edit.set-mark` starts a region that movement extends; C-g, editing, kill and copy end it.
54- Option as Meta per side, from the device-dependent modifier bits.
55- `onKeysPending(String?, [(key, title)])`, `onAppCommand(String)`, and `run(_ id:)` for menus and the palette.
56- [ ] 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.
57- [ ] Commit "Run commands from keys".
58
59### Task 3: App
60
61- Settings: preset, Option as Meta; `keymap.toml` loaded at launch and with "Reload Keymap".
62- Echo area at the bottom of the editor: pending keys and command messages. which-key popup lists the continuations with command titles.
63- Command palette (M-x, ⇧⌘P): fuzzy search over command titles, showing each command's keys; runs on the editor.
64- Org menu with the org commands.
65- [ ] Verify with the snapshot hook; commit "Keymaps, echo area and palette in the app".