A commented collection of practices I adhere to when using Vim – my editor of choice since many years. Includes ~/.vimrc entries, certain keyboard commands, plugin suggestions and esoteric concluding words.

Install Vim

sudo aptitude install vim-gtk

This installs Vim compiled with GUI support ("Gvim"), which I rarely use, but it is very convenient from time to time. The vi command is where all the magic happens, though.

I won't mention :q and such things. Simply run vimtutor if you're looking for a beginner's guide to Vim.

Configuration

Below I source selected portions of my ~/.vimrc, and comment on them. A " in ~/.vimrc denotes an in-file comment.

set backup         " use backup files
set backupdir=~/.vim-backup    " collect backup files

This makes Vim use a backup file, named e.g. myfile.tex~ in the same directory as the edited file myfile.tex. It is not a functionality that is often needed, but it is a minimal performance penalty, and some day it might save my/your sanity.

I really don't like Vim leaving backup files scattered around the file system, though. backupdir collects all the files in the given directory. I don't see any reason to keep files around indefinitely (actually, I see a reason not to), which I solve with this crontab entry:

0 6 * * *   find ~/.vim-backup/ ~/.vim/undodir/ -mindepth 1 -mtime +7 -delete

Every day at 06:00, the backup folder (and the undo directory, more on that later) is cleansed of all files older than a week.

Back to ~/.vimrc:

set showcmd        " display incomplete commands

This shows a status text on the bottom line of Vim with the current command status. It e.g. shows the range of current visual selection, and it is not intrusive, so it's a Good Thing.

set incsearch      " do incremental searching

Direct partial matching on / searches. This is a time saver, but I guess it can be annoying at first if one isn't used to it. Get used to it :-) .

set number         " row numbering

Shows a column at the far-left with row numbers. Great, actually not only for programming(where it is a must), but also for regular text editing. One of its major perks is that it quickly gives you an indication of which lines span multiple screen lines.

set ignorecase     " ignore case when searching
set smartcase      " case sensitive when using capitals in search phrase

The first line makes search case insensitive, which is often what I want. The second line enables smartcase. Searching for e.g. "kb" will be case insensitive, but "kB" will be case sensitive. Since smartcase only kicks in when you mix in capitals, it is never noticed unless you want it, and thus not a problem to enable. I use it sometimes.

set cursorline     " highlight current line

At first I thought this would be annoying, but it turned out to be great, not least when searching. Try it and see if you like it. The usability probably depends a great deal on the color scheme used.

set completeopt=menuone    " show completion menu also for single hits

When using the Ctrl+n auto complete feature, this calls the word menu even if there is only a single match. This makes the command more consistent in my eyes, which is probably my primary gauge of a good UI.

colorscheme zenburn

Zenburn is a popular, low-contrast color scheme for Vim. I've been hooked for many years.

To make Zenburn look nice, you need a 256 color terminal. My choice is rxvt-unicode compiled with 256 color support. It is currently available in the rxvt-unicode-256color package in Debian. See Configuring and using rxvt-unicode for more information.

let g:zenburn_old_Visual = 1   " marked lines are lightened

This is a Zenburn specific configuration that makes marked lines lighten instead of darken. With the above mentioned cursorline configuration, I find this to be better.

In /usr/share/vim/vimcurrent/vimrc_example.vim, an example configuration file maintained by Bram Moolenaar, the Vim author, can be found. Look through it yourself. It is already commented, and I use its options more or less unmodified. View the file in its current state.

" An example for a vimrc file.
"
" Maintainer:    Bram Moolenaar <Bram@vim.org>
" Last change:    2011 Apr 15
"
" To use it, copy it to
"     for Unix and OS/2:  ~/.vimrc
"          for Amiga:  s:.vimrc
"  for MS-DOS and Win32:  $VIM\_vimrc
"        for OpenVMS:  sys$login:.vimrc

" When started as "evim", evim.vim will already have done these settings.
if v:progname =~? "evim"
  finish
endif

" Use Vim settings, rather than Vi settings (much better!).
" This must be first, because it changes other options as a side effect.
set nocompatible

" allow backspacing over everything in insert mode
set backspace=indent,eol,start

if has("vms")
  set nobackup         " do not keep a backup file, use versions instead
else
  set backup           " keep a backup file
endif
set history=50         " keep 50 lines of command line history
set ruler              " show the cursor position all the time
set showcmd            " display incomplete commands
set incsearch          " do incremental searching

" For Win32 GUI: remove 't' flag from 'guioptions': no tearoff menu entries
" let &guioptions = substitute(&guioptions, "t", "", "g")

" Don't use Ex mode, use Q for formatting
map Q gq

" CTRL-U in insert mode deletes a lot.  Use CTRL-G u to first break undo,
" so that you can undo CTRL-U after inserting a line break.
inoremap <C-U> <C-G>u<C-U>

" In many terminal emulators the mouse works just fine, thus enable it.
if has('mouse')
  set mouse=a
endif

" Switch syntax highlighting on, when the terminal has colors
" Also switch on highlighting the last used search pattern.
if &t_Co > 2 || has("gui_running")
  syntax on
  set hlsearch
endif

" Only do this part when compiled with support for autocommands.
if has("autocmd")

  " Enable file type detection.
  " Use the default filetype settings, so that mail gets 'tw' set to 72,
  " 'cindent' is on in C files, etc.
  " Also load indent files, to automatically do language-dependent indenting.
  filetype plugin indent on

  " Put these in an autocmd group, so that we can delete them easily.
  augroup vimrcEx
  au!

  " For all text files set 'textwidth' to 78 characters.
  autocmd FileType text setlocal textwidth=78

  " When editing a file, always jump to the last known cursor position.
  " Don't do it when the position is invalid or when inside an event handler
  " (happens when dropping a file on gvim).
  " Also don't do it when the mark is in the first line, that is the default
  " position when opening a file.
  autocmd BufReadPost *
    \ if line("'\"") > 1 && line("'\"") <= line("$") |
    \   exe "normal! g`\"" |
    \ endif

  augroup END

else

  set autoindent       " always set autoindenting on

endif " has("autocmd")

" Convenient command to see the difference between the current buffer and the
" file it was loaded from, thus the changes you made.
" Only define it when not defined already.
if !exists(":DiffOrig")
  command DiffOrig vert new | set bt=nofile | r ++edit # | 0d_ | diffthis
          \ | wincmd p | diffthis
endif

hide

nnoremap <silent> <F2> :TlistToggle<CR>

This makes the F2 button call a tag list, provided by the Tag list plugin.

" mapping to make movements operate on 1 screen line in wrap mode
" <http://stackoverflow.com/questions/4946421/vim-moving-with-hjkl-in-long-lines-screen-lines>
function! ScreenMovement(movement)
    if &wrap
        return "g" . a:movement
    else
        return a:movement
    endif
endfunction

map <silent> <expr> <C-Down> ScreenMovement("j")
map <silent> <expr> <C-Up> ScreenMovement("k")
map <silent> <expr> <C-Home> ScreenMovement("0")
map <silent> <expr> <C-End> ScreenMovement("$")

Follow the link in the commented section for more info. This provides some nice navigation shortcuts. Using the Ctrl modifier, it converts line movements to screen movements.

Now we enter the dark realm of key codes. To make modifier behaviour consistent across terminals, SSH, GNU Screen, tmux, etc., a lot of magic seems to be needed. I will show the code, but it contains a lot of unprintable characters (certainly in HTML), so it won't do much good more than to show the problems. Brace yourself.

" This fixes key codes in rxvt-unicode. I used to set this in ~/.Xdefaults, but
" that did not translate to GNU Screen. By placing it in this file it needs less
" total code.
map ^[Oa <C-Up>
map ^[Ob <C-Down>
map ^[Oc <C-Right>
map ^[Od <C-Left>
map ^[[5^ <C-PageUp>
map ^[[6^ <C-PageDown>
map ^[[7^ <C-Home>
map ^[[8^ <C-End>

Small comment: the ^[ combination must be entered pressing Ctrl+v and then e.g. the up arrow. Copy+paste will not suffice. This is what I meant with "unprintable characters".

" In insert mode, the below commands should be executed. <C-o> switches to
" command mode for a single command.
"
" If I could have written e.g.
" imap <C-Up> <C-o><C-Up>
" , then the code could have been substantially shortened, but it doesn't seem to
" work in GNU Screen or tmux.
"
" If mode() realiably could determine the current mode, all the logic could have
" been sorted in ScreenMovement()
imap ^[Oa <C-o><C-Up>
imap ^[Ob <C-o><C-Down>
imap ^[Oc <C-o><C-Right>
imap ^[Od <C-o><C-Left>
imap ^[[7^ <C-o><C-Home>
imap ^[[8^ <C-o><C-End>
map ^[[a <S-Up>
map ^[[b <S-Down>
map ^[[c <S-Right>
map ^[[d <S-Left>
" Mappings must be given separately for insert mode, even if they do the same
" thing. I don't know why.
imap ^[[a <S-Up>
imap ^[[b <S-Down>
imap ^[[c <S-Right>
imap ^[[d <S-Left>

" Fixes key codes in tmux.
if &term =~ "^screen"
    map ^[[1;5A <C-Up>
    map ^[[1;5B <C-Down>
    map ^[[1;5C <C-Right>
    map ^[[1;5D <C-Left>
    map ^[[5;5~ <C-PageUp>
    map ^[[6;5~ <C-PageDown>
    map ^[[1;5H <C-Home>
    map ^[[1;5F <C-End>
    imap ^[[1;5A <C-o><C-Up>
    imap ^[[1;5B <C-o><C-Down>
    imap ^[[1;5C <C-o><C-Right>
    imap ^[[1;5D <C-o><C-Left>
    imap ^[[1;5H <C-o><C-Home>
    imap ^[[1;5F <C-o><C-End>
    " Shift is only sent if "xterm-keys on" is set in tmux.
    map ^[[1;2A <S-Up>
    map ^[[1;2B <S-Down>
    map ^[[1;2C <S-Right>
    map ^[[1;2D <S-Left>
    imap ^[[1;2A <S-Up>
    imap ^[[1;2B <S-Down>
    imap ^[[1;2C <S-Right>
    imap ^[[1;2D <S-Left>
endif

hide

" Save with sudo
cmap w!! %!sudo tee > /dev/null %

If you start a Vim session as a regular user, but want to save the modified buffer as root, you usually have to save it as a temporary file and manually move it with root privileges. With the above mapping, you can save the buffer with sudo by the :w!! command. You will be asked for your password, and Vim will sometimes be a bit bugged out display-wise initially after the save, but it beats the :wq! tempfile<CR>sudo mv tempfile /etc/originalfile<CR>mypassword<CR> dance.

Another advantage with opening files as a regular user and only writing with sudo is that all of your user's configuration, plugins, color schemes, etc., are used during editing, instead of the root user's configuration which nowadays most probably is sparse. It could also be argued that it is a more secure work practice to edit the file as a regular user and only save files with root privileges.

" Persistent undo - new function in Vim 7.3.
" Create the directory manually.
set undodir=~/.vim/undodir
set undofile

This makes undo history persistent across editing sessions. You can undo things you did yesterday before shutting down and going home. This is very useful. It can be a double-edged sword if one is used to (as I was) being able to just keep u pressed to return the file to the state it was in when the current session started, but that is a lazy practice in itself. Persistent undo wins in functionality.

To not hoard undo files from yesteryear, I use the Cron entry mentioned earlier.

Practical keyboard usage

There are more shortcuts than one has time to master within a lifetime, but just a subset of these will make editing much more seamless and pleasant. Below I list some that perhaps are not common knowledge, but that I find very useful. They are not ranked in usability order.

Ctrl+o - go forward in jumplist in normal mode
Ctrl+i - go backwards in jumplist in normal mode

See :h jumplist for more info on the jumplist. This enables you to quickly jump back and forth between the positions you've been at in your editing session. I use this frequently to e.g. search after a specific term, do a small fix/lookup, and then Ctrl+o to jump back again. This beats having to remember the line number, or even setting temporary marks.

* - search term under cursor and go to next match
# - search term under cursor and go to previous match

Place the cursor on a word and press * to jump to the next occurrence of the word. This can of course be repeated when you land on the next match.

Ctrl+ws      - split window horizontally
Ctrl+wv      - split window vertically
Ctrl+ww      - switch between windows
Ctrl+wq      - close window
:sp filename - split window horizontally and open "filename" in the new buffer
:vs filename - split window vertically and open "filename" in the new buffer

Working with split windows is one of the essential features of Vim power usage, imho. The above commands should be enough to get going, but it takes a while before it becomes second nature to use splitting.

A related tip is the -O switch to the vi command, which will open the specified files in vertically split buffers. This is frequently used by me.

> - indent selection

Indentation is essential, and should be easy, and it is. Mark a section of text and press the fantastically intuitive >. Ctrl+t also works. < and Ctrl+d are their opposites (the latter only works when indentation is a consequence of auto-indentation in some form, not a literal tab character).

If it concerns just a single row, >> indents the current row without having to select it beforehand (and analogously <<).

Shift+v - visual line selection

This is simple, but most often what I want to do with visual selection. This gives line-wise selection, no more, no less. Useful.

"+yy - copy line to clipboard register
"+p  - paste from clipboard register

...and lots of combinations on the concept. The " to access different paste buffers is the key. Especially useful are the "magic" paste buffers +, * and so on. :reg shows you the content of the registers (:h registers for more info on registers). This is essential when pasting/copying to/from other applications, and makes Vim a lot more useful.

ci( - change contents within () delimiters
ci[ - change contents within [] delimiters
ci" - change contents within "" delimiters
...

This works with many different delimiters. Place the cursor in between, execute the command and see the contents disappear and place you in insert mode just inside the delimiters. Magic :-) .

vi scp://server/file - edit a file over scp
vi scp://server/     - open directory view of login directory at server

This is a nice functionality. The second line is obvious, but mentioned since its brilliance might be hidden in the light. In addition to not having to open a separate SSH shell, it also gives you the functionality of your local, customized Vim installation when dealing directly with remote files.

This is maximally useful with hosts where you have SSH keys and passwordless login set up. To make it even better, use aliases in ~/.ssh/config, to be able to use e.g. the literal commands above.

:r filename - reads file contents into the current buffer
:r!date     - read command output and insert as new line

I've used the first variant several times in this document already. It simply reads the contents of a file and "pastes" it into the current buffer at the cursor position.

The second is more exotic, but as cool as they come. It will run the command date (of course replaceable with arbitrary commands) and insert the output as a new line.

Somewhat related is the following construction:

!!sh         - run current line as command and replace it with the command output
!!date       - replace current line with output from the date command
!!tr -d AEIO - remove all occurences of A/E/I/O from current line
!tr -d AEIO  - remove all occurences of A/E/I/O from current selection

!! is powerful. The examples demonstrate different uses, and a lot more can easily be thought of. Especially the last two examples, using external tools to operate directly on lines:

  • grep can be used on a selection to only keep lines with the given search argument,
  • grep -v can do the opposite,
  • sort can sort the lines in the selection,
  • par, tac, sed, uniq, nl, ...

Note that the command does not start with :. Also note how the last variation operates on selections. See also :g/word/d and :v/word/d for the mentioned grep functionality (but grep can of course perform more elaborate tasks).

:sav - save as

This saves the buffer with a new filename, and switches to that file descriptor. It has happened that I have saved a file as new filename, continued to operate on it and mechanically given :wq to quit and thus overwritten the original file as well. This avoids that when saving as a new filename.

Ctrl+n - keyword completion

Start writing a word and press Ctrl+n to initiate keyword completion. All current buffers are searched through for possible matches that are listed in a menu.

J - move next line to end of current line, separated by a space character

Simple function, but usable. Merges next line with current, separated by space. That is all, but it saves a surprising amount of keystrokes.

d/word - delete until first occurrence of word

This is a protoype for chaining commands and movements. This can be done with all applicable commands and movements – and "Search" is a movement!

"Delete until" is one of the most useful ones, easily modifiable to "Change until". "Yank until", "Capitalize until", "Format until", etc.

gqap - format current paragraph to <textwidth> characters
gw   - format visual selection to <textwidth> characters
gww  - format current row to <textwidth> characters

This has so many variations that :h gq is the only way present them all. Formatting lines is useful. One can even specify custom formatting commands, not only "limit line to 78 characters".

g Ctrl+g - count columns/lines/words/bytes in current buffer or selection

A small but nice functionality.


That was a run-through of some hopefully not obvious commands. Spending some time on getting more efficient with Vim is time well spent (disclaimer: only in selected circles of humanity :-D ).

Plugins

I don't use many plugins, not least since Vim has so much functionality built in, but some are worth mentioning.

First, I can mention vim-addon-manager, which is how Debian manages addons in its package system. Install the addon manager and run vim-addons to list available plugins. man vim-addons gives instructions on installing and administrating addons. Using this to full extent is recommended, since it will (should) be painless and keep the plugins updated through the package system.

The NERD Commenter

The NERD Commenter is the one plugin that I really use. It gives convenient commands to "magically" comment portions of code in different contexts and filetypes. Commenting is done by <Leader>cc, uncommenting by <Leader>cu, where <Leader> is a specially defined Vim key defaulting to \ (see :h leader).

Calendar

I don't really use Calendar, but I like it in theory. It adds the :Calendar command, which splits the window and opens up a spiffy calendar. It is usually clearer to run cal in a terminal, though (or through :!cal if you want to stay with Vim).

Tag list

I don't really use the Tag list plugin either, but it is even more useful in theory. :TlistToggle (conveniently mapped to e.g. F2) opens up a tag list window which contains the filenames, functions, variables, etc., of all open buffers, and enables you to quickly jump to them. It could actually be essential, and probably is to many full-time programmers, but I haven't yet made it into a part of my workflow.

Vim-LaTeX

Vim-LaTeX perhaps isn't technically a "plugin", but similar enough. It gives some nice functionality for working with LaTeX documents, which I do a lot. It enables folding, some macros, syntax highlighting, compilation from within Vim and jumping directly to compilation errors in the code, backsearch through xdvi and more. In Debian it is available as the vim-latexsuite package.

Extras

My ~/.bash_profile contains the line:

export PAGER="sh -c \"col -b | view -c 'set ft=man nomod nolist nonumber titlestring=MANPAGE' -c 'map q :q<CR>' -\""

This makes Vim the man page (among others) viewer. q is set as a shortcut to :q<CR> and thus quits on its own, but apart from that it is Vim's standard color scheme, shortcuts, etc.

Conclusions

Vim is my friend, and text files are beautiful in their simplicity. By not letting formatting tools getting in the way, I find that the creation is able to run more freely, and thus text editing should not be limited to code.

To learn Vim, make sure to look up the "correct" way to do things continuously when you encounter new situations. You will most probably find yourself in the same situation many more times in the future when it will save some time.

The pure summed time savings aren't the main reason for efficient editing, though. I find that the most important advantage is uninterrupted editing, which never intrudes on your thought process. The editor is first and foremost a tool that should help you with your main task. Vim's power lies in that it more than any other editor enables you to come closer to the perfect workflow where you just think of a solution, and suddenly see it appear on screen. When the Vim shortcuts are set in muscle memory, this is more or less what you get.