Skip to content

Vim mode

On by default. vim_mode = false turns it off, and the editor then behaves like any other — keys type, Ctrl+S saves, Ctrl+Z undoes.

This is not a full Vim. It is the subset that gets used, pinned by 218 conformance cases whose expected output was generated by running real vim, so where it differs from Vim that is a bug rather than a decision.

Modes

Normal, Insert, Visual, Visual Line, Replace. The current one is in the bottom-left. Esc always goes back towards Normal.

A half-typed command shows in the status bar as you type it, so 2d sitting there waiting for a motion is visible rather than a key that seemingly did nothing.

What is implemented

Motions — h j k l w W b B e E 0 ^ $ G gg f F t T ; , % { }, arrows, and counts on all of them.

Operators — d c y > <, each taking a motion or a count: d2w, 3dd, c$, >j. Doubling the operator takes the line: dd, cc, yy, >>.

Text objects — iw aw iW aW, and i/a with ( ) b, { } B, [ ], < >, ", ' and `. So ciw, da(, di{, yi".

Editing — x X r R s S J gJ ~ p P o O a A I i C D, and Ctrl+A / Ctrl+X to increment and decrement the number under the cursor. ~ toggles the case of the character under the cursor (and of the selection in Visual mode); it is not an operator, so there is no g~.

Registers — named "a to "z, uppercase to append. The unnamed register behaves as Vim's does.

Marks — ma to set, `a to jump exactly, 'a to jump to the line's first non-blank. Uppercase marks are global and name a file as well as a position.

Macros — qa to record, q to stop, @a to play, @@ to repeat. Counts work: 10@a.

Repeat — . repeats the last change, including after ciw, f, a macro, or a mark.

Search — / and ?, n and N, * and # for the word under the cursor.

Visual — v and V. Operators apply to the selection, and o swaps which end you are moving. r, J, ~, > and < work on it too.

Beyond Vim — gd goes to the definition under the cursor, using the language server rather than a local scan.

Indentation

A new line starts where the one it came from started — from o, O and Enter. The leading whitespace is copied exactly, so a tab-indented file stays tab-indented and nothing is silently converted.

An indent you never type on is taken back: o then Esc leaves the line empty rather than full of spaces, as vim does.

auto_indent = false turns it off. It is on by default, which is a deliberate divergence — vim ships autoindent off and nearly every vimrc turns it on. See ADR 0136.

Moving the keys

vim.normal.<key> = <keys> in your config is vim's nnoremap: the key on the left stands for the keys on the right, before anything else looks at it.

vim.normal.Y = y$     # the classic
vim.n = j             # a layout remap, in Normal and Visual
vim.e = k

vim.visual.<key> is Visual only; plain vim.<key> is both, and a mode-qualified one wins.

Because the substitution happens before the dispatcher, a count and a pending operator compose with it — 3n and dn work as 3j and dj without saying anything more.

The right-hand side is not itself remapped, so two keys can be swapped without looping. And a key that is an argument — the target of f, the register after ", the object after i — is never remapped, or f could not find a character you had rebound.

A remap that names no single key is reported when you save the config, not ignored. See ADR 0134.

The command line

: opens it. See The command line for what it takes, including :s/from/to/ and :42 to jump to a line.

What is not implemented

Blockwise visual (Ctrl+V), the case operators (gU, gu, g~), windows and splits as Vim has them (this editor's regions use Ctrl+W but are not Vim windows), folds, :g, registers beyond a–z and the unnamed one, and Vim's own scripting.

Vim-mode work stopped deliberately at the point where what remained was long-tail rather than daily. If something you use every day is missing, that is worth reporting.