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 1 commit
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
49 changes: 49 additions & 0 deletions helix-core/src/search.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,22 @@
use crate::RopeSlice;

// 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(text: RopeSlice, ch: char, mut pos: usize, n: usize) -> Option<usize> {
if pos >= text.len_chars() || n == 0 {
return None;
Expand All @@ -22,6 +39,38 @@ pub fn find_nth_next(text: RopeSlice, ch: char, mut pos: usize, n: usize) -> Opt
Some(pos - 1)
}

/// Like find_nth_next, but stops at the first newline character.
pub fn find_nth_next_until_newline<M: CharMatcher>(
text: RopeSlice,
char_matcher: M,
mut pos: usize,
n: usize,
) -> Option<usize> {
if pos >= text.len_chars() || n == 0 {
return None;
}

let mut chars = text.chars_at(pos);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rather than a separate method that has to check for \n & \r, you could modify find_nth_prev to use a matcher/pattern and pass in a text slice of a single line: text.line(n)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.
This made the function find_next_char_until_newline a bit more complicated, though.
Any idea if I could have done it in a simpler way?


for _ in 0..n {
loop {
let c = chars.next()?;

pos += 1;

if c == '\n' || c == '\r' {
return None;
}

if char_matcher.char_match(c) {
break;
}
}
}

Some(pos - 1)
}

pub fn find_nth_prev(text: RopeSlice, ch: char, mut pos: usize, n: usize) -> Option<usize> {
if pos == 0 || n == 0 {
return None;
Expand Down
36 changes: 31 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,33 @@ 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,
n: usize,
_inclusive: bool,
) -> Option<usize> {
search::find_nth_next_until_newline(text, char_matcher, pos, n)
}

/// 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