diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 1d6ac4bcca2c1dfb9e34b461965adc97e4a6b6eb..eec6e367dc597eb61a37ad33e116fd6688ba7c66 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -2518,6 +2518,10 @@ impl Editor { key_context } + pub fn last_bounds(&self) -> Option<&Bounds> { + self.last_bounds.as_ref() + } + fn show_mouse_cursor(&mut self, cx: &mut Context) { if self.mouse_cursor_hidden { self.mouse_cursor_hidden = false; diff --git a/crates/vim/src/test.rs b/crates/vim/src/test.rs index 03adfc8af15cf92f7ee6c4c857c0f154e2c969f3..867b0829dde3eef418049f2bb4eec2985fabcfad 100644 --- a/crates/vim/src/test.rs +++ b/crates/vim/src/test.rs @@ -2213,3 +2213,52 @@ async fn test_multi_cursor_replay(cx: &mut gpui::TestAppContext) { Mode::Normal, ); } + +#[gpui::test] +async fn test_clipping_on_mode_change(cx: &mut gpui::TestAppContext) { + let mut cx = VimTestContext::new(cx, true).await; + + cx.set_state( + indoc! { + " + ˇverylongline + andsomelinebelow + " + }, + Mode::Normal, + ); + + cx.simulate_keystrokes("v e"); + cx.assert_state( + indoc! { + " + «verylonglineˇ» + andsomelinebelow + " + }, + Mode::Visual, + ); + + let mut pixel_position = cx.update_editor(|editor, window, cx| { + let snapshot = editor.snapshot(window, cx); + let current_head = editor.selections.newest_display(cx).end; + editor.last_bounds().unwrap().origin + + editor + .display_to_pixel_point(current_head, &snapshot, window) + .unwrap() + }); + pixel_position.x += px(100.); + // click beyond end of the line + cx.simulate_click(pixel_position, Modifiers::default()); + cx.run_until_parked(); + + cx.assert_state( + indoc! { + " + verylonglinˇe + andsomelinebelow + " + }, + Mode::Normal, + ); +} diff --git a/crates/vim/src/vim.rs b/crates/vim/src/vim.rs index c7fb8ffa35ea090296f137b11f08379db968ce3d..1c2ea0dccbc6e84d7a871f7a4d30fb6c6949044e 100644 --- a/crates/vim/src/vim.rs +++ b/crates/vim/src/vim.rs @@ -1092,6 +1092,8 @@ impl Vim { let mut point = selection.head(); if !selection.reversed && !selection.is_empty() { point = movement::left(map, selection.head()); + } else if selection.is_empty() { + point = map.clip_point(point, Bias::Left); } selection.collapse_to(point, selection.goal) } else if !last_mode.is_visual() && mode.is_visual() && selection.is_empty() { @@ -1608,7 +1610,7 @@ impl Vim { && !is_multicursor && [Mode::Visual, Mode::VisualLine, Mode::VisualBlock].contains(&self.mode) { - self.switch_mode(Mode::Normal, true, window, cx); + self.switch_mode(Mode::Normal, false, window, cx); } }