Detailed changes
@@ -2,12 +2,6 @@
{
"context": "Editor && VimControl && !VimWaiting && !menu",
"bindings": {
- "g": [
- "vim::PushOperator",
- {
- "Namespace": "G"
- }
- ],
"i": [
"vim::PushOperator",
{
@@ -110,6 +104,32 @@
"*": "vim::MoveToNext",
"#": "vim::MoveToPrev",
"0": "vim::StartOfLine", // When no number operator present, use start of line motion
+ // "g" commands
+ "g g": "vim::StartOfDocument",
+ "g h": "editor::Hover",
+ "g t": "pane::ActivateNextItem",
+ "g shift-t": "pane::ActivatePrevItem",
+ "g d": "editor::GoToDefinition",
+ "g shift-d": "editor::GoToTypeDefinition",
+ "g .": "editor::ToggleCodeActions", // zed specific
+ "g shift-a": "editor::FindAllReferences", // zed specific
+ "g *": [
+ "vim::MoveToNext",
+ {
+ "partialWord": true
+ }
+ ],
+ "g #": [
+ "vim::MoveToPrev",
+ {
+ "partialWord": true
+ }
+ ],
+ // z commands
+ "z t": "editor::ScrollCursorTop",
+ "z z": "editor::ScrollCursorCenter",
+ "z b": "editor::ScrollCursorBottom",
+ // Count support
"1": [
"vim::Number",
1
@@ -234,12 +254,6 @@
"vim::PushOperator",
"Yank"
],
- "z": [
- "vim::PushOperator",
- {
- "Namespace": "Z"
- }
- ],
"i": [
"vim::SwitchMode",
"Insert"
@@ -278,6 +292,13 @@
"backwards": true
}
],
+ ";": "vim::RepeatFind",
+ ",": [
+ "vim::RepeatFind",
+ {
+ "backwards": true
+ }
+ ],
"ctrl-f": "vim::PageDown",
"pagedown": "vim::PageDown",
"ctrl-b": "vim::PageUp",
@@ -306,33 +327,11 @@
]
}
},
- {
- "context": "Editor && vim_operator == g",
- "bindings": {
- "g": "vim::StartOfDocument",
- "h": "editor::Hover",
- "t": "pane::ActivateNextItem",
- "shift-t": "pane::ActivatePrevItem",
- "d": "editor::GoToDefinition",
- "shift-d": "editor::GoToTypeDefinition",
- "*": [
- "vim::MoveToNext",
- {
- "partialWord": true
- }
- ],
- "#": [
- "vim::MoveToPrev",
- {
- "partialWord": true
- }
- ]
- }
- },
{
"context": "Editor && vim_operator == c",
"bindings": {
- "c": "vim::CurrentLine"
+ "c": "vim::CurrentLine",
+ "d": "editor::Rename" // zed specific
}
},
{
@@ -347,14 +346,6 @@
"y": "vim::CurrentLine"
}
},
- {
- "context": "Editor && vim_operator == z",
- "bindings": {
- "t": "editor::ScrollCursorTop",
- "z": "editor::ScrollCursorCenter",
- "b": "editor::ScrollCursorBottom"
- }
- },
{
"context": "Editor && VimObject",
"bindings": {
@@ -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,13 +132,15 @@ 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) {
- if let Some(Operator::Namespace(_))
- | Some(Operator::FindForward { .. })
- | Some(Operator::FindBackward { .. }) = Vim::read(cx).active_operator()
+ if let Some(Operator::FindForward { .. }) | Some(Operator::FindBackward { .. }) =
+ Vim::read(cx).active_operator()
{
Vim::update(cx, |vim, cx| vim.pop_operator(cx));
}
@@ -146,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 {
@@ -743,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;
+ }
}
@@ -107,7 +107,7 @@ pub fn normal_motion(
Some(Operator::Delete) => delete_motion(vim, motion, times, cx),
Some(Operator::Yank) => yank_motion(vim, motion, times, cx),
Some(operator) => {
- // Can't do anything for text objects or namespace operators. Ignoring
+ // Can't do anything for text objects, Ignoring
error!("Unexpected normal mode motion operator: {:?}", operator)
}
}
@@ -441,11 +441,8 @@ mod test {
use indoc::indoc;
use crate::{
- state::{
- Mode::{self, *},
- Namespace, Operator,
- },
- test::{ExemptionFeatures, NeovimBackedTestContext, VimTestContext},
+ state::Mode::{self},
+ test::{ExemptionFeatures, NeovimBackedTestContext},
};
#[gpui::test]
@@ -610,22 +607,6 @@ mod test {
.await;
}
- #[gpui::test]
- async fn test_g_prefix_and_abort(cx: &mut gpui::TestAppContext) {
- let mut cx = VimTestContext::new(cx, true).await;
-
- // Can abort with escape to get back to normal mode
- cx.simulate_keystroke("g");
- assert_eq!(cx.mode(), Normal);
- assert_eq!(
- cx.active_operator(),
- Some(Operator::Namespace(Namespace::G))
- );
- cx.simulate_keystroke("escape");
- assert_eq!(cx.mode(), Normal);
- assert_eq!(cx.active_operator(), None);
- }
-
#[gpui::test]
async fn test_gg(cx: &mut gpui::TestAppContext) {
let mut cx = NeovimBackedTestContext::new(cx).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,
@@ -16,16 +18,9 @@ impl Default for Mode {
}
}
-#[derive(Copy, Clone, Debug, PartialEq, Eq, Deserialize)]
-pub enum Namespace {
- G,
- Z,
-}
-
#[derive(Copy, Clone, Debug, PartialEq, Eq, Deserialize)]
pub enum Operator {
Number(usize),
- Namespace(Namespace),
Change,
Delete,
Yank,
@@ -40,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 {
@@ -126,8 +123,6 @@ impl Operator {
pub fn id(&self) -> &'static str {
match self {
Operator::Number(_) => "n",
- Operator::Namespace(Namespace::G) => "g",
- Operator::Namespace(Namespace::Z) => "z",
Operator::Object { around: false } => "i",
Operator::Object { around: true } => "a",
Operator::Change => "c",
@@ -14,8 +14,8 @@ use anyhow::Result;
use collections::CommandPaletteFilter;
use editor::{Bias, Editor, EditorMode, Event};
use gpui::{
- actions, impl_actions, 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;
@@ -90,7 +90,10 @@ pub fn init(cx: &mut AppContext) {
}
pub fn observe_keystrokes(cx: &mut WindowContext) {
- cx.observe_keystrokes(|_keystroke, _result, handled_by, cx| {
+ cx.observe_keystrokes(|_keystroke, result, handled_by, cx| {
+ if result == &MatchResult::Pending {
+ return true;
+ }
if let Some(handled_by) = handled_by {
// Keystroke is handled by the vim system, so continue forward
if handled_by.namespace() == "vim" {
@@ -243,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"}}