Practical Vim
Edit Text at the Speed of Thought
by Drew Neil
- On Amazon
- ISBN: 978-1934356982
Practical Vim is a collection of about 120 tips for using the Vim text editor more efficiently.
As Vim is my text editor of choice I was curious about this book, and it didn't disappoint me. I learned many new commands, which hopefully will make me a better Vim user once I have memorized them. Plus I liked the practical examples with the step-by-step instructions. Putting everything into tips felt not always right, some tips would have been a better fit for the introductory text of the respective chapters.
My notes
The Vim Way
We can always replay the last change with a single keystroke. Powerful as this sounds, it's useless unless we learn to craft our actions so that they perform a useful unit of work when replayed.
The dot command lets us repeat the last change.
To understand the power of the dot command, we have to realize that the "last change" could be one of many things. A change could act at the level of individual characters, entire lines, or even the whole file.
We also create a change each time we dip into Insert mode. From the moment we enter Insert mode until we return to Normal mode, Vim records every keystroke. After making a change such as this, the dot command will replay our keystrokes.
While the a
command appends after the current cursor position, the A
command appends at the end of the current line. It doesn't matter where our cursor is at the time, pressing A
will switch to Insert mode and move the cursor to the end of the line. In other words, it squashes $a
into a single keystroke.
The s
command compounds two steps into one: it deletes the character under the cursor and then enters Insert mode.
The f{char}
command tells Vim to look ahead for the next occurrence of the specified character and then move the cursor directly to it if a match is found.
The ;
command will repeat the last search that the f
command performed.
*
executes a search for the word under the cursor at that moment.
Modes
Normal Mode
In Vim, we can control the granularity of the undo command. From the moment we enter Insert mode until we return to Normal mode, everything we type (or delete) counts as a single change. So we can make the undo command operate on words, sentences, or paragraphs just by moderating our use of the <Esc>
key.
The daw
command is easily remembered by the mnemonic delete a word.
Many of the commands that are available in Normal mode can be prefixed with a count. Instead of executing the command just once, Vim will attempt to execute the command the specified number of times.
The <C-a>
and <C-x>
commands perform addition and subtraction on numbers. When run without a count they increment by one, but if we prefix a number, then we can add or subtract by any whole number.
When an operator command is invoked in duplicate, it acts upon the current line. So dd
deletes the current line, while >>
indents it.
Insert Mode
Insert Normal mode is a special version of Normal mode, which gives us one bullet. We can fire off a single command, after which we'll be returned to Insert mode immediately. From Insert mode, we can switch to Insert Normal mode by pressing <C-o>
.
Replace mode is identical to Insert mode, except that it overwrites existing text in the document. From Normal mode, we can engage Replace mode with the R
command.
Visual Mode
Vim's Visual mode allows us to define a selection of text and then operate upon it.
Vim has three kinds of Visual mode. In character-wise Visual mode, we can select anything from a single character up to a range of characters within a line or spanning multiple lines. This is suitable for working at the level of individual words or phrases. If we want to operate on entire lines, we can use line-wise Visual mode instead. Finally, block-wise Visual mode allows us to work with columnar regions of the document.
The v
key is our gateway into Visual mode. From Normal mode, we can press v
by itself to enable character-wise Visual mode. Line-wise Visual mode is enabled by pressing V
, and block-wise Visual mode by pressing <C-v>
.
The range of a Visual mode selection is marked by two ends: one end is fixed and the other moves freely with our cursor. We can use the o
key to toggle the free end. This is really handy if halfway through defining a selection we realize that we started in the wrong place. Rather than leaving Visual mode and starting afresh, we can just hit o
and redefine the bounds of the selection.
When we use the dot command to repeat a Visual mode command, it acts on the same amount of text as was marked by the most recent visual selection.
As a general rule, we should prefer operator commands over their Visual mode equivalents when working through a repetitive set of changes.
Command-Line Mode
When we press the :
key, Vim switches into Command-Line mode. This mode has some resemblance to the command line that we use in the shell. We can type the name of a command and then execute it by pressing <CR>
. At any time, we can switch from Command-Line mode back to Normal mode by pressing <Esc>
.
For historical reasons, the commands that we execute from Command-Line mode are called Ex commands.
Many Ex commands can be given a [range] of lines to act upon. We can specify the start and end of a range with either a line number, a mark, or a pattern.
In general, we could say that a range takes this form: :{start},{end}
.
We can use the .
symbol as an address to represent the current line. The %
symbol also has a special meaning – it stands for all the lines in the current file.
The general form for an offset goes like this: :{address}+n
. If n
is omitted, it defaults to 1. The {address}
could be a line number, a mark, or a pattern.
The :copy
command (and its shorthand :t
) lets us duplicate one or more lines from one part of the document to another, while the :move
command lets us place them somewhere else in the document.
The format of the :copy
command goes like this: :[range]copy {address}
.
The :move
looks similar to the :copy
command: :[range]move {address}
. We can shorten it to a single letter: :m
.
Repeating the last Ex command is as easy as pressing @:
.
If we want to run a Normal mode command on a series of consecutive lines, we can do so using the :normal
command. When used in combination with the dot command or a macro, we can perform repetitive tasks with very little effort.
With q:
you can open the command-line window. It is like a regular Vim buffer, where each line contains an item from our history.
From Vim's Command-Line mode, we can invoke external programs in the shell by prefixing them with a bang symbol, for example: :!ls
.
On Vim's command line, the %
symbol is shorthand for the current file name.
We can use Vim's :shell
command to start an interactive shell session.
The :read !{cmd}
command lets us direct standard output into a buffer. As you might expect, the :write !{cmd}
does the inverse: it uses the contents of the buffer as standard input for the specified {cmd}
.
Files
Manage Multiple Files
When we discuss our workflow, it's tempting to say that we're editing a file, but that's not what we're actually doing. Instead, we're editing an in-memory representation of a file, which is called a buffer in Vim's terminology.
The :ls
command gives us a listing of all the buffers that have been loaded into memory. We can traverse the buffer list using four commands – :bprev
and :bnext
to move backward and forward one at a time, and :bfirst
and :blast
to jump to the start or end of the list.
We can jump directly to a buffer by number, using the :buffer N
command. Alternatively, we can use the more intuitive form, :buffer {bufname}
. The {bufname}
need only contain enough characters from the filepath to uniquely identify the buffer.
In Vim's terminology, a window is a viewport onto a buffer. We can open multiple windows, each containing the same buffer, or we can load different buffers into each window.
We can divide a window horizontally with the <C-w>s
command, which creates two windows of equal height. Or we can use the <C-w>v
command to split the window vertically. Each time we use one of those commands, the two resulting split windows will contain the same buffer as the original window that was divided.
With the command :split {filename}
resp. :vsplit {filename}
we can split the current window horizontally resp. vertically and loading {filename}
into the new window.
In Vim, a tab page is a container that can hold a collection of windows.
Open Files and Save Them to Disk
If we launch Vim with the path to a directory rather than a file, it will start up with a file explorer window. We can also open the file explorer window with the :edit {path}
command by supplying a directory name as the {path}
argument.
Getting Around Faster
Navigate Inside Files with Motions
Vim makes a distinction between real lines and display lines. When the wrap
setting is enabled (and it's on by default), each line of text that exceeds the width of the window will display as wrapped, ensuring that no text is truncated from view. As a result, a single line in the file may be represented by multiple lines on the display.
Understanding the difference between real and display lines is important because Vim provides motions for interacting with both kinds. The j
and k
commands move down and up by real lines, whereas the gj
and gk
commands move down and up by display lines.
Vim provides a handful of motions that let us move the cursor forward and backward by one word at a time. w
moves forward to the start of the next word whereas b
moves backward to the start of the current/previous word. e
moves forward to the end of the current/next word whereas ge
moves backward to the end of the previous word.
Vim provides two definitions for what a "word" is and distinguishes between them by calling one a "word" and the other a "WORD". Each word-wise motion has a WORD-wise equivalent: W
, B
, E
, and gE
. A word consists of a sequence of letters, digits, and underscores, or as a sequence of other nonblank characters separated with whitespace. The definition of a WORD is simpler: it consists of a sequence of nonblank characters separated with whitespace.
The f{char}
command is one of the quickest methods of moving around in Vim. It searches for the specified character, starting with the cursor position and continuing to the end of the current line. If a match is found, then the cursor advances to the specified character. If no match is found, then the cursor stays put.
Text objects define regions of text by structure.
Vim's text objects consist of two characters, the first of which is always either i
or a
. In general, we can say that the text objects prefixed with i
select inside the delimiters, whereas those that are prefixed with a
select everything including the delimiters.
The m{a-zA-Z}
command marks the current cursor location with the designated letter. Lowercase marks are local to each individual buffer, whereas uppercase marks are globally accessible.
Vim provides two Normal mode commands for jumping to a mark. '{mark}
moves to the line where a mark was set, positioning the cursor on the first non-whitespace character. The `{mark}
command moves the cursor to the exact position where a mark was set, restoring the line and the column at once.
The %
command lets us jump between opening and closing sets of parentheses.
Navigate Between Files with Jumps
In web browsers, we're used to using the back button to return to pages that we visited earlier. Vim provides a similar feature by way of the jump list: the <C-o>
command is like the back button, while the complementary <C-i>
command is like the forward button.
We can inspect the contents of the jump list by running the :jumps
command. Any command that changes the active file for the current window can be described as a jump. In the jump list, Vim records the cursor location before and after running such a command.
Vim maintains a list of the modifications we make to each buffer during the course of an editing session. It's called the change list, and we can inspect its contents by running the :changes
command.
Vim automatically creates a couple of marks that complement the change list. The `.
mark always references the position of the last change, while the `^
mark tracks the position of the cursor the last time that Insert mode was stopped.
Vim treats filenames in our document as a kind of hyperlink and we can use the gf
command to go to the filename under the cursor.
Registers
Copy and Paste
The delete, yank, and put commands all interact with one of Vim's registers. We can specify which register we want to use by prefixing the command with "{register}
. If we don't specify a register, then Vim will use the unnamed register.
When we use the y{motion}
command, the specified text is copied not only into the unnamed register but also into the yank register, which is addressed by the 0
symbol. As the name suggests, the yank register is set only when we use the y{motion}
command. To put it another way: it's not set by the x
, s
, c{motion}
, and d{motion}
commands.
Vim has one named register for each letter of the alphabet. That means that we can cut ("ad{motion}
), copy ("ay{motion}
, or paste ("ap
) up to twenty-six pieces of text. When we address a named register with a lowercase letter, it overwrites the specified register, whereas when we use an uppercase letter, it appends to the specified register.
The black hole register is a place from which nothing returns. It's addressed by the underscore symbol.
If we want to copy some text from inside of Vim and paste it into an external program (or vice versa), then we have to use one of the system clipboards. Vim's plus register references the system clipboard and is addressed by the +
symbol.
Macros
Many repetitive tasks involve making multiple changes. If we want to automate these, we can record a macro and then execute it. The q
key functions both as the "record" button and the "stop" button. To begin recording our keystrokes, we type q{register}
, giving the address of the register where we want to save the macro. Every command that we execute will be captured, right up until we press q
again to stop recording.
The @{register}
command executes the contents of the specified register. We can also use @@
, which repeats the macro that was invoked most recently.
Patterns
Matching Patterns and Literals
The \v
pattern switch enables the very magic search, where all characters assume a special meaning, with the exception of "_", uppercase and lowercase letters, and the digits 0 through 9. The \v
pattern switch makes Vim's regular expression engine behave much more like that of Perl, Python, or Ruby.
The special characters used for defining regular expressions are handy when searching for patterns, but they can get in the way if we want to search for text verbatim. Using the very nomagic literal switch (\V
), we can cancel out most of the special meanings attached to characters such as .
, *
, and ?
.
As a general rule, if you want to search for a regular expression, use the \v
pattern switch, and if you want to search for verbatim text, use the \V
literal switch.
Anything that matches inside of parentheses is automatically assigned to a temporary silo. We can reference the captured text as \1
. If our pattern contained more than one set of parentheses, then we could reference the submatch for each pair of ()
by using \1
, \2
, and so on, up to \9
. The \0
item always refers to the entire match, whether or not parentheses were used in the pattern.
If we specifically want to match "the" as a word rather than a fragment, we can use word boundary delimiters. In very magic searches, these are represented by the <
and >
symbols.
The boundaries of a match normally correspond to the start and end of a pattern. But we can use the \zs
and \ze
items to crop the match, making it a subset of the entire pattern. The \zs
item marks the start of a match, while the \ze
item matches the end of a match.
Search
When a search is initiated with the /
key, Vim scans the document forward. If we use the ?
key to bring up the search prompt, Vim searches backward instead.
The n
command jumps to the next match, and the N
command jumps to the previous match.
Substitution
The substitute command allows us to find and replace one chunk of text with another. The command's syntax looks like this: :[range]s[ubstitute]/{pattern}/{string}/[flags]
.
The g
flag makes the substitute command act globally, causing it to change all matches within a line rather than just changing the first one.
The c
flag gives us the opportunity to confirm or reject each change.
Leaving the search field of the substitute command blank instructs Vim to reuse the most recent search pattern.
Global Commands
The :global
command allows us to run an Ex command on each line that matches a particular pattern. It has the following form: :[range] global[!] /{pattern}/ [cmd]
. The default range for the :global
command is the entire file. The {pattern}
field integrates with search history. That means we can leave it blank and Vim will automatically use the current search pattern. We can invert the behavior of the :global
command either by running :global!
or :vglobal
. Each of these tells Vim to execute [cmd]
on each line that doesn't match the specified pattern.
Tools
Index and Navigate Source Code with ctags
ctags
is an external program that scans through a codebase and generates an index of keywords.
Compile Code and Navigate Errors with the Quickfix list
Vim's :make
command isn't limited to calling the external make program; it can execute any compilers available on your machine.
Search Project-Wide with grep, vimgrep, and Others
Vim's :grep
command acts as a wrapper to an external grep
(or grep
-like) program. Using this wrapper, we can have grep
search for a pattern across multiple files without leaving Vim.
Vim provides a command called :vimgrep
, which uses Vim's built-in search engine. The syntax looks like this: :vim[grep][!] /{pattern}/[g][j] {file}
.
The main advantage to using :vimgrep
is that it understands the same patterns as Vim's search command, so we can compose a regular expression by searching in the current file. When we're satisfied that it matches where it should, we execute :vimgrep
using the exact same pattern.
Dial X for Autocompletion
Vim's autocompletion can be triggered from Insert mode with the <C-p>
and <C-n>
chords, which select the previous and next items in the word list, respectively.
We can end autocompletion by pressing <C-e>
, which dismisses the pop-up menu and restores the text in front of the cursor to the partial word that was typed before autocomplete was invoked.
To filter the word list as you type, you can press <C-n><C-p>
.
The simplest mechanism for populating the autocomplete word list would be to use words only from the current buffer. Current file keyword completion does just that and is triggered with <C-x><C-n<
.
Omni-completion provides a list of suggestions that's tailored for the context of the cursor position. It is triggered with the <C-x><C-o>
command.
Find and Fix Typos with Vim's Spell Checker
We can enable Vim's built-in spell checker with :set spell
.
We can jump backward and forward between flagged words with the [s
and ]s
commands, respectively. With our cursor positioned on a misspelled word, we can ask Vim for a list of suggested corrections by invoking the z=
command.
Sometimes Vim will incorrectly mark something as misspelled because the word doesn't appear in the dictionary. We can teach Vim to recognize words with the zg
command, which adds the word under the cursor to a spell file. Vim also provides a complementary zw
command, which marks the word under the cursor as a misspelled word.