Jan 04 2018

A Veterans Tutorial of Vim

vim tutorial screen


Many posts aimed at new Vim users tend to just paste a large .vimrc file and then explain a few plugins to try. Very little (if any) time is devoted to explaining what all those settings in .vimrc are actually doing.

I’d like to walk through my personal .vimrc file section by section and break down what and why I have the configuration settings I do. Given that my .vimrc has over 15 years’ worth of built-up cruft, this will also be a good exercise for me to clean it up a bit and update where needed as well.

Portable Vim

To be completely transparent, we won’t be really talking about the contents of my

file, because that file on my computer really only contains the following:

set runtimepath+=~/Dropbox/vim
source ~/Dropbox/vim/vimrc.vim

I like to have my whole Vim setup, plugins and all, available to me no matter what system I’m on, and using Dropbox allows me to keep all my computers in sync. Yes, setting the

and then sourcing the file will increase your startup time, but for me the slight time bump is worth it. So, the file we will be really looking at is my
file which acts like a normal

General Configuration

" Don't include vi compatibility
set nocompatible

Right off the bat, the first thing I do is turn off Vi compatibility. If you were never an ardent Vi user (not Vim, Vi) then just turn off Vi compatibility. Then I make the backspace key play a bit nicer on OS X too allow me to backspace over auto-indents, line breaks, and the start of inserts.

" os x backspace fix
set backspace=indent,eol,start
set modelines=0 " dont need modelines and the potential security hazard

Next I turn off modeline support, mostly because I don’t use modelines personally and I don’t want to open a file from someone else that could potentially harm my system by the usage of modelines. It’s just a risk not worth taking if you aren’t in the habit of using modelines for what they were intended for.

"set t_kb

Last up in this section I just fix the terminal code support so both my backspace and delete keys work as expected on OS X.

" Setup term color support
if $TERM == "xterm-256color" || $TERM == "screen-256color" || $COLORTERM == "gnome-terminal"
set t_Co=256

Next I turn on good color support for Vim terminal usage, and as a developer I turn on trailing whitespace support so I can see any trailing whitespace as I type it. We will revisit this setting again when I describe some settings that automatically strip trailing whitespace when a buffer is saved.

" show trailing whitespace chars
set list
set listchars=tab:>-,trail:.,extends:#,nbsp:.


set list
turns on the display of unprintable characters, and then
set listchars
enumerates which characters to display and which character to display them with.

" Better buffer management - hide buffers instead of closing them
set hidden
set history=100
" Default history is only 20
set undolevels=100 " Use more levels of undo

This next grouping relates to how Vim manages its buffers and history. I like Vim to hide my buffers instead of closing them completely. This allows me to keep modified buffers without being forced to save or abandon them. Next I have Vim remember 100 commands in history and set my undo level to 100 as well.

" tab -> spaces
set expandtab
set tabstop=2
" a tab is 2 spaces
set softtabstop=2 " tab size when insterting/pasting
set shiftwidth=2 " number of spaces to use for autoindenting
set shiftround
" use multiple of shiftwidth when indenting with '<' and '>'
set smarttab
" insert tabs on the start of a line according to shiftwidth, not tabstop

The prolific spaces vs. tags section is next up, and I’m firmly in the converting spaces to tabs camp, so my Vim configuration reflects that. The first line expands tabs to spaces, with tab spacing of 2.

sets the number of spaces used when autoindenting.

set autoindent
" always set autoindenting on
set copyindent
" copy the previous indentation on autoindenting

Finally we turn on auto-indentation and tell Vim to use the most recent indentation level when auto-indenting. These auto-indentation rules are just the defaults and will most-likely will be overridden by plugins you use for common programming languages.

" set foldenable
" fold by default
set nofoldenable
" dont fold by default
set foldmethod=indent " fold based on indentation
set foldnestmax=10
" deepest fold is 10 levels
set foldlevel=1

I prefer to not have my files open with folding turned on, but if you prefer, you can just uncomment

set foldenable
and remove
set nofoldenable
. I set my foldmethod to indent as this works well for just about all languages I code in, but other options include manual or custom syntax markers for fold deliniation. I tell Vim to only nest 10 folds deep (honestly probably 5 levels is too far in my opinion, but 10 is better than the default of 20).

" Ruler on
set ruler
" Line numbers on
set nu

I like to have line numbers turned on when editing, and also see which line and column I’m currently editing which is turned on by the ruler option.

" use ack for grepping
set grepprg=ack
set grepformat=%f:%l:%m

Also, I’m a user of Ack instead of grep so I tell Vim to use it instead of grep.

" dont use backup files
set nobackup
set noswapfile
set directory=~/.vim-tmp,~/.tmp,~/tmp,/var/tmp,/tmp " store swap files here

The next few lines are minor preferences, but pretty self-explanatory. I don’t have Vim make backup for swapfiles by default, but when it does, I set the directory hierarchy to use for creating those files.

syntax on
" Enable syntax highlighting
filetype on
" Enable filetype detection
filetype indent on " Enable filetype-specific indenting
filetype plugin on " Enable filetype-specific plugins

As a developer, I prefer to have syntax highlighting and I turn it on. I also turn on file type detection and file type specific indentation rules and plugin loading.

" Search Settings
set incsearch " show search matches as you type
set ignorecase " case insensitive search
set smartcase " If a capital letter is included in search, make it case-sensitive
set nohlsearch " dont highlight search results

Next I configure how I want Vim to behave when searching. I have Vim show me the search results incrementally, as I type my search query and perform case-insensitive searches by default unless I include a capital letter in my query. Finally, I prefer not to highlight search results by default.

colorscheme vividchalk
set background=dark

My color scheme settings come next and I use a dark background based on my primary usage of Vim from the terminal.

set scrolloff=2


setting is the number of lines of context to keep above and below the cursor when scrolling to the very top and bottom of the screen. I prefer to have a few lines of context visible always instead of having my cursor be at the very top or bottom of the screen.

set shortmess=atI

Customizing the

variable is probably too much to cover here, but is worth a read if you have the time (use
:help shortmess
), but know that it is just configuring what and how I want alert and system messages to be displayed.

set visualbell " don't beep
set noerrorbells " don't beep

I don’t like Vim beeping at me so I turn on the

and I don’t want any error bells triggered at all.

" Auto read when a file is changed on disk
set autoread


tells Vim to automatically read a file in memory again if it has been detected to have changed from outside of Vim.

" status line
set laststatus=2

Lastly, I set

to tell Vim to always show the status line (a value of 0 means never and 1 means only if there are at least 2 windows).

Plugin Management

" Use pathogen to easily modify the runtime path to include all
" plugins under the ~/.vim/bundle directory
execute pathogen#infect()

I use Pathogen as my Vim plugin manager. It seems most of the cool kids are now using Vundle which provides some nice, useful additions, but I’ve been on Pathogen for a long time and haven’t had the need to migrate yet. This command just initializes pathogen which loads all my plugins.

Key Mappings

To begin the discussion of keymapping, let’s take our first sidebar and remember that Vim is a modal editor and what this means for keymapping. While you will mostly only be concerned with mapping keys in the default, normal, and visual modes, here is the full list of modes and their corresponding mode symbol for mapping commands:

* n: normal only
* v: visual and select
* o: operator-pending
* x: visual only
* s: select only
* i: insert
* c: command-line
* l: insert, command-line, regexp-search (and others. Collectively called "Lang-Arg" pseudo-mode)

These mode symbols can be used as prefixes on the two mapping commands of

. Using
will map keys recursively while
is a non-recursive mapping.

Now, back to my


" space = pagedown, - = pageup
noremap <Space> <PageDown>
noremap - <PageUp>

The next major grouping of my

is my custom key mappings. I begin by mapping the
spacebar to send a page down command, and the
minus key to send a page up command. I sometimes find this easier than the default of

" make j and k act normally for wrapped lines
nnoremap j gj
nnoremap k gk
" remap ; to : so you can just do `;w` instead of <Shift-; w> nnoremap ; :

Next I make the normal up/down movement keys of j and k perform the same for wrapped lines as unwrapped lines, and I remap the semicolon key to send the colon key command. Since colon is used for entering Vim commands, and only 3 people on earth seem to know what the default semicolon key does in Vim, we shorten this up to a single keystroke and just use


(As an aside, to impress your friends, the default command for semicolon is to repeat the most recent f, t, F, or T command).

" make regexp search not suck by default -
nnoremap / /v
vnoremap / /v

Also, I make Vim’s regular expression pattern matching work a bit more sanely by default. This maps the search command of

to use
by default in both normal and visual modes, which allows you to use a more familiar regular expression syntax instead of the craziness that is Vims regexp syntax.

" remap'd keys
map <c-j> <c-w>j
map <c-k> <c-w>k
map <c-l> <c-w>l
map <c-h> <c-w>h
map <Tab><Tab> <C-W>w

Here I begin to remap some movement keys just to save keystrokes. I map

directly to the standard window movement commands so I can save a keystroke and not have to type
Ctrl-w j
. Same for k, l and h. I also map window hopping to a double press of the
key so I can quickly cycle through windows with a single key.

nnoremap <F2><F2> :vsplit<CR>
nnoremap <F3><F3> <C-W>w
nnoremap <F4><F4> :set invwrap wrap?<CR> " use f4f4 to toggle wordwrap
nnoremap <F5><F5> :set invhls hls?<CR> " use f5f5 to toggle search hilight
map <F7> :b#<CR>

For creating new windows I map a double press of

to vertically split my current window and then also map a double press of
to cycle again, just like my double
command does. Hey, there is no law against having two ways to do the same thing :). I remap double
to toggle word-wrapping on and off, and double
to toggle search result highlighting on and off.

" allow saving a sudo file if forgot to open as sudo
cmap w!! w !sudo tee % >/dev/null

Next come mappings for a command mode only way for me to save files that require root access, but which I forgot to open with the proper permissions.

" toggle paste mode
nmap <leader>o :set paste!<CR>
" toggle NERDTree drawer
map <leader>d <plug>NERDTreeTabsToggle<CR>

After that is way to quickly toggle paste mode on and off, which is very useful for pasting code snippets from GUI editors or found on the web, followed by a way for me to quickly toggle open the project drawer from the NERDTree plugin.

" remove trailing whitespace
nnoremap <leader>W :%s/\s\+$//<cr>:let @/=''<CR>

Lastly I map a quick command I can run to remove trailing whitespace on demand by typing

key is set to backslash).

Custom Functions

Vim allows for the usage of custom-defined functions through Vimscript and I have one defined for the scratch.vim plugin I use.

" toggle scratch buffer (scratch.vim plugin)
function! ToggleScratch()
if expand('%') == g:ScratchBufferName
map <leader>s :call ToggleScratch()<CR>

I define a function called

and then map that to
for easy usage. This opens my Scratch buffer if not open in a current window.

Auto Commands

Auto commands in Vim allow you to have certain commands automatically execute based on events. Events can be anything from file type detection to reading a buffer.

" auto remove whitespace on buffer save
autocmd! BufWrite * mark ' | silent! %s/\s\+$// | norm ''

My first few auto commands are simple enough. I have a command set on all filetypes when the buffer is saved to remove any trailing whitespace.

" Turn on spell check for certain filetypes automatically
autocmd BufRead,BufNewFile *.md setlocal spell spelllang=en_us
autocmd BufRead,BufNewFile *.txt setlocal spell spelllang=en_us
autocmd FileType gitcommit setlocal spell spelllang=en_us

Next I turn on spell check support for markdown and text files as well as my gitcommit messages to hopefully save myself any potential typo embarrassment.

" Autowrap text to 80 chars for certain filetypes
autocmd BufRead,BufNewFile *.md setlocal textwidth=80
autocmd BufRead,BufNewFile *.txt setlocal textwidth=80
autocmd FileType gitcommit setlocal textwidth=80

Also for markdown, text and gitcommit files I set the max text width to 80 characters on buffer read and new buffers.

Plugin Specific Options

The last section of my

contains customizations to the various plugins I use. Reading the help documentation for any plugin you use should provide documentation on the various settings you can configure to customize the plugin.

" gist-vim config
let g:gist_clip_command = 'pbcopy'
let g:gist_detect_filetype = 1
let g:gist_open_browser_after_post = 1
let g:gist_post_private = 1 " make gists private by default

In my case, I set some basic customizations to the gist.vim plugin to make my gists private by default and open the gist in my browser after creation.

" CtrlP Config
let g:ctrlp_map = '<leader>t'
let g:ctrlp_working_path_mode = 'ra'
let g:ctrlp_match_window_bottom = 0
let g:ctrlp_match_window_reversed = 0
let g:ctrlp_custom_ignore = '\v\~$|\.(o|swp|pyc|wav|mp3|ogg|blend)$|(^|[/\\])\.(hg|git|bzr)($|[/\\])|__init__\.py'
let g:ctrlp_working_path_mode = 0
let g:ctrlp_dotfiles = 0
let g:ctrlp_switch_buffer = 0

I use ctrlp.vim as my fuzzy file launcher and configure it mostly to tell it which files and directories to ignore and to map launching it to my


" Vimux config
let g:vroom_use_vimux = 1

Lastly I configure vim-vroom to play nice with vimux to get some nice interactions between tmux and Vim.

— Craig (@cpjolicoeur)

Craig P Jolicoeur