Vim Cheatsheet

Configuration and Mappings

Use this Vim reference while you build software engineering projects, review code, or refresh the syntax you reach for most.

Where Config Lives

EditorFile
Vim~/.vimrc or ~/.vim/vimrc
Vim (plugins, etc.)~/.vim/
Neovim~/.config/nvim/init.lua or init.vim
Neovim (data)~/.local/share/nvim/
:echo $MYVIMRC        " path to the config Vim actually loaded
:e $MYVIMRC           " edit it
:source $MYVIMRC      " reload it without restarting
:scriptnames          " every file Vim has sourced, in order
:version              " build features and config paths

Setting Options

:set number              " turn a boolean option on
:set nonumber           " turn it off
:set number!            " toggle it
:set number?            " show the current value
:set tabstop=4          " set a value
:set tabstop&           " reset to the default
:setlocal number        " only for this buffer or window
:set all                " every option
:verbose set number?    " which file last set this option

Options Worth Knowing

OptionDoes
number / relativenumberAbsolute / relative line numbers
expandtabInsert spaces instead of tab characters
tabstopHow wide a tab character renders
shiftwidthHow much >> and autoindent move
softtabstopHow many spaces Tab inserts
autoindent / smartindentCarry indent to the next line
wrap / linebreakSoft wrap, breaking at word boundaries
textwidthHard wrap column for gq
colorcolumn=80Draw a ruler at column 80
ignorecase / smartcaseCase handling in search
hlsearch / incsearchHighlight and live-preview search
scrolloff=8Keep 8 lines of context above and below
sidescrolloffThe same, horizontally
hiddenAllow unsaved buffers in the background
undofilePersist undo history across sessions
swapfileWrite a .swp recovery file
clipboard=unnamedplusMake y and p use the system clipboard
mouse=aEnable the mouse
termguicolors24-bit color in the terminal
signcolumn=yesAlways reserve the sign gutter
updatetime=300Idle delay before CursorHold fires
wildmenu / wildmodeCommand-line completion menu
list / listcharsShow tabs, trailing spaces, and so on
spell / spelllangSpell checking
foldmethodmanual, indent, syntax, expr, marker
laststatus=2Always show the status line
showcmd / rulerPartial command and cursor position
backspace=indent,eol,startMake Backspace behave normally in Insert
splitright / splitbelowWhere new splits go

A Reasonable Starting vimrc

set nocompatible
syntax on
filetype plugin indent on

" Look
set number relativenumber
set cursorline
set scrolloff=8
set signcolumn=yes
set termguicolors
set laststatus=2
set showcmd
set colorcolumn=80

" Indentation
set expandtab
set tabstop=2 shiftwidth=2 softtabstop=2
set autoindent smartindent

" Search
set ignorecase smartcase
set hlsearch incsearch

" Files and history
set hidden
set undofile
set undodir=~/.vim/undo
set noswapfile
set nobackup
set autoread

" Behaviour
set backspace=indent,eol,start
set splitright splitbelow
set wildmenu
set wildmode=longest:full,full
set mouse=a
set clipboard=unnamedplus

" Leader
let mapleader = " "

Create the undo directory once with mkdir -p ~/.vim/undo, or Vim will complain on every write.

Mappings

nnoremap <key> <action>   " Normal mode, non-recursive
inoremap …                " Insert mode
vnoremap …                " Visual and Select
xnoremap …                " Visual only
cnoremap …                " Command line
tnoremap …                " Terminal mode
map / imap / vmap         " recursive versions: avoid these
noremap                   " Normal, Visual, Select, Operator-pending
<buffer>                  " scope the mapping to this buffer
<silent>                  " do not echo the command
<expr>                    " evaluate the right side as an expression

Always prefer the nore ("non-recursive") forms. A recursive map can loop or pick up a plugin's remapping and behave differently than you wrote it.

NotationKey
<CR>Enter
<Esc>Escape
<Tab> / <S-Tab>Tab / Shift-Tab
<Space>Space
<BS>Backspace
<leader>Whatever mapleader is set to
<localleader>maplocalleader, for filetype maps
<C-x>Ctrl-x
<S-x> / <M-x> / <A-x>Shift / Meta / Alt
<F5>Function key
<Up> <Down> <Left> <Right>Arrows
<nop>Do nothing (disable a key)
let mapleader = " "

nnoremap <leader>w :w<CR>
nnoremap <leader>q :q<CR>
nnoremap <leader>h :noh<CR>
nnoremap <leader>e :Explore<CR>

" Window navigation without the Ctrl-w prefix
nnoremap <C-h> <C-w>h
nnoremap <C-j> <C-w>j
nnoremap <C-k> <C-w>k
nnoremap <C-l> <C-w>l

" Keep the cursor centered while searching
nnoremap n nzzzv
nnoremap N Nzzzv

" Move the selected lines up and down
vnoremap J :m '>+1<CR>gv=gv
vnoremap K :m '<-2<CR>gv=gv

" Paste over a selection without losing the register
xnoremap <leader>p "_dP

" System clipboard
nnoremap <leader>y "+y
vnoremap <leader>y "+y

:map <leader>       " list every mapping under leader
:verbose nmap <C-h> " find out which file defined a mapping

Abbreviations

iabbrev teh the
iabbrev @@ me@example.com
iabbrev ssig -- <CR>Pablo
:abclear            " clear all abbreviations

Autocommands

Autocommands run a command when an event fires. Always wrap them in an augroup with autocmd! so re-sourcing your config does not register them twice.

augroup MyConfig
  autocmd!
  autocmd BufWritePre * %s/\s\+$//e          " strip trailing whitespace
  autocmd FileType python setlocal shiftwidth=4 tabstop=4
  autocmd FileType markdown setlocal wrap linebreak spell
  autocmd BufReadPost * if line("'\"") > 1 && line("'\"") <= line("$")
    \ | execute "normal! g`\"" | endif       " restore cursor position
  autocmd TextYankPost * silent! lua vim.highlight.on_yank()
augroup END
EventFires
BufRead / BufReadPostAfter reading a file into a buffer
BufWritePre / BufWritePostBefore / after writing
BufEnter / BufLeaveEntering / leaving a buffer
BufNewFileOpening a name that does not exist yet
FileTypeThe filetype has been detected
InsertEnter / InsertLeaveInsert mode boundaries
TextChangedThe buffer changed in Normal mode
CursorHoldThe cursor sat still for updatetime
VimEnter / VimLeaveStartup / shutdown
WinEnter / WinLeaveWindow focus
TermOpenA terminal buffer opened

Custom Commands and Functions

command! Reload source $MYVIMRC
command! -nargs=1 Grep execute 'vimgrep /<args>/ **/*'
command! -range Sortu <line1>,<line2>sort u

function! ToggleNumber()
  if &number
    set nonumber norelativenumber
  else
    set number relativenumber
  endif
endfunction
nnoremap <leader>n :call ToggleNumber()<CR>

User commands must start with a capital letter, and the ! after command means "redefine if it already exists", which you want when re-sourcing.