Vim, with Vigor
TLCL Chapter 12 taught us the basic skills necessary to use the vim text editor. However, we barely scratched the surface of its capabilities. Vim is a very powerful program. In fact, it's safe to say that vim can do anything. It's just a question of figuring out how. In this adventure, we will acquire an intermediate level of skill in this popular tool. In particular, we will look at ways to improve our productivity writing shell programs, configuration files, and documentation. Even better, after we get the hang of some of these additional features, using vim actually becomes fun.
During this adventure, we will be looking at some of the features that make vim so popular among developers and administrators. The community around vim is large and vigorous. Vim is extremely rich in features and is highly scriptable. As a result, there are many plugin add-ons available. However, we are going to restrict ourselves to stock vim and the plugins that normally ship with it.
A note about nomenclature: in TLCL we used the terms "command", "insert", and "ex" to identify the three primary modes of vim. We did this to match the traditional modes of vim's ancestor, vi. Since this is an all-vim adventure, we will switch to the names used in the vim documentation which are normal, insert, and command.
Let's Get Started
First, we need to be sure we are running the full version of vim. Many distributions only ship with an abbreviated version. To get the full version, install the vim package if it's not already installed. This is also be a good time to add an alias to our
Next, let's create a minimal
Edit the file so it contains these two lines:
This will ensure that vim is not restricted to the vi feature set, and will load a standard plugin that lets vim recognize different file types. After inserting the two lines of text, return to normal mode and (just for fun) type lowercase 'm' followed by uppercase 'V':
Nothing will appear to happen, and that's OK. We'll come back to that later. Now save the file and exit vim:
Vim has an extensive built-in help system. If we start vim:
and enter the command:
It will appear at the top of the display.
While help is extensive and very useful, it immediately presents a problem because it creates a split in the display; a rather advanced feature that needs some explanation.
Vim can divide the display into multiple panes, which in vim parlance are called windows. These are very useful when working with multiple files and other vim features such as help. When the display is divided in this way, we can toggle between the windows by typing
When working with files, it's important to note that "closing" a window (with either
To exit help, make sure the cursor is in the help window and enter the quit command:
to quit that window.
But enough about windows, let's get back to help. If we scroll around the initial help file, we see that it is a hypertext document full of links to various topics and it begins with the commands we need to navigate the help system. This is all well and good, but it's not the most interesting way to use it.
The best way is to just type :h followed by the thing we are interested in. The fact that we don't have to type out ":help" reveals that most vim commands can be abbreviated. This saves a lot of work. In general, commands can be shortened to their smallest non-ambiguous form. Frequently used commands, like help, are often shortened to a single character but the system of abbreviations isn't predictable, so we have to use help to find them. For the remainder of this adventure, we will try to use the shortest available form.
There is an important table near the beginning of the initial help file:
This table describes how we should ask for help in particular contexts. We're familiar with the normal mode command 'i' which invokes insert mode. In the case of such a normal mode command, we simply type:
to display its help page. For command mode commands, we precede the command with a ':', for example:
gets help with the
There are other contexts for modes we have yet to cover. We'll get to those in a little bit.
As we go along, feel free to use help to learn more about the commands we discuss. As this adventure goes on, the text will include suggested help topics to explore.
Oh, and while we're on the subject of command mode, now is a good time to point out that command mode has command line history similar to the shell. After typing ':' we can use the up and down arrows to scroll through past commands.
Starting A Script
In order to demonstrate features in vim, we're going to write a shell script. What it does is not important, in fact, it won't do anything at all except to show how we can edit scripts. To begin, let's start vim with the name of an non-existent script file:
and we will get our familiar "new file" window:
Setting the Filetype
At this point vim has no idea what kind of file we are creating. If we had named the file
When we use the
indicating that the
to get more information. To see all the current option settings, we can do this:
and the entire list will appear.
Since we want our new file to be treated as a shell script, we can set the filetype manually:
Next, let's enter insert mode and type the first couple of lines in our script:
Exit insert mode by pressing the
Now that our file contains the shebang on the first line, the filetype plugin will recognize the file as a shell script whenever it is loaded.
Using The Shell
One thing we can do with filetypes is create a configuration file for each of the supported types. Normally, these are placed in the
We don't have leave vim to do this; we can launch a shell from within vim. This is easily done by entering the command:
After doing this, a shell prompt will appear and we can enter our shell command:
When we're done with the shell, we return to vim by exiting the shell:
Now that we have a place for our configuration file to live, let's create it. We'll open a new file:
Before we start editing our new file, let's look at what vim is doing. Each file that we edit is stored in a buffer. We can look the current list of buffers this way:
This will display the list. There are several ways that we can switch buffers. The first way is to cycle between them:
This command (short for
We can even refer to a buffer by using a portion of the file name:
Let's cycle back to our new buffer and add this line to our configuration file:
This will turn on line numbering each time we load a shell script. Notice that we use the
We can also control syntax highlighting while we're here. We can turn it on with:
or turn it off with:
We'll save this file now, but before we do that, let's type
If we return to the buffer containing our shell script, we should see the effects of our
and it will display the name. To see the entire set of available color schemes, type
The 'desert' color scheme looks pretty good with shell scripts, so let's add this to our
Notice that we used the long form of the
There are many additional color schemes for vim on the Internet. To use one, first create a
Now, save the file and return to our shell script.
Marks And File Marks
We know there are various ways of moving around within document in vim. For example, to get to the top, we can type:
and to the bottom by typing:
but vim (and real vi for that matter) also allows us to mark an arbitrary location within a document that we can recall at will. To demonstrate this, go to the top of the script and type:
then go to the bottom of the document and type:
We have just set two marks, the first called "a" and the second called "b". To recall a mark, we precede the name of the mark with the ' character, like so:
and we are taken to the top of the file again.
We can use any lowercase letter to name a mark. Now, the clever among us will remember that we set marks in both the
Yes we did, because they're special. They're called file marks and they let us set a mark in a file that vim will remember between sessions. Since we set the V mark in the
vim will immediately take us to that mark even if vim has to load the file to do it. By doing this to
Among the best features that vim adds to ordinary vi is visual mode. This mode allows us to visually select text in our document. If we type:
An indicator will appear at the bottom of the screen showing that we have entered this mode. While in visual mode, when we move the cursor (using any of the available movement commands), the text is both visually highlighted and selected. Once this is done we can apply the normal editing commands on the selected text such as
we again enter visual mode, but this time selection is done on a line-by-line basis rather than by individual characters. This is handy when cutting and copying blocks of code.
There is a third way of using visual mode. If we type:
we are able to select rectangular blocks of text by columns. For example, we could select a column from a table.
We're going to continue working on our shell script, but first we need to talk a little about indentation. As we know, indentation is used in programming to help communicate program structure. The shell does not require any particular style of indentation; it's purely for the benefit of the humans trying to read the code. However, some other computer languages, such as Python, require indentation to express program structure.
Indentation is accomplished in one of two ways; either by inserting tab characters or by inserting a sequence of spaces. To understand the difference, we have to go way back in time to typewriters and teletype machines.
In the beginning, there were typewriters. On a typewriter, in order to make indenting the first line of a paragraph easier, someone invented a mechanical device that would move the carriage over a set amount of space. Over time, these devices became more sophisticated and allowed multiple tab stops to be set. When teletype machines came about, they implemented tabs with a specific ASCII character called HT (horizontal tab, code 9) which, by default, was rendered by moving the cursor to the next character position evenly divisible by 8.
In the early days of computing, when memory was precious, it made sense to conserve space in text files by using tab characters to avoid having to pad the text file with spaces.
Using tab characters creates a problem, though. Since a tab character has no intrinsic width (it only signifies the desire to move to the next tab stop), it's up to the receiving program to render the tab with some defined width. This means that a file containing tabs could be rendered in different ways in different programs and in different contexts.
Since memory is no longer expensive, and using tabs creates this rendering confusion, modern practice calls for spaces instead of tabs to perform indentation (though this remains somewhat controversial). Vim provides a number of options for setting tabs and indentation. An excerpt from the help file for the
Indentation Settings For Scripts
For our purposes, we will use method 2 and add the following lines to our
In addition to the tab settings, we also included the
After adding the indentation settings to our
As we type these additional lines into our script, we notice that vim can now automatically provide indentation as needed. The
If we find ourselves editing an existing script with a indentation scheme differing from our current settings, vim can convert the file. This is done by typing:
and the file will have its tabs adjusted to match our current indentation style.
As we learned in TLCL, vim has lots of movement commands we can use to quickly navigate around our documents. These commands can be employed in many useful ways.
Here is a list of the common movement commands. Some of this is review, some is new.
Remember, each of these commands can be preceded with a count of how many times the command is to be performed.
Movement commands are often used in conjunction with operators. The movement command determines how much of the text the operator affects. Here is a list of the most commonly used operators:
We can use visual mode to easily demonstrate the movement commands. Move the cursor to the beginning of line 3 of our script and type:
This will select the text from the beginning of the line to the end of the first sentence. Press
to select the first sentence. Cancel visual mode again and type:
to select the entire paragraph (any block of text delimited by a blank line). Pressing } again extends the selection to the next paragraph.
Text Object Selection
In addition to the traditional vi movement commands, vim adds a related feature called text object selection. These commands only work in conjunction with operators. These commands are:
The text objects are:
The way these work is very interesting. If we place our cursor on a word for example, and type:
(short for "change all word"), vim selects the entire word, deletes it, and switches to insert mode. Text objects work with visual mode too. Try this: move to line 11 and place the cursor inside the quoted string and type:
The interior of the quoted string will be selected. If we instead type:
the entire string including the quotes is selected.
Let's say we wanted to add a license header to the beginning of our script. This would consist of a comment block near the top of the file that includes the text of the copyright notice.
We'll move to line 3 of our script and add the text, but before we start, let's tell vim how long we want the lines of text to be. First we'll ask vim what the current setting is:
Vim should respond:
"tw" is short for
Vim will now wrap lines (at word boundaries) when the length of a line exceeds this value.
Normally, we wouldn't want to set a text width while writing code (though keeping line length below 80 characters is a good practice), but for this task it will be useful.
So let's add our text. Type this in:
Notice the magic of vim as we type. Each time the length of the line reaches the text width, vim automatically starts a new line including, the comment symbol. While the filetype is set for shell scripting, vim understands certain things about shell syntax and tries to help. Very handy.
Now let's say we were not happy with the length of these lines, or that we have edited the text in such a way that some of the lines are either too long or too short to maintain our well-formatted text. Wouldn't be great is we could reformat our comment block? Well, we can. Very easily, in fact.
To demonstrate, let's change the text width to 65 characters:
Now place the cursor inside the comment block and type:
(meaning "format in paragraph") and watch what happens. Presto, the block is reformatted to the new text width! A little later, we will show how to reduce this four key sequence down to a single key.
There is a fun trick we can perform on this comment block. When we write code, we frequently perform testing and debugging by commenting out sections. Vim makes this process pretty easy. To try this out, let's first remove the commenting from our block. We will do this by using visual mode to select a block. Place the cursor on the first column of the first line of the comment block, then enter visual mode:
Then, move the cursor right one column and then down to the bottom of the block.
This will delete the contents of the selected area. Now our block is uncommented.
To comment the block again, move the cursor to the first character of the block and, using visual block selection, select the first 2 columns of the block.
Next, enter insert mode using
From time to time, we need to change text from upper to lower case and vice versa. vim has the following case conversion commands:
File Format Conversion
Once in a while, we are inflicted with a text file that was created on a DOS/Windows system. These files will contain an extra carriage return at the end of each line. Vim will indicate this after loading the file by displaying a "DOS" message at the bottom of the editing window. To correct this annoying condition, do the following:
and the file will be rewritten in the correct format.
Text editing sometimes means we get stuck with a tedious repetitive editing task where we do the same set of operations over and over again. This is the bane of every computer user. Fortunately, vim provides us a way to record a sequence of operations we can later playback as needed. These recordings are called macros.
To create a macro, we begin recording by typing
To demonstrate, let's consider our comment block again. To create a macro that will remove a comment symbol from the beginning of the line, we would do this: move to the first line in the comment block and type the following command:
Let's break down what this sequence does:
Now that we have removed the comment symbol from the first line and our cursor is on the second line, we can replay our macro by typing:
and the recorded sequence will be performed. To repeat the macro on succeeding lines, we can use the repeat last macro command which is:
Or we could precede the macro invocation with a count as with other commands. For example, if we type:
the macro will be repeated 5 times.
We can undo the effect of the macro by repeatedly typing:
One nice thing about macros is that vim remembers them. Each time we exit vim, the current macro definitions are stored and ready for reuse the next time we start another editing session.
We are no doubt familiar with the idea of copying and pasting in text editors. With vim, we know
Registers are named areas of memory where vim stores text. We can think of them as a series of string variables. Vim uses one particular set to store text that we delete, but there are others that we can use to store text and restore it as we desire. It's like having a multi-element clipboard.
To refer to a register, we type " followed by a lowercase letter or a digit (though these have a special use), for example:
refers to the register named "a". To place something in the register, we follow the register with an operation like "yank to end of the line":
To recall the contents of a register, we follow the name of the register with a paste operation like so:
Using registers enables us to place many chunks of text into our clipboard at the same time. But even without consciously trying to use registers, vim is using them while we perform deletes and yanks.
As we mentioned earlier, the registers named 0-9 have a special use. When we perform ordinary yanks and deletes, vim places our latest yank in register 0 and our last nine deletes in registers 1-9. As we continue to make deletions, vim moves the previous deletion to the next number, so register 1 will contain our most recent deletion and register 9 the oldest.
Knowing this allows us to overcome the problem of performing a yank and then a delete and losing the text we yanked (a common hazard when using vim). We can always recall the latest yank by referencing register 0.
To see the current contents of the registers we can use the command:
While it's not obvious, vim has a set of commands inside of insert mode. Most of these commands invoke some form of automatic completion to make our typing faster. They're a little clumsy, but might be worth a try.
Automatically Complete Word Ctrl-n
Let's go to the bottom of our script file and enter insert mode to add a new line at the bottom. We want the line to read:
We start to type the first few characters ("afun") and press
Insert Register Contents - Ctrl-r
Automatically Complete Line - Ctrl-x Ctrl-l
If we have typed the first few letters of a line found in this or any other file that vim has open, typing
Automatically Complete Filename Ctrl-x Ctrl-f
This will perform filename completion. If we start the name of an existing path/file, we can type
Dictionary Lookup - Ctrl-x Ctrl-k
If we define a dictionary (i.e., a sorted list of words), by adding this line to our configuration file:
which is the default dictionary on most Linux systems, we can begin typing a word, type
Like many interactive command line programs, vim allows users to remap keys to customize vim's behavior. It has a specific command for this,
Before we go on, we should point out that use of the
Earlier, we looked at the paragraph reformatting command sequence
After executing this command, pressing the
Most of the time we will be remapping normal mode keys, so the
This command maps the
So how do we know which keys are available for remapping assignment? As vim uses almost every key for something, we have to make a judgment call as to what native functionality we are willing to give up to get the mapping we want. In the case of the Q key, which we used in our first example, it is normally used to invoke ex mode, a very rarely used feature. There are many such cases in vim; we just have to be selective. It is best to check the key first by doing something like:
to see how a key is being used before we apply our own mapping.
To make mappings permanent, we can add these mapping commands to our
Mapping is not restricted to single characters. We can use sequences too. This is often helpful when we want to create a number of easily remembered, related commands of our own design. Take for example, inserting boilerplate text into a document. If we had a collection of these snippets, we might want to uniquely name them but have a common structure to the name for easily recollection.
We added the GPL notice to the comment block at the beginning of our script. As this is rather tedious to type, and we might to use it again, it makes a good candidate for being a snippet.
To do this, we'll first go out to the shell and create a directory to store our snippet text files. It doesn't matter where we put the snippet files, but in the interest of keeping all the vim stuff together, we'll put them with our other vim-related files.
Next, we'll copy the license by highlighting the text in visual mode and yanking it. To create the snippet file, we'll open a new buffer:
Thus creating a new file called gpl.sh. Finally, we'll paste the copied text into our new file and save it:
Now that we have our snippet file in place, we are ready to define our mapping:
We map ",GPL" to a command that will cause vim to read the snippet file into the current buffer. The leading comma is used as a leader character. The comma is a rarely used command that is usually safe to remap. Using a leader character will reduce the number of actual vim commands we have to remap if we create a lot of snippets.
As we add mappings, it's useful to know what they all are. To display a list of mappings, we use the
Once we are satisfied with our remapping, we can add it to one of our vim configuration files. If we want it to be global (that is, it applies to all types of files), we could put it in our
If, on the other hand, we want it to be specific to a particular file type, we would put it in the appropriate file such as
In this case, we add the special argument <buffer> to make the mapping local to the current buffer containing the particular file type.
Finishing Our Script
With all that we have learned so far, it should be pretty easy to go ahead and finish our script:
Using External Commands
Vim is able to execute external commands and add the result to the current buffer or to filter a text selection using an external command.
Loading Output From a Command Into the Buffer
Let's edit an old friend. If we don't have a copy to edit, we can make one. First we'll open a buffer:
Next, we'll load the buffer with some appropriate text:
This will read the results of the specified external command into our buffer.
Running an External Command on the Current File
Let's save our file and then run an external command on it:
Here we tell vim to execute the
Using an External Command to Filter the Current Buffer
Let's apply a filter to the text. To do this, we need to select some text. The easiest way to do this is with visual mode:
This will move the cursor to the beginning of the file and enter visual mode then move to the end of the file, thus selecting the entire buffer.
We'll filter the selection to remove everything except lines containing the string "zip". When we start entering a command after performing a visual selection, the presence of the selection will be indicated this way:
This actually signifies a range. We could just as easily specify a pair of line numbers such as 1, 100 instead. To complete our command, we add our filter:
Notice that we are not limited to a single command. We can also specify pipelines, for example:
After running this command, our buffer contains a small selection of files, each containing the letters "zip" in the name.
Help topics: :
File System Management and Navigation
We know that we can load files into vim by specifying them on the command line when we initially invoke vim, and that we can load files from within vim with the
When we load the filetype plugin (as we have set up our
To start the browser in the current window, we use the
At the top, we have the banner which provides some clues to the browser's operation, followed by a vertical list of directories and files. We can toggle the banner on and off with
Using the browser is easy. To select a file or directory, we can use the up and down arrows (or
The action of the
Adding this to the path allows
Another cool enhancement we can apply is the wildmenu. This is a highlighted bar that will appear above the command line when we are entering file names. The word "wild" in the name refers to use of the "wild" key, by default the Tab key. When the wild key is pressed, automatic completion is attempted with the list of possible matches displayed in the wildmenu. Using the left and right arrow keys (or
We can turn on the wildmenu with this command:
Opening Files Named in a Document
If the document we are editing contains a file name, we can open that file by placing the cursor on the file name and typing either of these commands:
One Does Not Live by Code Alone
While vim is most often associated with writing code of all sorts, it's good at writing ordinary prose as well. Need proof? All of these adventures were written using vim running on a Raspberry Pi!
We can configure vim to work well with text by creating a file for the text file type in the
This configuration will automatically wrap lines at word boundaries once the line length exceeds 75 characters, and will set tabs to 4 spaces wide. Remember that when
When we write text, it's handy to perform spell checking while we type. Fortunately, vim can do this, too. If we add the following lines to our
The first line defines the language for spell checking, in this case US English. Next, we specify the dictionary file to use. Most Linux distributions include this list of words, but other dictionary files can be used. The final line turns on the spell checker. When active, the spell checker highlights misspelled words (that is, any word not found in the dictionary) as we type.
Correcting misspelled words is pretty easy. Vim provides the following commands:
To correct a misspelling, we place the cursor on the highlighted word and type:
Vim will display a list of suggested corrections and we choose from the list. It is also possible to maintain a personal dictionary of words not found in the main dictionary, for example specialized technical terms. Vim creates the personal dictionary automatically (in
Once the word is added to our personal dictionary it will no longer be marked as misspelled by the spelling checker.
More .vimrc Tricks
Before we go, there are a few more features we can add to our
This will cause vim to display a status bar near the bottom of the display. It will normally appear when more than one window is open (
will display the cursor position (row, column, relative %) in the window status bar. Handy for knowing where we are within a file.
Finally, we'll add mouse support (not that we should ever use a mouse ;-):
This will activate mouse support if vim detects a mouse. Mouse support allows us to position the cursor, switching windows if needed. It works in visual mode too.
We can sometimes think of vim as being a metaphor for the command line itself. Both are arcane, vast, and capable of many amazing feats. Despite its ancient ancestry, vim remains a vital and popular tool for developers and administrators.
Here are the final versions of our 3 configuration files:
We covered a lot of ground in this adventure and it will take some time for it to all sink in. The best advice was given back in TLCL. The only way to become a master is to "practice, practice, practice!"
Vim has a large and enthusiastic user community. As a result, there are many online help and training resources. Here are some that I found useful during my research for this adventure.
There are also a lot of video tutorials for vim. Here are a few:
© 2000-2018, William E. Shotts, Jr. Verbatim copying and distribution of this entire article is permitted in any medium, provided this copyright notice is preserved.
Linux® is a registered trademark of Linus Torvalds.