Lesson 4 — Text Objects

Two things happen constantly while you work with a coding agent. You mistype a word in the middle of a long prompt. And it prints cannot open "src/lib/router.ts" and you want that path for your next one.

Both have the same clumsy answer without Vim: walk to the start of the thing, then measure your way to its end, and hope you counted right. Text objects remove the measuring. You name the thing instead: the word, the quoted string, the block I am standing in — and the cursor only has to be somewhere inside it.

That is what a text object is: not a direction to travel, but a noun. Three keys, and the whole thing is yours — ciw to replace the word you are standing on, yi" to lift the path out of the error, diW to remove a URL you pasted and thought better of.

This is the lesson that pays for the course. Motions are how you get around; objects are how you change what you are writing and lift things out of what you are reading.

Inner and around

Every object is two keys: i for inner, a for around, then a letter or a delimiter saying what kind of thing you mean.

  • iw — the word, and nothing else.
  • aw — the word with the space after it. That is what you want when the deletion shouldn't leave a double space behind, or when the yank is going to be pasted up against something else.

Put an operator in front and read it as a sentence. yiw is yank inner word. daW is delete around WORD. vip is select inner paragraph. yi" is yank inner quotes.

The part that feels like a trick the first time: you do not have to be at the start of the thing. Anywhere inside it will do. Land somewhere in the middle of a filename and yiW takes all of it, both directions at once. That is the whole reason objects beat motions for this errand — a motion has to start from the right place, and an object doesn't care.

The kinds of object

Everything here has both an i and an a form.

ObjectWhat it namesWhere it works
iw awa word — a run of letters and digitsBoth
iW aWa WORD — a run of non-space. A URL is one of theseBoth
ip apthe block you're standing in — a paragraph, or one run of output rows. ap reaches on over the blank rows after itBoth (copy and select only, in a terminal)
i" i' and the backtick formthe quoted runBoth
i( i) — or vim's alias ibparenthesesBoth
i[ i]square bracketsBoth
i{ i} — or vim's alias iBbracesBoth
i< i>angle bracketsBoth

Three things about the delimiters are worth knowing, because they save you keystrokes every time:

Either half of a pair names it. yi( and yi) are the same command, and so are da[ and da]. Press whichever one your thumb or your hand reaches first — there is nothing to remember.

In a terminal you don't even have to be inside the pair. If there is no pair around the cursor, the next one along the line is taken instead. Sitting at the start of a line of output and pressing yi" lifts the quoted path out of it. Landing on the right line is usually enough.

i on an empty pair takes nothing. Given () there is no inside, so nothing is yanked — rather than quietly handing you a bracket you didn't ask for. And a quote the shell escaped with a backslash isn't treated as a delimiter, so a path with an escaped quote in it still comes out whole.

Putting an operator in front

Every operator you learned in Lesson 3 takes an object, and so does v. That is the entire combination table — you already know both halves.

To do thisPressWhere it works
Copy the word under the cursoryiwBoth
Copy a URLyiWBoth
Copy a word with its trailing spaceyawBoth
Copy the block an agent just printedmove up into it, then yipBoth
Copy that block and the gap after ityapBoth
Select that block to look at it firstvipBoth
Lift a path out of cannot open "src/x.ts"yi"Both
Copy what's inside parenthesesyi(Both
Select a URL you're about to act onviWBoth
Select the word under the cursorviwBoth
Delete a pasted URL you don't wantdiWOn the line you're typing
Replace the word you're onciwOn the line you're typing
Delete a word and its trailing spacedawOn the line you're typing
Fix a quoted string you're typingci'On the line you're typing
Delete a bracketed section, brackets and allda[On the line you're typing
Replace what's inside bracesci{On the line you're typing
Select inside angle bracketsvi<Both
Fix the case of a wordgUiw guiwOn the line you're typing

Notice that nothing here is new vocabulary. c still means change, y still means yank, i still means inner. You learned the pieces separately and the combinations arrived free.

Where each of these works

The rule from Lesson 3 holds, and objects are where you feel it most.

Copying and selecting reach the whole screen. yiw, yiW, yip, yi", viW — all of these work wherever your cursor is, including far up in old output, in an error from ten commands ago, in the middle of an agent's reply while it is still thinking. Reading is always allowed.

Changing and deleting reach exactly one line — the one you are currently typing, and only while nothing is running. Put the cursor up in the output and diw simply does nothing; that output has already happened. Copying, though, works anywhere on the screen, which is what you wanted up there anyway.

dip does nothing. Use yip instead — copying a block is almost always what you meant.

A copy that crosses rows comes out whole lines at a time. yip gives you the block linewise, which is what you want when the next stop is a prompt or a chat message.

ip means the block your cursor is standing in. That is worth saying plainly, because the commonest surprise is pressing Esc then yip and getting your own prompt box: the cursor starts at the prompt, so that is the block it is in. Step up into the agent's reply first — k a few times, or { to jump a whole block — and then yip takes what you meant.

Every yank also lands on your system clipboard, so yip in a terminal on your phone can be pasted into anything else on the device.

In a code editor

Objects behave the way you expect from Vim: the same i and a forms, the same delimiters, and no restriction about which line you are on. ciw on line four hundred is as ordinary as ciw on the line you just typed, and ci( inside a function call replaces the arguments wherever they are.

The other difference is that a code editor has u. That makes it the best place to learn objects: put the cursor somewhere awkward, press daw, look at what vanished, press u, try diw instead. Five minutes of that and you will never think about the difference again.

Try it

Open a terminal with Vim keys on, and run something that prints a path in quotes — cat "no/such/file.txt" will do it, and so will any agent that has just failed at something.

  1. Press Esc to reach normal mode.
  2. Move onto the line with the quoted path in it. You do not need to be inside the quotes.
  3. Press yi". The path is now on your clipboard as well as in Vim's register.
  4. Press p to drop the path into your prompt, then i to carry on typing. That order matters: p only means paste in normal mode.
  5. Press Esc, then move up into a block of output — an agent's reply, or the output of one command — and press yip to take the whole block.
  6. Press i first — yip left you in normal mode — then type a command with a wrong word in it. Press Esc, move onto that word and press ciw. Type the correction; you are already in the right place.
  7. Now paste a long URL into your prompt, press Esc, land anywhere on it, and press diW. Three keys, whatever its length.
  8. For contrast, move up into old output, land on a word, and press diw. Nothing happens — changing only reaches the line you are typing.

You now know: how to name a thing instead of measuring it — i for inner and a for around, over words, WORDs, blocks, quotes and brackets. Naming works everywhere for copying things out of output, and on the line you are typing for changing what you are writing.

Lesson 3 — Operators, Counts and the Grammar · Lesson 5 — Search, Yank, Registers and Marks