# Vim mode

Vim keybindings for editing in Pasty, off by default. Turn them on from `⌘K` → `Enable Vim mode`, or from Settings (in the account menu, and in the `⌘K` palette). It applies everywhere, not per document, and it is remembered: on your account if you have one, in this browser if you do not, and it survives signing up.

## What works

Motions (`h j k l`, `w b e`, `W B E`, `ge gE`, `0 ^ $ g_`, `gg G`, `{ }`, counts), insertion (`i I a A o O gi`), the single-key edits (`x X r s J u <C-r>`), the operators `d c y > <` with `dd cc yy >> <<`, `p P`, `v V gv`, text objects (`iw aw iW aW`, `i" a"`, `i( a(`, `i[ i{`), marks (`m{a-z}`, `` `{a} ``, `'{a}`, and two backticks to return to the previous jump), the full register set (`"a`–`"z`, `"A`–`"Z` to append, `"0`, `"1`–`"9`, `"_`, `"+`), repeat (`.`), search (`/ ? n N * #`), character find (`f F t T ; ,`), matching brackets (`%`), the shorthands (`D C Y ~`), macros (`q{a-z}`, `@{a-z}`, `@@`), and the scroll family (`<C-e> <C-y> <C-d> <C-u> <C-f> <C-b>`).

The supported set is defined by a test, not a list: a command is not supported until it has a case in the compatibility suite, and every place Pasty deliberately does something other than terminal Vim is recorded there too.

## Three things worth knowing

- **`j` and `k` move by logical line**, not by rendered row, so a wrapped paragraph is one line. The gutter down the left shows those same logical lines, so what it says and what `42G` does are the same thing. The engine measures no geometry at all, which is what makes every motion deterministic and testable without a browser. `gj`/`gk` and Visual Block are not supported for the same reason.
- **Search patterns are Vim patterns**, with smartcase: a bounded subset of magic mode rather than the whole language. `.` `*` `[…]` `^` `$`, the backslashed forms (`\+ \? \| \( \) \{n,m}`), the word boundaries `\<` `\>`, the classes `\w \s \d \a` and their negations, and `\c`/`\C`. Anything else (`\zs`, `\%(`, lookaround, a pattern spanning lines) is refused with `Invalid pattern` rather than reinterpreted, and JavaScript's own regex syntax never leaks through: in magic mode `+ ? ( ) { } |` are ordinary characters, so searching for `f(x)` finds `f(x)`.
- **`>>` means nesting**, in a list, and two literal spaces inside a code block. On a paragraph it does nothing: Markdown has no indented paragraph, and four leading spaces are a code block.

A macro is a list of commands, not keystrokes, so replaying re-resolves every motion where the cursor now is. A macro stops when a command in it *fails* (`j` on the last line, `5rZ` on a two-character line), which is what makes `9@a` terminate at the end of a document; a command that succeeded without moving anything, like `0` in column 1, does not stop it. Clipboard puts are deliberately not recorded: `"+p` completes asynchronously after a permission prompt, and a macro containing one would sometimes run out of order.

## The command line

`/`, `?` and `:` open a command line at the bottom of the page. `:` accepts eight spellings: `:s/pat/repl/`, `:s/pat/repl/g`, the same two with a `%` range, `:noh`/`:nohlsearch`, and `:w`/`:w!`. `:w` is not a second way to save: Pasty saves continuously, so it flushes what is already in flight and says `Saved` only once it has actually landed. Everything else it refuses out loud. `:q` in a browser tab and `:e` with no filesystem each have an obvious wrong answer, and a command line that half-works is worse than one that says what it does not do.

## Who owns a keystroke

Application shortcuts always win: `⌘K`, `⌘Z`, `⌘V` and every other modified chord reach their owners untouched, and an input method is never interrupted. Ctrl is the other way round, and deliberately so: in NORMAL, VISUAL and VISUAL LINE **every** Ctrl chord belongs to Vim, whether implemented, aliased (`<C-[>` and `<C-c>` are Escape), answered (`<C-v>` says Visual Block is not supported), or swallowed. Left unclaimed they would reach macOS's emacs bindings inside the document, where `Ctrl-D` deletes and `Ctrl-K` kills the line while the indicator still says NORMAL. INSERT is the opposite: it is ordinary typing, so the platform keeps its bindings there.

## Debugging

Debugging a keyboard problem is otherwise guesswork, so there is a logger, and it ships off. `localStorage.setItem('pasty.vim_debug', '1')` prints every keydown with the engine's verdict for it (`claimed: motion`, `consumed: unsupported-modal-chord`, `passed: modifier-passthrough`, …), and `window.__pasty.vim.debugMacro('a')` prints what a macro actually recorded. Removing the key silences it immediately; nothing is logged or sent anywhere without it.
