When I started this series, I had been using Vim for years without getting much beyond basic editing. Learning the command structure gave all those cryptic combinations somewhere to fit.

We have covered quite a few commands since then. Enough that finding one again can mean scrolling through three posts 😅. This part brings the useful ones together, grouped by what you want to do.

If something here feels unfamiliar, the earlier posts have the explanations and examples:

There is also the Vim cheatsheet for a separate reference.

Start With the Edit You Want

Suppose your cursor is inside this string:

const message = "Hello, World!";

You want to replace the greeting. You could move to one end and delete character by character. Or you could tell Vim: change inside the quotes.

ci"

Type the replacement, then press Esc.

The same pieces work elsewhere: di" deletes the contents, and yi" copies them. Swap i" for iw to work on a word, or i( to work inside parentheses.

The useful question is: what action, on which text? Once you know those two things, you can often assemble the command rather than look up a new shortcut.

A motion describes where to move, such as w or $. A text object describes a piece of text, such as iw or i". Both can give an operator its target, but text objects like iw aren’t standalone Normal-mode movement commands. The motion and text-object reference explains the distinction.

The Everyday Editing Cheatsheet

Unless noted otherwise, start in Normal mode. Press Esc to leave Insert or Visual mode. Type sequences one key after another; Ctrl-r means hold Control while pressing r. Commands beginning with : need Enter.

What you wantKeys
Insert before the cursori
Append at the end of the lineA
Open a new line belowo
Change the word under the cursorciw
Change inside double quotesci"
Change inside parenthesesci(
Delete the current linedd
Delete three lines, starting with this one3dd
Delete from the cursor to the end of the lined$
Copy the current lineyy
Put copied text after the cursor or below the linep
Put it before the cursor or above the lineP
Undo / redou / Ctrl-r
Repeat the last change.

For p and P, the placement depends on whether the register contains characterwise or linewise text. Copy a whole line with yy, and putting it gives you another line.

The dot command is worth remembering early. Change one quoted string with ci", type the new value, and press Esc. Move inside another quoted string and press . to apply that change again. Check the result before repeating it everywhere.

Get to the Right Place

Where you want to goKeys
Next word start / previous word startw / b
End of a worde
Start of the line / first nonblank character / end0 / ^ / $
First line / last linegg / G
Next = on this linef=
Just before the next = on this linet=
Repeat the last character search / reverse it; / ,
Search forward for text/text, then Enter
Repeat the search / reverse its directionn / N

A count is useful when you know how far to go: 3w moves three words forward. When the target is already visible, searching for it can save you from counting.

When You Want to See the Selection First

There is nothing wrong with selecting text before editing it. Seeing the highlighted area can be reassuring when you aren’t sure what a command will include.

SelectionKeys
Charactersv, then move
Whole linesV, then move
Rectangular blockCtrl-v, then move
Inside quotesvi"
Inner paragraphvip
Previous Visual selectiongv

After selecting, use d, c, or y. Use > or < to indent or dedent.

For three lines, press V2j: the starting line plus two below it. For the block-insert example from part 2, start at the first character of the first line, type Ctrl-v, 2j, then I. Type your prefix and press Esc; it appears on all three selected lines.

When the Same Edit Applies to Many Lines

Start with a small range when trying a bulk edit. Add confirmation to substitutions when you want to inspect each replacement.

TaskCommand
Replace all foo matches on the current line:s/foo/bar/g
Replace throughout the file, asking each time:%s/foo/bar/gc
Replace on lines 5 through 10:5,10s/foo/bar/g
Sort lines 5 through 10:5,10sort
Append a semicolon to lines 5 through 10:5,10normal! A;
Delete every line containing TODO:g/TODO/d

In a substitution, % means the whole file; g means every match on each addressed line. The search pattern is a regular expression, so the examples above aren’t a general literal-text replacement recipe.

From Visual mode, pressing : fills in :'<,'>. For example, select some lines and enter :'<,'>s/foo/bar/gc.

That range addresses whole lines. Selecting a few characters or a rectangular block does not limit this substitution to only the highlighted columns.

The ! in :normal! means to ignore mappings when executing the Normal-mode keys. It doesn’t mean the same thing as the force flag in :q!. Read the command before borrowing its punctuation. See the Normal-command reference for the details.

When a Delete Replaces Your Copied Text

This was the opening problem in part 3: yank a line, delete another one, then p puts the deleted line instead.

TaskKeys
Put the latest yank made without choosing another register"0p
Delete a line without updating the usual registers"_dd
Keep a line in register a"ayy
Put from register a"ap
Inspect registers:registers
Copy a line to the system clipboard"+yy
Copy a Visual selection to the system clipboardSelect first, then "+y
Put from the system clipboard"+p

The clipboard commands need working clipboard integration. In Vim, check :help clipboard; in Neovim, start with :checkhealth. Over SSH, the remote editor may not have access to your local clipboard. Part 3 covers those setup differences. The register reference explains where each operation stores text.

When You Need Another File

Keep the three terms straight: a buffer holds text, a window displays a buffer, and a tab page holds a window layout.

TaskCommand or keys
Open a file in this window:edit utils.js
List buffers:ls
Switch to an existing buffer by a unique name fragment:buffer utils
Return to this window’s alternate buffer:buffer #
Open a file beside the current one:vsplit utils.js
Open it above or below:split utils.js
Move between windowsCtrl-w, then h, j, k, or l
Equalize window sizesCtrl-w, then =
Close the current split:close
Save the current buffer:w
Create another tab layout:tabnew
Next / previous tab pagegt / gT

If switching files produces an unsaved-change warning, save first. :set hidden lets modified buffers stay in memory without being displayed, but it doesn’t save them. Don’t add ! to a warning just to make it go away. The multiple-files tutorial explains the save and switch workflow.

Try One Small Workflow

Put this in a disposable practice file:

const title = "Old title";
const subtitle = "Old subtitle";
console.log(title);

Then try:

  1. Move inside the first string. Use ci", type New title, and press Esc.
  2. Move to the subtitle line and yank it with yy.
  3. Delete the log line with dd.
  4. Press "0p to put the subtitle you yanked, rather than the log you just deleted.
  5. Undo that paste with u.
  6. Save with :w.

You have used the command language, a register, and undo in one ordinary edit. Repeat it until you can explain why each key is there, then try a different change.

Keep the Reference and Get Back to Editing

You don’t need to remember this whole page. Pick the command that solves today’s annoyance. Look it up again tomorrow if you forget it.

That was the point of this series: getting comfortable enough with Vim that a simple edit doesn’t turn into a fight with the editor. There is plenty left to learn, but there is also plenty here to use already.

When you need the exact behavior, Vim’s own help is worth opening: :help text-objects, :help registers, :help :substitute, and :help windows. For an online reference, see the Vim user manual.