The Problem#
You’re editing code and your cursor is somewhere inside a string:
message = "The quick brown fox jumps over the lazy dog"
^ cursor hereYou want to replace the entire string content without disturbing the quotes. Moving to the first quote, deleting to the second quote, then repositioning — that’s slow. Vim’s text objects let you do it in three keystrokes.
Inner vs Around#
Every delimiter-based text object has two variants:
| Prefix | Meaning | Includes delimiters? |
|---|---|---|
i | Inner | No — just the content between |
a | Around | Yes — content plus the delimiters themselves |
"hello world"
^^^^^^^^^^^ ← ci" changes this (inner)
^^^^^^^^^^^^^ ← ca" changes this (around)Quotes#
Double quotes#
ci" " Change inside double quotes
di" " Delete inside double quotes
yi" " Yank inside double quotes
vi" " Visually select inside double quotes
ca" " Change around double quotes (removes the quotes too)
da" " Delete around double quotesExample:
name = "John Smith"With cursor anywhere between the quotes:
ci"→ deletesJohn Smith, leaves you in insert mode between empty quotes:name = ""di"→ deletesJohn Smith, stays in normal mode:name = ""da"→ deletes"John Smith"entirely:name =
Single quotes#
ci' " Change inside single quotes
di' " Delete inside single quotes
yi' " Yank inside single quotesconst greeting = 'hello world'
// ci' → 'hello world' becomes ''
Backticks#
ci` " Change inside backticks
di` " Delete inside backticksUse the `find` command to search.
// ci` → `find` becomes ``Parentheses#
ci( " Change inside parentheses
ci) " Same thing
cib " Also the same (b = block/brackets)
di( " Delete inside parentheses
yi( " Yank inside parentheses
vi( " Select inside parenthesesExample:
int result = calculate(x + y, z * 2);Cursor anywhere inside the parens:
ci(→ deletesx + y, z * 2, leaves you typing inside empty parens:calculate()da(→ deletes(x + y, z * 2)including the parens
Nested parentheses#
Vim finds the innermost pair containing your cursor:
printf("Result: %d\n", compute(a + b));
^ cursorci(→ changesa + b(innermost)- Move cursor outside
compute(...)andci(changes"Result: %d\n", compute(a + b)(outer pair)
Curly Braces#
ci{ " Change inside curly braces
ci} " Same thing
ciB " Also the same (B = Block)
di{ " Delete inside braces
yi{ " Yank inside bracesExample — empty a function body:
function processData(input) {
const cleaned = sanitize(input);
const result = transform(cleaned);
return result;
}Cursor anywhere inside the braces:
ci{→ deletes the entire body, puts you in insert mode between the bracesdi{→ deletes the body, stays in normal mode
This works across multiple lines — Vim finds the matching { and }.
Square Brackets#
ci[ " Change inside square brackets
ci] " Same thing
di[ " Delete inside brackets
yi[ " Yank inside bracketsExample:
colors = ["red", "green", "blue"]ci[→ deletes"red", "green", "blue", leavescolors = []yi[→ yanks the array contents
Angle Brackets#
ci< " Change inside angle brackets
ci> " Same thing
di< " Delete inside angle bracketsExample:
<div class="container">ci<→ changesdiv class="container"inside the angle brackets
HTML/XML Tags#
cit " Change inside tag (content between opening and closing tag)
dit " Delete inside tag
yit " Yank inside tag
vit " Select inside tag
cat " Change around tag (includes the tags themselves)
dat " Delete around tagExample:
<p>This is a paragraph with <strong>bold text</strong> in it.</p>Cursor on “bold text”:
cit→ deletesbold text, leaves<strong></strong>dat→ deletes<strong>bold text</strong>entirely
Cursor on “This is a paragraph…”:
cit→ deletes everything between<p>and</p>dat→ deletes the entire<p>...</p>element
Nested tags#
<div>
<ul>
<li>Item one</li>
</ul>
</div>Cursor on “Item one”:
cit→ changes content of<li>(innermost)- Move up,
citon the<ul>line → changes content of<ul>
Combining with the Dot Command#
The real power shows when you repeat these edits:
Replace multiple strings#
config = {
"host": "old-server.example.com",
"port": "8080",
"path": "/api/v1"
}- Cursor on first value →
ci"new-value Esc - Move to next string →
.(repeatsci"with “new-value”)
Or more selectively:
/"oldto searchci"replacement Escnto next match,.to repeat
Empty all function arguments#
doSomething(complex, args, here);
doAnother(more, stuff);
doThird(yet, more, things);- On first line:
ci(Esc` - Move to next line:
j .→ repeats the “change inside parens” edit
Working with Nested Delimiters#
When delimiters are nested, Vim uses the innermost pair containing your cursor:
console.log("Value: " + calculate(x, y));| Cursor position | ci( changes |
|---|---|
On x | x, y |
On "Value | "Value: " + calculate(x, y) |
If you need the outer pair when cursor is deep inside, use visual mode to expand:
vi(selects inner pair- Repeat
i(to expand to the next outer pair (in some Vim versions) - Or manually position with
F(thenci(
Quick Reference#
| Text Object | Inner (i) | Around (a) |
|---|---|---|
| Double quotes | ci" di" yi" | ca" da" ya" |
| Single quotes | ci' di' yi' | ca' da' ya' |
| Backticks | ci` di` | ca` da` |
| Parentheses | ci( di( yi( | ca( da( ya( |
| Curly braces | ci{ di{ yi{ | ca{ da{ ya{ |
| Square brackets | ci[ di[ yi[ | ca[ da[ ya[ |
| Angle brackets | ci< di< yi< | ca< da< ya< |
| HTML tags | cit dit yit | cat dat yat |
Best Practices#
- Use
ci(change inner) when you want to replace content — it deletes and drops you into insert mode in one motion - Use
diwhen you want to empty something without typing new content - Use
yito grab content without modifying anything — paste it elsewhere withp - Use
a(around) when you want to remove the delimiters too —da"removes the quotes,da(removes the parens - Your cursor can be anywhere inside the delimiters — you don’t need to be at the start
- Combine with the dot command for repetitive edits across similar structures
- For deeply nested structures, use
vi(first to visually confirm what’s selected, then operate on it


