helix: Fix normal-mode selection expansion during `vim::Scroll` commands (#47024)

Josh Robson Chase and dino created

Fixes the `vim::ScrollUp/Down` commands when a normal-mode vim or helix
selection exists.

Pretty straightforward: make the `scroll_editor` function aware of the
current vim/helix mode, and only expand the selection if a visual mode
is active.

Closes #47022

Release Notes:

- Fixed bug causing normal-mode vim/helix selections to get expanded
during `vim::Scroll` commands

---------

Co-authored-by: dino <dinojoaocosta@gmail.com>

Change summary

crates/vim/src/helix.rs         | 77 +++++++++++++++++++++++++++++++++++
crates/vim/src/normal/scroll.rs |  8 ++-
2 files changed, 82 insertions(+), 3 deletions(-)

Detailed changes

crates/vim/src/helix.rs 🔗

@@ -1758,4 +1758,81 @@ mod test {
             assert_eq!(vim_mode, Some(Mode::HelixNormal));
         });
     }
+
+    #[gpui::test]
+    async fn test_scroll_with_selection(cx: &mut gpui::TestAppContext) {
+        let mut cx = VimTestContext::new(cx, true).await;
+        cx.enable_helix();
+
+        // Start with a selection
+        cx.set_state(
+            indoc! {"
+            «lineˇ» one
+            line two
+            line three
+            line four
+            line five"},
+            Mode::HelixNormal,
+        );
+
+        // Scroll down, selection should collapse
+        cx.simulate_keystrokes("ctrl-d");
+        cx.assert_state(
+            indoc! {"
+            line one
+            line two
+            line three
+            line four
+            line fiveˇ"},
+            Mode::HelixNormal,
+        );
+
+        // Make a new selection
+        cx.simulate_keystroke("b");
+        cx.assert_state(
+            indoc! {"
+            line one
+            line two
+            line three
+            line four
+            line «ˇfive»"},
+            Mode::HelixNormal,
+        );
+
+        // And scroll up, once again collapsing the selection.
+        cx.simulate_keystroke("ctrl-u");
+        cx.assert_state(
+            indoc! {"
+            line one
+            line two
+            line three
+            line ˇfour
+            line five"},
+            Mode::HelixNormal,
+        );
+
+        // Enter select mode
+        cx.simulate_keystroke("v");
+        cx.assert_state(
+            indoc! {"
+            line one
+            line two
+            line three
+            line «fˇ»our
+            line five"},
+            Mode::HelixSelect,
+        );
+
+        // And now the selection should be kept/expanded.
+        cx.simulate_keystroke("ctrl-d");
+        cx.assert_state(
+            indoc! {"
+            line one
+            line two
+            line three
+            line «four
+            line fiveˇ»"},
+            Mode::HelixSelect,
+        );
+    }
 }

crates/vim/src/normal/scroll.rs 🔗

@@ -1,4 +1,4 @@
-use crate::Vim;
+use crate::{Vim, state::Mode};
 use editor::{
     DisplayPoint, Editor, EditorSettings, SelectionEffects,
     display_map::{DisplayRow, ToDisplayPoint},
@@ -95,16 +95,18 @@ impl Vim {
         by: fn(c: Option<f32>) -> ScrollAmount,
     ) {
         let amount = by(Vim::take_count(cx).map(|c| c as f32));
+        let mode = self.mode;
         Vim::take_forced_motion(cx);
         self.exit_temporary_normal(window, cx);
         self.update_editor(cx, |_, editor, cx| {
-            scroll_editor(editor, move_cursor, amount, window, cx)
+            scroll_editor(editor, mode, move_cursor, amount, window, cx)
         });
     }
 }
 
 fn scroll_editor(
     editor: &mut Editor,
+    mode: Mode,
     preserve_cursor_position: bool,
     amount: ScrollAmount,
     window: &mut Window,
@@ -255,7 +257,7 @@ fn scroll_editor(
                     _ => selection.goal,
                 };
 
-                if selection.is_empty() {
+                if selection.is_empty() || !mode.is_visual() {
                     selection.collapse_to(new_head, goal)
                 } else {
                     selection.set_head(new_head, goal)