Text Objects: The Grammar Multiplier
What a Text Object Is
A text object is a region of text Vim can identify on its own: a word, a sentence, a paragraph, the contents of quotes, the contents of brackets. You tell Vim "the word I'm inside of" or "what's between these parentheses", and Vim finds the boundaries for you.
Text objects replace motions in the operator grammar. Instead of:
operator + motion
You get:
operator + text object
Example. Cursor anywhere inside the word price:
diw delete inner word (the word "price", not the space after it)
daw delete a word (the word and one surrounding space)
ciw change inner word: delete the word and enter insert mode
yiw yank the word into the unnamed register
No precise positioning, no counts, no guessing. Vim finds the word for you.
Inner vs Around
Every text object has two forms:
i{object}means inner: just the contenta{object}means around: content plus surrounding whitespace or delimiters
For quoted strings and brackets, "inner" means inside the quotes, "around" means inside plus the quotes themselves.
"Alice"
ci" deletes Alice, enters insert. Quotes remain
ca" deletes "Alice", including quotes. Enters insert
Learn both. i{object} is what you want 90% of the time.
Word Objects
iw inner word: a word without surrounding whitespace
aw a word: a word plus trailing (or leading) whitespace
iW inner WORD: a space-delimited token
aW a WORD: with surrounding whitespace
Like motions, lowercase w breaks on punctuation, uppercase W does not.
user.name
ciw with cursor on "user", changes "user". "name" remains
ciW with cursor anywhere in "user.name", changes the whole thing
Quote Objects
i" inner double-quoted string
a" around double-quoted string (includes the quotes)
i' inner single-quoted string
a' around single-quoted string
i` inner backtick string
a` around backtick string
Cursor does not need to be inside the quotes. Vim looks forward on the current line for the nearest matching pair:
const name = "Alice"
# cursor is at the end. ci" still works
This is the cleanest way to change a string literal.
Bracket Objects
Every pair of brackets gets a text object:
i( or ib inner parentheses
a( or ab around parentheses (includes the parens)
i{ or iB inner braces
a{ or aB around braces
i[ inner square brackets
a[ around square brackets
i< inner angle brackets
a< around angle brackets
You can also use the closing bracket, which means the same thing: i) equals i(.
Example. Change the body of a function:
function hello() {
console.log("hi");
}
Position anywhere inside the braces, then:
ci{ delete everything between the braces, enter insert mode
Works for function calls, array literals, object literals, HTML attributes, anything bracketed.
Tag Objects
For HTML, XML, and anything with <tag>content</tag>:
it inner tag: content between the tags
at around tag: content plus the tags themselves
<h1>Hello</h1>
dit deletes "Hello", leaves <h1></h1>
dat deletes the whole thing
Paragraph and Sentence Objects
ip inner paragraph
ap a paragraph (includes a trailing blank line)
is inner sentence
as a sentence (includes trailing whitespace)
Paragraphs are text blocks separated by blank lines. Sentences are ended by ., ?, or ! followed by whitespace. Both definitions are tweakable; see :help paragraph and :help sentence if you care.
dap deletes a whole paragraph, including the blank line after. Great for notes, docstrings, and email drafts.
Block Object
Vim has a text object for the block you're inside of:
iB inner block (inside { })
aB around block
This is the same as i{ / a{, with a nicer mnemonic. I use i{ out of habit.
Composition Examples
Every operator you learned in chapter 4 works with every text object:
diw delete a word
ciw change a word
yiw yank a word
di( delete contents of parentheses
ci" change contents of quotes
ya( yank the parentheses and their contents
>ip indent a paragraph
=ip auto-format a paragraph
gUiw uppercase a word
vip select a paragraph (visual mode)
vi{ select a block
You do not have to memorise combinations. You memorise the operators, you memorise the objects, and you compose them at runtime. That is the whole design.
A Realistic Session
You have this function:
function calculate(a, b, c) {
return (a + b) * c;
}
Rename calculate to total:
Cursor on "calculate"
ciw
total<Esc>
Remove the third parameter:
Cursor inside the parens
# Need to remove ", c"
f, jump to the comma
dt) delete up to (not including) the closing paren
Or better, if c is the only one you want to keep:
f,dw jump to the comma, delete word to the end
Change the body of the function:
cib change inner block (the { } body)
return a * b;<Esc>
Pitfalls and Surprises
"ci" didn't work, I thought my cursor was inside the string." Vim searches forward on the line for the nearest quote pair. If the cursor is past the last quote, there is nothing to find. Move the cursor to the left of the string first.
"I pressed cw and it didn't include the space." cw and dw are motion-based, not object-based. They go to the start of the next word, which means the space before the next word is included by d but not by c (because ce is what c actually uses, for historical reasons). Use ciw for consistent behaviour on single words.
"i( jumps me to parens I'm not inside." It does not. If the cursor is not inside any pair on the current line, Vim looks forward. If there are none, you get a beep. That is Vim's polite way of saying no.
"I want to change a word that has a dash in it." A dash breaks "word" (iw). It does not break "WORD" (iW). ciW is what you want for foo-bar or text-align.
The Text Object Cheat Sheet
Burn this into memory. It is three columns and two operators per row:
Content Inner Around
Word iw aw
WORD iW aW
Double quotes i" a"
Single quotes i' a'
Backticks i` a`
Parentheses i( a(
Braces i{ a{
Brackets i[ a[
Angle brackets i< a<
Tags it at
Sentence is as
Paragraph ip ap
Block (code) iB aB
Practice until you can reach for any of these without thinking. That is the point where Vim stops feeling like a puzzle.
Next Steps
Continue to 06-search-and-replace.md for finding and changing text across a whole file.