vim: (BREAKING) clean up keymap contexts (#14233)

Conrad Irwin and Thorsten created

Release Notes:

- vim: (BREAKING) Improved vim keymap contexts.

Previously `vim_mode == normal` was true even when operators were
pending, which led to bugs like #13789 and a requirement for custom
keymaps to exclude various conditions like (`!VimObject` and
`!VimWaiting`) to avoid bugs.

Now `vim_mode` will be set to `operator` or `waiting` in these cases as
described in [the docs](https://zed.dev/docs/vim#keybindings). For most
custom keymaps this change will be a no-op or an improvement, but if you
were deliberately relying on the old behaviour (if you were relying on
`VimObject` or `VimWaiting` becoming true) you will need to update your
keymap.

---------

Co-authored-by: Thorsten <thorsten@zed.dev>

Change summary

assets/keymaps/vim.json | 263 ++++++++++++++++++------------------------
crates/vim/src/state.rs |  99 +++++++--------
crates/vim/src/vim.rs   |   6 
docs/src/vim.md         |  50 ++-----
4 files changed, 179 insertions(+), 239 deletions(-)

Detailed changes

assets/keymaps/vim.json πŸ”—

@@ -1,12 +1,6 @@
 [
   {
-    "context": "ProjectPanel || Editor",
-    "bindings": {
-      "ctrl-6": "pane::AlternateFile"
-    }
-  },
-  {
-    "context": "Editor && VimControl && !VimWaiting && !menu",
+    "context": "VimControl && !menu",
     "bindings": {
       "i": ["vim::PushOperator", { "Object": { "around": false } }],
       "a": ["vim::PushOperator", { "Object": { "around": true } }],
@@ -198,20 +192,21 @@
       "ctrl-w g shift-d": "editor::GoToTypeDefinitionSplit",
       "ctrl-w space": "editor::OpenExcerptsSplit",
       "ctrl-w g space": "editor::OpenExcerptsSplit",
-      "-": "pane::RevealInProjectPanel"
+      "-": "pane::RevealInProjectPanel",
+      "ctrl-6": "pane::AlternateFile"
     }
   },
   {
-    // escape is in its own section so that it cancels a pending count.
-    "context": "Editor && vim_mode == normal && vim_operator == none && !VimWaiting",
+    "context": "VimControl && VimCount",
     "bindings": {
-      "escape": "editor::Cancel",
-      "ctrl-[": "editor::Cancel"
+      "0": ["vim::Number", 0]
     }
   },
   {
-    "context": "Editor && vim_mode == normal && vim_operator == none && !VimWaiting",
+    "context": "vim_mode == normal",
     "bindings": {
+      "escape": "editor::Cancel",
+      "ctrl-[": "editor::Cancel",
       ".": "vim::Repeat",
       "c": ["vim::PushOperator", "Change"],
       "shift-c": "vim::ChangeToEndOfLine",
@@ -255,12 +250,48 @@
       "] d": "editor::GoToDiagnostic",
       "[ d": "editor::GoToPrevDiagnostic",
       "] c": "editor::GoToHunk",
-      "[ c": "editor::GoToPrevHunk"
+      "[ c": "editor::GoToPrevHunk",
+      "g c c": "vim::ToggleComments"
     }
   },
   {
-    "context": "Editor && vim_mode == visual && vim_operator == none && !VimWaiting",
+    "context": "vim_mode == visual",
     "bindings": {
+      "u": "vim::ConvertToLowerCase",
+      "U": "vim::ConvertToUpperCase",
+      "o": "vim::OtherEnd",
+      "shift-o": "vim::OtherEnd",
+      "d": "vim::VisualDelete",
+      "x": "vim::VisualDelete",
+      "shift-d": "vim::VisualDeleteLine",
+      "shift-x": "vim::VisualDeleteLine",
+      "y": "vim::VisualYank",
+      "shift-y": "vim::VisualYank",
+      "p": "vim::Paste",
+      "shift-p": ["vim::Paste", { "preserveClipboard": true }],
+      "s": "vim::Substitute",
+      "shift-s": "vim::SubstituteLine",
+      "shift-r": "vim::SubstituteLine",
+      "c": "vim::Substitute",
+      "~": "vim::ChangeCase",
+      "*": ["vim::MoveToNext", { "partialWord": true }],
+      "#": ["vim::MoveToPrev", { "partialWord": true }],
+      "ctrl-a": "vim::Increment",
+      "ctrl-x": "vim::Decrement",
+      "g ctrl-a": ["vim::Increment", { "step": true }],
+      "g ctrl-x": ["vim::Decrement", { "step": true }],
+      "shift-i": "vim::InsertBefore",
+      "shift-a": "vim::InsertAfter",
+      "shift-j": "vim::JoinLines",
+      "r": ["vim::PushOperator", "Replace"],
+      "ctrl-c": ["vim::SwitchMode", "Normal"],
+      "escape": ["vim::SwitchMode", "Normal"],
+      "ctrl-[": ["vim::SwitchMode", "Normal"],
+      ">": "vim::Indent",
+      "<": "vim::Outdent",
+      "i": ["vim::PushOperator", { "Object": { "around": false } }],
+      "a": ["vim::PushOperator", { "Object": { "around": true } }],
+      "g c": "vim::ToggleComments",
       "\"": ["vim::PushOperator", "Register"],
       // tree-sitter related commands
       "[ x": "editor::SelectLargerSyntaxNode",
@@ -268,89 +299,52 @@
     }
   },
   {
-    "context": "Editor && VimCount && vim_mode != insert",
+    "context": "vim_mode == insert",
     "bindings": {
-      "0": ["vim::Number", 0]
-    }
-  },
-  {
-    "context": "Editor && vim_operator == c",
-    "bindings": {
-      "c": "vim::CurrentLine",
-      "d": "editor::Rename" // zed specific
-    }
-  },
-  {
-    "context": "Editor && vim_mode == normal && vim_operator == c",
-    "bindings": {
-      "s": ["vim::PushOperator", { "ChangeSurrounds": {} }]
-    }
-  },
-  {
-    "context": "Editor && vim_operator == d",
-    "bindings": {
-      "d": "vim::CurrentLine"
-    }
-  },
-  {
-    "context": "Editor && vim_operator == gu",
-    "bindings": {
-      "g u": "vim::CurrentLine",
-      "u": "vim::CurrentLine"
-    }
-  },
-  {
-    "context": "Editor && vim_operator == gU",
-    "bindings": {
-      "g shift-u": "vim::CurrentLine",
-      "shift-u": "vim::CurrentLine"
-    }
-  },
-  {
-    "context": "Editor && vim_operator == g~",
-    "bindings": {
-      "g ~": "vim::CurrentLine",
-      "~": "vim::CurrentLine"
-    }
-  },
-  {
-    "context": "Editor && vim_mode == normal && vim_operator == d",
-    "bindings": {
-      "s": ["vim::PushOperator", "DeleteSurrounds"]
-    }
-  },
-  {
-    "context": "Editor && vim_operator == y",
-    "bindings": {
-      "y": "vim::CurrentLine"
-    }
-  },
-  {
-    "context": "Editor && vim_mode == normal && vim_operator == y",
-    "bindings": {
-      "s": ["vim::PushOperator", { "AddSurrounds": {} }]
+      "escape": "vim::NormalBefore",
+      "ctrl-c": "vim::NormalBefore",
+      "ctrl-[": "vim::NormalBefore",
+      "ctrl-x": null,
+      "ctrl-x ctrl-o": "editor::ShowCompletions",
+      "ctrl-x ctrl-a": "assistant::InlineAssist", // zed specific
+      "ctrl-x ctrl-c": "editor::ShowInlineCompletion", // zed specific
+      "ctrl-x ctrl-l": "editor::ToggleCodeActions", // zed specific
+      "ctrl-x ctrl-z": "editor::Cancel",
+      "ctrl-w": "editor::DeleteToPreviousWordStart",
+      "ctrl-u": "editor::DeleteToBeginningOfLine",
+      "ctrl-t": "vim::Indent",
+      "ctrl-d": "vim::Outdent",
+      "ctrl-r": ["vim::PushOperator", "Register"]
     }
   },
   {
-    "context": "Editor && vim_operator == ys",
+    "context": "vim_mode == replace",
     "bindings": {
-      "s": "vim::CurrentLine"
+      "escape": "vim::NormalBefore",
+      "ctrl-c": "vim::NormalBefore",
+      "ctrl-[": "vim::NormalBefore",
+      "backspace": "vim::UndoReplace",
+      "tab": "vim::Tab",
+      "enter": "vim::Enter"
     }
   },
   {
-    "context": "Editor && vim_operator == >",
+    "context": "vim_mode == waiting",
     "bindings": {
-      ">": "vim::CurrentLine"
+      "tab": "vim::Tab",
+      "enter": "vim::Enter"
     }
   },
   {
-    "context": "Editor && vim_operator == <",
+    "context": "vim_mode == operator",
     "bindings": {
-      "<": "vim::CurrentLine"
+      "escape": "vim::ClearOperators",
+      "ctrl-c": "vim::ClearOperators",
+      "ctrl-[": "vim::ClearOperators"
     }
   },
   {
-    "context": "Editor && VimObject",
+    "context": "vim_operator == a || vim_operator == i || vim_operator == cs",
     "bindings": {
       "w": "vim::Word",
       "shift-w": ["vim::Word", { "ignorePunctuation": true }],
@@ -375,100 +369,64 @@
     }
   },
   {
-    "context": "Editor && vim_mode == visual && !VimWaiting && !VimObject",
+    "context": "vim_operator == c",
     "bindings": {
-      "u": "vim::ConvertToLowerCase",
-      "U": "vim::ConvertToUpperCase",
-      "o": "vim::OtherEnd",
-      "shift-o": "vim::OtherEnd",
-      "d": "vim::VisualDelete",
-      "x": "vim::VisualDelete",
-      "shift-d": "vim::VisualDeleteLine",
-      "shift-x": "vim::VisualDeleteLine",
-      "y": "vim::VisualYank",
-      "shift-y": "vim::VisualYank",
-      "p": "vim::Paste",
-      "shift-p": ["vim::Paste", { "preserveClipboard": true }],
-      "s": "vim::Substitute",
-      "shift-s": "vim::SubstituteLine",
-      "shift-r": "vim::SubstituteLine",
-      "c": "vim::Substitute",
-      "~": "vim::ChangeCase",
-      "*": ["vim::MoveToNext", { "partialWord": true }],
-      "#": ["vim::MoveToPrev", { "partialWord": true }],
-      "ctrl-a": "vim::Increment",
-      "ctrl-x": "vim::Decrement",
-      "g ctrl-a": ["vim::Increment", { "step": true }],
-      "g ctrl-x": ["vim::Decrement", { "step": true }],
-      "shift-i": "vim::InsertBefore",
-      "shift-a": "vim::InsertAfter",
-      "shift-j": "vim::JoinLines",
-      "r": ["vim::PushOperator", "Replace"],
-      "ctrl-c": ["vim::SwitchMode", "Normal"],
-      "escape": ["vim::SwitchMode", "Normal"],
-      "ctrl-[": ["vim::SwitchMode", "Normal"],
-      ">": "vim::Indent",
-      "<": "vim::Outdent",
-      "i": ["vim::PushOperator", { "Object": { "around": false } }],
-      "a": ["vim::PushOperator", { "Object": { "around": true } }]
+      "c": "vim::CurrentLine",
+      "d": "editor::Rename", // zed specific
+      "s": ["vim::PushOperator", { "ChangeSurrounds": {} }]
     }
   },
   {
-    "context": "Editor && vim_mode == normal && !VimWaiting",
+    "context": "vim_operator == d",
     "bindings": {
-      "g c c": "vim::ToggleComments"
+      "d": "vim::CurrentLine",
+      "s": ["vim::PushOperator", "DeleteSurrounds"]
     }
   },
   {
-    "context": "Editor && vim_mode == visual",
+    "context": "vim_operator == gu",
     "bindings": {
-      "g c": "vim::ToggleComments"
+      "g u": "vim::CurrentLine",
+      "u": "vim::CurrentLine"
     }
   },
   {
-    "context": "Editor && vim_mode == insert",
+    "context": "vim_operator == gU",
     "bindings": {
-      "escape": "vim::NormalBefore",
-      "ctrl-c": "vim::NormalBefore",
-      "ctrl-[": "vim::NormalBefore",
-      "ctrl-x": null,
-      "ctrl-x ctrl-o": "editor::ShowCompletions",
-      "ctrl-x ctrl-a": "assistant::InlineAssist", // zed specific
-      "ctrl-x ctrl-c": "editor::ShowInlineCompletion", // zed specific
-      "ctrl-x ctrl-l": "editor::ToggleCodeActions", // zed specific
-      "ctrl-x ctrl-z": "editor::Cancel",
-      "ctrl-w": "editor::DeleteToPreviousWordStart",
-      "ctrl-u": "editor::DeleteToBeginningOfLine",
-      "ctrl-t": "vim::Indent",
-      "ctrl-d": "vim::Outdent",
-      "ctrl-r": ["vim::PushOperator", "Register"]
+      "g shift-u": "vim::CurrentLine",
+      "shift-u": "vim::CurrentLine"
     }
   },
   {
-    "context": "Editor && vim_mode == replace",
+    "context": "vim_operator == g~",
     "bindings": {
-      "escape": "vim::NormalBefore",
-      "ctrl-c": "vim::NormalBefore",
-      "ctrl-[": "vim::NormalBefore",
-      "tab": "vim::Tab",
-      "enter": "vim::Enter",
-      "backspace": "vim::UndoReplace"
+      "g ~": "vim::CurrentLine",
+      "~": "vim::CurrentLine"
     }
   },
   {
-    "context": "Editor && vim_mode != replace && VimWaiting",
+    "context": "vim_operator == y",
     "bindings": {
-      "tab": "vim::Tab",
-      "enter": "vim::Enter",
-      "escape": ["vim::SwitchMode", "Normal"],
-      "ctrl-[": ["vim::SwitchMode", "Normal"]
+      "y": "vim::CurrentLine",
+      "s": ["vim::PushOperator", { "AddSurrounds": {} }]
     }
   },
   {
-    "context": "Editor && vim_mode == insert && VimWaiting",
+    "context": "vim_operator == ys",
     "bindings": {
-      "escape": "vim::NormalBefore",
-      "ctrl-[": "vim::NormalBefore"
+      "s": "vim::CurrentLine"
+    }
+  },
+  {
+    "context": "vim_operator == >",
+    "bindings": {
+      ">": "vim::CurrentLine"
+    }
+  },
+  {
+    "context": "vim_operator == <",
+    "bindings": {
+      "<": "vim::CurrentLine"
     }
   },
   {
@@ -508,7 +466,8 @@
       "x": "project_panel::RevealInFileManager",
       "shift-g": "menu::SelectLast",
       "g g": "menu::SelectFirst",
-      "-": "project_panel::SelectParent"
+      "-": "project_panel::SelectParent",
+      "ctrl-6": "pane::AlternateFile"
     }
   },
   {

crates/vim/src/state.rs πŸ”—

@@ -228,21 +228,19 @@ impl EditorState {
         }
     }
 
-    pub fn vim_controlled(&self) -> bool {
-        let is_insert_mode = matches!(self.mode, Mode::Insert);
-        if !is_insert_mode {
-            return true;
+    pub fn editor_input_enabled(&self) -> bool {
+        match self.mode {
+            Mode::Insert => {
+                if let Some(operator) = self.operator_stack.last() {
+                    !operator.is_waiting(self.mode)
+                } else {
+                    true
+                }
+            }
+            Mode::Normal | Mode::Replace | Mode::Visual | Mode::VisualLine | Mode::VisualBlock => {
+                false
+            }
         }
-        matches!(
-            self.operator_stack.last(),
-            Some(Operator::FindForward { .. })
-                | Some(Operator::FindBackward { .. })
-                | Some(Operator::Mark)
-                | Some(Operator::Register)
-                | Some(Operator::RecordRegister)
-                | Some(Operator::ReplayRegister)
-                | Some(Operator::Jump { .. })
-        )
     }
 
     pub fn should_autoindent(&self) -> bool {
@@ -264,48 +262,39 @@ impl EditorState {
 
     pub fn keymap_context_layer(&self) -> KeyContext {
         let mut context = KeyContext::new_with_defaults();
-        context.set(
-            "vim_mode",
-            match self.mode {
-                Mode::Normal => "normal",
-                Mode::Visual | Mode::VisualLine | Mode::VisualBlock => "visual",
-                Mode::Insert => "insert",
-                Mode::Replace => "replace",
-            },
-        );
 
-        if self.vim_controlled() {
-            context.add("VimControl");
+        let mut mode = match self.mode {
+            Mode::Normal => "normal",
+            Mode::Visual | Mode::VisualLine | Mode::VisualBlock => "visual",
+            Mode::Insert => "insert",
+            Mode::Replace => "replace",
         }
+        .to_string();
 
-        if self.active_operator().is_none() && self.pre_count.is_some()
-            || self.active_operator().is_some() && self.post_count.is_some()
+        let mut operator_id = "none";
+
+        let active_operator = self.active_operator();
+        if active_operator.is_none() && self.pre_count.is_some()
+            || active_operator.is_some() && self.post_count.is_some()
         {
             context.add("VimCount");
         }
 
-        let active_operator = self.active_operator();
-
-        if let Some(active_operator) = active_operator.clone() {
-            for context_flag in active_operator.context_flags().into_iter() {
-                context.add(*context_flag);
+        if let Some(active_operator) = active_operator {
+            if active_operator.is_waiting(self.mode) {
+                mode = "waiting".to_string();
+            } else {
+                mode = "operator".to_string();
+                operator_id = active_operator.id();
             }
         }
 
-        context.set(
-            "vim_operator",
-            active_operator
-                .clone()
-                .map(|op| op.id())
-                .unwrap_or_else(|| "none"),
-        );
-
-        if self.mode == Mode::Replace
-            || (matches!(active_operator, Some(Operator::AddSurrounds { .. }))
-                && self.mode.is_visual())
-        {
-            context.add("VimWaiting");
+        if mode != "waiting" && mode != "insert" && mode != "replace" {
+            context.add("VimControl");
         }
+        context.set("vim_mode", mode);
+        context.set("vim_operator", operator_id);
+
         context
     }
 }
@@ -340,9 +329,9 @@ impl Operator {
         }
     }
 
-    pub fn context_flags(&self) -> &'static [&'static str] {
+    pub fn is_waiting(&self, mode: Mode) -> bool {
         match self {
-            Operator::Object { .. } | Operator::ChangeSurrounds { target: None } => &["VimObject"],
+            Operator::AddSurrounds { target } => target.is_some() || mode.is_visual(),
             Operator::FindForward { .. }
             | Operator::Mark
             | Operator::Jump { .. }
@@ -351,10 +340,18 @@ impl Operator {
             | Operator::RecordRegister
             | Operator::ReplayRegister
             | Operator::Replace
-            | Operator::AddSurrounds { target: Some(_) }
-            | Operator::ChangeSurrounds { .. }
-            | Operator::DeleteSurrounds => &["VimWaiting"],
-            _ => &[],
+            | Operator::ChangeSurrounds { target: Some(_) }
+            | Operator::DeleteSurrounds => true,
+            Operator::Change
+            | Operator::Delete
+            | Operator::Yank
+            | Operator::Indent
+            | Operator::Outdent
+            | Operator::Lowercase
+            | Operator::Uppercase
+            | Operator::Object { .. }
+            | Operator::ChangeSurrounds { target: None }
+            | Operator::OppositeCase => false,
         }
     }
 }

crates/vim/src/vim.rs πŸ”—

@@ -76,6 +76,7 @@ struct SelectRegister(String);
 actions!(
     vim,
     [
+        ClearOperators,
         Tab,
         Enter,
         Object,
@@ -129,6 +130,9 @@ fn register(workspace: &mut Workspace, cx: &mut ViewContext<Workspace>) {
             Vim::update(cx, |vim, cx| vim.push_operator(operator.clone(), cx))
         },
     );
+    workspace.register_action(|_: &mut Workspace, _: &ClearOperators, cx| {
+        Vim::update(cx, |vim, cx| vim.clear_operator(cx))
+    });
     workspace.register_action(|_: &mut Workspace, n: &Number, cx: _| {
         Vim::update(cx, |vim, cx| vim.push_count_digit(n.0, cx));
     });
@@ -973,7 +977,7 @@ impl Vim {
             editor.set_cursor_shape(state.cursor_shape(), cx);
             editor.set_clip_at_line_ends(state.clip_at_line_ends(), cx);
             editor.set_collapse_matches(true);
-            editor.set_input_enabled(!state.vim_controlled());
+            editor.set_input_enabled(state.editor_input_enabled());
             editor.set_autoindent(state.should_autoindent());
             editor.selections.line_mode = matches!(state.mode, Mode::VisualLine);
             if editor.is_focused(cx) || editor.mouse_menu_is_focused(cx) {

docs/src/vim.md πŸ”—

@@ -85,36 +85,22 @@ Finally, Vim mode's search and replace functionality is backed by Zed's. This me
 ## Custom key bindings
 
 You can edit your personal key bindings with `:keymap`.
-For vim-specific shortcuts, you may find the following template a good place to start:
+For vim-specific shortcuts, you may find the following template a good place to start.
+
+> **Note:** We made some breaking changes in Zed version `0.145.0`. For older versions, see [the previous version of this document](https://github.com/zed-industries/zed/blob/c67aeaa9c58619a58708722ac7d7a78c75c29336/docs/src/vim.md#L90).
 
 ```json
 [
   {
-    "context": "Editor && (vim_mode == normal || vim_mode == visual) && !VimWaiting && !menu",
+    "context": "VimControl && !menu",
     "bindings": {
       // put key-bindings here if you want them to work in normal & visual mode
     }
   },
   {
-    "context": "Editor && vim_mode == normal && !VimWaiting && !menu",
-    "bindings": {
-      // put key-bindings here if you want them to work only in normal mode
-      // "down": ["workspace::SendKeystrokes", "4 j"]
-      // "up": ["workspace::SendKeystrokes", "4 k"]
-    }
-  },
-  {
-    "context": "Editor && vim_mode == visual && !VimWaiting && !menu",
+    "context": "vim_mode == insert",
     "bindings": {
-      // visual, visual line & visual block modes
-    }
-  },
-  {
-    "context": "Editor && vim_mode == insert && !menu",
-    "bindings": {
-      // put key-bindings here if you want them to work in insert mode
-      // e.g.
-      // "j j": "vim::NormalBefore" // remap jj in insert mode to escape.
+      // "j k": "vim::NormalBefore" // remap jk in insert mode to escape.
     }
   },
   {
@@ -122,7 +108,6 @@ For vim-specific shortcuts, you may find the following template a good place to
     "bindings": {
       // put key-bindings here (in addition to above) if you want them to
       // work when no editor exists
-      // e.g.
       // "space f": "file_finder::Toggle"
     }
   }
@@ -133,20 +118,15 @@ If you would like to emulate vim's `map` (`nmap` etc.) commands you can bind to
 
 You can see the bindings that are enabled by default in vim mode [here](https://github.com/zed-industries/zed/blob/main/assets/keymaps/vim.json).
 
-The details of the context are a little out of scope for this doc, but suffice to say that `menu` is true when a menu is open (e.g. the completions menu), `VimWaiting` is true after you type `f` or `t` when we’re waiting for a new key (and you probably don’t want bindings to happen). Please reach out on [GitHub](https://github.com/zed-industries/zed) if you want help making a key bindings work.
+#### Contexts
 
-### Examples
+Zed's keyboard bindings are evaluated only when the `"context"` matches the location you are in on the screen. Locations are nested, so when you're editing you're in the `"Workspace"` location is at the top, containing a `"Pane"` which contains an `"Editor"`. Contexts are matched only on one level at a time. So it is possible to combine `Editor && vim_mode == normal`, but `Workspace && vim_mode == normal` will never match because we set the vim context at the `Editor` level.
 
-Binding `jk` to exit insert mode and go to normal mode:
+Vim mode adds several contexts to the `Editor`:
 
-```
-{
-  "context": "Editor && vim_mode == insert && !menu",
-  "bindings": {
-    "j k": ["vim::SwitchMode", "Normal"]
-  }
-}
-```
+* `vim_mode` is similar to, but not identical to, the current mode. It starts as one of `normal`, `visual`, `insert` or `replace` (depending on your mode). If you are mid-way through typing a sequence, `vim_mode` will be either `waiting` if it's waiting for an arbitrary key (for example after typing `f` or `t`), or `operator` if it's waiting for another binding to trigger (for example after typing `c` or `d`).
+* `vim_operator` is set to `none` unless `vim_mode == operator` in which case it is set to the current operator's default keybinding (for example after typing `d`, `vim_operator == d`).
+* `"VimControl"` indicates that vim keybindings should work. It is currently an alias for `vim_mode == normal || vim_mode == visual || vim_mode == operator`, but the definition may change over time.
 
 ### Restoring some sense of normality
 
@@ -155,7 +135,7 @@ that you can't live without. You can restore them to their defaults by copying t
 
 ```
 {
-  "context": "Editor && !VimWaiting && !menu",
+  "context": "Editor && !menu",
   "bindings": {
     "ctrl-c": "editor::Copy",          // vim default: return to normal mode
     "ctrl-x": "editor::Cut",           // vim default: increment
@@ -304,7 +284,7 @@ Subword motion is not enabled by default. To enable it, add these bindings to yo
 
 ```json
   {
-    "context": "Editor && VimControl && !VimWaiting && !menu",
+    "context": "VimControl && !menu",
     "bindings": {
       "w": "vim::NextSubwordStart",
       "b": "vim::PreviousSubwordStart",
@@ -318,7 +298,7 @@ Surrounding the selection in visual mode is also not enabled by default (`shift-
 
 ```json
   {
-    "context": "Editor && vim_mode == visual && !VimWaiting && !VimObject",
+    "context": "vim_mode == visual",
     "bindings": {
       "shift-s": [
         "vim::PushOperator",