mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
fix(tui): support modified backspace/delete keys (#21058)
## Why Fixes #21046. Codex TUI 0.128.0 can show Backspace/Delete-related editor shortcuts in `/keymap`, but Windows-style modified Backspace/Delete events were still dropped by the composer because the default editor keymap did not include those modified special-key variants. On Windows/CMD this meant `Shift+Backspace` and `Shift+Delete` did not fall through to normal character deletion, and `Ctrl+Backspace` / `Ctrl+Delete` did not perform the word deletion users expect from Windows text inputs. ## What Changed - Added default editor bindings for `shift-backspace` and `shift-delete` so shifted delete keys keep normal grapheme deletion behavior. - Added default editor bindings for `ctrl-backspace`, `ctrl-shift-backspace`, `ctrl-delete`, and `ctrl-shift-delete` so Windows-style word deletion works when terminals preserve those modifiers. - Added regression coverage for the resolved default keymap and textarea behavior. ## How to Test 1. Start Codex in the TUI on Windows CMD or another terminal that reports modified Backspace/Delete keys distinctly. 2. Type `hello world` in the composer. 3. Press `Ctrl+Backspace`; confirm `world` is removed and `hello ` remains. 4. Type `world` again, move the cursor before it, then press `Ctrl+Delete`; confirm the next word is removed. 5. Type a few characters and press `Shift+Backspace` and `Shift+Delete`; confirm they delete one character in the expected direction instead of doing nothing. 6. Open `/keymap`, inspect the Editor deletion actions, and confirm the modified Backspace/Delete aliases are visible as configurable defaults. Targeted tests: - `cargo test -p codex-tui keymap::tests` - `cargo test -p codex-tui bottom_pane::textarea::tests` - `cargo test -p codex-tui keymap_setup::tests`
This commit is contained in:
committed by
GitHub
Unverified
parent
a6599b8202
commit
87d2235b54
@@ -2779,6 +2779,53 @@ mod tests {
|
||||
assert_eq!(t.cursor(), 6);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn shift_backspace_and_shift_delete_keep_grapheme_delete_behavior() {
|
||||
let mut t = ta_with("abc");
|
||||
t.set_cursor(/*pos*/ 2);
|
||||
|
||||
t.input(KeyEvent::new(KeyCode::Backspace, KeyModifiers::SHIFT));
|
||||
assert_eq!(t.text(), "ac");
|
||||
assert_eq!(t.cursor(), 1);
|
||||
|
||||
let mut t = ta_with("abc");
|
||||
t.set_cursor(/*pos*/ 1);
|
||||
|
||||
t.input(KeyEvent::new(KeyCode::Delete, KeyModifiers::SHIFT));
|
||||
assert_eq!(t.text(), "ac");
|
||||
assert_eq!(t.cursor(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn control_backspace_variants_delete_backward_word() {
|
||||
for modifiers in [
|
||||
KeyModifiers::CONTROL,
|
||||
KeyModifiers::CONTROL | KeyModifiers::SHIFT,
|
||||
] {
|
||||
let mut t = ta_with("hello world");
|
||||
t.set_cursor(t.text().len());
|
||||
|
||||
t.input(KeyEvent::new(KeyCode::Backspace, modifiers));
|
||||
assert_eq!(t.text(), "hello ");
|
||||
assert_eq!(t.cursor(), 6);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn control_delete_variants_delete_forward_word() {
|
||||
for modifiers in [
|
||||
KeyModifiers::CONTROL,
|
||||
KeyModifiers::CONTROL | KeyModifiers::SHIFT,
|
||||
] {
|
||||
let mut t = ta_with("hello world");
|
||||
t.set_cursor(/*pos*/ 0);
|
||||
|
||||
t.input(KeyEvent::new(KeyCode::Delete, modifiers));
|
||||
assert_eq!(t.text(), " world");
|
||||
assert_eq!(t.cursor(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn delete_backward_word_handles_narrow_no_break_space() {
|
||||
let mut t = ta_with("32\u{202F}AM");
|
||||
|
||||
@@ -588,11 +588,21 @@ impl RuntimeKeymap {
|
||||
move_line_end: default_bindings![plain(KeyCode::End), ctrl(KeyCode::Char('e'))],
|
||||
delete_backward: default_bindings![
|
||||
plain(KeyCode::Backspace),
|
||||
shift(KeyCode::Backspace),
|
||||
ctrl(KeyCode::Char('h'))
|
||||
],
|
||||
delete_forward: default_bindings![plain(KeyCode::Delete), ctrl(KeyCode::Char('d'))],
|
||||
delete_forward: default_bindings![
|
||||
plain(KeyCode::Delete),
|
||||
shift(KeyCode::Delete),
|
||||
ctrl(KeyCode::Char('d'))
|
||||
],
|
||||
delete_backward_word: default_bindings![
|
||||
alt(KeyCode::Backspace),
|
||||
ctrl(KeyCode::Backspace),
|
||||
raw(KeyBinding::new(
|
||||
KeyCode::Backspace,
|
||||
KeyModifiers::CONTROL | KeyModifiers::SHIFT,
|
||||
)),
|
||||
ctrl(KeyCode::Char('w')),
|
||||
raw(KeyBinding::new(
|
||||
KeyCode::Char('h'),
|
||||
@@ -601,6 +611,11 @@ impl RuntimeKeymap {
|
||||
],
|
||||
delete_forward_word: default_bindings![
|
||||
alt(KeyCode::Delete),
|
||||
ctrl(KeyCode::Delete),
|
||||
raw(KeyBinding::new(
|
||||
KeyCode::Delete,
|
||||
KeyModifiers::CONTROL | KeyModifiers::SHIFT,
|
||||
)),
|
||||
alt(KeyCode::Char('d'))
|
||||
],
|
||||
kill_line_start: default_bindings![ctrl(KeyCode::Char('u'))],
|
||||
@@ -1919,6 +1934,54 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn default_editor_deletion_includes_modified_backspace_delete_aliases() {
|
||||
let runtime = RuntimeKeymap::defaults();
|
||||
|
||||
assert!(
|
||||
runtime
|
||||
.editor
|
||||
.delete_backward
|
||||
.contains(&key_hint::shift(KeyCode::Backspace))
|
||||
);
|
||||
assert!(
|
||||
runtime
|
||||
.editor
|
||||
.delete_forward
|
||||
.contains(&key_hint::shift(KeyCode::Delete))
|
||||
);
|
||||
assert!(
|
||||
runtime
|
||||
.editor
|
||||
.delete_backward_word
|
||||
.contains(&key_hint::ctrl(KeyCode::Backspace))
|
||||
);
|
||||
assert!(
|
||||
runtime
|
||||
.editor
|
||||
.delete_backward_word
|
||||
.contains(&KeyBinding::new(
|
||||
KeyCode::Backspace,
|
||||
KeyModifiers::CONTROL | KeyModifiers::SHIFT
|
||||
))
|
||||
);
|
||||
assert!(
|
||||
runtime
|
||||
.editor
|
||||
.delete_forward_word
|
||||
.contains(&key_hint::ctrl(KeyCode::Delete))
|
||||
);
|
||||
assert!(
|
||||
runtime
|
||||
.editor
|
||||
.delete_forward_word
|
||||
.contains(&KeyBinding::new(
|
||||
KeyCode::Delete,
|
||||
KeyModifiers::CONTROL | KeyModifiers::SHIFT
|
||||
))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn default_composer_toggle_shortcuts_includes_shift_question_mark() {
|
||||
let runtime = RuntimeKeymap::defaults();
|
||||
|
||||
Reference in New Issue
Block a user