vim: Fix `"seed_search_query_from_cursor" : "selection"` (#26107)

0x2CA created

Closes #9311
Closes #14843

Release Notes:

- Fixed vim `"seed_search_query_from_cursor" : "selection"`

Change summary

crates/vim/src/normal/search.rs |  7 ++++++-
crates/vim/src/vim.rs           | 24 +++++++++++++++++++++++-
2 files changed, 29 insertions(+), 2 deletions(-)

Detailed changes

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

@@ -304,6 +304,7 @@ impl Vim {
         };
         let count = Vim::take_count(cx).unwrap_or(1);
         let prior_selections = self.editor_selections(window, cx);
+        let cursor_word = self.editor_cursor_word(window, cx);
         let vim = cx.entity().clone();
 
         let searched = pane.update(cx, |pane, cx| {
@@ -325,10 +326,14 @@ impl Vim {
                 if !search_bar.show(window, cx) {
                     return None;
                 }
-                let Some(query) = search_bar.query_suggestion(window, cx) else {
+                let Some(query) = search_bar
+                    .query_suggestion(window, cx)
+                    .or_else(|| cursor_word)
+                else {
                     drop(search_bar.search("", None, window, cx));
                     return None;
                 };
+
                 let query = regex::escape(&query);
                 Some(search_bar.search(&query, Some(options), window, cx))
             });

crates/vim/src/vim.rs 🔗

@@ -30,7 +30,7 @@ use gpui::{
     KeyContext, KeystrokeEvent, Render, Subscription, Task, WeakEntity, Window,
 };
 use insert::{NormalBefore, TemporaryNormal};
-use language::{CursorShape, Point, Selection, SelectionGoal, TransactionId};
+use language::{CharKind, CursorShape, Point, Selection, SelectionGoal, TransactionId};
 pub use mode_indicator::ModeIndicator;
 use motion::Motion;
 use normal::search::SearchSubmit;
@@ -1199,6 +1199,28 @@ impl Vim {
         .unwrap_or_default()
     }
 
+    fn editor_cursor_word(
+        &mut self,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) -> Option<String> {
+        self.update_editor(window, cx, |_, editor, window, cx| {
+            let selection = editor.selections.newest::<usize>(cx);
+
+            let snapshot = &editor.snapshot(window, cx).buffer_snapshot;
+            let (range, kind) = snapshot.surrounding_word(selection.start, true);
+            if kind == Some(CharKind::Word) {
+                let text: String = snapshot.text_for_range(range).collect();
+                if !text.trim().is_empty() {
+                    return Some(text);
+                }
+            }
+
+            None
+        })
+        .unwrap_or_default()
+    }
+
     /// When doing an action that modifies the buffer, we start recording so that `.`
     /// will replay the action.
     pub fn start_recording(&mut self, cx: &mut Context<Self>) {