on master help sign in
Jump to the next number on the line before incrementing (#1778)
47fe739antoyo
helix-core/src/search.rs+24−2
@@ -1,6 +1,28 @@
1use crate::RopeSlice;
1use crate::RopeSlice;
2
2
3−pub fn find_nth_next(text: RopeSlice, ch: char, mut pos: usize, n: usize) -> Option<usize> {
3+// TODO: switch to std::str::Pattern when it is stable.
4+pub trait CharMatcher {
5+ fn char_match(&self, ch: char) -> bool;
6+}
7+
8+impl CharMatcher for char {
9+ fn char_match(&self, ch: char) -> bool {
10+ *self == ch
11+ }
12+}
13+
14+impl<F: Fn(&char) -> bool> CharMatcher for F {
15+ fn char_match(&self, ch: char) -> bool {
16+ (*self)(&ch)
17+ }
18+}
19+
20+pub fn find_nth_next<M: CharMatcher>(
21+ text: RopeSlice,
22+ char_matcher: M,
23+ mut pos: usize,
24+ n: usize,
25+) -> Option<usize> {
4 if pos >= text.len_chars() || n == 0 {
26 if pos >= text.len_chars() || n == 0 {
5 return None;
27 return None;
6 }
28 }
@@ -13,7 +35,7 @@ pub fn find_nth_next(text: RopeSlice, ch: char, mut pos: usize, n: usize) -> Opt
13
35
14 pos += 1;
36 pos += 1;
15
37
16− if c == ch {
38+ if char_matcher.char_match(c) {
17 break;
39 break;
18 }
40 }
19 }
41 }
helix-term/src/commands.rs+37−5
@@ -18,7 +18,8 @@ use helix_core::{
18 movement::{self, Direction},
18 movement::{self, Direction},
19 object, pos_at_coords,
19 object, pos_at_coords,
20 regex::{self, Regex, RegexBuilder},
20 regex::{self, Regex, RegexBuilder},
21+ search::{self, CharMatcher},
21− search, selection, shellwords, surround, textobject,
22+ selection, shellwords, surround, textobject,
22 tree_sitter::Node,
23 tree_sitter::Node,
23 unicode::width::UnicodeWidthChar,
24 unicode::width::UnicodeWidthChar,
24 LineEnding, Position, Range, Rope, RopeGraphemes, RopeSlice, Selection, SmallVec, Tendril,
25 LineEnding, Position, Range, Rope, RopeGraphemes, RopeSlice, Selection, SmallVec, Tendril,
@@ -1053,15 +1054,15 @@ where
1053//
1054//
1054
1055
1055#[inline]
1056#[inline]
1056−fn find_char_impl<F>(
1057+fn find_char_impl<F, M: CharMatcher + Clone + Copy>(
1057 editor: &mut Editor,
1058 editor: &mut Editor,
1058 search_fn: &F,
1059 search_fn: &F,
1059 inclusive: bool,
1060 inclusive: bool,
1060 extend: bool,
1061 extend: bool,
1061− ch: char,
1062+ char_matcher: M,
1062 count: usize,
1063 count: usize,
1063) where
1064) where
1064− F: Fn(RopeSlice, char, usize, usize, bool) -> Option<usize> + 'static,
1065+ F: Fn(RopeSlice, M, usize, usize, bool) -> Option<usize> + 'static,
1065{
1066{
1066 let (view, doc) = current!(editor);
1067 let (view, doc) = current!(editor);
1067 let text = doc.text().slice(..);
1068 let text = doc.text().slice(..);
@@ -1076,7 +1077,7 @@ fn find_char_impl<F>(
1076 range.head
1077 range.head
1077 };
1078 };
1078
1079
1079− search_fn(text, ch, search_start_pos, count, inclusive).map_or(range, |pos| {
1080+ search_fn(text, char_matcher, search_start_pos, count, inclusive).map_or(range, |pos| {
1080 if extend {
1081 if extend {
1081 range.put_cursor(text, pos, true)
1082 range.put_cursor(text, pos, true)
1082 } else {
1083 } else {
@@ -4327,8 +4328,39 @@ fn decrement(cx: &mut Context) {
4327 increment_impl(cx, -(cx.count() as i64));
4328 increment_impl(cx, -(cx.count() as i64));
4328}
4329}
4329
4330
4331+/// This function differs from find_next_char_impl in that it stops searching at the newline, but also
4332+/// starts searching at the current character, instead of the next.
4333+/// It does not want to start at the next character because this function is used for incrementing
4334+/// number and we don't want to move forward if we're already on a digit.
4335+fn find_next_char_until_newline<M: CharMatcher>(
4336+ text: RopeSlice,
4337+ char_matcher: M,
4338+ pos: usize,
4339+ _count: usize,
4340+ _inclusive: bool,
4341+) -> Option<usize> {
4342+ // Since we send the current line to find_nth_next instead of the whole text, we need to adjust
4343+ // the position we send to this function so that it's relative to that line and its returned
4344+ // position since it's expected this function returns a global position.
4345+ let line_index = text.char_to_line(pos);
4346+ let pos_delta = text.line_to_char(line_index);
4347+ let pos = pos - pos_delta;
4348+ search::find_nth_next(text.line(line_index), char_matcher, pos, 1).map(|pos| pos + pos_delta)
4349+}
4350+
4330/// Decrement object under cursor by `amount`.
4351/// Decrement object under cursor by `amount`.
4331fn increment_impl(cx: &mut Context, amount: i64) {
4352fn increment_impl(cx: &mut Context, amount: i64) {
4353+ // TODO: when incrementing or decrementing a number that gets a new digit or lose one, the
4354+ // selection is updated improperly.
4355+ find_char_impl(
4356+ cx.editor,
4357+ &find_next_char_until_newline,
4358+ true,
4359+ true,
4360+ char::is_ascii_digit,
4361+ 1,
4362+ );
4363+
4332 let (view, doc) = current!(cx.editor);
4364 let (view, doc) = current!(cx.editor);
4333 let selection = doc.selection(view.id);
4365 let selection = doc.selection(view.id);
4334 let text = doc.text().slice(..);
4366 let text = doc.text().slice(..);