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

Fix find commands for buffers with non-LF line-endings #8111

Merged
merged 2 commits into from
Sep 3, 2023
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
69 changes: 62 additions & 7 deletions helix-term/src/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1251,6 +1251,65 @@ fn extend_next_long_word_end(cx: &mut Context) {
extend_word_impl(cx, movement::move_next_long_word_end)
}

/// Separate branch to find_char designed only for <ret> char.
//
// This is necessary because the one document can have different line endings inside. And we
// cannot predict what character to find when <ret> is pressed. On the current line it can be `lf`
// but on the next line it can be `crlf`. That's why [`find_char_impl`] cannot be applied here.
fn find_char_ending(
cx: &mut Context,
count: usize,
direction: Direction,
inclusive: bool,
extend: bool,
) {
let (view, doc) = current!(cx.editor);
let text = doc.text().slice(..);

let selection = doc.selection(view.id).clone().transform(|range| {
let cursor = range.cursor(text);
let cursor_line = range.cursor_line(text);

// Finding the line where we're going to find <ret>. Depends mostly on
// `count`, but also takes into account edge cases where we're already at the end
// of a line or the beginning of a line
let find_on_line = match direction {
Direction::Forward => {
let on_edge = line_end_char_index(&text, cursor_line) == cursor;
let line = cursor_line + count - 1 + (on_edge as usize);
if line >= text.len_lines() - 1 {
return range;
} else {
line
}
}
Direction::Backward => {
let on_edge = text.line_to_char(cursor_line) == cursor && !inclusive;
let line = cursor_line as isize - (count as isize - 1 + on_edge as isize);
if line <= 0 {
return range;
} else {
line as usize
}
}
};

let pos = match (direction, inclusive) {
(Direction::Forward, true) => line_end_char_index(&text, find_on_line),
(Direction::Forward, false) => line_end_char_index(&text, find_on_line) - 1,
(Direction::Backward, true) => line_end_char_index(&text, find_on_line - 1),
(Direction::Backward, false) => text.line_to_char(find_on_line),
};

if extend {
range.put_cursor(text, pos, true)
} else {
Range::point(range.cursor(text)).put_cursor(text, pos, true)
}
});
doc.set_selection(view.id, selection);
}

fn find_char(cx: &mut Context, direction: Direction, inclusive: bool, extend: bool) {
// TODO: count is reset to 1 before next key so we move it into the closure here.
// Would be nice to carry over.
Expand All @@ -1264,13 +1323,9 @@ fn find_char(cx: &mut Context, direction: Direction, inclusive: bool, extend: bo
KeyEvent {
code: KeyCode::Enter,
..
} =>
// TODO: this isn't quite correct when CRLF is involved.
// This hack will work in most cases, since documents don't
// usually mix line endings. But we should fix it eventually
// anyway.
{
doc!(cx.editor).line_ending.as_str().chars().next().unwrap()
} => {
find_char_ending(cx, count, direction, inclusive, extend);
return;
}

KeyEvent {
Expand Down
39 changes: 39 additions & 0 deletions helix-term/tests/test/movement.rs
Original file line number Diff line number Diff line change
Expand Up @@ -513,3 +513,42 @@ async fn select_mode_tree_sitter_prev_function_goes_backwards_to_object() -> any

Ok(())
}

#[tokio::test(flavor = "multi_thread")]
async fn find_char_ending() -> anyhow::Result<()> {
test((
helpers::platform_line(indoc! {
"\
one
#[|t]#wo
three"
}),
"T<ret>gll2f<ret>",
helpers::platform_line(indoc! {
"\
one
two#[
|]#three"
}),
))
.await?;

test((
helpers::platform_line(indoc! {
"\
#[|o]#ne
two
three"
}),
"f<ret>2t<ret>ghT<ret>F<ret>",
helpers::platform_line(indoc! {
"\
one#[|
t]#wo
three"
}),
))
.await?;

Ok(())
}
Loading