Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 49 additions & 8 deletions codex-rs/tui/src/bottom_pane/textarea.rs
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,13 @@ impl TextArea {
code: KeyCode::Enter,
..
} => self.insert_str("\n"),
KeyEvent {
code: KeyCode::Char('h'),
modifiers,
..
} if modifiers == (KeyModifiers::CONTROL | KeyModifiers::ALT) => {
Comment on lines +236 to +238
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
modifiers,
..
} if modifiers == (KeyModifiers::CONTROL | KeyModifiers::ALT) => {
modifiers: KeyModifiers::CONTROL | KeyModifiers::ALT,
..
} => {

Copy link
Contributor Author

@vinaybantupalli vinaybantupalli Aug 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@nornagon-openai tried that.

compiler is not treating that as a bitwise OR, but as a pattern alternation operator (basically OR).
So, either ctrl+h, or alt+h can trigger it, which we don't want.
The match guard is a comparison to bitwise OR, so it works for us.

warning: unreachable pattern
   --> tui/src/bottom_pane/textarea.rs:249:15
    |
234 | /             KeyEvent {
235 | |                 code: KeyCode::Char('h'),
236 | |                 modifiers: KeyModifiers::CONTROL | KeyModifiers::ALT,
237 | |                 ..
238 | |             } => self.delete_backward_word(),
    | |_____________- matches all the relevant values
...
249 |               | KeyEvent {
    |  _______________^
250 | |                 code: KeyCode::Char('h'),
251 | |                 modifiers: KeyModifiers::CONTROL,
252 | |                 ..
253 | |             } => self.delete_backward(1),
    | |_____________^ no value can reach this
    |
    = note: `#[warn(unreachable_patterns)]` on by default

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added another test (ctrl_h_backspace) for simple CTRL+H to backspace conversion, both these tests are failing if I do modifiers: KeyModifiers::CONTROL | KeyModifiers::ALT, using match's pattern matching

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah, makes sense. thanks for checking!

self.delete_backward_word()
},
KeyEvent {
code: KeyCode::Backspace,
modifiers: KeyModifiers::ALT,
Expand Down Expand Up @@ -1138,10 +1145,6 @@ mod tests {

#[test]
fn control_b_and_f_move_cursor() {
use crossterm::event::KeyCode;
use crossterm::event::KeyEvent;
use crossterm::event::KeyModifiers;

let mut t = ta_with("abcd");
t.set_cursor(1);

Expand All @@ -1154,10 +1157,6 @@ mod tests {

#[test]
fn control_b_f_fallback_control_chars_move_cursor() {
use crossterm::event::KeyCode;
use crossterm::event::KeyEvent;
use crossterm::event::KeyModifiers;

let mut t = ta_with("abcd");
t.set_cursor(2);

Expand All @@ -1171,6 +1170,48 @@ mod tests {
assert_eq!(t.cursor(), 2);
}

#[test]
fn delete_backward_word_alt_keys() {
// Test the custom Alt+Ctrl+h binding
let mut t = ta_with("hello world");
t.set_cursor(t.text().len()); // cursor at the end
t.input(KeyEvent::new(
KeyCode::Char('h'),
KeyModifiers::CONTROL | KeyModifiers::ALT,
));
assert_eq!(t.text(), "hello ");
assert_eq!(t.cursor(), 6);

// Test the standard Alt+Backspace binding
let mut t = ta_with("hello world");
t.set_cursor(t.text().len()); // cursor at the end
t.input(KeyEvent::new(KeyCode::Backspace, KeyModifiers::ALT));
assert_eq!(t.text(), "hello ");
assert_eq!(t.cursor(), 6);
}

#[test]
fn control_h_backspace() {
// Test Ctrl+H as backspace
let mut t = ta_with("12345");
t.set_cursor(3); // cursor after '3'
t.input(KeyEvent::new(KeyCode::Char('h'), KeyModifiers::CONTROL));
assert_eq!(t.text(), "1245");
assert_eq!(t.cursor(), 2);

// Test Ctrl+H at beginning (should be no-op)
t.set_cursor(0);
t.input(KeyEvent::new(KeyCode::Char('h'), KeyModifiers::CONTROL));
assert_eq!(t.text(), "1245");
assert_eq!(t.cursor(), 0);

// Test Ctrl+H at end
t.set_cursor(t.text().len());
t.input(KeyEvent::new(KeyCode::Char('h'), KeyModifiers::CONTROL));
assert_eq!(t.text(), "124");
assert_eq!(t.cursor(), 3);
}

#[test]
fn cursor_vertical_movement_across_lines_and_bounds() {
let mut t = ta_with("short\nloooooooooong\nmid");
Expand Down
Loading