Editing: Operators, Counts, and the Dot Command

This chapter turns motions into edits, introduces the four operators you'll use most, and explains the dot command that makes repetition almost free.

The Operator + Motion Formula

The core idea of Vim editing is:

operator + motion

An operator says what to do. A motion says how far. You already know the motions from chapter 3. Now meet the operators.

d    delete
c    change (delete, then enter insert mode)
y    yank (copy)
>    shift right (indent)
<    shift left (outdent)
=    auto-format
gU   uppercase
gu   lowercase
~    toggle case

Put them together:

dw       delete a word
d$       delete to end of line
dG       delete to end of file
cw       change a word
y$       yank to end of line
>ip      indent the current paragraph
gUw      uppercase a word

This grammar is the reason Vim is powerful. Five motions times eight operators is forty combinations, and you learned them by learning each piece separately.

Linewise Operators

Doubling an operator applies it to the whole current line:

dd    delete the current line
cc    change the current line (replaces the line with insert mode)
yy    yank the current line (or Y, same thing)
>>    indent the current line
<<    outdent the current line
==    auto-format the current line

Counts work here too:

3dd    delete 3 lines
5yy    yank 5 lines
2>>    indent 2 lines

dd is probably the single most common editing command in Vim. Learn it first.

Counts with Operators

You can put a count in two places, and they mean the same thing:

3dw    delete 3 words (count before operator)
d3w    delete through 3 words (count between)
3d3w   delete 9 words total (counts multiply)

Use whichever order feels natural. I tend to write the count first: 3dd, 5j, 10>>.

The Dot Command

Press . in normal mode to repeat the last edit.

dw       delete a word
.        delete another word
.        and another

. is the killer feature most new Vim users miss. It captures the entire last change, not just the last keystroke. If you typed ciwtest<Esc> (change inner word to "test"), pressing . somewhere else repeats the whole sequence: change inner word to "test".

Combined with n (next search match) and f (find character), . is how you do surgical edits across a file:

/TODO<Enter>       search for TODO
cw                  change the word (delete TODO, enter insert)
DONE<Esc>           type the replacement
n                   jump to the next TODO
.                   replace that one too
n .                 repeat

Call this the dot formula: one key to move, one key to repeat. It beats most macros for small-to-medium jobs.

Undo and Redo

u        undo the last change
<C-r>    redo
U        undo all changes on the current line (rare)

Vim's undo granularity is "one change", which usually means "everything between entering and leaving insert mode". If you type a paragraph, <Esc>, then u, the whole paragraph disappears. That is usually what you want.

The undo tree

Vim does not just have a linear undo history. It has a tree. If you undo, then make a new change, the old branch is not lost:

:undolist    show the tree (or :u to undo)
g-           go back in time
g+           go forward in time
:earlier 5m  revert to the state 5 minutes ago
:later 1h    advance an hour

Most people never touch these. But if you ever need them, they are there. Plugins like undotree give you a visual browser.

Put: Paste in Vim

After yanking or deleting, the text is in a register. Put it back with:

p    put after the cursor (or below, for linewise)
P    put before the cursor (or above, for linewise)

Linewise and charwise put behave differently. If you yanked a whole line with yy, p pastes it on the next line, not in the middle of the current one. Vim figures this out from how you yanked.

yy     yank this line
p      paste it below

yw     yank a word
p      paste it after the cursor on this line

The classic pitfall

Yank a word with yw. Delete another word with dw. Now press p. It pastes the word you just deleted, not the one you yanked.

Every operation that removes text (delete, change) also writes to the unnamed register, overwriting your yank. This is a real problem that every Vim user bumps into. The fixes, in order of increasing convenience:

  1. Use the yank register "0p to paste the last yank, not the last delete
  2. Use named registers: "ayw yanks into register a, then "ap pastes from it
  3. Set set clipboard=unnamedplus and use the system clipboard (see chapter 8)

More on registers in chapter 8. For now: if p pasted the wrong thing, try "0p.

Single-Key Shortcuts

Vim has dedicated keys for common operator-motion pairs. Learn these. They save keystrokes.

x       same as dl (delete the character under the cursor)
X       same as dh (delete the character before the cursor)
D       same as d$ (delete to end of line)
C       same as c$ (change to end of line)
Y       same as yy (yank the whole line)
s       same as cl (substitute a character: delete and enter insert)
S       same as cc (substitute the line)
r{x}    replace the single character under the cursor with x
R       enter replace mode (overwrite as you type)

D and C are the two I use constantly. "Delete to end of line" and "change to end of line" are extremely common.

r is great for single-character typos. Position on the wrong character, ra to replace with a, done. No mode switch.

Putting It Together

A realistic edit: change const user = "Alice" to let person = "Bob".

^          jump to start of line content
cw         change "const" to "let"
let<Esc>   type the replacement
w          move forward a word
cw         change "user" to "person"
person<Esc>
f"         jump to the first quote
ci"        change inside the quotes
Bob<Esc>

Or, with less typing and more composition:

^cwlet<Esc>
wcwperson<Esc>
f"ci"Bob<Esc>

Or, if every const in the file should become let and every user should become person, skip forward to chapter 6 on :substitute.

Case Changes

Case operators work exactly like the others. They need a motion:

gUw        uppercase a word
guw        lowercase a word
gUU        uppercase the current line
guu        lowercase the current line
~          toggle case of the character under the cursor
g~w        toggle case of a word

Useful for constants, SQL, and yelling at your codebase.

Common Pitfalls

"p pasted the wrong text." You deleted something after you yanked. Use "0p to paste the last yank, or read chapter 8 on named registers.

"I pressed . and it didn't do what I expected." . repeats the last change, not the last motion or command. dw then . repeats the delete. /foo<Enter> then . does not repeat the search (use n for that).

"I undid too far and lost real work." <C-r> redoes. If you've made new changes in the middle, :earlier 1m might bring it back via the undo tree.

"My c operator entered insert mode with a dollar sign showing." c$ and cw leave a $ at the end of the target text to show what's about to be replaced. Type your replacement, <Esc>, and the old text disappears.

Next Steps

Continue to 05-text-objects.md for the feature that multiplies every operator you just learned.