From ad7b1f52b46efa4ad968d1b5cb8751ae606dd596 Mon Sep 17 00:00:00 2001 From: Dino Date: Tue, 10 Feb 2026 10:33:50 +0000 Subject: [PATCH] vim: Make end of line infallible (#48867) When using `$` to move to the end of line (`vim::EndOfLine`), the `vim::motion::Motion.move_point` method checks whether the new point, that is, the point after the motion is applied is different from the point that was passed as a method argument. If the point is not different, the point and selection goals are only updated if `vim::motion::Motion.infallible` returns true for the motion in question. In short, this means that, if the cursor was already at the end of the line, and it got there using `vim::Right`, for example, the selection goal wouldn't actually be set to `SelectionGoal::HorizontalPosition(f64::INFINITY)`, so when the cursor was moved to a shorter line, it wouldn't be set at the end of that line, even though `$` had been used. This commit updates `vim::motion::Motion.infallible` to ensure that, for `vim::motion::Motion::EndOfLine`, it returns `true`, so that the selection goal is always updated, regardless of whether the cursor is already at the end of the line. Closes #48855 - [X] Tests or screenshots needed? - [X] Code Reviewed - [X] Manual QA Release Notes: - vim: Fixed `$` not sticking to end-of-line on vertical motions (`j`/`k`) when the cursor was already at the end of the line via `l` or arrow keys --- crates/vim/src/motion.rs | 26 +++++++++++++++++-- ...test_end_of_line_with_vertical_motion.json | 6 +++++ 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/crates/vim/src/motion.rs b/crates/vim/src/motion.rs index 8d14e5a3e2c9b0e9be207942275b8ea447f43c70..b33d85bc5a5398b912ea999865729c362dced995 100644 --- a/crates/vim/src/motion.rs +++ b/crates/vim/src/motion.rs @@ -899,10 +899,9 @@ impl Motion { pub fn infallible(&self) -> bool { use Motion::*; match self { - StartOfDocument | EndOfDocument | CurrentLine => true, + StartOfDocument | EndOfDocument | CurrentLine | EndOfLine { .. } => true, Down { .. } | Up { .. } - | EndOfLine { .. } | MiddleOfLine { .. } | Matching { .. } | UnmatchedForward { .. } @@ -3901,6 +3900,29 @@ mod test { jumps over the lazy dog "}); + + // Test that, when the cursor is moved to the end of the line using `l`, + // if `$` is used, the cursor stays at the end of the line when moving + // to a longer line, ensuring that the selection goal was correctly + // updated. + cx.set_shared_state(indoc! {" + The quick brown fox + jumps over the + lazy dˇog + "}) + .await; + cx.simulate_shared_keystrokes("l").await; + cx.shared_state().await.assert_eq(indoc! {" + The quick brown fox + jumps over the + lazy doˇg + "}); + cx.simulate_shared_keystrokes("$ k").await; + cx.shared_state().await.assert_eq(indoc! {" + The quick brown fox + jumps over thˇe + lazy dog + "}); } #[gpui::test] diff --git a/crates/vim/test_data/test_end_of_line_with_vertical_motion.json b/crates/vim/test_data/test_end_of_line_with_vertical_motion.json index 16487a5d4c25a32d096ae274ec023b7d9ebf8c55..35d16b2843f5c829e3272a72b2edd8e0574ed8c1 100644 --- a/crates/vim/test_data/test_end_of_line_with_vertical_motion.json +++ b/crates/vim/test_data/test_end_of_line_with_vertical_motion.json @@ -14,3 +14,9 @@ {"Get":{"state":"The quick brown fox\njumps ˇover the\nlazy dog\n","mode":"Normal"}} {"Key":"k"} {"Get":{"state":"The quˇick brown fox\njumps over the\nlazy dog\n","mode":"Normal"}} +{"Put":{"state":"The quick brown fox\njumps over the\nlazy dˇog\n"}} +{"Key":"l"} +{"Get":{"state":"The quick brown fox\njumps over the\nlazy doˇg\n","mode":"Normal"}} +{"Key":"$"} +{"Key":"k"} +{"Get":{"state":"The quick brown fox\njumps over thˇe\nlazy dog\n","mode":"Normal"}}