Advanced Motions & Text Objects
Vim Intermediate/Advanced
Chapter 3 — Advanced Motions & Text Objects
Beyond Basic Motions
Learning Vim covered motions as ways of moving the cursor — w to the next word, f{char} to a character, G to the end of the file. Combined with an operator (d, c, y), a motion tells Vim where to stop — but you still have to think in terms of "where," which usually means precise cursor positioning.
Text objects are a different idea entirely: instead of "where," they describe what — "the word," "the paragraph," "the thing inside these parentheses" — regardless of exactly where your cursor happens to sit inside it. This chapter is about that shift, and it's one of the biggest jumps in editing speed once it becomes automatic.
The Text Object Model: Inner vs. Around
Every text object comes in two forms, always following the same pattern: {operator}{i or a}{object}.
i — inner
Just the content itself — the word, the text between the brackets, the paragraph's lines — with no surrounding delimiter or trailing whitespace included.
a — around
The content plus its natural surroundings — the delimiter characters themselves, or trailing whitespace/blank line, depending on the object.
# cursor anywhere inside "hello": di" # delete inner quotes -> "" (quotes remain, content gone) da" # delete around quotes -> (quotes AND content both gone)
This i/a distinction is consistent across every text object below — learn it once, and it applies everywhere.
Word & WORD Text Objects
diw # delete inner word — just the word itself daw # delete a word — the word PLUS one side of surrounding whitespace diW # delete inner WORD — a whitespace-delimited chunk, ignoring punctuation boundaries
Lowercase word objects respect punctuation as boundaries (user_id and user-id are treated differently by iw, since _ counts as a word character but - doesn't); uppercase WORD objects only care about whitespace, treating my-variable.name() as one single unit. This mirrors the same lowercase/uppercase word-vs-WORD motion distinction from Learning Vim's basic motions — text objects just extend it into operator targets.
dw (which deletes from the cursor forward to the next word boundary), diw selects the whole word the cursor is currently anywhere inside — including characters to the left of the cursor. Position no longer has to be exact.
Paragraph & Sentence Text Objects
dip # delete inner paragraph — the lines of text, not the blank line after dap # delete a paragraph — the lines PLUS the trailing blank line separating it from the next dis # delete inner sentence das # delete a sentence, including trailing whitespace
A "paragraph," to Vim, is any run of non-blank lines — no special Markdown or prose awareness needed. dap is a fast, reliable way to delete an entire block (a function, a config section, a prose paragraph) in one motion, as long as it's separated from its neighbors by a blank line.
Bracket & Quote Text Objects
di( # or di) — inner parentheses di{ # or di} — inner curly braces di[ # or di] — inner square brackets di" # inner double quotes di' # inner single quotes
These work from anywhere inside the delimiters — you don't need your cursor on the opening or closing character, or even know exactly where they are. This is the single biggest practical upgrade over manual motions like f(: no hunting for the matching bracket first.
outer(middle(inner)), with the cursor on inner, di( deletes only inner — the innermost pair the cursor is directly inside. To act on the next level out instead, prefix with a count: 2di( deletes middle(inner)'s contents, treating the second-innermost pair as the target.
Tag Text Objects (HTML/XML)
dit # delete inner tag — the content between <tag> and </tag>, tags left intact dat # delete a tag — the content AND both the opening and closing tags
# cursor anywhere inside: <strong>Job Reference:</strong> citJob Ref<Esc> # -> <strong>Job Ref</strong> (only the text content changed)
Tag objects only apply in filetypes where Vim recognizes tag syntax (HTML, XML, and similar) — but for editing markup, dit/dat/cit replace what would otherwise be a fiddly manual selection of exactly the right span of characters.
Combining Operators with Text Objects
Every text object works with every operator — the same small set of building blocks recombines into dozens of practical commands:
| Command | Effect |
|---|---|
ciw | Change inner word — delete the word, drop into Insert mode |
ya{ | Yank around braces — copy the braces and everything inside |
va" | Visually select around quotes — see the exact span before acting |
dip | Delete inner paragraph |
>i{ | Indent everything inside braces one level |
v (Visual mode) combines with text objects too, letting you inspect the exact selection before committing to an operator — useful the first time you try a new object on unfamiliar content.
A Worked Example
Workflow — changing a function argument buried in nested parentheses
( and ) and enters Insert mode, regardless of where inside them the cursor landed.
Compare this to the manual-motion equivalent: locate the opening paren with f(, move one right, visually select to the matching close with some combination of % and adjustments, then delete. Text objects collapse that whole sequence into one memorable, position-independent command.