vim: Fix cgn backwards movement when there is no matches (#10237)

Release Notes:

- Fixed `cgn` backwards movement problem in #9982

There are two issues:

- When there are no more matches, the next repetition still moves the
cursor to the left. After that, the recording is cleared. For this I
simply move the cursor to the right, but it doesn't work when the cursor
is at the end of the line.
- If `cgn` is used when there are no matches, it cleans the previous
recorded actions. Maybe there should be a way to revert the recording.
This also happens when using `c` and `esc`

---------

Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com>
This commit is contained in:
joaquin30 2024-04-08 15:51:36 -05:00 committed by GitHub
parent 0390df27d4
commit f9bf60f017
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 79 additions and 3 deletions

View File

@ -1100,6 +1100,11 @@ impl BufferSearchBar {
}
}
}
pub fn match_exists(&mut self, cx: &mut ViewContext<Self>) -> bool {
self.update_match_index(cx);
self.active_match_index.is_some()
}
}
#[cfg(test)]

View File

@ -172,6 +172,13 @@ pub(crate) fn repeat(cx: &mut WindowContext, from_insert_mode: bool) {
editor.show_local_selections = false;
})?;
for action in actions {
if !matches!(
cx.update(|cx| Vim::read(cx).workspace_state.replaying),
Ok(true)
) {
break;
}
match action {
ReplayableAction::Action(action) => {
if should_replay(&action) {

View File

@ -341,6 +341,10 @@ impl Vim {
}
}
pub fn stop_replaying(&mut self) {
self.workspace_state.replaying = false;
}
/// When finishing an action that modifies the buffer, stop recording.
/// as you usually call this within a keystroke handler we also ensure that
/// the current action is recorded.
@ -499,6 +503,7 @@ impl Vim {
self.sync_vim_settings(cx);
popped_operator
}
fn clear_operator(&mut self, cx: &mut WindowContext) {
self.take_count(cx);
self.update_state(|state| state.operator_stack.clear());

View File

@ -509,7 +509,6 @@ pub fn select_match(
vim.update_active_editor(cx, |_, editor, _| {
editor.set_collapse_matches(false);
});
if vim_is_normal {
pane.update(cx, |pane, cx| {
if let Some(search_bar) = pane.toolbar().read(cx).item_of_type::<BufferSearchBar>() {
@ -521,21 +520,27 @@ pub fn select_match(
}
});
}
vim.update_active_editor(cx, |_, editor, cx| {
let latest = editor.selections.newest::<usize>(cx);
start_selection = latest.start;
end_selection = latest.end;
});
let mut match_exists = false;
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| {
search_bar.update_match_index(cx);
search_bar.select_match(direction, count, cx);
match_exists = search_bar.match_exists(cx);
});
}
});
if !match_exists {
vim.clear_operator(cx);
vim.stop_replaying();
return;
}
vim.update_active_editor(cx, |_, editor, cx| {
let latest = editor.selections.newest::<usize>(cx);
if vim_is_normal {
@ -553,6 +558,7 @@ pub fn select_match(
});
editor.set_collapse_matches(true);
});
match vim.maybe_pop_operator() {
Some(Operator::Change) => substitute(vim, None, false, cx),
Some(Operator::Delete) => {
@ -561,7 +567,7 @@ pub fn select_match(
}
Some(Operator::Yank) => yank(vim, cx),
_ => {} // Ignoring other operators
};
}
}
#[cfg(test)]
@ -1195,4 +1201,29 @@ mod test {
cx.simulate_shared_keystrokes(["."]).await;
cx.assert_shared_state("aa x ˇx aa aa").await;
}
#[gpui::test]
async fn test_cgn_nomatch(cx: &mut gpui::TestAppContext) {
let mut cx = NeovimBackedTestContext::new(cx).await;
cx.set_shared_state("aaˇ aa aa aa aa").await;
cx.simulate_shared_keystrokes(["/", "b", "b", "enter"])
.await;
cx.assert_shared_state("aaˇ aa aa aa aa").await;
cx.simulate_shared_keystrokes(["c", "g", "n", "x", "escape"])
.await;
cx.assert_shared_state("aaˇaa aa aa aa").await;
cx.simulate_shared_keystrokes(["."]).await;
cx.assert_shared_state("aaˇa aa aa aa").await;
cx.set_shared_state("aaˇ bb aa aa aa").await;
cx.simulate_shared_keystrokes(["/", "b", "b", "enter"])
.await;
cx.assert_shared_state("aa ˇbb aa aa aa").await;
cx.simulate_shared_keystrokes(["c", "g", "n", "x", "escape"])
.await;
cx.assert_shared_state("aa ˇx aa aa aa").await;
cx.simulate_shared_keystrokes(["."]).await;
cx.assert_shared_state("aa ˇx aa aa aa").await;
}
}

View File

@ -0,0 +1,28 @@
{"Put":{"state":"aaˇ aa aa aa aa"}}
{"Key":"/"}
{"Key":"b"}
{"Key":"b"}
{"Key":"enter"}
{"Get":{"state":"aaˇ aa aa aa aa","mode":"Normal"}}
{"Key":"c"}
{"Key":"g"}
{"Key":"n"}
{"Key":"x"}
{"Key":"escape"}
{"Get":{"state":"aaˇaa aa aa aa","mode":"Normal"}}
{"Key":"."}
{"Get":{"state":"aaˇa aa aa aa","mode":"Normal"}}
{"Put":{"state":"aaˇ bb aa aa aa"}}
{"Key":"/"}
{"Key":"b"}
{"Key":"b"}
{"Key":"enter"}
{"Get":{"state":"aa ˇbb aa aa aa","mode":"Normal"}}
{"Key":"c"}
{"Key":"g"}
{"Key":"n"}
{"Key":"x"}
{"Key":"escape"}
{"Get":{"state":"aa ˇx aa aa aa","mode":"Normal"}}
{"Key":"."}
{"Get":{"state":"aa ˇx aa aa aa","mode":"Normal"}}