@@ -197,10 +197,11 @@
"p": "vim::Paste",
"u": "editor::Undo",
"ctrl-r": "editor::Redo",
- "/": [
- "buffer_search::Deploy",
+ "/": "vim::Search",
+ "?": [
+ "vim::Search",
{
- "focus": true
+ "backwards": true,
}
],
"ctrl-f": "vim::PageDown",
@@ -356,7 +357,8 @@
{
"context": "BufferSearchBar",
"bindings": {
- "enter": "buffer_search::FocusEditor"
+ "enter": "buffer_search::FocusEditor",
+ "escape": "buffer_search::Dismiss"
}
}
]
@@ -28,7 +28,6 @@ use self::{
case::change_case,
change::{change_motion, change_object},
delete::{delete_motion, delete_object},
- search::{move_to_next, move_to_prev},
substitute::substitute,
yank::{yank_motion, yank_object},
};
@@ -59,8 +58,7 @@ pub fn init(cx: &mut AppContext) {
cx.add_action(insert_line_above);
cx.add_action(insert_line_below);
cx.add_action(change_case);
- cx.add_action(move_to_next);
- cx.add_action(move_to_prev);
+ search::init(cx);
cx.add_action(|_: &mut Workspace, _: &Substitute, cx| {
Vim::update(cx, |vim, cx| {
let times = vim.pop_number_operator(cx);
@@ -1,4 +1,4 @@
-use gpui::{impl_actions, ViewContext};
+use gpui::{impl_actions, AppContext, ViewContext};
use search::{BufferSearchBar, SearchOptions};
use serde_derive::Deserialize;
use workspace::{searchable::Direction, Workspace};
@@ -19,25 +19,47 @@ pub(crate) struct MoveToPrev {
partial_word: bool,
}
-impl_actions!(vim, [MoveToNext, MoveToPrev]);
+#[derive(Clone, Deserialize, PartialEq)]
+pub(crate) struct Search {
+ #[serde(default)]
+ backwards: bool,
+}
-pub(crate) fn move_to_next(
- workspace: &mut Workspace,
- action: &MoveToNext,
- cx: &mut ViewContext<Workspace>,
-) {
+impl_actions!(vim, [MoveToNext, MoveToPrev, Search]);
+
+pub(crate) fn init(cx: &mut AppContext) {
+ cx.add_action(move_to_next);
+ cx.add_action(move_to_prev);
+ cx.add_action(search);
+}
+
+fn move_to_next(workspace: &mut Workspace, action: &MoveToNext, cx: &mut ViewContext<Workspace>) {
move_to_internal(workspace, Direction::Next, !action.partial_word, cx)
}
-pub(crate) fn move_to_prev(
- workspace: &mut Workspace,
- action: &MoveToPrev,
- cx: &mut ViewContext<Workspace>,
-) {
+fn move_to_prev(workspace: &mut Workspace, action: &MoveToPrev, cx: &mut ViewContext<Workspace>) {
move_to_internal(workspace, Direction::Prev, !action.partial_word, cx)
}
-fn move_to_internal(
+fn search(workspace: &mut Workspace, action: &Search, cx: &mut ViewContext<Workspace>) {
+ let pane = workspace.active_pane().clone();
+ pane.update(cx, |pane, cx| {
+ if let Some(search_bar) = pane.toolbar().read(cx).item_of_type::<BufferSearchBar>() {
+ search_bar.update(cx, |search_bar, cx| {
+ let options = SearchOptions::CASE_SENSITIVE | SearchOptions::REGEX;
+ let direction = if action.backwards {
+ Direction::Prev
+ } else {
+ Direction::Next
+ };
+ search_bar.select_match(direction, cx);
+ search_bar.show_with_options(true, false, options, cx);
+ })
+ }
+ })
+}
+
+pub fn move_to_internal(
workspace: &mut Workspace,
direction: Direction,
whole_word: bool,
@@ -60,6 +82,7 @@ fn move_to_internal(
#[cfg(test)]
mod test {
+ use editor::DisplayPoint;
use search::BufferSearchBar;
use crate::{state::Mode, test::VimTestContext};
@@ -105,4 +128,65 @@ mod test {
search_bar.next_notification(&cx).await;
cx.assert_state("hi\nˇhigh\nhi\n", Mode::Normal);
}
+
+ #[gpui::test]
+ async fn test_search(cx: &mut gpui::TestAppContext) {
+ let mut cx = VimTestContext::new(cx, true).await;
+
+ cx.set_state("aa\nbˇb\ncc\ncc\ncc\n", Mode::Normal);
+ cx.simulate_keystrokes(["/", "c", "c"]);
+
+ let search_bar = cx.workspace(|workspace, cx| {
+ workspace
+ .active_pane()
+ .read(cx)
+ .toolbar()
+ .read(cx)
+ .item_of_type::<BufferSearchBar>()
+ .expect("Buffer search bar should be deployed")
+ });
+
+ search_bar.read_with(cx.cx, |bar, cx| {
+ assert_eq!(bar.query_editor.read(cx).text(cx), "cc");
+ });
+
+ // wait for the query editor change event to fire.
+ search_bar.next_notification(&cx).await;
+
+ cx.update_editor(|editor, cx| {
+ let highlights = editor.all_background_highlights(cx);
+ assert_eq!(3, highlights.len());
+ assert_eq!(
+ DisplayPoint::new(2, 0)..DisplayPoint::new(2, 2),
+ highlights[0].0
+ )
+ });
+
+ cx.simulate_keystrokes(["enter"]);
+
+ // n to go to next/N to go to previous
+ cx.assert_state("aa\nbb\nˇcc\ncc\ncc\n", Mode::Normal);
+ cx.simulate_keystrokes(["n"]);
+ cx.assert_state("aa\nbb\ncc\nˇcc\ncc\n", Mode::Normal);
+ cx.simulate_keystrokes(["shift-n"]);
+
+ // ?<enter> to go to previous
+ cx.assert_state("aa\nbb\nˇcc\ncc\ncc\n", Mode::Normal);
+ cx.simulate_keystrokes(["?", "enter"]);
+ cx.assert_state("aa\nbb\ncc\ncc\nˇcc\n", Mode::Normal);
+ cx.simulate_keystrokes(["?", "enter"]);
+
+ // /<enter> to go to next
+ cx.assert_state("aa\nbb\ncc\nˇcc\ncc\n", Mode::Normal);
+ cx.simulate_keystrokes(["/", "enter"]);
+ cx.assert_state("aa\nbb\ncc\ncc\nˇcc\n", Mode::Normal);
+
+ // ?{search}<enter> to search backwards
+ cx.simulate_keystrokes(["?", "b", "enter"]);
+
+ // wait for the query editor change event to fire.
+ search_bar.next_notification(&cx).await;
+
+ cx.assert_state("aa\nbˇb\ncc\ncc\ncc\n", Mode::Normal);
+ }
}