зеркало из
https://github.com/glebtv/tui-textarea.git
synced 2026-08-28 11:36:17 +03:00
feat(keymap): bind Ctrl+Backspace/Ctrl+Delete to word deletion
Add the de-facto standard terminal/editor hotkeys to the default key map: - Ctrl+Backspace -> delete_word - Ctrl+Delete -> delete_next_word Previously only Alt+Backspace/Ctrl+w and Alt+Delete/Alt+d were bound, which conflicts with muscle memory from readline, VS Code and other TUIs.
Этот коммит содержится в:
@@ -1,5 +1,11 @@
|
||||
# Changelog
|
||||
|
||||
## [0.12.2] - 2026-08-17 [Changes][v0.12.2]
|
||||
|
||||
### Features
|
||||
|
||||
- **Common word-deletion hotkeys**: Bind `Ctrl+Backspace` to [`TextArea::delete_word`] and `Ctrl+Delete` to [`TextArea::delete_next_word`] in the default key map, matching the de-facto terminal/editor convention (readline, VS Code, opencode).
|
||||
|
||||
## [0.12.1] - 2026-07-10 [Changes][v0.12.1]
|
||||
|
||||
### Features
|
||||
|
||||
@@ -3,7 +3,7 @@ name = "tui-textarea-2"
|
||||
homepage = "https://github.com/srothgan/tui-textarea#readme"
|
||||
repository = "https://github.com/srothgan/tui-textarea"
|
||||
documentation = "https://docs.rs/tui-textarea-2/latest/tui_textarea/"
|
||||
version = "0.12.1"
|
||||
version = "0.12.2"
|
||||
edition = "2024"
|
||||
rust-version = "1.88.0" # for Ratatui 0.30 split crates
|
||||
authors = ["Simon Peter Rothgang <simonrothgang@icloud.com>", "rhysd <lin90162@yahoo.co.jp>"]
|
||||
|
||||
103
src/textarea.rs
103
src/textarea.rs
@@ -480,6 +480,12 @@ impl<'a> TextArea<'a> {
|
||||
ctrl: false,
|
||||
alt: true,
|
||||
..
|
||||
}
|
||||
| Input {
|
||||
key: Key::Backspace,
|
||||
ctrl: true,
|
||||
alt: false,
|
||||
..
|
||||
} => self.delete_word(),
|
||||
Input {
|
||||
key: Key::Delete,
|
||||
@@ -492,6 +498,12 @@ impl<'a> TextArea<'a> {
|
||||
ctrl: false,
|
||||
alt: true,
|
||||
..
|
||||
}
|
||||
| Input {
|
||||
key: Key::Delete,
|
||||
ctrl: true,
|
||||
alt: false,
|
||||
..
|
||||
} => self.delete_next_word(),
|
||||
Input {
|
||||
key: Key::Char('n'),
|
||||
@@ -3294,4 +3306,95 @@ mod tests {
|
||||
|
||||
assert!(panic.is_err());
|
||||
}
|
||||
|
||||
fn key_input(key: Key, ctrl: bool, alt: bool) -> Input {
|
||||
Input {
|
||||
key,
|
||||
ctrl,
|
||||
alt,
|
||||
shift: false,
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ctrl_backspace_deletes_word_before_cursor() {
|
||||
let mut textarea = TextArea::from(["aaa bbb ccc"]);
|
||||
textarea.move_cursor(CursorMove::End);
|
||||
|
||||
assert!(textarea.input(key_input(Key::Backspace, true, false)));
|
||||
|
||||
assert_eq!(textarea.lines(), ["aaa bbb "]);
|
||||
assert_eq!(textarea.cursor(), (0, 8));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ctrl_backspace_at_whitespace_skips_run_and_deletes_previous_word() {
|
||||
let mut textarea = TextArea::from(["aaa bbb"]);
|
||||
textarea.move_cursor(CursorMove::End);
|
||||
|
||||
textarea.input(key_input(Key::Backspace, true, false));
|
||||
|
||||
assert_eq!(textarea.lines(), ["aaa "]);
|
||||
assert_eq!(textarea.cursor(), (0, 6));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ctrl_delete_deletes_word_after_cursor() {
|
||||
let mut textarea = TextArea::from(["aaa bbb ccc"]);
|
||||
|
||||
assert!(textarea.input(key_input(Key::Delete, true, false)));
|
||||
|
||||
assert_eq!(textarea.lines(), [" bbb ccc"]);
|
||||
assert_eq!(textarea.cursor(), (0, 0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ctrl_delete_at_end_of_word_deletes_next_word() {
|
||||
let mut textarea = TextArea::from(["aaa bbb ccc"]);
|
||||
textarea.move_cursor(CursorMove::Jump(0, 4));
|
||||
|
||||
assert!(textarea.input(key_input(Key::Delete, true, false)));
|
||||
|
||||
assert_eq!(textarea.lines(), ["aaa ccc"]);
|
||||
assert_eq!(textarea.cursor(), (0, 4));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn plain_backspace_still_deletes_single_char() {
|
||||
let mut textarea = TextArea::from(["aaa bbb"]);
|
||||
textarea.move_cursor(CursorMove::End);
|
||||
|
||||
assert!(textarea.input(key_input(Key::Backspace, false, false)));
|
||||
|
||||
assert_eq!(textarea.lines(), ["aaa bb"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn plain_delete_still_deletes_single_char() {
|
||||
let mut textarea = TextArea::from(["aaa bbb"]);
|
||||
|
||||
assert!(textarea.input(key_input(Key::Delete, false, false)));
|
||||
|
||||
assert_eq!(textarea.lines(), ["aa bbb"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn alt_backspace_still_deletes_word() {
|
||||
let mut textarea = TextArea::from(["aaa bbb ccc"]);
|
||||
textarea.move_cursor(CursorMove::End);
|
||||
|
||||
assert!(textarea.input(key_input(Key::Backspace, false, true)));
|
||||
|
||||
assert_eq!(textarea.lines(), ["aaa bbb "]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ctrl_w_still_deletes_word() {
|
||||
let mut textarea = TextArea::from(["aaa bbb ccc"]);
|
||||
textarea.move_cursor(CursorMove::End);
|
||||
|
||||
assert!(textarea.input(key_input(Key::Char('w'), true, false)));
|
||||
|
||||
assert_eq!(textarea.lines(), ["aaa bbb "]);
|
||||
}
|
||||
}
|
||||
|
||||
Ссылка в новой задаче
Block a user