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

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>
This commit is contained in:
Conrad Irwin
2024-07-11 13:16:26 -06:00
committed by GitHub
co-authored by Thorsten
parent 8e853e2b56
commit b0dbc80575
4 changed files with 186 additions and 246 deletions
+15 -35
View File
@@ -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",
"context": "vim_mode == insert",
"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",
"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 were waiting for a new key (and you probably dont 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",