From ebed567adbb0cde7f63a830ef295df5cb91d2577 Mon Sep 17 00:00:00 2001 From: Dino Date: Mon, 2 Jun 2025 08:50:13 -0700 Subject: [PATCH] vim: Handle paste in visual line mode when cursor is at newline (#30791) This Pull Request fixes the current paste behavior in vim mode, when in visual mode, and the cursor is at a newline character. Currently this joins the pasted contents with the line right below it, but in vim this does not happen, so these changes make it so that Zed's vim mode behaves the same as vim for this specific case. Closes #29270 Release Notes: - Fixed pasting in vim's visual line mode when cursor is on a newline character --- crates/vim/src/normal/paste.rs | 26 ++++++++++++++++++++- crates/vim/test_data/test_paste_visual.json | 8 +++++++ 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/crates/vim/src/normal/paste.rs b/crates/vim/src/normal/paste.rs index 68b0acefacdc4b61d53862cfa6a2e6e73433ed11..f16f442705d18946104d603acb562bf06d38c49d 100644 --- a/crates/vim/src/normal/paste.rs +++ b/crates/vim/src/normal/paste.rs @@ -124,7 +124,20 @@ impl Vim { } let display_range = if !selection.is_empty() { - selection.start..selection.end + // If vim is in VISUAL LINE mode and the column for the + // selection's end point is 0, that means that the + // cursor is at the newline character (\n) at the end of + // the line. In this situation we'll want to move one + // position to the left, ensuring we don't join the last + // line of the selection with the line directly below. + let end_point = + if vim.mode == Mode::VisualLine && selection.end.column() == 0 { + movement::left(&display_map, selection.end) + } else { + selection.end + }; + + selection.start..end_point } else if line_mode { let point = if before { movement::line_beginning(&display_map, selection.start, false) @@ -553,6 +566,17 @@ mod test { ˇfox jumps over the lazy dog"}); cx.shared_clipboard().await.assert_eq("The quick brown\n"); + + // Copy line and paste in visual mode, with cursor on newline character. + cx.set_shared_state(indoc! {" + ˇThe quick brown + fox jumps over + the lazy dog"}) + .await; + cx.simulate_shared_keystrokes("y y shift-v j $ p").await; + cx.shared_state().await.assert_eq(indoc! {" + ˇThe quick brown + the lazy dog"}); } #[gpui::test] diff --git a/crates/vim/test_data/test_paste_visual.json b/crates/vim/test_data/test_paste_visual.json index c5597ba0f35d0a25a18cb7b9de345c694a505502..fb10f947827791ad69b4534523f1dd35cee67e92 100644 --- a/crates/vim/test_data/test_paste_visual.json +++ b/crates/vim/test_data/test_paste_visual.json @@ -41,3 +41,11 @@ {"Key":"p"} {"Get":{"state":"ˇfox jumps over\nthe lazy dog","mode":"Normal"}} {"ReadRegister":{"name":"\"","value":"The quick brown\n"}} +{"Put":{"state":"ˇThe quick brown\nfox jumps over\nthe lazy dog"}} +{"Key":"y"} +{"Key":"y"} +{"Key":"shift-v"} +{"Key":"j"} +{"Key":"$"} +{"Key":"p"} +{"Get":{"state":"ˇThe quick brown\nthe lazy dog","mode":"Normal"}}