vim: Retain search direction upon search submit (#16754)

fletcher gornick and Conrad Irwin created

Before, when using `?` and `#` for backwards search it would initially
search for the previous match, but upon subsequent inputs to `n` and
`N`, `n` is always treated as "forward" and `N` is always treated as
"backward", instead of continuing the search direction.

now, if i use `?` or `#` for backward search, `n` will go to the
previous selection, and `N` will go to the next. Functionality stays the
same for `/` and `*`.

Release Notes:

- vim: Fixed `n` direction after searching backwards

---------

Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com>

Change summary

crates/vim/src/normal/search.rs            | 19 +++++++++++++++++--
crates/vim/test_data/test_backwards_n.json |  8 ++++++++
2 files changed, 25 insertions(+), 2 deletions(-)

Detailed changes

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

@@ -81,11 +81,11 @@ impl Vim {
     }
 
     fn move_to_next_match(&mut self, _: &MoveToNextMatch, cx: &mut ViewContext<Self>) {
-        self.move_to_match_internal(Direction::Next, cx)
+        self.move_to_match_internal(self.search.direction, cx)
     }
 
     fn move_to_prev_match(&mut self, _: &MoveToPrevMatch, cx: &mut ViewContext<Self>) {
-        self.move_to_match_internal(Direction::Prev, cx)
+        self.move_to_match_internal(self.search.direction.opposite(), cx)
     }
 
     fn search(&mut self, action: &Search, cx: &mut ViewContext<Self>) {
@@ -236,6 +236,7 @@ impl Vim {
         let vim = cx.view().clone();
 
         let searched = pane.update(cx, |pane, cx| {
+            self.search.direction = direction;
             let Some(search_bar) = pane.toolbar().read(cx).item_of_type::<BufferSearchBar>() else {
                 return false;
             };
@@ -705,6 +706,20 @@ mod test {
         cx.shared_state().await.assert_eq("ˇcd a.c. abcd");
     }
 
+    #[gpui::test]
+    async fn test_backwards_n(cx: &mut gpui::TestAppContext) {
+        let mut cx = NeovimBackedTestContext::new(cx).await;
+
+        cx.set_shared_state("ˇa b a b a b a").await;
+        cx.simulate_shared_keystrokes("*").await;
+        cx.simulate_shared_keystrokes("n").await;
+        cx.shared_state().await.assert_eq("a b a b ˇa b a");
+        cx.simulate_shared_keystrokes("#").await;
+        cx.shared_state().await.assert_eq("a b ˇa b a b a");
+        cx.simulate_shared_keystrokes("n").await;
+        cx.shared_state().await.assert_eq("ˇa b a b a b a");
+    }
+
     #[gpui::test]
     async fn test_v_search(cx: &mut gpui::TestAppContext) {
         let mut cx = NeovimBackedTestContext::new(cx).await;

crates/vim/test_data/test_backwards_n.json 🔗

@@ -0,0 +1,8 @@
+{"Put":{"state":"ˇa b a b a b a"}}
+{"Key":"*"}
+{"Key":"n"}
+{"Get":{"state":"a b a b ˇa b a","mode":"Normal"}}
+{"Key":"#"}
+{"Get":{"state":"a b ˇa b a b a","mode":"Normal"}}
+{"Key":"n"}
+{"Get":{"state":"ˇa b a b a b a","mode":"Normal"}}