From af8c1cdf12ca0418ee253abb43a0849da15fd571 Mon Sep 17 00:00:00 2001 From: pap-openai Date: Thu, 7 Aug 2025 00:16:47 +0100 Subject: [PATCH] fix meta+b meta+f (option+left/right) (#1895) Option+Left or Option+Right should move cursor to beginning/end of the word. We weren't listening to what terminals are sending (on MacOS) and were therefore printing b or f instead of moving cursor. We were actually in the first match clause and returning char insertion (https://github.com/openai/codex/pull/1895/files#diff-6bf130cd00438cc27a38c5a4d9937a27cf9a324c191de4b74fc96019d362be6dL209) Tested on Apple Terminal, iTerm, Ghostty --- codex-rs/tui/src/bottom_pane/textarea.rs | 53 +++++++++++++++++------- 1 file changed, 38 insertions(+), 15 deletions(-) diff --git a/codex-rs/tui/src/bottom_pane/textarea.rs b/codex-rs/tui/src/bottom_pane/textarea.rs index cb30c2ac..8e6e8b07 100644 --- a/codex-rs/tui/src/bottom_pane/textarea.rs +++ b/codex-rs/tui/src/bottom_pane/textarea.rs @@ -206,7 +206,10 @@ impl TextArea { match event { KeyEvent { code: KeyCode::Char(c), - modifiers: KeyModifiers::NONE | KeyModifiers::SHIFT | KeyModifiers::ALT, + // Insert plain characters (and Shift-modified). Do NOT insert when ALT is held, + // because many terminals map Option/Meta combos to ALT+ (e.g. ESC f/ESC b) + // for word navigation. Those are handled explicitly below. + modifiers: KeyModifiers::NONE | KeyModifiers::SHIFT, .. } => self.insert_str(&c.to_string()), KeyEvent { @@ -245,6 +248,23 @@ impl TextArea { } => { self.delete_backward_word(); } + // Meta-b -> move to beginning of previous word + // Meta-f -> move to end of next word + // Many terminals map Option (macOS) to Alt. Some send Alt|Shift, so match contains(ALT). + KeyEvent { + code: KeyCode::Char('b'), + modifiers: KeyModifiers::ALT, + .. + } => { + self.set_cursor(self.beginning_of_previous_word()); + } + KeyEvent { + code: KeyCode::Char('f'), + modifiers: KeyModifiers::ALT, + .. + } => { + self.set_cursor(self.end_of_next_word()); + } KeyEvent { code: KeyCode::Char('u'), modifiers: KeyModifiers::CONTROL, @@ -275,6 +295,23 @@ impl TextArea { } => { self.move_cursor_right(); } + // Some terminals send Alt+Arrow for word-wise movement: + // Option/Left -> Alt+Left (previous word start) + // Option/Right -> Alt+Right (next word end) + KeyEvent { + code: KeyCode::Left, + modifiers: KeyModifiers::ALT, + .. + } => { + self.set_cursor(self.beginning_of_previous_word()); + } + KeyEvent { + code: KeyCode::Right, + modifiers: KeyModifiers::ALT, + .. + } => { + self.set_cursor(self.end_of_next_word()); + } KeyEvent { code: KeyCode::Up, .. } => { @@ -312,20 +349,6 @@ impl TextArea { } => { self.move_cursor_to_end_of_line(true); } - KeyEvent { - code: KeyCode::Left, - modifiers: KeyModifiers::CONTROL | KeyModifiers::ALT, - .. - } => { - self.set_cursor(self.beginning_of_previous_word()); - } - KeyEvent { - code: KeyCode::Right, - modifiers: KeyModifiers::CONTROL | KeyModifiers::ALT, - .. - } => { - self.set_cursor(self.end_of_next_word()); - } o => { tracing::debug!("Unhandled key event in TextArea: {:?}", o); }