mirror of https://github.com/helix-editor/helix
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_nextpull/1917/head
parent
855e438f55
commit
47fe739757
|
@ -1,6 +1,28 @@
|
||||||
use crate::RopeSlice;
|
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 {
|
if pos >= text.len_chars() || n == 0 {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
@ -13,7 +35,7 @@ pub fn find_nth_next(text: RopeSlice, ch: char, mut pos: usize, n: usize) -> Opt
|
||||||
|
|
||||||
pos += 1;
|
pos += 1;
|
||||||
|
|
||||||
if c == ch {
|
if char_matcher.char_match(c) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,8 @@ use helix_core::{
|
||||||
movement::{self, Direction},
|
movement::{self, Direction},
|
||||||
object, pos_at_coords,
|
object, pos_at_coords,
|
||||||
regex::{self, Regex, RegexBuilder},
|
regex::{self, Regex, RegexBuilder},
|
||||||
search, selection, shellwords, surround, textobject,
|
search::{self, CharMatcher},
|
||||||
|
selection, shellwords, surround, textobject,
|
||||||
tree_sitter::Node,
|
tree_sitter::Node,
|
||||||
unicode::width::UnicodeWidthChar,
|
unicode::width::UnicodeWidthChar,
|
||||||
LineEnding, Position, Range, Rope, RopeGraphemes, RopeSlice, Selection, SmallVec, Tendril,
|
LineEnding, Position, Range, Rope, RopeGraphemes, RopeSlice, Selection, SmallVec, Tendril,
|
||||||
|
@ -1053,15 +1054,15 @@ where
|
||||||
//
|
//
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn find_char_impl<F>(
|
fn find_char_impl<F, M: CharMatcher + Clone + Copy>(
|
||||||
editor: &mut Editor,
|
editor: &mut Editor,
|
||||||
search_fn: &F,
|
search_fn: &F,
|
||||||
inclusive: bool,
|
inclusive: bool,
|
||||||
extend: bool,
|
extend: bool,
|
||||||
ch: char,
|
char_matcher: M,
|
||||||
count: usize,
|
count: usize,
|
||||||
) where
|
) 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 (view, doc) = current!(editor);
|
||||||
let text = doc.text().slice(..);
|
let text = doc.text().slice(..);
|
||||||
|
@ -1076,7 +1077,7 @@ fn find_char_impl<F>(
|
||||||
range.head
|
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 {
|
if extend {
|
||||||
range.put_cursor(text, pos, true)
|
range.put_cursor(text, pos, true)
|
||||||
} else {
|
} else {
|
||||||
|
@ -4327,8 +4328,39 @@ fn decrement(cx: &mut Context) {
|
||||||
increment_impl(cx, -(cx.count() as i64));
|
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`.
|
/// Decrement object under cursor by `amount`.
|
||||||
fn increment_impl(cx: &mut Context, amount: i64) {
|
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 (view, doc) = current!(cx.editor);
|
||||||
let selection = doc.selection(view.id);
|
let selection = doc.selection(view.id);
|
||||||
let text = doc.text().slice(..);
|
let text = doc.text().slice(..);
|
||||||
|
|
Loading…
Reference in New Issue