1use gpui::{impl_actions, ViewContext};
2use search::{BufferSearchBar, SearchOptions};
3use serde_derive::Deserialize;
4use workspace::{searchable::Direction, Workspace};
5
6use crate::Vim;
7
8#[derive(Clone, Deserialize, PartialEq)]
9#[serde(rename_all = "camelCase")]
10pub(crate) struct MoveToNext {
11 #[serde(default)]
12 partial_word: bool,
13}
14
15#[derive(Clone, Deserialize, PartialEq)]
16#[serde(rename_all = "camelCase")]
17pub(crate) struct MoveToPrev {
18 #[serde(default)]
19 partial_word: bool,
20}
21
22impl_actions!(vim, [MoveToNext, MoveToPrev]);
23
24pub(crate) fn move_to_next(
25 workspace: &mut Workspace,
26 action: &MoveToNext,
27 cx: &mut ViewContext<Workspace>,
28) {
29 move_to_internal(workspace, Direction::Next, !action.partial_word, cx)
30}
31
32pub(crate) fn move_to_prev(
33 workspace: &mut Workspace,
34 action: &MoveToPrev,
35 cx: &mut ViewContext<Workspace>,
36) {
37 move_to_internal(workspace, Direction::Prev, !action.partial_word, cx)
38}
39
40fn move_to_internal(
41 workspace: &mut Workspace,
42 direction: Direction,
43 whole_word: bool,
44 cx: &mut ViewContext<Workspace>,
45) {
46 Vim::update(cx, |vim, cx| {
47 let pane = workspace.active_pane().clone();
48 pane.update(cx, |pane, cx| {
49 if let Some(search_bar) = pane.toolbar().read(cx).item_of_type::<BufferSearchBar>() {
50 search_bar.update(cx, |search_bar, cx| {
51 let mut options = SearchOptions::CASE_SENSITIVE;
52 options.set(SearchOptions::WHOLE_WORD, whole_word);
53 search_bar.select_word_under_cursor(direction, options, cx);
54 });
55 }
56 });
57 vim.clear_operator(cx);
58 });
59}
60
61#[cfg(test)]
62mod test {
63 use search::BufferSearchBar;
64
65 use crate::{state::Mode, test::VimTestContext};
66
67 #[gpui::test]
68 async fn test_move_to_next(cx: &mut gpui::TestAppContext) {
69 let mut cx = VimTestContext::new(cx, true).await;
70 let search_bar = cx.workspace(|workspace, cx| {
71 workspace
72 .active_pane()
73 .read(cx)
74 .toolbar()
75 .read(cx)
76 .item_of_type::<BufferSearchBar>()
77 .expect("Buffer search bar should be deployed")
78 });
79 cx.set_state("ˇhi\nhigh\nhi\n", Mode::Normal);
80
81 cx.simulate_keystrokes(["*"]);
82 search_bar.next_notification(&cx).await;
83 cx.assert_state("hi\nhigh\nˇhi\n", Mode::Normal);
84
85 cx.simulate_keystrokes(["*"]);
86 search_bar.next_notification(&cx).await;
87 cx.assert_state("ˇhi\nhigh\nhi\n", Mode::Normal);
88
89 cx.simulate_keystrokes(["#"]);
90 search_bar.next_notification(&cx).await;
91 cx.assert_state("hi\nhigh\nˇhi\n", Mode::Normal);
92
93 cx.simulate_keystrokes(["#"]);
94 search_bar.next_notification(&cx).await;
95 cx.assert_state("ˇhi\nhigh\nhi\n", Mode::Normal);
96
97 cx.simulate_keystrokes(["g", "*"]);
98 search_bar.next_notification(&cx).await;
99 cx.assert_state("hi\nˇhigh\nhi\n", Mode::Normal);
100
101 cx.simulate_keystrokes(["n"]);
102 cx.assert_state("hi\nhigh\nˇhi\n", Mode::Normal);
103
104 cx.simulate_keystrokes(["g", "#"]);
105 search_bar.next_notification(&cx).await;
106 cx.assert_state("hi\nˇhigh\nhi\n", Mode::Normal);
107 }
108}