vim: ? to search backwards, and /<enter> to repeat search

This commit is contained in:
Conrad Irwin 2023-07-06 13:21:01 -06:00
parent d70f415e8e
commit 6cf13c62d1
5 changed files with 106 additions and 21 deletions

View File

@ -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"
}
}
]

View File

@ -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);

View File

@ -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);
}
}

View File

@ -97,7 +97,7 @@ async fn test_buffer_search(cx: &mut gpui::TestAppContext) {
});
search_bar.read_with(cx.cx, |bar, cx| {
assert_eq!(bar.query_editor.read(cx).text(cx), "jumps");
assert_eq!(bar.query_editor.read(cx).text(cx), "");
})
}

View File

@ -90,6 +90,7 @@ impl<'a> VimTestContext<'a> {
self.cx.set_state(text)
}
#[track_caller]
pub fn assert_state(&mut self, text: &str, mode: Mode) {
self.assert_editor_state(text);
assert_eq!(self.mode(), mode, "{}", self.assertion_context());