diff options
| author | Michelle Tilley <michelle@michelletilley.net> | 2026-02-11 10:44:10 -0800 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-02-11 10:44:10 -0800 |
| commit | 634bf6653ab1fa80ae7250ad86b4f5646aed454e (patch) | |
| tree | 121b6c40c37788cf271b797b2b82f446bb76d1f9 | |
| parent | chore(deps): bump debian from bookworm-20260112-slim to bookworm-20260202-sli... (diff) | |
| download | atuin-634bf6653ab1fa80ae7250ad86b4f5646aed454e.zip | |
fix(tui): enter in vim normal mode, shift-tab keybind (#3158)
Fixes #3156
Fixes #3085
* `Enter` was not bound in the default vim-normal mode keybinds
* `Shift-tab` can be reported as `BackTab`; we convert this at detection
time to `shift-tab`
* `Insert` key was not handled
Verified fixes locally.
| -rw-r--r-- | crates/atuin/src/command/client/search/keybindings/defaults.rs | 24 | ||||
| -rw-r--r-- | crates/atuin/src/command/client/search/keybindings/key.rs | 72 |
2 files changed, 96 insertions, 0 deletions
diff --git a/crates/atuin/src/command/client/search/keybindings/defaults.rs b/crates/atuin/src/command/client/search/keybindings/defaults.rs index ba96bc88..e6d36b80 100644 --- a/crates/atuin/src/command/client/search/keybindings/defaults.rs +++ b/crates/atuin/src/command/client/search/keybindings/defaults.rs @@ -306,6 +306,10 @@ pub fn default_vim_normal_keymap(settings: &Settings) -> Keymap { km.bind(key("pagedown"), Action::ScrollPageDown); km.bind(key("pageup"), Action::ScrollPageUp); + // --- Accept --- + let accept = accept_action(settings); + km.bind(key("enter"), accept); + km } @@ -728,6 +732,26 @@ mod tests { ); } + #[test] + fn vim_normal_enter_returns_selection() { + // enter_accept=false in test defaults → ReturnSelection + let km = default_vim_normal_keymap(&default_settings()); + let ctx = make_ctx(0, 0, 0, 10); + assert_eq!( + km.resolve(&key("enter"), &ctx), + Some(Action::ReturnSelection) + ); + } + + #[test] + fn vim_normal_enter_accept_true_uses_accept() { + let mut settings = default_settings(); + settings.enter_accept = true; + let km = default_vim_normal_keymap(&settings); + let ctx = make_ctx(0, 0, 0, 10); + assert_eq!(km.resolve(&key("enter"), &ctx), Some(Action::Accept)); + } + // -- Vim Insert keymap tests -- #[test] diff --git a/crates/atuin/src/command/client/search/keybindings/key.rs b/crates/atuin/src/command/client/search/keybindings/key.rs index bd61b1fe..e9fb777b 100644 --- a/crates/atuin/src/command/client/search/keybindings/key.rs +++ b/crates/atuin/src/command/client/search/keybindings/key.rs @@ -23,6 +23,7 @@ pub enum KeyCodeValue { Tab, Backspace, Delete, + Insert, Up, Down, Left, @@ -70,8 +71,19 @@ impl SingleKey { KeyCode::Enter => KeyCodeValue::Enter, KeyCode::Esc => KeyCodeValue::Esc, KeyCode::Tab => KeyCodeValue::Tab, + // BackTab is sent by many terminals for Shift+Tab + KeyCode::BackTab => { + return Some(SingleKey { + code: KeyCodeValue::Tab, + ctrl, + alt, + shift: true, + super_key, + }); + } KeyCode::Backspace => KeyCodeValue::Backspace, KeyCode::Delete => KeyCodeValue::Delete, + KeyCode::Insert => KeyCodeValue::Insert, KeyCode::Up => KeyCodeValue::Up, KeyCode::Down => KeyCodeValue::Down, KeyCode::Left => KeyCodeValue::Left, @@ -125,6 +137,7 @@ impl SingleKey { "tab" => KeyCodeValue::Tab, "backspace" => KeyCodeValue::Backspace, "delete" | "del" => KeyCodeValue::Delete, + "insert" | "ins" => KeyCodeValue::Insert, "up" => KeyCodeValue::Up, "down" => KeyCodeValue::Down, "left" => KeyCodeValue::Left, @@ -202,6 +215,7 @@ impl fmt::Display for SingleKey { KeyCodeValue::Tab => write!(f, "tab"), KeyCodeValue::Backspace => write!(f, "backspace"), KeyCodeValue::Delete => write!(f, "delete"), + KeyCodeValue::Insert => write!(f, "insert"), KeyCodeValue::Up => write!(f, "up"), KeyCodeValue::Down => write!(f, "down"), KeyCodeValue::Left => write!(f, "left"), @@ -523,4 +537,62 @@ mod tests { let parsed = SingleKey::parse("f12").unwrap(); assert_eq!(from_event, parsed); } + + #[test] + fn from_event_backtab_becomes_shift_tab() { + // Many terminals send BackTab for Shift+Tab + let event = KeyEvent::new(KeyCode::BackTab, KeyModifiers::NONE); + let k = SingleKey::from_event(&event).unwrap(); + assert_eq!(k.code, KeyCodeValue::Tab); + assert!(k.shift); + assert!(!k.ctrl && !k.alt); + } + + #[test] + fn from_event_backtab_matches_parsed_shift_tab() { + let event = KeyEvent::new(KeyCode::BackTab, KeyModifiers::NONE); + let from_event = SingleKey::from_event(&event).unwrap(); + let parsed = SingleKey::parse("shift-tab").unwrap(); + assert_eq!(from_event, parsed); + } + + #[test] + fn from_event_backtab_with_ctrl() { + // BackTab with ctrl modifier + let event = KeyEvent::new(KeyCode::BackTab, KeyModifiers::CONTROL); + let k = SingleKey::from_event(&event).unwrap(); + assert_eq!(k.code, KeyCodeValue::Tab); + assert!(k.shift); + assert!(k.ctrl); + } + + #[test] + fn parse_insert_key() { + let k = SingleKey::parse("insert").unwrap(); + assert_eq!(k.code, KeyCodeValue::Insert); + assert!(!k.ctrl && !k.alt && !k.shift); + + let k = SingleKey::parse("ins").unwrap(); + assert_eq!(k.code, KeyCodeValue::Insert); + + let k = SingleKey::parse("ctrl-insert").unwrap(); + assert_eq!(k.code, KeyCodeValue::Insert); + assert!(k.ctrl); + } + + #[test] + fn from_event_insert_key() { + let event = KeyEvent::new(KeyCode::Insert, KeyModifiers::NONE); + let k = SingleKey::from_event(&event).unwrap(); + assert_eq!(k.code, KeyCodeValue::Insert); + } + + #[test] + fn insert_key_round_trip() { + let k = KeyInput::parse("insert").unwrap(); + let display = k.to_string(); + assert_eq!(display, "insert"); + let k2 = KeyInput::parse(&display).unwrap(); + assert_eq!(k, k2); + } } |
