diff --git a/TODO.md b/TODO.md index 72ceaadc..f65a3536 100644 --- a/TODO.md +++ b/TODO.md @@ -25,7 +25,7 @@ - [ ] CI binary builds - [ ] regex search / select next -- [ ] f / t mappings +- [x] f / t mappings - [ ] open_above (O) command - [ ] = for auto indent line/selection - [x] q should only close the view, if all are closed, close the editor diff --git a/helix-core/src/search.rs b/helix-core/src/search.rs index c03f60df..af754ab7 100644 --- a/helix-core/src/search.rs +++ b/helix-core/src/search.rs @@ -1,6 +1,12 @@ use crate::RopeSlice; -pub fn find_nth_next(text: RopeSlice, ch: char, mut pos: usize, n: usize) -> Option { +pub fn find_nth_next( + text: RopeSlice, + ch: char, + mut pos: usize, + n: usize, + inclusive: bool, +) -> Option { // start searching right after pos let mut chars = text.chars_at(pos + 1); @@ -16,10 +22,20 @@ pub fn find_nth_next(text: RopeSlice, ch: char, mut pos: usize, n: usize) -> Opt } } + if !inclusive { + pos -= 1; + } + Some(pos) } -pub fn find_nth_prev(text: RopeSlice, ch: char, mut pos: usize, n: usize) -> Option { +pub fn find_nth_prev( + text: RopeSlice, + ch: char, + mut pos: usize, + n: usize, + inclusive: bool, +) -> Option { // start searching right before pos let mut chars = text.chars_at(pos.saturating_sub(1)); @@ -35,5 +51,9 @@ pub fn find_nth_prev(text: RopeSlice, ch: char, mut pos: usize, n: usize) -> Opt } } + if !inclusive { + pos -= 1; + } + Some(pos) } diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs index f60f646e..edc38a48 100644 --- a/helix-term/src/commands.rs +++ b/helix-term/src/commands.rs @@ -5,7 +5,7 @@ regex::{self, Regex}, register, search, selection, state::{coords_at_pos, pos_at_coords, Direction, Granularity, State}, - Change, ChangeSet, Position, Range, Selection, Tendril, Transaction, + Change, ChangeSet, Position, Range, RopeSlice, Selection, Tendril, Transaction, }; use once_cell::sync::Lazy; @@ -236,7 +236,13 @@ pub fn extend_next_word_end(cx: &mut Context) { doc.set_selection(selection); } -pub fn find_next_char(cx: &mut Context) { +#[inline] +fn _find_char(cx: &mut Context, search_fn: F, inclusive: bool, extend: bool) +where + // TODO: make an options struct for and abstract this Fn into a searcher type + // use the definition for w/b/e too + F: Fn(RopeSlice, char, usize, usize, bool) -> Option, +{ // TODO: count is reset to 1 before next key so we move it into the closure here. // Would be nice to carry over. let count = cx.count; @@ -252,9 +258,13 @@ pub fn find_next_char(cx: &mut Context) { let text = doc.text().slice(..); let selection = doc.selection().transform(|mut range| { - if let Some(pos) = search::find_nth_next(text, ch, range.head, count) { - Range::new(range.head, pos) - // or (range.anchor, pos) for extend + if let Some(pos) = search::find_nth_next(text, ch, range.head, count, inclusive) { + if extend { + Range::new(range.anchor, pos) + } else { + // select + Range::new(range.head, pos) + } // or (pos, pos) to move to found val } else { range @@ -266,6 +276,78 @@ pub fn find_next_char(cx: &mut Context) { }) } +pub fn find_till_char(cx: &mut Context) { + _find_char( + cx, + search::find_nth_next, + false, /* inclusive */ + false, /* extend */ + ) +} + +pub fn find_next_char(cx: &mut Context) { + _find_char( + cx, + search::find_nth_next, + true, /* inclusive */ + false, /* extend */ + ) +} + +pub fn extend_till_char(cx: &mut Context) { + _find_char( + cx, + search::find_nth_next, + false, /* inclusive */ + true, /* extend */ + ) +} + +pub fn extend_next_char(cx: &mut Context) { + _find_char( + cx, + search::find_nth_next, + true, /* inclusive */ + true, /* extend */ + ) +} + +pub fn till_prev_char(cx: &mut Context) { + _find_char( + cx, + search::find_nth_prev, + false, /* inclusive */ + false, /* extend */ + ) +} + +pub fn find_prev_char(cx: &mut Context) { + _find_char( + cx, + search::find_nth_prev, + true, /* inclusive */ + false, /* extend */ + ) +} + +pub fn extend_till_prev_char(cx: &mut Context) { + _find_char( + cx, + search::find_nth_prev, + false, /* inclusive */ + true, /* extend */ + ) +} + +pub fn extend_prev_char(cx: &mut Context) { + _find_char( + cx, + search::find_nth_prev, + true, /* inclusive */ + true, /* extend */ + ) +} + fn scroll(view: &mut View, offset: usize, direction: Direction) { use Direction::*; let text = view.doc.text().slice(..); diff --git a/helix-term/src/keymap.rs b/helix-term/src/keymap.rs index d956679a..58a465fe 100644 --- a/helix-term/src/keymap.rs +++ b/helix-term/src/keymap.rs @@ -137,10 +137,10 @@ pub fn default() -> Keymaps { key!('k') => commands::move_line_up, key!('l') => commands::move_char_right, - // key!('t') => commands::till_next_char, + key!('t') => commands::find_till_char, key!('f') => commands::find_next_char, - // key!('T') => commands::till_prev_char, - // key!('f') => commands::find_prev_char, + shift!('T') => commands::till_prev_char, + shift!('F') => commands::find_prev_char, // and matching set for select mode (extend) key!('0') => commands::move_line_start, @@ -261,6 +261,11 @@ pub fn default() -> Keymaps { key!('b') => commands::extend_prev_word_start, key!('e') => commands::extend_next_word_end, + key!('t') => commands::extend_till_char, + key!('f') => commands::extend_next_char, + shift!('T') => commands::extend_till_prev_char, + shift!('F') => commands::extend_prev_char, + Key { code: KeyCode::Esc, modifiers: Modifiers::NONE