Home CPSC 225

More About Vim

Building Vim Commands

One of the powerful things about Vim which is unlike other editors is that its commands are able to be combined with each other to form larger commands. For instance, the 'd' command is for deleting things. We have seen using 'dd' for deleting a line, but 'd' is really a Vim "verb" which can be combined with "movements" which allow us to delete other things:

In fact, we can make "verb-movement" commands from any of these verbs:

VerbMeaning
dDelete.
cChange (delete and enter insert mode).
yCopy.
=Re-indent.
>Indent one more level.
<Indent one fewer level.

And these movements:

MovementMeaning
hThe character to the left.
jThis line and the one below it.
kThis line and the one above it.
lThe character to the right.
^From here to the start of the line.
$From here to the end of the line.
ggFrom here to the start of the file.
GFrom here to the end of the file.
{From here to the start of the paragraph.
}From here to the end of the paragraph.
%From here to the matching parenthesis, brace or bracket.
f{char}From here until the next instance of {char} on the current line. For instance fw will move to the next w character on the current line.
t{char}From here until the character right before the next instance of {char} on the current line.
F{char}From here until the previous instance of {char} on the current line.
T{char}From here until the character right after the previous instance of {char} on the current line.

For example, the command =G will re-indent everything from the current cursor location up until the end of the file, and yfP will copy everything from the current location until (and including) the next 'P' character.

As we've seen, we can also use "verb-verb" commands such as "dd" or "yy" which mean to apply whichever verb for just the current line.


Text Objects

Vim also allows applying verbs to higher level textual structures it calls text objects. With these we can perform "verb-text object" commands which apply the verb to the text object. Some useful text objects are listed below:

Text ObjectMeaning
i}Text inside curly braces.
a}Text inside curly braces and the braces themselves.
i"Text inside quotations.
a"Text inside quotations and the quotations themselves.
i)Text inside parentheses.
a)Text inside parentheses and the parentheses themselves.
iwThe current word.
awThe current word and the space after it.
isThe current sentence.
asThe current sentence and the space after it.
ipThe current paragraph.
apThe current paragraph and the new line after it.

The first six are most useful for programming, while the last six are more useful for writing regular text.

These allow us to build very succinct shortcuts. For instance, suppose we are editing a program with the line:


print("Hello, how are you?")

And we wish to change the printed message. This can be done using the ci" command which will jump to the quotes, erase everything inside of it, and put us into insert mode. This works even if the cursor is not inside the quotation marks.

Likewise, if we are writing a paper, and wish to move a sentence, we can issue the das command, then move to where we wish to place it and enter the p command.

It takes time to get used to using these bigger commands, and taking advantage of text objects, but it allows you to edit text at a higher level. Instead of dealing with just a string of characters, you can edit text at the sentence, paragraph, or code block level.


Counts

Vim commands can also be prefaced by counts which are integers which indicate how many times the command should be performed. For instance, 3dd says to delete three lines.

Movements can contain counts as well. For instance, we can type 3w which moves us three words forwards and is equivalent to typing www. Movements within commands can have counts as well. For instance the command y2fw will copy from here until the second instance of the 'w' character.

Counts can also be used with text objects which can be really helpful. Suppose we have the following code:


if (x == y) {
    // some comment
    while (x < 0) {
        x = x + 1;
        print("Some stuff is happening!");
    }
}

Also suppose that the cursor is within the while loop body, on the print statement line. If we wanted to delete the entire body of the while loop, we could use di} which would give us:


if (x == y) {
    // some comment
    while (x < 0) {
    }
}

The text object i} matches the most nested set of braces we are in. If we wanted to remove everything in the if block, however, we could instead use d2i} which deletes the second most nested brace pair, giving us:


if (x == y) {
}

Registers

When we use the d or y verb in a command, it saves the text which is deleted or yanked into a register which is like a Vim variable used to hold text (similar to the "clipboard" used by GUI systems). When we use the p or P commands, the text in a register is put into the file we are editing.

By default, Vim uses the "unnamed register" for these purposes. If you only ever use the unnamed register, then each time you delete or yank something, it will overwrite the previous text of the register.

Vim also allows us to specify a named register which can be any lowercase letter. This is done by prefacing a command with a " character, then the register we wish to use.

For instance, to delete a line into the "j" register, we could use "jdd. Then if we wish to paste from that register, we could use "jp.

Using registers is nice since it lets you save something in a clipboard location and then be able to use the regular copy and cut commands without losing it.


Marks

Vim marks are like bookmarks in the file you are editing. They allow you to mark specific points in the file which you can quickly return to later.

You can create a mark with the m command followed by a lowercase letter. This gives you 26 marks to work with. So ma will mark the current location as mark 'a'.

We can then jump back to mark 'a' at any point with either the command 'a or `a. The only difference is the first will take you back to the start of the line containing mark a, while the second will take you to the exact position you marked (the line and column).

Because our mark jumps are Vim motions, they can also be used with verbs to form commands. For instance, d'a says to delete everything from here until the line containing mark a.

Your marks in a file will persist across multiple launches of Vim. So you can assign a mark to an important part of a file, then jump to it each time you open that file.


More Ways to Enter Insert Mode

We have seen that the i key can be used to enter insert mode, but there are several other ways to enter insert mode which are convenient in some situations:

KeyMeaning
iEnter insert mode starting here.
IEnter insert mode at the beginning of this line.
aEnter insert mode one character to the right.
AEnter insert mode at the end of this line.
oAdd a line after this line and enter insert mode.
OAdd a line before this line and enter insert mode.
sDelete the current character and enter insert mode.

The . Command

The '.' command is, in my opinion, the best feature of Vim, that I miss most sorely when I need to use an inferior editor. The '.' command repeats the last action that you performed. For instance, if we have the text:

Hello this is a sentence.

And the cursor is on the word "this". We can delete the word "this" by typing daw. This will give us:

Hello is a sentence.

Now if we wanted to delete "is", we could again hit daw, or we could just repeat the last one with . which will give us:

Hello a sentence.

Tap . two more times an all we have is:

Hello.

The . command can allow you to save a lot of time. As another example, suppose we have some code like this:


name = input()

while name == "":
    print("Invalid name!")
    name = input()

And suppose we wanted to comment out the while loop. We could do that by manually moving to the beginning of each line and adding a '#' character. Or we could go to the first line to comment and type I to insert at the beginning of the line. Then type the # and then hit escape. Now the action we just did is "insert a # at the beginning of the line", and this whole action can be repeated. To comment the other lines, we can just type j.j. as j moves down a line and . comments it out.


External Commands

Vim allows you to run Unix commands, and view the results with the ":!" command. For example, if you need to know what time it is:

:!date

You can also use this to execute the current file in an interpreter (Vim interprets "%" as the name of the current file).

:!python %

Or check the number of words you've written:

:!wc -w %

Vim also lets you paste the output of a UNIX command into the current buffer with the ":.!" command:

:.!date

This runs an external command and puts the output into the file you are editing.


Key Mappings

Another way to customize Vim is to give custom key mappings. This can be done to shorten often used operations. These key mappings create new normal mode commands.

For example, we can map the 'n' key to mean toggle line numbering by putting the following into the .vimrc file:

nmap n :set number!<CR>

This maps the 'n' key to the key sequence on the right. The command "set number!" is to toggle the number option. The "<CR>" is a carriage return and refers to the enter key.

However, the problem with this is that 'n' already has a meaning in Vim - it means jump to the next result of the most recent search. In fact, nearly every key has some existing meaning. To get around this, Vim has a "leader key" that has no default meaning. The default leader key is the "\" key. To make the new mapping using the leader key, you could do the following:

nmap <Leader>n :set number!<CR>

Now entering the command "\n" will toggle line numbering.

We have as many commands on the right hand side as we want. The following mapping maps "\p" to a command which toggles both line numbering and Vim's "paste mode":

nmap <Leader>p :set paste!<CR>:set number!<CR>
This is helpful for copying and pasting to or from Vim since, we don't normally want to copy line numbers and Vim's paste mode disables auto-indentation, abbreviations etc.

You can change the leader key if you want to. Popular alternatives to '\' include ',':

let mapleader=","

And the space bar:

let mapleader=" "

Abbreviations

Another handy thing to put in your .vimrc is an abbreviation. This works by replacing a piece of text you type with another. For example, with the following line in the .vimrc:
ab tumw The University of Mary Washington

We can type "tumw" and Vim will replace it with "The University of Mary Washington".

Note that the key mappings discussed above are for normal mode, while abbreviations are for insert mode. The left part of an abbreviation should not be anything that you would type normally, as Vim will automatically replace it with the right hand side.


Completion

Vim has a simple ability to do completion. It is not based on semantic meaning (like that of an IDE), but only on the words that exist in the same file.

It can be triggered by typing Ctrl-N which will choose the next word that matches. If there are multiple, you can cycle through them with Ctrl-N and Ctrl-P.

Copyright © 2018 Ian Finlayson | Licensed under a Creative Commons Attribution 4.0 International License.