Detailed changes
@@ -292,6 +292,13 @@
"backwards": true
}
],
+ ";": "vim::RepeatFind",
+ ",": [
+ "vim::RepeatFind",
+ {
+ "backwards": true
+ }
+ ],
"ctrl-f": "vim::PageDown",
"pagedown": "vim::PageDown",
"ctrl-b": "vim::PageUp",
@@ -62,6 +62,12 @@ struct PreviousWordStart {
ignore_punctuation: bool,
}
+#[derive(Clone, Deserialize, PartialEq)]
+struct RepeatFind {
+ #[serde(default)]
+ backwards: bool,
+}
+
actions!(
vim,
[
@@ -82,7 +88,10 @@ actions!(
NextLineStart,
]
);
-impl_actions!(vim, [NextWordStart, NextWordEnd, PreviousWordStart]);
+impl_actions!(
+ vim,
+ [NextWordStart, NextWordEnd, PreviousWordStart, RepeatFind]
+);
pub fn init(cx: &mut AppContext) {
cx.add_action(|_: &mut Workspace, _: &Left, cx: _| motion(Motion::Left, cx));
@@ -123,7 +132,10 @@ pub fn init(cx: &mut AppContext) {
&PreviousWordStart { ignore_punctuation }: &PreviousWordStart,
cx: _| { motion(Motion::PreviousWordStart { ignore_punctuation }, cx) },
);
- cx.add_action(|_: &mut Workspace, &NextLineStart, cx: _| motion(Motion::NextLineStart, cx))
+ cx.add_action(|_: &mut Workspace, &NextLineStart, cx: _| motion(Motion::NextLineStart, cx));
+ cx.add_action(|_: &mut Workspace, action: &RepeatFind, cx: _| {
+ repeat_motion(action.backwards, cx)
+ })
}
pub(crate) fn motion(motion: Motion, cx: &mut WindowContext) {
@@ -145,6 +157,35 @@ pub(crate) fn motion(motion: Motion, cx: &mut WindowContext) {
Vim::update(cx, |vim, cx| vim.clear_operator(cx));
}
+fn repeat_motion(backwards: bool, cx: &mut WindowContext) {
+ let find = match Vim::read(cx).state.last_find.clone() {
+ Some(Motion::FindForward { before, text }) => {
+ if backwards {
+ Motion::FindBackward {
+ after: before,
+ text,
+ }
+ } else {
+ Motion::FindForward { before, text }
+ }
+ }
+
+ Some(Motion::FindBackward { after, text }) => {
+ if backwards {
+ Motion::FindForward {
+ before: after,
+ text,
+ }
+ } else {
+ Motion::FindBackward { after, text }
+ }
+ }
+ _ => return,
+ };
+
+ motion(find, cx)
+}
+
// Motion handling is specified here:
// https://github.com/vim/vim/blob/master/runtime/doc/motion.txt
impl Motion {
@@ -742,4 +783,23 @@ mod test {
cx.simulate_shared_keystrokes(["%"]).await;
cx.assert_shared_state("func boop(ˇ) {\n}").await;
}
+
+ #[gpui::test]
+ async fn test_comma_semicolon(cx: &mut gpui::TestAppContext) {
+ let mut cx = NeovimBackedTestContext::new(cx).await;
+
+ cx.set_shared_state("ˇone two three four").await;
+ cx.simulate_shared_keystrokes(["f", "o"]).await;
+ cx.assert_shared_state("one twˇo three four").await;
+ cx.simulate_shared_keystrokes([","]).await;
+ cx.assert_shared_state("ˇone two three four").await;
+ cx.simulate_shared_keystrokes(["2", ";"]).await;
+ cx.assert_shared_state("one two three fˇour").await;
+ cx.simulate_shared_keystrokes(["shift-t", "e"]).await;
+ cx.assert_shared_state("one two threeˇ four").await;
+ cx.simulate_shared_keystrokes(["3", ";"]).await;
+ cx.assert_shared_state("oneˇ two three four").await;
+ cx.simulate_shared_keystrokes([","]).await;
+ cx.assert_shared_state("one two thˇree four").await;
+ }
}
@@ -3,6 +3,8 @@ use language::CursorShape;
use serde::{Deserialize, Serialize};
use workspace::searchable::Direction;
+use crate::motion::Motion;
+
#[derive(Clone, Copy, Debug, PartialEq, Eq, Deserialize, Serialize)]
pub enum Mode {
Normal,
@@ -33,6 +35,8 @@ pub struct VimState {
pub mode: Mode,
pub operator_stack: Vec<Operator>,
pub search: SearchState,
+
+ pub last_find: Option<Motion>,
}
pub struct SearchState {
@@ -14,8 +14,8 @@ use anyhow::Result;
use collections::CommandPaletteFilter;
use editor::{Bias, Editor, EditorMode, Event};
use gpui::{
- actions, impl_actions,keymap_matcher::MatchResult, keymap_matcher::KeymapContext, AppContext, Subscription, ViewContext,
- ViewHandle, WeakViewHandle, WindowContext,
+ actions, impl_actions, keymap_matcher::KeymapContext, keymap_matcher::MatchResult, AppContext,
+ Subscription, ViewContext, ViewHandle, WeakViewHandle, WindowContext,
};
use language::CursorShape;
use motion::Motion;
@@ -246,10 +246,14 @@ impl Vim {
match Vim::read(cx).active_operator() {
Some(Operator::FindForward { before }) => {
- motion::motion(Motion::FindForward { before, text }, cx)
+ let find = Motion::FindForward { before, text };
+ Vim::update(cx, |vim, _| vim.state.last_find = Some(find.clone()));
+ motion::motion(find, cx)
}
Some(Operator::FindBackward { after }) => {
- motion::motion(Motion::FindBackward { after, text }, cx)
+ let find = Motion::FindBackward { after, text };
+ Vim::update(cx, |vim, _| vim.state.last_find = Some(find.clone()));
+ motion::motion(find, cx)
}
Some(Operator::Replace) => match Vim::read(cx).state.mode {
Mode::Normal => normal_replace(text, cx),
@@ -0,0 +1,17 @@
+{"Put":{"state":"ˇone two three four"}}
+{"Key":"f"}
+{"Key":"o"}
+{"Get":{"state":"one twˇo three four","mode":"Normal"}}
+{"Key":","}
+{"Get":{"state":"ˇone two three four","mode":"Normal"}}
+{"Key":"2"}
+{"Key":";"}
+{"Get":{"state":"one two three fˇour","mode":"Normal"}}
+{"Key":"shift-t"}
+{"Key":"e"}
+{"Get":{"state":"one two threeˇ four","mode":"Normal"}}
+{"Key":"3"}
+{"Key":";"}
+{"Get":{"state":"oneˇ two three four","mode":"Normal"}}
+{"Key":","}
+{"Get":{"state":"one two thˇree four","mode":"Normal"}}