Skip to content
| Marketplace
Sign in
Visual Studio Code>Keymaps>Vim UltraNew to Visual Studio Code? Get it now.
Vim Ultra

Vim Ultra

weixu

|
1 install
| (0) | Free
Modal Vim editing on a Rust/WASM engine — motions, operators, text objects, counts, registers, visual modes, multiple cursors, regex search (/ ? n N * #) and search-and-replace (:s). Partial Vim: no other ex commands, macros, marks, dot-repeat, named registers, replace mode, or block visual.
Installation
Launch VS Code Quick Open (Ctrl+P), paste the following command, and press enter.
Copied to clipboard
More Info

Vim Ultra

Modal Vim editing for VSCode, powered by a from-scratch Vim engine written in Rust (crates/vim-engine) and compiled to WASM. The TypeScript side is a thin host: it forwards keys, applies the engine's effects (edits, selections, commands), and mirrors external document changes back into the engine.

Supported today

  • Modes: normal, insert, visual, visual line. Status bar shows the mode and pending keys; the cursor is a block outside insert mode.
  • Motions: h j k l, 0 ^ $, w W b B e E, gg G, { }, %, f F t T with ;/,, enter + -, all with counts (3w, 2f,).
  • Search: / and ? (the pattern replaces the mode text in the status bar while you type it; backspace edits it, escape aborts, enter runs it), n/N to repeat, */# for the word under the cursor. Searches wrap around the buffer, take counts (3n), an empty pattern repeats the last one, and — being exclusive motions — they combine with operators (d/foo⏎, dn, y*). Typing a pattern highlights its matches and peeks at the one enter would land on (vim's incsearch): the view scrolls there without moving the cursor, comes back if you escape, and stays if you commit. A landed search reports match 3 of 11 in the status bar.
  • Regular expressions in every pattern: . * \+ \? \{n,m} (and the non-greedy \{-}), \(…\) groups with \%(…\) non-capturing, \| alternation, [a-z] and [^…] collections with [:alpha:] names, the classes \w \W \s \S \d \D \a \l \u \h \x \o (and negations), ^ $ \< \> anchors, \zs/\ze to trim the match, \c/\C for case, and the magic levels \v \m \M \V.
  • Search and replace: :[range]s/pattern/replacement/flags, with ranges %, 5, 1,$, .,+3, '<,'> (typing : in visual mode fills that in) and flags g i I n e. Replacements take & / \0 for the whole match, \1…\9 for groups, ~ for the previous replacement, \r for a line break, and \u \l \U \L \E for case. & repeats the last substitution on the current line, as does a bare :s, and :12 jumps to a line. The status bar reports the result (3 substitutions on 2 lines) and any error.
  • Operators: d c y > < with any motion (d2w, ci", y$, >j), doubled for lines (dd, 3yy, cc, >>), and on visual selections.
  • Text objects: iw aw iW aW, i" a" i' a' i\ a``, i( a( i[ a[ i{ a{ i< a< (aliases b/B).
  • Actions: x X s S D C Y r ~ J p P o O, u / ctrl+r (delegates to VSCode undo/redo), zz zt zb, ctrl+d/u/f/b scrolling. r<CR> is vim's line-splitting special case: the characters go away and a single line break takes their place, however many the count asked for.
  • Registers: the unnamed register, charwise and linewise, with counted pastes.
  • Multiple cursors: make them the VSCode way — cmd+alt+up/down for a column, cmd+d for the next occurrence, alt-click — and every key runs at all of them. I opens insert mode at each line's first non-blank, A at each end, i a o O where you'd expect; motions, operators, text objects, x, p and r all work per cursor, and the status bar counts them. Escape leaves insert mode with the cursors intact; escape again drops back to one. Selections made with cmd+d come in as visual mode at every cursor, so c changes each occurrence. What is shared is the mode, the register (p pastes the same text everywhere) and the search history; what runs once is : commands and undo, which already cover the document.
  • Mouse clicks and drags work: a drag enters visual mode.

Not yet implemented

Marks, macros, dot-repeat, named registers, replace mode (R), block visual (ctrl+v — the column editing it is for is what multiple cursors do here) and the jumplist. :s is the only ex command: no :w, :g, :sort, … — an unknown one says so in the status bar rather than doing something surprising.

Multiple cursors edit one document, so two of them reaching for the same text (dd on adjoining lines, d/foo⏎ across a neighbour) is a conflict: the editor rejects the whole transaction, the key does nothing, and the engine rebuilds its mirror from the document rather than guessing. Each cursor also reads the buffer as the cursors below it have already left it, so a motion that runs down past another cursor's edit sees the new text.

Pattern support stops at look-around (\@= and friends), \&, ~ for the previous substitute pattern, and multi-line matching: a match never spans a line break, so \n in a pattern matches nothing. Matching is case-sensitive unless the pattern says otherwise (ignorecase and smartcase are not implemented). Match highlights show while a pattern is being typed (incsearch) but clear once it runs — there is no persistent hlsearch. The c (confirm) flag on :s is rejected instead of silently replacing without asking. In a replacement, \n breaks the line like \r does, rather than inserting Vim's NUL.

Searching is bounded, not linear-time: patterns run on a backtracking VM, so a catastrophic one (\%(\w*\)*\d\{9} and relatives) is stopped by a step budget sized to the buffer — a tenth of a second or so — and reported as gave up: pattern is too slow to run here rather than passed off as "not found" or allowed to freeze the editor. Ordinary patterns are nowhere near it: most lines are rejected by a required-literal test before matching starts, which puts a whole-buffer search over a 1 MB file at well under a millisecond. See crates/vim-engine/tests/perf.rs for the measurements.

Development

pnpm run build:wasm   # wasm-pack build of crates/vim-engine into wasm/
pnpm run build        # tsdown bundle into dist/
pnpm run test         # vitest (host helpers)
cargo test -p vim-engine   # the engine's real test suite

The engine keeps a line-based mirror of the document (UTF-16 columns, so positions round-trip with the VSCode API). Every edit the engine emits is self-applied to its mirror and applied to the document under a suppression flag; all other document changes flow back via apply_changes. If an edit is rejected (readonly file), the mirror is rebuilt from the document.

  • Contact us
  • Jobs
  • Privacy
  • Manage cookies
  • Terms of use
  • Trademarks
© 2026 Microsoft