Vim Cheatsheet

Ex Commands and the Global Command

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

Ex Command Ranges

Every : command can take a range. Ranges are what turn one substitution into a project-wide edit.

RangeMeans
:5Line 5
:.The current line
:$The last line
:%The whole file (same as 1,$)
:5,20Lines 5 through 20
:.,+5Here and the next 5 lines
:-3,.The 3 lines above through here
:'a,'bFrom mark a to mark b
:'<,'>The last Visual selection
:/start/,/end/From the next "start" to the next "end"
:.,/end/Here to the next "end"
:0Above the first line (for :put and :r)

Moving and Copying Lines

CommandAction
:m 0Move this line to the top
:m $Move it to the bottom
:m +1Move it down one
:m -2Move it up one
:5,10m 20Move lines 5-10 to after line 20
:t . or :copy .Duplicate this line below
:1,5t $Copy lines 1-5 to the end
:'<,'>m '>+1Move the selection down one line
:dDelete this line
:5,10d aDelete lines 5-10 into register a
:yYank this line
:normal ddRun a Normal-mode command from Ex
:%normal A;Append a semicolon to every line

Global Command

:g runs an Ex command on every line matching a pattern. It is the single most powerful command in Vim.

:g/pattern/cmd        " run cmd on every matching line
:v/pattern/cmd        " run cmd on every NON-matching line
:g!/pattern/cmd       " same as :v
RecipeDoes
:g/TODO/dDelete every line containing TODO
:v/keep/dDelete every line that does not contain "keep"
:g/^$/dDelete every blank line
:g/^\s*$/dDelete every whitespace-only line
:g/pattern/normal ddSame as :g/pattern/d, the long way
:g/pattern/t $Copy every match to the end of the file
:g/pattern/m 0Move every match to the top, reversing order
:g/pattern/m $Move every match to the bottom, order preserved
:g/pattern/s/old/new/gSubstitute only on matching lines
:g/^/m 0Reverse the whole file
:g/pattern/normal @aRun macro a on every matching line
:g/pattern/-1dDelete the line above each match
:g/pattern/+1dDelete the line below each match
:g/{/,/}/dDelete every brace block
:g/pattern/#Print matching lines with numbers
:g/pattern/y ACollect every match into register a
" Collect every import line into a register, then paste them elsewhere
qaq                  " clear register a
:g/^import/y A       " append each match to a
:$put a              " dump them at the end

" Number only the lines inside a list
:g/^- /s/^- /\=line('.').'. '/

:v (invert) is underrated: "keep only the lines I care about" is usually shorter to express than "delete all the lines I do not".

Command-Line Mode Editing

KeyAction
Ctrl-r Ctrl-wInsert the word under the cursor
Ctrl-r Ctrl-aInsert the WORD under the cursor
Ctrl-r Ctrl-fInsert the filename under the cursor
Ctrl-r {reg}Insert a register
Ctrl-r =Insert an expression result
Ctrl-wDelete the previous word
Ctrl-uClear the line
Ctrl-b / Ctrl-eStart / end of the line
Up / DownHistory filtered by what you typed
Ctrl-p / Ctrl-nUnfiltered history
TabComplete
Ctrl-dList the completion candidates
Ctrl-fOpen the command-line window to edit history as a buffer
q:Same command-line window, from Normal mode
q/The search history window

The command-line window (q:) is a real buffer, so you can navigate your command history with normal motions, edit a long command comfortably, and press Enter to run it.

History and Repeat

:history         " command history
:history /       " search history
:history :       " Ex command history
@:               " repeat the last Ex command
@@               " repeat it again
:@a              " run register a as Ex commands
:normal! @a      " run register a as Normal-mode keys, ignoring mappings

Expressions

:echo line('.')          " current line number
:echo line('$')          " last line number
:echo col('.')           " current column
:echo expand('%')        " current filename, relative
:echo expand('%:p')      " absolute path
:echo expand('%:h')      " directory
:echo expand('%:t')      " tail (basename)
:echo expand('%:r')      " root (no extension)
:echo expand('%:e')      " extension
:echo getline('.')       " text of the current line
:echo getline(1,'$')     " every line as a list
:echo &filetype          " an option's value
:echo @a                 " a register's contents
:echo bufnr('%')         " current buffer number
:echo strftime('%F')     " today's date
:echo system('git branch --show-current')
:put =range(1,10)        " insert the numbers 1 to 10
:put =strftime('%F')     " insert today's date

Terminal Mode

:terminal              " open a shell in a buffer
:term ls -la           " run one command in a terminal buffer
:vert term             " in a vertical split
:term ++close cmd      " close the window when the command exits
KeyAction
Ctrl-\ Ctrl-nLeave Terminal mode for Normal mode
Ctrl-w NSame, in Vim
i or aReturn to Terminal mode
Ctrl-w :Run an Ex command from Terminal mode
Ctrl-w "{reg}Paste a register into the terminal
tnoremap <Esc> <C-\><C-n>     " make Esc leave the terminal

Shell Escapes

:!cmd            " run a shell command, show the output
:!!              " repeat the last shell command
:sh              " drop to a shell, `exit` to come back
Ctrl-z           " suspend Vim, `fg` to resume
:!%              " run the current file as a program
:!node %         " run the current file with node
:silent !cmd     " run without the press-Enter prompt
:!git add %      " stage the file you are editing

Ctrl-z then fg is the fastest round trip to a shell, since it reuses the shell that launched Vim instead of starting a new one.