diff options
-rw-r--r-- | helix-core/src/lib.rs | 1 | ||||
-rw-r--r-- | helix-core/src/movement.rs | 8 | ||||
-rw-r--r-- | helix-core/src/words.rs | 33 | ||||
-rw-r--r-- | helix-term/src/commands.rs | 20 | ||||
-rw-r--r-- | helix-term/src/keymap.rs | 1 |
5 files changed, 56 insertions, 7 deletions
diff --git a/helix-core/src/lib.rs b/helix-core/src/lib.rs index 6b93de78..4be3e71b 100644 --- a/helix-core/src/lib.rs +++ b/helix-core/src/lib.rs @@ -16,6 +16,7 @@ pub mod selection; mod state; pub mod syntax; mod transaction; +pub mod words; pub(crate) fn find_first_non_whitespace_char2(line: RopeSlice) -> Option<usize> { // find first non-whitespace char diff --git a/helix-core/src/movement.rs b/helix-core/src/movement.rs index 297911ae..c5e2df4a 100644 --- a/helix-core/src/movement.rs +++ b/helix-core/src/movement.rs @@ -174,22 +174,22 @@ pub fn move_next_word_end(slice: RopeSlice, mut begin: usize, count: usize) -> O // used for by-word movement -fn is_word(ch: char) -> bool { +pub(crate) fn is_word(ch: char) -> bool { ch.is_alphanumeric() || ch == '_' } -fn is_horiz_blank(ch: char) -> bool { +pub(crate) fn is_horiz_blank(ch: char) -> bool { matches!(ch, ' ' | '\t') } #[derive(Debug, Eq, PartialEq)] -enum Category { +pub(crate) enum Category { Whitespace, Eol, Word, Punctuation, } -fn categorize(ch: char) -> Category { +pub(crate) fn categorize(ch: char) -> Category { if ch == '\n' { Category::Eol } else if ch.is_ascii_whitespace() { diff --git a/helix-core/src/words.rs b/helix-core/src/words.rs new file mode 100644 index 00000000..0099d56a --- /dev/null +++ b/helix-core/src/words.rs @@ -0,0 +1,33 @@ +use crate::movement::{categorize, is_horiz_blank, is_word, skip_over_prev}; +use ropey::RopeSlice; + +#[must_use] +pub fn nth_prev_word_boundary(slice: RopeSlice, mut char_idx: usize, count: usize) -> usize { + let mut with_end = false; + + for _ in 0..count { + if char_idx == 0 { + break; + } + + // return if not skip while? + skip_over_prev(slice, &mut char_idx, |ch| ch == '\n'); + + with_end = skip_over_prev(slice, &mut char_idx, is_horiz_blank); + + // refetch + let ch = slice.char(char_idx); + + if is_word(ch) { + with_end = skip_over_prev(slice, &mut char_idx, is_word); + } else if ch.is_ascii_punctuation() { + with_end = skip_over_prev(slice, &mut char_idx, |ch| ch.is_ascii_punctuation()); + } + } + + if with_end { + char_idx + } else { + char_idx + 1 + } +} diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs index c88a5eee..f340da91 100644 --- a/helix-term/src/commands.rs +++ b/helix-term/src/commands.rs @@ -3,8 +3,8 @@ use helix_core::{ movement::{self, Direction}, object, pos_at_coords, regex::{self, Regex}, - register, search, selection, Change, ChangeSet, Position, Range, Rope, RopeSlice, Selection, - SmallVec, Tendril, Transaction, + register, search, selection, words, Change, ChangeSet, Position, Range, Rope, RopeSlice, + Selection, SmallVec, Tendril, Transaction, }; use helix_view::{ @@ -1782,7 +1782,6 @@ pub mod insert { pub fn delete_char_forward(cx: &mut Context) { let count = cx.count; - let doc = cx.doc(); let (view, doc) = cx.current(); let text = doc.text().slice(..); let transaction = @@ -1795,6 +1794,21 @@ pub mod insert { }); doc.apply(&transaction, view.id); } + + pub fn delete_word_backward(cx: &mut Context) { + let count = cx.count; + let (view, doc) = cx.current(); + let text = doc.text().slice(..); + let transaction = + Transaction::change_by_selection(doc.text(), doc.selection(view.id), |range| { + ( + words::nth_prev_word_boundary(text, range.head, count), + range.head, + None, + ) + }); + doc.apply(&transaction, view.id); + } } // Undo / Redo diff --git a/helix-term/src/keymap.rs b/helix-term/src/keymap.rs index e51dfbc5..6ef53915 100644 --- a/helix-term/src/keymap.rs +++ b/helix-term/src/keymap.rs @@ -360,6 +360,7 @@ pub fn default() -> Keymaps { } => commands::insert::insert_tab, ctrl!('x') => commands::completion, + ctrl!('w') => commands::insert::delete_word_backward, ), ) } |