I’ve had an off/on relationship with Vim for the past many years.
Before, I never felt like we understood each other properly. Vim is
almost useless without plugins and some essential settings in .vimrc
, but fiddling with all the knobs and
installing all the plugins that I thought I needed was a process that in
the end stretched out from few hours to weeks, months even; and it the
end it just caused frustration instead of making me a happier coder.
Recently, I decided to give Vim another shot. This time around it was different – something in my brain switched and now for the first time in my life I’m proud of my knowledge of Vim. My philosophy of it has changed to “less is moreâ€, my approach was more disciplined and my motivation stronger. And so you don’t spend as much time learning as I did, I am going to lay down some fundamentals.
Start with a basic setup – but not zero
I learned this the hard way: you shouldn’t start with a huge
.vimrc
file that you copied from someone else
nor should you install every single plugin that seems useful at that
moment.
This is a good starting point for your .vimrc
:
set nocompatible
syntax enable
set encoding=utf-8
set showcmd " display incomplete commands
filetype plugin indent on " load file type plugins + indentation
"" Whitespace
set nowrap " don't wrap lines
set tabstop=2 shiftwidth=2 " a tab is two spaces
set expandtab " use spaces, not tabs
set backspace=indent,eol,start " backspace through everything in insert mode
"" Searching
set hlsearch " highlight matches
set incsearch " incremental searching
set ignorecase " searches are case insensitive...
set smartcase " ... unless they contain at least one capital letter
Everything mentioned in this article is valid in vim running in a terminal as well as graphical (GUI) vim such as gvim or MacVim. A graphical vim setting offers extra features, but I’m not covering any of those here.
Guidelines for expanding your vim settings later on:
- Never copy something from other people unless you know exactly what that particular setting is doing and you recognize that you’ve needed it before.
- Don’t use too many plugins; start with 3-5 and add others as your skill progresses. Use Pathogen for managing them.
- Overly eager plugins like Syntastic can actually hurt your development even when you’re not actively using them. Zap such offenders quickly.
- Discover your Vim heroes and periodically check how they have set their editor up. A lot of people publish their dotfiles on GitHub.
You can view my personal vim configuration here.
Don’t use Janus. It’s a community maintained vim configuration
project that in theory sounds nice, but once you start using it it’s
not all rainbows. The current version of Janus installs harmful plugins
such as Syntastic and adds tons of opinionated mappings and piles of
hacks on top of one another. The “experimental†version of Janus is a
rewrite that’s meant to be more configurable, but in fact it’s just more
fragmented and harder to follow what’s going on. You should be in charge
of your .vimrc
and always know what’s going
on, but with Janus you’re not.
Make it pretty
Aesthetics of your text editor are very important given the time you’re spending in it. You should use a good programming font for your terminal emulator: for example DejaVu Sans Mono or Inconsolata. This will affect vim running in the terminal. Next is the color scheme: pick one by cycling through available colors with :color <Tab>.
The quality of the color theme will depend severely on your terminal emulator. You should consider using a graphical Vim to reach the highest quality of typeface and color; a graphical environment can also offers extra options such as antialiasing and line spacing settings.
Find your killer features
Right from the start, you’ll need to discover some sweet power moves that’ll keep you returning to Vim rather than your old editor.
Vim has two main modes you need to care about first: the normal mode (where you move, perform commands) and the insert mode (where you type in text). The insert mode is on the surface nothing special when compared to your old editor: you press i, you’re in insert mode, now you type text as your normally would. Pressing <Esc> exits back to normal mode.
But it is how you enter insert mode that can gain you some advantage:
i | insert before character under cursor |
---|---|
a | insert after cursor |
I | insert at beginning of current line |
A | insert at end of the line |
o | starts insert mode in a new line below current one |
O | insert in a new line above current one |
You can also enter insert mode by replacing existing text at the same time:
ciw | ("change inner word") change word under cursor |
---|---|
ci" | change double-quoted string (but keep the quotes) |
ci( | change text between matching parentheses, also works with brackets |
cc | change whole line |
These shortcuts are a very compelling argument why Vim is more efficient at editing code than most editors, but they are just the tip of the iceberg of all the things possible with different motions.
For more information, see :help change.txt.
Learn to move
In Vim, efficient movement is everything. After all, most of your time is spent editing, not inserting text and code. Commands for selecting, changing, moving and transforming text all follow the same motion rules, thus making them fundamental in knowing Vim. For example, in the ciw command mentioned previously, the iw is a motion that means “inner wordâ€.
Since there are too many different motions to remember at once, you’ll need some sort of a mnemonic to help you while learning.
The best Vim cheat sheet you’ll ever use
Do an image search for “vim cheat sheet†and you’ll quickly hate life. But not all of them are ugly – there is a smart wallpaper originally designed by Ted Naleid and re-worked with smoother colors:
I don’t actually use it as a wallpaper; while learning I’ve kept it open in a separate window and switch to it whenever I needed to remind myself of what kind of motions are there.
Knowing where you want to go
When you think about it, you always know where you want to position your cursor before editing – if you can describe your intent to Vim, you can perform it faster than hitting cursor keys repeatedly or even using the mouse. In general:
- If you repeatedly (more than 2-3 times in a row) hit cursor keys to get somewhere, there is a better way.
- If you press backspace more than a couple of times to delete text, there is a better way.
- If you find yourself performing the same changes on several lines, there is a better way.
Here’s an overview of several useful methods for navigating your document, with 2nd column indicating the corresponding backwards movement:
→ | ↠| description |
---|---|---|
/ | ? | search for a pattern of text, jump to it by hitting Enter (<CR>) |
* | # | search for the word under cursor |
n | N | jump to the next match for the previous search |
$ | ^ | position cursor at end of current line |
f | F | position cursor on the character in the same line that matches the next keystroke |
t | T | position cursor before the next character that matches the keystroke |
; | , | repeat the last f, F, t, or T |
w | b | move to start of next word |
W | B | move to start of next "WORD" (sequence of non-blank characters) |
} | { | move down one paragraph (block of text separated by blank lines) |
gg | jump to first line of document | |
G | jump to end of document |
To me, the killer ones on this list are “wordâ€, “WORD†and paragraph motions.
Pro tip: while in insert mode, <C-w> deletes the last word before cursor. This is more efficient than backspacing.
For in-depth documentation on motions, see :help motion.txt.
After the jump
Some of the above motions jump, and it is very useful to know how to backtrack those jumps. From the :help jump-motions documentation:
A “jump†is one of the following commands:
'
,`
,G
,/
,?
,n
,N
,%
,(
,)
,[[
,]]
,{
,}
,:s
,:tag
,L
,M
,H
and the commands that start editing a new file. If you make the cursor “jump†with one of these commands, the position of the cursor before the jump is remembered.
Mastering jumps is insanely powerful. Suppose you edited one line, exited insert mode and now you are navigating the document to find out something. Now you want to continue editing from the same spot. The `. motion brings the cursor back on the exact place where the last change was made.
Another example: you are in the middle of a script and need to add some
code, but that needs adding an extra require
statement (or import
,
include
, or similar) near the top of the file. You jump to the top
with gg, add the require
statement and jump back to before
the previous jump with ``.
And if you did multiple jumps, you can backtrack with <C-o>.
Yanking: copy & paste
Once you know basic motions and navigating around jumps, you can be efficient with copy (“yank†in Vim terms) and paste.
Y | yank current line; prepend with number to yank that many lines |
---|---|
y} | yank until end of paragraph |
dd | delete current line but copy it too (think "cut") |
d3d | delete 3 lines starting from current one |
p | paste yanked text at cursor; prepend number to paste that many times |
P | paste before cursor |
What isn’t obvious at first, but you’re gonna notice soon, is that Vim doesn’t use your OS clipboard by default for these operations; i.e. you can’t paste text copied from Vim in other apps and p won’t paste into Vim what you just copied from another program on your computer.
Vim uses its registers as its internal clipboard. You can even save
yanked text into a named register of your choosing to ensure it is never
overwritten and paste it later from that register if you need to paste
it multiple times in different spots. Commands for selecting registers
start with "
:
"aY | yank current line into register "a" |
---|---|
"ap | paste from register "a" |
"*Y | yank line into special register "*" which is the system clipboard |
"*p | paste from register "*": the system clipboard |
"_D | delete from cursor until the end of line, but don't copy |
The examples above show how you can explicitly opt-in to use the system
clipboard via "*
, showing that Vim does have access to the
system clipboard, but doesn’t use it by default. The last example uses
the "_
(the “black holeâ€) register, which is a way to discard text
without copying it and overriding the default register.
Pro tip: after you paste code, it might not be indented correctly in
the new context. You can select just pasted lines and autoindent them
with V`]=. This should fix the indentation in most cases.
Breaking it down: uppercase V
enters line-based visual mode, `]
is a motion that selects everything until the end of just pasted text,
and =
performs auto-indentation.
You can inspect the current state of all registers with :registers. See :help registers.
Quickly navigate files
Real-world projects have more than one file to edit. Efficient switching between files is just as important as motion commands.
Folks coming from TextMate, IDEs, or gedit will quickly find themselves
craving for a directory tree side pane, and the community is going to
unanimously recommend NERD tree. Don’t use the NERD tree plugin. It
is clumsy, will hurt your split windows workflow because of edge-case
bugs and plugin incompatibilities, and you never needed a file browser
pane in the first place, anyway. The only thing it can be useful for is
familiarizing yourself with the directory/file structure of a new
project, and you can easily do that with the tree
command-line tool
(easily installed via package manager of choice, such as Homebrew on OS
X).
What you need is a plugin for fuzzy searching of file names in a project. I find the Command-T plugin very capable and fast, but it requires ruby on the system and vim compiled with ruby support. If that’s a deal breaker for you, some people swear by ctrlp.vim.
Here are my mappings for starting a file search with Command-T. I start a project-wide search with ,f and search the directory of the current file with ,F:
" use comma as <Leader> key instead of backslash
let mapleader=","
" double percentage sign in command mode is expanded
" to directory of current file - http://vimcasts.org/e/14
cnoremap %% <C-R>=expand('%:h').'/'<cr>
map <leader>f :CommandTFlush<cr>\|:CommandT<cr>
map <leader>F :CommandTFlush<cr>\|:CommandT %%<cr>
For this to be fast, ensure that you don’t have any other mappings that
start with <leader>f
, otherwise Vim will always force a slight delay
after keystrokes before resolving the mapping. (See all of the current
mappings in effect with :map.)
In the same session, Vim remembers previously open files. You can list
them with :buffers. Without even using any plugins, you can
jump to a buffer on this list by typing :b and a part of a
buffer’s name. For example, if you’ve previously opened
lib/api_wrapper.rb
, you can return to it with :b api. Hit
<Tab> to cycle between multiple matches.
To switch between the currently open buffer and the previous one, use
<C->. This key combination is a bit hard to reach, so
you can remap to, for instance, twice hitting <leader>
:
nnoremap <leader><leader> <c-^>
Since my <leader>
is set to comma, I just have to hit ,,
to
alternate between files, for instance tests and implementation.
Split windows
You can split the current buffer horizontally or vertically. This is useful, for instance, to view simultaneously the top and the bottom part of the same file. But it’s even more useful to view different files in splits; for instance tests and implementation.
where | horizonal | vertical |
---|---|---|
normal mode | :split | :vsplit |
:CommandT | <C-s> | <C-v> |
:Ack | n/a | v |
To switch cursor between these windows, use <C-w> (as in “windowâ€) and then direction, where direction is one of: ↑k ↓j â†h →l. To ease this navigation, you can use the following mappings so you can omit the <C-w> prefix:
" easier navigation between split windows
nnoremap <c-j> <c-w>j
nnoremap <c-k> <c-w>k
nnoremap <c-h> <c-w>h
nnoremap <c-l> <c-w>l
For more information, see :help windows.
Further resources for learning
- :help – it’s more useful than it appears at first!
- Coming home to Vim
- VimCasts
- Destroy All Software – there are some episodes on Vim
- VimGolf – time-consuming but rewarding game
- Walking Without Crutches – presentation by Drew Neil
- My vim configuration and plugins
Thanks Gary Bernhardt, Drew Neil for tips and inspiration, and Tim Pope for all your work on plugins.