Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Jump to the next number on the line before incrementing #1778

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 24 additions & 2 deletions helix-core/src/search.rs
Original file line number Diff line number Diff line change
@@ -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;
}
Expand All @@ -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;
}
}
Expand Down
42 changes: 37 additions & 5 deletions helix-term/src/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -1072,15 +1073,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(..);
Expand All @@ -1095,7 +1096,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 {
Expand Down Expand Up @@ -4331,8 +4332,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(..);
Expand Down