зеркало из
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
|
# 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]
|
## [0.12.1] - 2026-07-10 [Changes][v0.12.1]
|
||||||
|
|
||||||
### Features
|
### Features
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ name = "tui-textarea-2"
|
|||||||
homepage = "https://github.com/srothgan/tui-textarea#readme"
|
homepage = "https://github.com/srothgan/tui-textarea#readme"
|
||||||
repository = "https://github.com/srothgan/tui-textarea"
|
repository = "https://github.com/srothgan/tui-textarea"
|
||||||
documentation = "https://docs.rs/tui-textarea-2/latest/tui_textarea/"
|
documentation = "https://docs.rs/tui-textarea-2/latest/tui_textarea/"
|
||||||
version = "0.12.1"
|
version = "0.12.2"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
rust-version = "1.88.0" # for Ratatui 0.30 split crates
|
rust-version = "1.88.0" # for Ratatui 0.30 split crates
|
||||||
authors = ["Simon Peter Rothgang <simonrothgang@icloud.com>", "rhysd <lin90162@yahoo.co.jp>"]
|
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,
|
ctrl: false,
|
||||||
alt: true,
|
alt: true,
|
||||||
..
|
..
|
||||||
|
}
|
||||||
|
| Input {
|
||||||
|
key: Key::Backspace,
|
||||||
|
ctrl: true,
|
||||||
|
alt: false,
|
||||||
|
..
|
||||||
} => self.delete_word(),
|
} => self.delete_word(),
|
||||||
Input {
|
Input {
|
||||||
key: Key::Delete,
|
key: Key::Delete,
|
||||||
@@ -492,6 +498,12 @@ impl<'a> TextArea<'a> {
|
|||||||
ctrl: false,
|
ctrl: false,
|
||||||
alt: true,
|
alt: true,
|
||||||
..
|
..
|
||||||
|
}
|
||||||
|
| Input {
|
||||||
|
key: Key::Delete,
|
||||||
|
ctrl: true,
|
||||||
|
alt: false,
|
||||||
|
..
|
||||||
} => self.delete_next_word(),
|
} => self.delete_next_word(),
|
||||||
Input {
|
Input {
|
||||||
key: Key::Char('n'),
|
key: Key::Char('n'),
|
||||||
@@ -3294,4 +3306,95 @@ mod tests {
|
|||||||
|
|
||||||
assert!(panic.is_err());
|
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