Vim: Demystifying the Beast

While being very powerful, Vim is notorious for it's steep learning curve and perceived user-unfriendliness. This article will attempt to clarify Vim's peculiarities and demonstrate it's efficiency.

In this post I will attempt to provide the reader with an overview of some of the ways in which Vim's design is conducive to highly efficient text editing. It is not intended to be a tutorial or comprehensive beginners guide. Rather, this post is intended to encourage the reader to learn more about Vim, having seen a small subset of it's functionality.

Modes

Unlike the majority of text editors, Vim is modal. This tends to be a major hurdle for new users. There are three main modes:

  • Normal - Key strokes are interpreted as commands
  • Insert - Key strokes are inserted into the file as text
  • Ex - A command-line style interface for typing complex commands

If you aren't sure what mode you are in, you can always press `Escape` to get back to Normal mode. When you are in Insert mode, Vim tells you so with a message in the status bar at the bottom of the screen:

Screenshot of Vim in insert mode.
Vim in Insert Mode

There are many ways to get into Insert Mode. Perhaps the most basic is to press `i` while in Normal Mode, which lets you start inserting before the cursor. Likewise, `a` (for append) puts you in Insert Mode after the cursor. The uppercase versions of these commands move the cursor to the start or end of the line respectively before switching to insert mode.

Pressing `:` switches you to Ex mode. The cursor is moved to the bottom of the screen and you can type commands. This gives you access to many advanced commands to do all sorts of things, from reading the Vim documentation to performing substitutions with regular expressions. The most famous use of Ex mode is probably quitting - `:wq` to save and quit, or `:q!` to quit without saving.

Welcome To Your New Home (Row)

Vim's key mappings are focused on keeping your fingers on the home row at all times. Having to move one's hand to the arrow keys, page up/down keys or numpad may not seem like a huge amount of wasted time, but when you consider that moving around a document is such a fundamental part of text editing, the time spent going to-and-from the home row adds up.

This explains what is perhaps the most immediate oddity in Vim's key mappings: basic movement is done with the `h`, `j`, `k`, and `l` keys. `h` moves left, `j` moves down, `k` moves up, and `l` moves right. It definitely feels strange at first, especially when you are used to using the arrow keys, but you will find that many classic command-line tools such as man and less also support this form of input.

Getting About

Vim provides many ways to get around rapidly without straying far from the home row.

You can move forward to the start of the next word with `w`, and do the opposite with `b`.

You can move forward to a specific character by typing `f` followed by the character, and perform the opposite by typing `F` followed by the character. The latest `f`/`F` command can be repeated with the `;` key, allowing you to quickly jump through occurrences of the character in question.

By far the fastest way to move around a many-line document is to search. Pressing `/` followed by a search string immediately takes you to the next occurrence of the string, as you type (subject to certain config settings being on). You can then press `Enter` to go back to normal mode, and use `n` to jump to the next match and `N` to jump to the previous match.

Vim's search is very powerful, and supports regular expressions and other advanced features.

Commands In Motion

Many commands used in normal mode accept (or require) the motions used above when being invoked. For example, `d` is the delete command. To delete a single line, you can type `dd`, but you can also delete from the cursor to the end of the current word with `dw`, to the first occurrence of a semi-colon with `df;` or even to the next occurrence of the word 'string' with 'd/string'.

Most commands can also be prefixed with a number to make them repeatable. Using the same example, typing `20dd` will delete twenty lines, `10dw` will delete the next ten words starting from the cursor, `5df;` will delete up to the 5th occurrence of a semi-colon on the current line, and '2d/string' will delete from the cursor to the second occurrence of 'string', across lines.

Inside and Around

Vim also allows you to apply a command 'inside' or 'around' certain blocks of text. For instance, suppose the cursor is inside the body (between the curly braces) of this PHP function:

function scandir_except_dots(string $directory)
{
    return array_filter(
        scandir($directory, SCANDIR_SORT_DESCENDING),
        fn($filename) => !in_array($filename, ['.', '..'])
    );
}

Typing `di{`, which could be read as 'delete inside curly braces' will remove the body, like so:

function scandir_except_dots(string $directory)
{
}

You can also apply a command around some text. Let's say the cursor is inside the single quotes of the following PHP expression:

$filenames = scandir_except_dots('/tmp/directory');

Typing `ca'` (change around ') will delete the text inside the single quotes, and the single quotes, and switch to Insert Mode so you can immediately start typing a new parameter.

Repeat Yourself

Perhaps the most powerful single keystroke in Vim is `.`, which repeats the last command. As a contrived example, let's suppose we had decided that the variables in the following BASH code should be stylised in lowercase instead of uppercase:

if [[ $# -ne 2 ]]; then
    printf "Usage: %s SOURCE_DIRECTORY DESTINATION_DIRECTORY\n" $0
    exit 1
fi

SOURCE_DIRECTORY=$1
DESTINATION_DIRECTORY=$2

find $SOURCE_DIRECTORY -type d |\
    sed -E -e "s!^$SOURCE_DIRECTORY!$DESTINATION_DIRECTORY!"\
           -e "s!^!mkdir -p !" |\
    sh -x

First we need to determine a string that matches all the words we want to change. '_DIR' should do the trick, so we can type `/_DIR` and hit enter to find the first word to be changed. This method might seem a bit tricky at first but with practice you can quickly work out the correct string.

We can type `guiw` in Normal Mode to transform the word under the cursor to lowercase. The `gu` command is used to transform text into lowercase. `iw` uses the inside concept explained above - when `i` is combined with `w` it applies the command to the word under the cursor.

We can then jump to the next item that matches the search with `n`, and press `.` to repeat the transformation to lowercase. Note that `n` does not get repeated - typically only commands that make edits are repeated.

Now we can just type `n.` as many times as we need to. The result:

if [[ $# -ne 2 ]]; then
    printf "Usage: %s source_directory destination_directory\n" $0
    exit 1
fi

source_directory=$1
destination_directory=$2

find $source_directory -type d |\
    sed -E -e "s!^$source_directory!$destination_directory!"\
           -e "s!^!mkdir -p !" |\
    sh -x

Learning More

This concludes our brief survey of Vim. For a more comprehensive tutorial (that actually runs in Vim) you can try vimtutor - if you have Vim installed you can simply run `vimtutor` in the command line.

From within Vim you can also type `:h` (optionally followed by the help section) to bring up the builtin documentation. Generally this command is used more for reference, but there is a user manual (type `:h user-manual` to see it).

You might also like...

Universal Works goes live

Dan Walsh by Dan Walsh