feat(input): readline keymap — Esc-clear + Ctrl-A/E/W/K/U (#29)
Implement the deferred I1b readline shortcuts in the command input field (ADR-0049, closing issue #29): Esc clear a partly-typed command (only when no completion memo) Ctrl-A cursor to line start (Home alias) Ctrl-E cursor to line end (End alias) Ctrl-W delete the previous word (readline-style, UTF-8 safe) Ctrl-K kill to end of line Ctrl-U kill to start of line Esc precedence is preserved: a live Tab-completion memo still wins (Esc undoes the completion first, ADR-0022); Esc clears only when no memo is alive. While a sidebar panel is focused (Ctrl-O), Esc exits navigation mode upstream and never clears the input draft. Cursor-only keys leave history navigation intact like Home/End; buffer-mutating keys end it like Backspace. New helpers clear_input / delete_prev_word / kill_to_end / kill_to_start in src/app.rs. 22 new Tier-1 tests (2458 pass / 0 fail / 0 skip, clippy clean). ADR-0049 amends ADR-0046's OOS list; requirements.md I1b marked done.
This commit is contained in:
@@ -525,7 +525,9 @@ All tiers green, zero skips; clippy clean (nursery).
|
||||
submits over a multi-logical-line buffer. DA3/DA4 keep a single
|
||||
logical line; this remains a separate, deferred feature.
|
||||
- **Readline shortcuts (I1b)** — Ctrl-A/E/W/K/U stay reserved-deferred;
|
||||
not touched here.
|
||||
not touched here. *(Superseded 2026-06-12: I1b is now in scope and
|
||||
decided by **ADR-0049** — Esc-clear + Ctrl-A/E/W/K/U in the input
|
||||
field, issue #29.)*
|
||||
- **Cross-session sidebar persistence** — visibility is session-only
|
||||
(DB1); persisting it would amend ADR-0015.
|
||||
- **The output panel as a third navigation focus target** — navigation
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
# ADR-0049: Input-field readline keymap — Esc-clear + Ctrl-A/E/W/K/U (I1b)
|
||||
|
||||
## Status
|
||||
|
||||
**Accepted + implemented 2026-06-12 (issue #29).** Closes Gitea **#29**
|
||||
("Command input keystroke support") and the deferred **I1b** readline
|
||||
requirement in `requirements.md`. Every fork below was escalated to the
|
||||
user and user-chosen before any code was written; implemented test-first
|
||||
(22 new Tier-1 tests in `src/app.rs`, all green; clippy nursery clean).
|
||||
|
||||
This ADR **amends ADR-0046**, which explicitly listed "readline
|
||||
shortcuts (I1b)" in its out-of-scope set: that item is now in scope and
|
||||
decided here. It is orthogonal to ADR-0003's input-*mode* model (simple
|
||||
vs advanced, the `:` sigil) — these are editing keys within the input
|
||||
field, not mode or sigil changes — and it extends the single-line cursor
|
||||
editing already shipped under requirement **I1a** (Left/Right/Home/End/
|
||||
Backspace/Delete, `app.rs`).
|
||||
|
||||
## Context
|
||||
|
||||
The input field already supported in-line cursor editing (I1a): Left/
|
||||
Right by char (UTF-8 aware), Home/End to the extremes, Backspace/Delete.
|
||||
Two gaps remained, raised in issue #29:
|
||||
|
||||
1. No way to **clear a partly-typed command** in one keystroke — a user
|
||||
who started typing the wrong thing had to hold Backspace.
|
||||
2. No **readline cursor/kill shortcuts** (Ctrl-A/Ctrl-E and friends) for
|
||||
keyboards without Home/End and for muscle-memory in a command-driven
|
||||
workflow. This is requirement I1b, deferred by ADR-0046.
|
||||
|
||||
`Esc` was free in the input field except that a *live Tab-completion
|
||||
memo* consumes it first (to undo the completion in one keystroke,
|
||||
ADR-0022). Ctrl-A/E/W/K/U were unbound. The existing chords are Ctrl-C
|
||||
(quit), Ctrl-O (nav focus cycle, ADR-0046), and Ctrl-`]` (demo caption
|
||||
toggle, ADR-0047) — none collide with a/e/w/k/u.
|
||||
|
||||
## Decision
|
||||
|
||||
Bind the following in the input field (non-modal, non-navigation,
|
||||
both input modes), in `App::handle_key`:
|
||||
|
||||
| Key | Action |
|
||||
|-----------|---------------------------------------------------|
|
||||
| `Esc` | Clear the input (empty buffer, cursor→0, scroll→0)|
|
||||
| `Ctrl-A` | Cursor to line start (alias of Home) |
|
||||
| `Ctrl-E` | Cursor to line end (alias of End) |
|
||||
| `Ctrl-W` | Delete the word before the cursor |
|
||||
| `Ctrl-K` | Kill from the cursor to end of line |
|
||||
| `Ctrl-U` | Kill from start of line to the cursor |
|
||||
|
||||
Behavioural rules:
|
||||
|
||||
- **Esc precedence.** A live completion memo still wins: the first Esc
|
||||
undoes the completion (ADR-0022), and Esc only *clears* when no memo
|
||||
is alive. This is a natural progression — Esc once to back out the
|
||||
completion, Esc again to clear.
|
||||
- **Esc does not clear while navigating the sidebar.** When a sidebar
|
||||
panel is focused (Ctrl-O, ADR-0046 DC3), `handle_key` routes every
|
||||
key to the navigation handler *before* the input-field keymap, where
|
||||
Esc exits navigation mode (`nav_exit`). Entering nav mode never
|
||||
touched the input buffer, so Esc-to-close-the-panel returns focus to
|
||||
the input with the partly-typed command intact — it cannot reach the
|
||||
clear binding. Locked by a regression test.
|
||||
- **Single Esc clears** (user-chosen over double-Esc). Discoverable and
|
||||
fast; the trade-off (an accidental Esc wipes an unsubmitted line) was
|
||||
accepted. A submitted line is always recoverable from history; only
|
||||
*unsubmitted* draft text is lost.
|
||||
- **Cursor-only keys don't touch history navigation.** Ctrl-A/Ctrl-E,
|
||||
like Home/End, move the cursor without ending history recall.
|
||||
- **Buffer-mutating keys end history navigation.** Esc-clear and
|
||||
Ctrl-W/K/U call `cancel_history_navigation` (the cleared/edited line
|
||||
*is* the new draft), matching Backspace/Delete.
|
||||
- **Ctrl-W is readline-style and UTF-8 safe.** It eats any run of
|
||||
trailing whitespace, then the preceding run of non-whitespace; word
|
||||
boundaries are found on char boundaries so multi-byte words delete
|
||||
cleanly. It only ever deletes back to the cursor (a mid-line Ctrl-W
|
||||
leaves the suffix intact).
|
||||
|
||||
Helpers added: `clear_input`, `delete_prev_word`, `kill_to_end`,
|
||||
`kill_to_start` (`src/app.rs`), mirroring the existing `cursor_left` /
|
||||
`delete_before_cursor` style.
|
||||
|
||||
## Forks (all user-chosen)
|
||||
|
||||
- **Esc semantics:** single-Esc-clears, *not* double-Esc — discoverable
|
||||
over accident-proof.
|
||||
- **Scope:** the *full* I1b set (Esc-clear + Ctrl-A/E/W/K/U), not just
|
||||
the issue's literal Ctrl-A/E + Esc — closes the whole I1b requirement
|
||||
in one pass rather than leaving Ctrl-W/K/U for a follow-up.
|
||||
- **Documentation:** a new ADR (this one), recording the input-field
|
||||
keymap convention and amending ADR-0046's OOS list — over folding it
|
||||
into ADR-0046 or shipping it I1a-style with no ADR.
|
||||
|
||||
## Consequences
|
||||
|
||||
- I1b is complete; `requirements.md` I1b moves to `[x]`.
|
||||
- The new keys are **not yet advertised on screen.** Surfacing per-focus
|
||||
keybindings in the bottom status line is issue #27's domain (a
|
||||
separate, in-design UX change); this ADR makes the keys *work*, #27
|
||||
will make them *discoverable*.
|
||||
- **Demo-mode badges** (ADR-0047) are *not* extended to the new Ctrl-
|
||||
chords here. Esc already badges as `[ESC]`; Ctrl-A/E/W/K/U are
|
||||
glyph-less and would be invisible in an asciinema cast. Whether to add
|
||||
`[CTRL-A]`…`[CTRL-U]` badges is left to ADR-0047's scope and flagged
|
||||
as a follow-up — it is a cast-polish concern, not a #29 requirement.
|
||||
|
||||
## Out of scope
|
||||
|
||||
- On-screen keybinding hints for the input field (issue #27).
|
||||
- Demo badges for the new chords (ADR-0047 follow-up; flagged above).
|
||||
- Multi-line input (I1) and its Ctrl-Enter submit — unrelated, still
|
||||
deferred.
|
||||
- Word-wise *cursor motion* (Alt-B/Alt-F) and transpose/yank — not
|
||||
requested; not part of I1b.
|
||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user