level 04

Operators + Motions

The superpower: compose operators with motions and text objects.

This is the level people mean when they say Vim “clicked.” Everything so far was vocabulary. Here you start forming sentences. Operators combine with the motions and text objects you already know, so the number of edits you can express explodes — and you never had to memorize any of them. This is also home to Vim’s best idea, the one almost no other editor copied: text objects.

The Main Operators

d  → delete (also cuts to register)
c  → change (delete and enter Insert mode)
y  → yank (copy)
>  <  =  → indent, de-indent, auto-indent

Common Combinations

dw dd D   → delete word / line / to end of line
cc C      → change line / to end of line
yy        → yank line
>> << ==  → indent / de-indent / auto-indent line

Text Objects: The Real Power

Text objects operate on semantic units. They take i (inner) or a (around):

iw aw     → inner / a word
ip ap     → inner / a paragraph
i" a"     → inside quotes / including the quotes
i( a(     → inside / including parentheses
i{ a{     → inside / including braces
it at     → inside / including an HTML/JSX tag

Combine an operator with a text object and you describe an edit precisely: ci" means change the thing inside these quotes — no manual selecting required. ciw is the most-used rename command.

Delete with text objects

  1. With the cursor in "Hello, world!", press di" — deletes inside the quotes; the quotes stay.
  2. Undo, then press da" — deletes the string including the quotes.
  3. On the numbers line, put the cursor inside [...] and press di[.
  4. On the user line, put the cursor inside {...} and press di{.
normalpress i to type · hjkl to move

goal Feel the difference between i (inner) and a (around).

Change with text objects

  1. On calculateTotal, press ciw, type computeSum, press Esc.
  2. On price in the parameter list, press ciw, type unitPrice, press Esc.
  3. Inside the (...) parameters, press ci(, type item, count, rate, press Esc.
  4. Inside the function body {...}, press ci{ and type a new body.
normalpress i to type · hjkl to move

goal Use ciw to rename words and ci( or ci{ to replace delimited content.

Yank and paste with text objects

  1. With the cursor in "localhost", press yi" to yank the content.
  2. Move to the empty "" in devConfig, press ci", then Ctrl+r " to paste the register, press Esc.
  3. Put the cursor in the first {...}, press yi{ to yank the block.
  4. Move to the second {...} and press ci{ then Ctrl+r ".
normalpress i to type · hjkl to move

goal Copy and replace structured content with yi and ci.

Operators with motions

  1. On line 2, press d2j — deletes the current line plus two below (three total). Counts on j reach that many lines down, so d2j spans three lines, not two.
  2. Undo. On line 1, press y2j to yank three lines, then G and p to paste at the end.
  3. On any line, press >> to indent, then << to un-indent.
normalpress i to type · hjkl to move

goal Operators combine with counts and motions, not just text objects.

HTML / JSX text objects

  1. Inside the <h1> content, press cit, type Hello World, press Esc.
  2. On the <strong> element, press dat — delete the entire tag.
  3. Inside "container", press ci" and type wrapper.
  4. On the word handleClick, press ciw and type onButtonClick.
normalpress i to type · hjkl to move

goal cit and dat operate on whole tags.

Challenge — refactor without the mouse

  1. Change "John" to "Jane".
  2. Change "Doe" to "Smith".
  3. Change the email to "jane.smith@example.com".
  4. Delete the phoneNumber line with dd.
  5. Change "New York" to "Los Angeles".
normalpress i to type · hjkl to move

goal All edits with text objects and operators only.

Capstone — reshape a function, operators only

The three const lines were copy-pasted and never finished — every one still reads req.body.id. Fix the whole function with operators and motions:

  1. Rename handler to updateUser: cursor on the word, ciw, type it, Esc.
  2. Line 2 keeps .id. On line 3, put the cursor on the trailing id and ciw it to name. On line 4, ciw the trailing id to email.
  3. Replace the response payload: cursor inside the { ok: true }, press ci{, type id, name, email, Esc.
  4. The function has no error guard. On the const id line press O, type if (!req.body) return res.status(400).end();, Esc.
  5. Grab the whole body in one stroke: yi{ yanks everything inside the function’s braces — confirm the register holds all the inner lines at once.
normalpress i to type · hjkl to move

goal Compose text objects, counts, dot, and yank/paste into one clean refactor. No mouse, no Insert-mode navigation.