From ce2f997b84ac99204add8798afeab0ba988bdf90 Mon Sep 17 00:00:00 2001 From: Levi Neuwirth Date: Thu, 21 May 2026 01:35:02 +0000 Subject: [PATCH] minibuffer: Escape cancels session (#44) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- src/minibuffer.rs | 53 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/src/minibuffer.rs b/src/minibuffer.rs index 5330068..7eda07d 100644 --- a/src/minibuffer.rs +++ b/src/minibuffer.rs @@ -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();