From 5e31d86f1f127058b0626a18f87d7eee11ab1f76 Mon Sep 17 00:00:00 2001 From: Conrad Irwin Date: Tue, 22 Apr 2025 22:28:13 -0600 Subject: [PATCH] Fix panic in vim selection restoration (#29251) Closes #27986 Closes #ISSUE Release Notes: - vim: Fixed a panic when using `gv` after `p` in visual line mode --- crates/vim/src/visual.rs | 39 +++++++++++++++++++++++--- crates/vim/test_data/test_p_g_v_y.json | 11 ++++++++ 2 files changed, 46 insertions(+), 4 deletions(-) create mode 100644 crates/vim/test_data/test_p_g_v_y.json diff --git a/crates/vim/src/visual.rs b/crates/vim/src/visual.rs index 6827c2c055..29ef3943b5 100644 --- a/crates/vim/src/visual.rs +++ b/crates/vim/src/visual.rs @@ -2,7 +2,7 @@ use std::sync::Arc; use collections::HashMap; use editor::{ - Bias, DisplayPoint, Editor, ToOffset, + Bias, DisplayPoint, Editor, display_map::{DisplaySnapshot, ToDisplayPoint}, movement, scroll::Autoscroll, @@ -132,16 +132,26 @@ pub fn register(editor: &mut Editor, cx: &mut Context) { } vim.update_editor(window, cx, |_, editor, window, cx| { + editor.set_clip_at_line_ends(false, cx); editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| { let map = s.display_map(); let ranges = ranges .into_iter() .map(|(start, end, reversed)| { - let new_end = movement::saturating_right(&map, end.to_display_point(&map)); + let mut new_end = + movement::saturating_right(&map, end.to_display_point(&map)); + let mut new_start = start.to_display_point(&map); + if new_start >= new_end { + if new_end.column() == 0 { + new_end = movement::right(&map, new_end) + } else { + new_start = movement::saturating_left(&map, new_end); + } + } Selection { id: s.new_selection_id(), - start: start.to_offset(&map.buffer_snapshot), - end: new_end.to_offset(&map, Bias::Left), + start: new_start.to_point(&map), + end: new_end.to_point(&map), reversed, goal: SelectionGoal::None, } @@ -1729,4 +1739,25 @@ mod test { Mode::Visual, ); } + + #[gpui::test] + async fn test_p_g_v_y(cx: &mut gpui::TestAppContext) { + let mut cx = NeovimBackedTestContext::new(cx).await; + + cx.set_shared_state(indoc! { + "The + quicˇk + brown + fox" + }) + .await; + cx.simulate_shared_keystrokes("y y j shift-v p g v y").await; + cx.shared_state().await.assert_eq(indoc! { + "The + quick + ˇquick + fox" + }); + cx.shared_clipboard().await.assert_eq("quick\n"); + } } diff --git a/crates/vim/test_data/test_p_g_v_y.json b/crates/vim/test_data/test_p_g_v_y.json new file mode 100644 index 0000000000..a275c333a3 --- /dev/null +++ b/crates/vim/test_data/test_p_g_v_y.json @@ -0,0 +1,11 @@ +{"Put":{"state":"The\nquicˇk\nbrown\nfox"}} +{"Key":"y"} +{"Key":"y"} +{"Key":"j"} +{"Key":"shift-v"} +{"Key":"p"} +{"Key":"g"} +{"Key":"v"} +{"Key":"y"} +{"Get":{"state":"The\nquick\nˇquick\nfox","mode":"Normal"}} +{"ReadRegister":{"name":"\"","value":"quick\n"}}