minibuffer: Escape cancels session (#44)

Add `KeyCode::Esc => MinibufferAction::Cancel` to
`MinibufferAction::from_chord`'s no-modifier branch. Matches Emacs
convention; surfaced during session 5 manual validation of the
session-4/5 pmacs-gpu work — there was no way to abandon a `C-x C-f`
prompt without typing `C-g`, which is awkward for muscle-memory users.

Adds four unit tests covering the chord dispatcher (Escape→Cancel,
C-g→Cancel, Enter→Accept, char→SelfInsert); none existed before, so
this also seeds the test set for the dispatch table.

C-g remains a Cancel binding — Escape is added in parallel, not
substituted. Both work.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Levi Neuwirth 2026-05-21 01:35:02 +00:00 committed by GitHub
parent 32b529eea5
commit ce2f997b84
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
1 changed files with 53 additions and 0 deletions

View File

@ -463,6 +463,7 @@ impl MinibufferAction {
if !ctrl && !alt {
match chord.code {
KeyCode::Enter => return Self::Accept,
KeyCode::Esc => return Self::Cancel,
KeyCode::Tab => return Self::Complete,
KeyCode::Up => return Self::HistoryPrev,
KeyCode::Down => return Self::HistoryNext,
@ -855,6 +856,58 @@ mod tests {
assert_eq!(mb.buffer.len(), 0);
}
#[test]
fn from_chord_escape_cancels() {
use crossterm::event::{KeyCode, KeyModifiers};
let esc = Chord {
code: KeyCode::Esc,
modifiers: KeyModifiers::NONE,
};
assert!(matches!(
MinibufferAction::from_chord(esc),
MinibufferAction::Cancel
));
}
#[test]
fn from_chord_ctrl_g_cancels() {
use crossterm::event::{KeyCode, KeyModifiers};
let cg = Chord {
code: KeyCode::Char('g'),
modifiers: KeyModifiers::CONTROL,
};
assert!(matches!(
MinibufferAction::from_chord(cg),
MinibufferAction::Cancel
));
}
#[test]
fn from_chord_enter_accepts() {
use crossterm::event::{KeyCode, KeyModifiers};
let ret = Chord {
code: KeyCode::Enter,
modifiers: KeyModifiers::NONE,
};
assert!(matches!(
MinibufferAction::from_chord(ret),
MinibufferAction::Accept
));
}
#[test]
fn from_chord_char_self_inserts() {
use crossterm::event::{KeyCode, KeyModifiers};
let a = Chord {
code: KeyCode::Char('a'),
modifiers: KeyModifiers::NONE,
};
match MinibufferAction::from_chord(a) {
MinibufferAction::SelfInsert('a') => {}
other => panic!("expected SelfInsert('a'), got {other:?}"),
}
}
#[test]
fn insert_and_backspace_round_trip() {
let mut mb = Minibuffer::new();