Skip to main content

Advanced Motions

Once the fundamental key bindings are understood, you can start developing fast vim motions. This starts with understanding Text Objects.

Text Objects

With the standard operator-motion the command acts on the specific cursor position. For example, dw deletes the word starting from your cursor position, not the entire word your cursor sits on. This means that if you position in the middle of a word and press dw you will only delete half of it. Having to position cursors precisely for proper actions puts a bottleneck on movement and operation speed. Text objects broadens the scope from cursor position to the object of interest - instead of w you have aw for "a word" allowing the command daw to delete the word regardless of how the cursor is positioned on it. This is called operator-text object.

To change a whole sentence regardless of cursor position, you use cis. The change operator acts on the text object is which stands for "inner sentence". This is not be confused with changing the entire line - if you have multiple sentences on one line, it only acts on the sentence you are on. If you have another sentence after the one you delete and you do not want a whitespace there you may consider using as. The text object as stands for "a sentence" which compared to is includes the white space after the sentence.

Key Idea

Combine any operators like y yank, c change, v select etc. (say ya{) with text objects to be able to operate on chunks of text more quickly. For instance if your cursor is within a word, viw easily selects the whole word.

The key difference between a and i is more nuanced than just whitespace:

  • i (inner): The text object itself, excluding delimiters/boundaries
  • a (around/a): The text object plus its surrounding delimiters/boundaries

This is fundamentally about delimiters and boundaries, not just whitespace. For example:

  • i" = content inside quotes (excluding the quotes)
  • a" = content plus the quote marks
  • i) = content inside parentheses
  • a) = content plus the parentheses

For words specifically:

  • iw = just the word characters
  • aw = the word plus trailing whitespace (or leading if no trailing)
Trick

Remember: "a"round includes the boundaries, "i"nside excludes them.

There are a long list of text objects. The ones to get familiar with for now are:

Words & Sentences:

  • w / aw / iw - word
  • W / aW / iW - WORD (capital W)
  • s / as / is - sentence
  • p / ap / ip - paragraph

Quotes & Brackets:

  • " / a" / i" - double quotes
  • ' / a' / i' - single quotes
  • ` / a` / i` - backticks
  • ( or ) / a( / i( - parentheses
  • [ or ] / a[ / i[ - square brackets
  • { or } / a{ / i{ - curly braces
  • < or > / a< / i< - angle brackets
Trick

A fast way to get to the bottom or top of a large function or list far outside your viewing window is simply to do vi{ or vi[ to select around the list or function (which puts you at the bottom) and then to just escape the selection.

lst = [
"orange",
"doorhinge",
"apple",
"grapple",
... # imagine your cursor is in the middle of a long list
"words",
"more words",
"long list",
"hello",
"hey" # <-- vi[ sends your cursor right here
]

Word vs WORD (w vs W)

The distinction between lowercase "w" and uppercase "W" is crucial for practical editing.
  • aw (a word): Selects words defined by iskeyword setting - typically letters, digits, and underscores. Stops at punctuation and whitespace.
  • aW (a WORD): Selects any sequence of non-whitespace characters separated by whitespace, including punctuation.

Why capital W is essential:

filename.txt    config-file.json    user@domain.com    /path/to/file
  • daw on "filename.txt" only deletes "filename" (stops at the dot)

  • daW on "filename.txt" deletes the entire "filename.txt"

  • daw on "config-file.json" only deletes "config" (stops at the hyphen)

  • daW on "config-file.json" deletes "config-file.json"

Trick

Use capital W text objects when working with filenames, URLs, email addresses, or any text containing punctuation that you want to treat as a single unit.

Pitfall

Many beginners get frustrated trying to delete filenames or URLs with daw - remember to use daW for complete non-whitespace sequences!

Text Object Motions

Instead of slowly moving horizontally by word with w you can move by sentences or paragraphs via text object motions:

  • ( moves sentences backward
  • ) moves sentences forward
  • { moves paragraphs backward
  • } moves paragraphs forward

Visual Mode

With text objects, now you can select whole sentences or lines inside parenthesis without needing to do a lot of cursor moving and combos! For example, to select all the arguments in the function parenthesis, just type vi(:

def baz(arg1, arg2, arg3, arg4):
pass

Then you can just either delete it with d or use c change operator to delete it and start modifying in insert mode! If you are not within a parenthesis or bracket, vi{ or vi( will just jump you to the next one in the line, which is actually a neat feature. You can also use yi{ to yank something inside a bracket, then vaw to select a word, and then p to paste over that word. As you can see, you can combine text objects with visual mode selection to do a lot of actions.

The capital V is actually also powerful for dealing with selection linewise as opposed to character-wise with v. For instance, if you wanted to select the entire file contents, ggvG will only select up to the start of the last line, because it operates by character. The solution is ggVG which selects all lines in the file! Or if you want to grab all 4 lines below V4j is much better than v4j$.

Another powerful key for Visual Mode is the o key for jumping the cursor between top and bottom of selection while still keeping the selection.

Trick

Jump with o to the start of your selection while keeping the current selection alive. This allows you to increase the selection boundary at the top rather than just at the bottom. You can press o repeatedly to toggle between the top and bottom of your selection.

Other Motion Patterns

Beyond text objects, there are other powerful motion patterns that complement advanced editing workflows.

Till and Find Motions

These motions allow you to jump to specific characters on the current line:

Find motions (f/F):

  • f{char} - jump forward to next occurrence of character (inclusive)
  • F{char} - jump backward to previous occurrence of character (inclusive)

Till motions (t/T):

  • t{char} - jump forward till (before) next occurrence of character
  • T{char} - jump backward till (after) previous occurrence of character
The key difference: "f" lands ON the character, "t" lands one position BEFORE it.

Common patterns:

  • dt, - delete till next comma (useful for function parameters)
  • ct. - change till next period (sentence editing)
  • dF( - delete back through previous opening parenthesis
  • df; - delete through next semicolon (including the semicolon)

Combining with text objects:

function example(arg1, arg2, arg3) {
return processData(arg1);
}
  • F(vi( - jump to previous (, then select inner parentheses content
  • f"ci" - jump to next ", then change inside quotes
  • t,caw - jump till comma, then change a word
Trick

Use t motions when you want to preserve the target character (like commas in parameter lists), and f motions when you want to include/delete the character.

Detail

After using f, F, t, or T, you can repeat the motion with ; (forward) or , (backward).

% Jump to Matching

Jump between matching parentheses (), [], {} with the % key.

A common pattern in fast vim motions is to find the matching parenthesis. While you can use text objects to select around parentheses, you might want to be more flexible and for example select everything before a parenthesis, then add on all the chunks up to and including the closing parenthesis.

For example, to select and delete an entire function, you might position your cursor at the first line and do Vf{%d which first selects up to the first bracket, then uses % to increase the selection to the closing bracket before deleting.

function new_fn(bar: number) {
const item = newItem({
...data
});
...
}

Toggle Jump List

A jump is defined as any command that moves the cursor several lines away. Examples of jump commands: G, /, ?, n, N, %, (, ), [[, ]], {, }, :s.

You will often want to go back to places your previously jumped your cursor. Smoothly move through history of cursor positions using Ctrl+o and Ctrl+i.

Pitfall

Note to self: I always forget the reverse jump Ctrl+i is just as important - it is very common to accidentally jump back too far with Ctrl+o, and need to go back to your original position.

Mashing Ctrl+o takes you through previous cursor jumps. To reverse this operation and jump forward through cursor positions do Ctrl+i. You can see your jump list with :ju. Clear the jump list with :cle as the maximum entries is 100.

Trick

You can even combine ctrl+o with LSPs go to definition grd to jump back to where you were originally after jumping to the definition.

Practical Workflow Examples

These patterns become powerful when chained together:

  1. Refactoring function calls:

    • F(ci( - jump to opening parenthesis, change inside
  2. Editing lists/arrays:

    • f[vi[ - jump to bracket, select inner content
    • t,daw - jump till comma, delete word (removing parameter)
  3. Text editing:

    • T"vt" - select from after previous quote till before next quote
    • dt: - delete till colon (useful for key-value editing)

Resources

For more information on advanced vim motions, I highly recommend consulting videos like this one, where they walk you through what the advanced vim motions actually look and feel like.

I also found a fun game you can play that makes you practice your vim motions, it's cool!

Test Your Knowledge

Question 1 of 10
What is the fundamental difference between operator-motion and operator-text object?