Motions and text objects are two related, but technically different, concepts in vim. Motions are a commands that move the cursor. They typically work in normal mode, in addition to other modes. f, t, and / are examples of motions. Text objects are mappings that work in visual and operator pending mode to operate on various portions of text, like iw and a". But both of them work with operators, which are actions you can take on text - copy it, remove it, duplicate it, change it, etc. - so I'm going to look at them all together to demonstrate vim's flexibility. Understanding these vim fundamentals can enable you to perform real, actual MAGIC. By which I mean, you'll be able to edit text really fast and impress your friends.

Operators

Let's start by looking at some of the more common actions you can do in vim (cause there are some definitively uncommon ones, like ROT13 encoding via g?). These operators have a normal mode mapping that usually has a pretty helpful mnemonic. You can read more about operators with :h operator, and more about specific operators by looking up the help for the key that triggers it, for instance :h d for more on delete.

Delete

The d key removes the text indicated by the subsequent motion and leaves you in normal mode, but it's really more analogous to "cut" in other editors because it places the removed text in the default register. Instead of a single global clipboard (although vim can access the system clipboard as well if compiled with +clipboard), vim has a thing called "registers" (see :h registers). Imagine having multiple system clipboards so that copying new things didn't blow away old things. That's roughly (at a very high, introductory level) how registers work.

Change

The c key changes text. This, like delete, removes text, but leaves you in insert mode instead.

Yank

In other programs, we call this "copy" but in vim we call it "yank" because it's mapped to the y key. This copies text to the unnamed register, just like d does. Actually most (all?) of these actions put the text acted upon into the unnamed register. When you delete text, it goes into the unnamed register. When you change text, the original text goes into the unnamed register. This let's you do some really useful things. But that's for a different post.

Swap case

g~ (and also ~ in some cases) changes the case of all the letters included in the subsequent motion. Lower case letters become upper case and vice versa. This is mostly useful for converting a fully upper case word to lower case (or the opposite), even though there's also:

Upper case and lower case

gU and gu make text upper and lower case respectively.

Fix indenting

Many editors provide this in a menu option. In vim, you can just use = to fix indenting.

Indent and outdent

> indents text one additional level and < outdents it one level.

Selecting

v selects text for future operations. This isn't technically one of the vim operators; it's not listed under :h operator, but both motions and text objects work in visual mode (triggered by v), so it's similar to an operator in that sense. That is to say that viw isn't interpreted by vim as an operator, followed by the operator pending mapping iw. v launches visual mode right away, but iw expands the current selection to include the inner word, and so the end result is the same.

Text Objects

Operators are useless if not paired with a motion or text object. If you press d by itself in vim, nothing happens. You've just told vim, "Delete!" but not told it what to delete. To make operators useful, they need to be followed by motions or text objects which tell vim what to delete. The term "text object" in vim refers to mappings that only work in operator pending mode (and visual mode), that is, after a key that triggers an operator. All of the text objects (built-in, I mean) begin with i and a, which both trigger insert mode when used in normal mode without an operator. There are a lot of text objects that vim can operate on, but they all use i and a, and in all these text objects, the i and a mean the same thing: "inside" and "around". (Technically, vim's help says "inner" and "a" but those don't read as smoothly on some text objects to me. You'll see what I mean.)

i and a can be combined with any of the following "text object" mappings to operate on text "inside" and "around":

w

Operate on a "word." This means all contiguous letters specified by the iskeyword setting (which changes from filetype to filetype - see :h iskeyword or try :echo &iskeyword). In general, it means "characters valid in variable names." Here, iw means just the word itself ("inside word" or "inner word"), and aw means the word plus some spacing ("around word" or "a word").

E.g. daw (delete around word) and guiw (lower case inside word).

W

Operate on a "WORD." Yes, this is different from "word." In vim, the special all-upper-case "WORD" means all contiguous, non-white-space characters.

E.g. g~iW (change case inside WORD) and caW (change around WORD).

s

Operate on a "sentence." I won't try to explain here exactly how vim interprets a sentence (see :h sentence for that). You probably more or less understand what that means (minus maybe strange punctation). I don't use this very often, as programming doesn't typically have sentences. I should write a plugin to make s operate on a statement or expression instead, which would be much more useful.

E.g. >is (indent inside sentence)

p

Operate on a paragraph. Which basically means any text between two blank lines. This is a paragraph, for example:

// other code above this

function foo() {  
  let result = bar();
  takeThat(result);
}

// other code below this

E.g. =ap (fix indenting around paragraph)

[ or ]

Operate on text within (or including) brackets. I use this often to change array indexing with the ci[ (change inside brackets) mapping. In this and the following symbol-based motions, you can see why "inside" and "around" are a little more natural language. "Yank a bracket" or "change inner bracket" just don't feel semantically correct.

( or ) or b

Operate on text within (or including) parentheses. Again, I use this often for changing function parameters: di( (delete inside parentheses).

The b mapping (for "block") also randomly does the same thing.

< or >

Operate on text within (or including) angled brackets. Kind of useful when editing html, although I often want to change the tag but leave the attributes the same, so this is not the right tool for changing an anchor tag to a button (for example).

E.g. vi< (select inside angled brackets), da> (delete around angled brackets)

t

Operate on an html or xml tag. I use the "inside" variant of this often to change the text of a tag (cit), for example, although there is merit in deleting around a tag (dat) if you want to move it somewhere else. Note that "around" means everything inside an opening and closing tag of the same type plus the opening and closing tags themselves. It's still not the right tool for changing an anchor to a button. But see below for the right tool.

{ or } or B

Operate on text within (or including) braces. This has all sorts of practical uses in javascript, like deleting a function body, selecting an if block, or changing an object's key/value pairs. And like with b, B is thrown in as a bonus. I've literally never used b or B in these mappings, as [ and { always spring to mind quicker since they're literally present in the text. I suppose the argument for them is that they're slightly easier to trigger.

E.g. <a{ (outdent around braces), ci} (change insde braces)

" or ' or `

Operate on text within (or including) various quotation marks, another common html text object.

E.g. ci" (change inside double quotes), yi' (yank inside single quotes), ca` (change around backticks)

Motions

In addition to these specialized operator pending motions, you can use all of the normal mode motions with operators, such as:

  • f - find forward, as in dfa (delete forward until and including "a")
  • F - find backward, as in cF' (change backward until and including the previous ')
  • t - find "till," as in ct( (change forward till - but not including - the next opening parenthesis)
  • T - find backward till, as in cT, (change backward till - but not including - the previous comma)
  • e - end of word, as in ye (yank till end of word)
  • E - end of WORD, as in vE (select till end of WORD)
  • w - start of next word, as in dw (delete to next word)
  • W - start of next WORD, as in cW (change to next WORD)
  • b - beginning of word, as in g~b (change case to beginning of word)
  • B - beginning of WORD, as in guB (lower case to beginning of WORD)
  • ge - end of previous word, as in cge (change to end of previous word)
  • gE - end of previous WORD, as in dgE (delete to end of previous WORD)
  • / - next occurrence of (regardless of line breaks), as in y/the (yank until the next occurrence of "the")
  • ? - previous occurrence of (regardless of line breaks), as in d?taco (delete backward to the first occurrence of "taco")
  • j - down one (or more) lines, as in dj (delete this line and the next . . . I use this often).
  • k - up one (or more) lines, as in =k (fix indenting in this and previous line)
  • h - left one (or more) letters, as in ch (change the previous letter)
  • l - right one (or more) letters, as in cl (change the next letter . . . which for vim means the letter under cursor. A nice mnemonic for this is "change letter").
  • ( - sentence backward, as in g~( (change the case from to the beginning of the sentence).
  • ) - sentence forward, as in >) (indent to the end of the sentence).
  • { - paragraph backward, as in d{ (which I basically read as "delete upward till the next blank line" which can be really useful if you use separating space efficiently).
  • } - paragraph forward, as in c} (change down till the next blank line)

And there are still many more that I'm not going to go into, including [[, [], ]], ][, ctrl-d, ctrl-d, ctrl-f, ctrl-y, ctrl-u, ctrl-b, H, L, M, etc. etc. etc. . . . Basically anything that moves you around in vim - and if you're new to vim, you can't possibly grasp just how many things this is - can be combined with an operator.

Putting it together

You should hopefully see that there are just a staggering number of combinations. Let's do a little math to get a vague idea how many.

There are 15 operators listed in :h operator, plus v which I'm including because motions and text objects work in conjunction with visual mode.

I listed 34 motions that can be combined with these operators, although as I noted, there are far more. So just combining operators with true stand alone motions, there are 544 combinations.

But there are also the i- and a-based operator pending keys, which can each be combined with 12 text objects, so that's another 384 combinations, which brings the total to 928.

And besides this, there are specialized operators like yy and dd which yank and delete one line respectively; {range} indicators, like 2yw (yank two words) 2da{ (delete around two braches, which would operate on mustache interpolation like {{foo}}), and 3dk (delete 4 lines backward - this line and 3 more); the indispensable . operator to repeat the previous operation on a new target; as well as the extensibility that vim gives you to add your own operators, motions, and text objects. Yes, that's right, we've only been talking about built in operators, motions, and text objects.

Extending Vim

I also have plugins installed that let me operate on: the whole file (yie for "yank inside everything"); html attributes (cax for "change around xml attribute"); the next and previous character pairings (like cin' to change inside the next set of single quotes or dap{ to delete the previous brace set); parts of words, based on camel-casing, kebab-casing, etc. (div for "delete inside this variable segment"); functions (daf to delete a function and move it elsewhere); arguments (daa to delete an argument . . . and it's smart about commas); even between lines marked by git changes (dig to delete inside git changes). Note that adding text objects is somewhat complicated but is simplified significantly by installing vim-textobj-user which provides a nice interface for defining text objects.

There's also vim-surround which adds the s key as a corollary to i and a. It means operate on the surroundings, and all the normal text objects work with it, so you can cs"' to change the surrounding double quotes to single quotes, you can ds{ to remove braces, and you can ysiw" (vim-surround advocates reading this as "you surround inside word") to add quotes around a word. This is the right tool referred to above for changing an anchor to a button, because you can do cst<button{Enter} to change a tag name without changing it's attributes.

There are also plugins that add new motions that work with existing operators, such as vim-unimpaired which adds a lot [- and ]-based movements. Many, such as mappings that jump between files, are not appropriate to pair with operators, but some are, such as [n and ]n which jump to the previous and next conflict markers. Which means you can do d[n and d]n inside a conflict.

Finally, it's possible to add new operators to vim as well, although again, as with text objects, it's easier to do with the help of a plugin that smooths out the rough edges for you. I have plugins that add operators to grep for text, comment and uncomment text, and swap text. There's also a plugin to Google text, which I use to have installed, but I found that I didn't really use it and it's mappings conflicted with other things I wanted to do.

Conclusion

In conclusion, vim gives you a near limitless combination of ways to act upon text without ever entering insert mode. The only problem I've found is that there are more possible text objects I might want to act upon than keys to map those actions to. Seriously, have a look at this. I'm running out of letters, and there are still things every day where I think to myself "I wish this were a text object." Fortunately, the <leader> key can provide another whole layer to these mappings, so the limit in vim really is your imagination and willingness to persevere and make it happen.