Drill 9: Repeating Oneself

This Week's Drill

A thread on the Ruby Parley email list caught my attention a while back. The author mentioned that he had accidentally hit ff in Vim, when he had meant to hit gg to go to the top of the file. This lead him to think about what all the duplicated letter commands do in Vim.

What I find really interesting about this line of thought isn't what the specific commands do, but instead the patterns in them, and how Vim parses and executes the commands.

I came up with four totally distinct ways that Vim interprets duplicate patterns. (which conveniently each get a section below!)

Two Separate Commands

The first major group of the duplicated-letter commands I saw was simply where Vim interpreted them as duplicated commands.

jj is an excellent instance of this. The first j says to go down a line. And the second j says to go down a second line. Two totally separate commands.

Not much more to say here...

One Command with a Motion

A second major grouping appears after filtering out the simply duplicated single-letter commands. This is the group of commands that take a motion afterward.

d is an excellent prototype to examine. If you simply hit d in Vim. Nothing visible happens. Vim knows you're about to delete something, but is waiting for you to tell it what. You tell it with a motion.

Examples With Motions

dw - Delete word

d$ - Delete until end of line

di) - Delete inner parenthesis (see also: a refresher on Text Objects)

Yeah, but What about dd?

So when we type d, then Vim waits for a motion, just as every time above. But if we hit d again, it knows to delete the entire line. BUT WAIT! d is not a motion command. How does it know what to delete?

It turns out this is a clever standard that Vim uses. Every command that I've run across (I'd guess all, but I am not 100% sure) that waits for a motion interprets its own keystroke again as an instruction to operate on the entire line.

dd - Deletes the entire line

>> - Indents the entire line

But if we try to mix the two:

>d - Whoops, Vim doesn't know what to do with this, and beeps at you.

d> - No luck again. Only confused beeps from our editor.

So only duplicates have this behavior.

One Command with an Argument

This is another distinct way that Vim could interpret a duplicated letter.

tt is the best example of this style of commands. The first t is the "until" command. But Vim needs to know "until WHAT", and so it waits for an argument. When it hears the second t it can finish the command. But that second t is not a motion, it's not another command, it's the argument to the command.

@@ is similar. @ is the command to run a macro. But any register could be holding a macro in it, so it waits for you to tell it which macro to run. In this case, it borrows the thought process from dd and it special cases the otherwise nonsensical @ argument (which is not a register), it executes a smart default, which in this case is rerunning the last macro.

Namespaced Commands

gg doesn't match any of the previous categories. g isn't a command by itself. It's not waiting for an argument, or a motion. Instead, it's a whole namespace of distinct commands. If you go to :help g, you get an entire section of distinct commands.

Cool enough, some of those namespaced commands in turn follow the rules laid out above. For example g? takes a motion to rot13 some text. g?g? rot13s the current line. gq to reformat text does the same, 'gqgq' operates on the current line.

So back to gg. What's up here? It's just a single command inside the g namespace, the way the unnamespaced j and k are. Vim parses it as a single entity, not as any fancy combination.

z is a similar namespace to 'g', holding such amazing commands as zz to center the viewport around your cursor (moves the window, but not your cursor inside the text)


Vim has its own logic. It's consistent in ways I hadn't even realized before writing this article. Send me some feedback below in the comments, or via email at chris@vimdrills.com. I'd love to know if I missed anything in this analysis.


comments powered by Disqus