diff --git a/crates/vim/src/helix.rs b/crates/vim/src/helix.rs index 923bd8c6a057819129b29b86e559c79a30f011f9..fe25852682c579733124224b0d2f996bad1440b6 100644 --- a/crates/vim/src/helix.rs +++ b/crates/vim/src/helix.rs @@ -157,6 +157,23 @@ impl Vim { } let (new_head, goal) = match motion { + // EndOfLine positions after the last character, but in + // helix visual mode we want the selection to end ON the + // last character. Adjust left here so the subsequent + // right-expansion (below) includes the last char without + // spilling into the newline. + Motion::EndOfLine { .. } => { + let (point, goal) = motion + .move_point( + map, + current_head, + selection.goal, + times, + &text_layout_details, + ) + .unwrap_or((current_head, selection.goal)); + (movement::saturating_left(map, point), goal) + } // Going to next word start is special cased // since Vim differs from Helix in that motion // Vim: `w` goes to the first character of a word @@ -1991,6 +2008,23 @@ mod test { cx.assert_state("h«ellˇ»o", Mode::HelixSelect); } + #[gpui::test] + async fn test_helix_select_end_of_line(cx: &mut gpui::TestAppContext) { + let mut cx = VimTestContext::new(cx, true).await; + cx.enable_helix(); + + // v g l d should delete to end of line without consuming the newline + cx.set_state("ˇThe quick brown\nfox jumps over", Mode::HelixNormal); + cx.simulate_keystrokes("v g l d"); + cx.assert_state("ˇ\nfox jumps over", Mode::HelixNormal); + + // same from the middle of a line — cursor lands on the last + // remaining character (the space) after delete + cx.set_state("The ˇquick brown\nfox jumps over", Mode::HelixNormal); + cx.simulate_keystrokes("v g l d"); + cx.assert_state("Theˇ \nfox jumps over", Mode::HelixNormal); + } + #[gpui::test] async fn test_helix_select_mode_motion_multiple_cursors(cx: &mut gpui::TestAppContext) { let mut cx = VimTestContext::new(cx, true).await;