aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorantoyo2022-04-01 13:14:37 +0000
committerGitHub2022-04-01 13:14:37 +0000
commit47fe7397574208ecdcccc63770ce1b0374de1126 (patch)
tree41a9186280d79aa8fd77ace87c09eb136f173f0a
parent855e438f55cb278de8203ac4911561c4c7ad656c (diff)
Jump to the next number on the line before incrementing (#1778)
* Jump to the next number on the line before incrementing Partially fix #1645 * Refactor to avoid duplicating find_nth_next
-rw-r--r--helix-core/src/search.rs26
-rw-r--r--helix-term/src/commands.rs42
2 files changed, 61 insertions, 7 deletions
diff --git a/helix-core/src/search.rs b/helix-core/src/search.rs
index 243ac227..81cb4129 100644
--- a/helix-core/src/search.rs
+++ b/helix-core/src/search.rs
@@ -1,6 +1,28 @@
use crate::RopeSlice;
-pub fn find_nth_next(text: RopeSlice, ch: char, mut pos: usize, n: usize) -> Option<usize> {
+// TODO: switch to std::str::Pattern when it is stable.
+pub trait CharMatcher {
+ fn char_match(&self, ch: char) -> bool;
+}
+
+impl CharMatcher for char {
+ fn char_match(&self, ch: char) -> bool {
+ *self == ch
+ }
+}
+
+impl<F: Fn(&char) -> bool> CharMatcher for F {
+ fn char_match(&self, ch: char) -> bool {
+ (*self)(&ch)
+ }
+}
+
+pub fn find_nth_next<M: CharMatcher>(
+ text: RopeSlice,
+ char_matcher: M,
+ mut pos: usize,
+ n: usize,
+) -> Option<usize> {
if pos >= text.len_chars() || n == 0 {
return None;
}
@@ -13,7 +35,7 @@ pub fn find_nth_next(text: RopeSlice, ch: char, mut pos: usize, n: usize) -> Opt
pos += 1;
- if c == ch {
+ if char_matcher.char_match(c) {
break;
}
}
diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs
index 3c5e0852..104db459 100644
--- a/helix-term/src/commands.rs
+++ b/helix-term/src/commands.rs
@@ -18,7 +18,8 @@ use helix_core::{
movement::{self, Direction},
object, pos_at_coords,
regex::{self, Regex, RegexBuilder},
- search, selection, shellwords, surround, textobject,
+ search::{self, CharMatcher},
+ selection, shellwords, surround, textobject,
tree_sitter::Node,
unicode::width::UnicodeWidthChar,
LineEnding, Position, Range, Rope, RopeGraphemes, RopeSlice, Selection, SmallVec, Tendril,
@@ -1053,15 +1054,15 @@ where
//
#[inline]
-fn find_char_impl<F>(
+fn find_char_impl<F, M: CharMatcher + Clone + Copy>(
editor: &mut Editor,
search_fn: &F,
inclusive: bool,
extend: bool,
- ch: char,
+ char_matcher: M,
count: usize,
) where
- F: Fn(RopeSlice, char, usize, usize, bool) -> Option<usize> + 'static,
+ F: Fn(RopeSlice, M, usize, usize, bool) -> Option<usize> + 'static,
{
let (view, doc) = current!(editor);
let text = doc.text().slice(..);
@@ -1076,7 +1077,7 @@ fn find_char_impl<F>(
range.head
};
- search_fn(text, ch, search_start_pos, count, inclusive).map_or(range, |pos| {
+ search_fn(text, char_matcher, search_start_pos, count, inclusive).map_or(range, |pos| {
if extend {
range.put_cursor(text, pos, true)
} else {
@@ -4327,8 +4328,39 @@ fn decrement(cx: &mut Context) {
increment_impl(cx, -(cx.count() as i64));
}
+/// This function differs from find_next_char_impl in that it stops searching at the newline, but also
+/// starts searching at the current character, instead of the next.
+/// It does not want to start at the next character because this function is used for incrementing
+/// number and we don't want to move forward if we're already on a digit.
+fn find_next_char_until_newline<M: CharMatcher>(
+ text: RopeSlice,
+ char_matcher: M,
+ pos: usize,
+ _count: usize,
+ _inclusive: bool,
+) -> Option<usize> {
+ // Since we send the current line to find_nth_next instead of the whole text, we need to adjust
+ // the position we send to this function so that it's relative to that line and its returned
+ // position since it's expected this function returns a global position.
+ let line_index = text.char_to_line(pos);
+ let pos_delta = text.line_to_char(line_index);
+ let pos = pos - pos_delta;
+ search::find_nth_next(text.line(line_index), char_matcher, pos, 1).map(|pos| pos + pos_delta)
+}
+
/// Decrement object under cursor by `amount`.
fn increment_impl(cx: &mut Context, amount: i64) {
+ // TODO: when incrementing or decrementing a number that gets a new digit or lose one, the
+ // selection is updated improperly.
+ find_char_impl(
+ cx.editor,
+ &find_next_char_until_newline,
+ true,
+ true,
+ char::is_ascii_digit,
+ 1,
+ );
+
let (view, doc) = current!(cx.editor);
let selection = doc.selection(view.id);
let text = doc.text().slice(..);