Vim has a notorious learning curve which often leads beginners to give up and return to a more traditional editor or IDE. I tried to learn Vim many times before I was able to fully embrace it, each time spending mountains of effort simply trying to get to parity with my productivity in other editors. While learning Vim will always require some frustration, a key insight is to find the killer features that no other editor will provide you. This is part one in a series of posts about features in Vim I couldn’t live without.
Imperative vs. Declarative Editing
Most common editors require navigating by mouse or keyboard to the end of a block of text and backspacing character by character before typing some new text. While often there is a way to do this word by word, or for an entire line (with modifier keys or multiple clicks in rapid succession) it is still a very imperative paradigm. We must tell the editor how to manipulate the text at a low level, instead of simply stating what we want done.
Vim’s modal paradigm instead works by providing us with a set of basic tools that we can compose in a very declarative manner. Vim extends the set of motions available to navigate around the file, for example } to move the cursor by paragraphs and fx to find the next occurrence of the character ‘x’. Ted Naleid and Michael Klein have come up with a great reference wallpaper for motions.
Vim also introduces operators which perform actions on blocks of text. For
instance d is used to delete text and = can automatically
correct indentation. Composing operators and motions is simple to do and can
achieve powerful results. For instance we can use dG to
delete the rest of a file from our current line, or
cT“ to change text up to, (but not including)
the previous double quote on the line. For more information see
While it’s certainly useful to have many more options to navigate around the file and perform actions on text, this method closely resembles the imperative workflow of other editors since we still need to navigate to the start and end of any region of text we wish to operate on.
To compound the problem, Vim movements are extremely unintuitive to the novice. There are many ways to move from point a to b, and determining the most efficient combination of keys to push requires understanding many movements and committing them to muscle memory. Navigating to the precise start and end of text blocks is not easy for Vim beginners, and even feels clumsy to all but seasoned Vim experts.
Introducing the Text Object
Luckily, Vim allows us to specify a block of text in a more intuitive way. Plain
text is naturally broken up into words, sentences, and paragraphs. When
programming, pieces of text are often delimited by punctuation, like
<html>tags</html>. We can reference text
that is grouped into logical chunks like this using Vim’s text objects. Any
operator that takes a motion will take a text object instead. This lets us avoid
having to navigate to the start and end of regions, we simply need to move the
cursor anywhere within the region.
All text objects come in two forms, normal and inner (prefixed by a and i, respectively). As a general rule, normal text objects include the characters that delimit the object, while the inner form will leave the delimiters intact. The normal form is often used when deleting an object and the inner form is often useful for changing an object. For example, da“ will delete a double quoted string (perhaps to replace it with a variable), while ci“ will leave the quotes intact, and only delete the content inside the string, ready to be altered.
Built-in Text Objects
Vim comes bundled with a number of text objects defined. I’ve listed them below,
but you can get more detailed descriptions of them by viewing
- Word by punctuation: aw/iw
- Word by whitespace: aW/iW (see
- Sentence: as/is
- Paragraph: ap/ip
- Quotes: a“/i“
- Parentheses: a)/i)
- Brackets: a]/i]
- Braces: a}/i}
- Angle Brackets: a>/i>
- Tags (e.g.
User Text Objects
There are also a number of useful plugins which extend Vim’s built-in pool of text objects to power some very useful workflows.
Adds an object for arguments lists of the form
(arg1, arg2, arg3), where
iawill target an argument and
aawill include the comma if present.
Gives us an object to refer to the entire buffer. This is useful to replace the
gg<command>Gidiom. For example, to format the entire buffer use
=ae, or to start fresh use
Allows us to select lines based on their indentation. Useful in languages with syntactically significant whitespace like python or coffeescript.
iitargets all contiguous lines with the same or deeper indentation, while
aidoes the same while skipping over whitespace. The
iIvariants are similar, but only select lines with the same level of indentation.
Adds support for URLs with the
With this plugin, we can target text inside multi-line and single line comments, with
acincluding the comment tokens (
/* */) and