Getting Started
Modes & Basic Movement
Vim is modal — different modes serve different purposes. Esc returns to Normal mode from any other mode. The hjkl keys keep your hands on the home row. w/b move by word, 0/$ move to line boundaries, gg/G jump to file start/end, :N jumps to line N.
# Vim Modes
# Normal mode (Esc) - default, for navigation
# Insert mode (i) - for typing text
# Visual mode (v) - for selecting text
# Command mode (:) - for Ex commands
# Basic movement (Normal mode)
h # left
j # down
k # up
l # right
w # next word start
b # previous word start
0 # beginning of line
$ # end of line
gg # first line
G # last line
:42 # go to line 42Exiting & Saving
ZZ and ZQ are the fastest ways to exit — they don't require reaching for the colon. :x only writes if the file has changed (preserves mtime), unlike :wq which always writes. :w! overrides read-only flags if filesystem permissions allow. Use :qa! to bail out of multiple modified buffers.
:w # write (save) file
:w! # force write (override read-only)
:q # quit
:q! # force quit (discard changes)
:wq # write and quit
:x # write only if changed, then quit
ZZ # same as :x (write if changed, quit)
ZQ # quit without saving (like :q!)
:w newfile.txt # save as new file
:sav newfile.txt # save as new file and switch to it
:wa # write all buffers
:xa # write all changed buffers and exit
:qa! # force quit all (discard everything)Help System
Vim's help is comprehensive and hyperlinked. Ctrl-] follows a tag (link) and Ctrl-T pops back. Help notation: i_CTRL-N means Ctrl-N in insert mode, 'number' (with quotes) means an option. :helpgrep searches all help files at once — use :cn/:cp to navigate matches. K invokes man for the keyword under the cursor (configurable via 'keywordprg').
:help # main help
:help subject # help on subject (e.g. :help insert)
:help i_CTRL-N # help on Ctrl-N in insert mode
:help 'number' # help on the 'number' option (quotes)
:helpgrep pattern # search all help files for pattern
:cn / :cp # next/prev helpgrep match
K # man page for word under cursor (Normal mode)
Ctrl-] # jump to tag under cursor (follow link)
Ctrl-T # jump back from tag (pop tag stack)
:helptags ~/.vim/doc # regenerate help tags for a doc directoryWord & Character Movement
Lowercase motions (w/b/e) treat punctuation as word separators — useful for code. Uppercase motions (W/B/E) only treat whitespace as separators — useful for prose. e/E move to the end of the next word; ge/gE move to the end of the previous word (rarely used but handy).
# Word motions (punctuation-aware)
w # next word start
b # previous word start
e # end of next word
ge # end of previous word
# WORD motions (whitespace-only delimiters, uppercase)
W # next WORD start
B # previous WORD start
E # end of next WORD
gE # end of previous WORD
# Word boundaries:
# word: sequence of word-characters OR sequence of punctuation
# WORD: anything separated by whitespace
# Examples (cursor on 'a' in "foo.bar baz"):
# w -> 'bar' (punctuation splits words)
# W -> 'baz' (only whitespace splits WORDS)Line Movement
0 and $ go to absolute line start/end (including whitespace). ^ and g_ skip leading/trailing whitespace — usually what you want. When 'wrap' is on, g0/g$ operate on screen lines while 0/$ operate on logical lines. | jumps to a specific column number.
0 # first column (absolute start)
^ # first non-blank character
$ # end of line (last character)
g_ # last non-blank character
g0 # start of screen line (with wrap)
g$ # end of screen line (with wrap)
gm # middle of screen line
gM # middle of text line
| # column N (e.g. 10| = column 10)
+ # first non-blank of next line
- # first non-blank of previous line
_ # first non-blank of current line [count down]File & Screen Movement
H/M/L jump within the visible screen. Ctrl-d/u scroll half a page (less disorienting than full-page Ctrl-f/b). zz/zt/zb scroll the current line to the middle/top/bottom of the screen without moving the cursor — extremely useful for keeping context while editing. N% jumps to a percentage of the file.
gg # go to first line
G # go to last line
Ngg / NG # go to line N (e.g. 50gg or 50G)
:N # go to line N (Ex command)
H # top of screen (High)
M # middle of screen
L # bottom of screen (Low)
N% # jump to N% of the file
Ctrl-f # scroll forward (full page)
Ctrl-b # scroll backward (full page)
Ctrl-d # scroll down (half page)
Ctrl-u # scroll up (half page)
zz # scroll current line to middle
zt # scroll current line to top
zb # scroll current line to bottomInserting Text
Basic Insert (i, I)
i/I insert before the cursor / at line start. a/A insert after the cursor / at line end. gi returns to the last insert position (mark '^) — handy when you Esc to do something then want to keep typing. gI goes to the absolute column 1, ignoring leading whitespace.
i # insert before cursor
I # insert at first non-blank (same as ^i)
gi # insert at last insert position (also sets mark)
gI # insert at column 1 (same as 0i)
a # insert after cursor
A # insert at end of line (same as $a)
# Examples:
# foo|bar + i -> foo|bar (cursor stays, type before)
# foo|bar + I -> |foobar (cursor at first non-blank)
# foo|bar + A -> foobar| (cursor at end)
# gi returns to where you last left insert mode —
# very useful after escaping to run a Normal-mode command.Open New Lines (o, O)
o and O open a new line below/above the cursor and enter insert mode. They're the fastest way to add a line — no need to position the cursor at line end. Indentation is auto-copied from the surrounding lines (controlled by 'autoindent').
o # open new line BELOW cursor, enter insert
O # open new line ABOVE cursor, enter insert
# Before (cursor on middle line):
# line one
# li|ne two
# line three
# After 'o':
# line one
# line two
# | <- new blank line, insert mode
# line three
# After 'O':
# line one
# | <- new blank line, insert mode
# line two
# line threeChange Commands (c, C, s, S)
c{motion} deletes the motion target and enters insert mode — the most flexible edit command. C is c$ (change to end of line), S is cc (change whole line). s deletes one character and enters insert; combine with counts like 3s. ciw/caw use text objects (see dedicated section).
cw / ce # change to end of word
cb # change backward to word start
c$ / C # change to end of line
cc / S # change entire line
c0 # change to beginning of line
c^ # change to first non-blank
ct, # change up to (but not including) next ','
cfx # change up to and including next 'x'
ciw # change inner word (no surrounding whitespace)
caw # change a word (with surrounding whitespace)
cap # change around paragraph
s # substitute: delete char under cursor, insert
S # substitute: delete entire line, insert (like cc)Replace Mode (r, R, gr)
r replaces a single character and returns to Normal mode immediately (no mode change). R enters Replace mode where typing overwrites characters — Backspace restores them, unlike Insert mode which just deletes. Use gr/gR (virtual replace) when working with tabs — it preserves tab alignment instead of shifting text.
r{x} # replace single char under cursor with x
gr{x} # virtual replace (doesn't shift tabs)
R # enter Replace mode (overwrite until Esc)
gR # virtual Replace mode (tabs stay aligned)
3r{x} # replace 3 chars with x (single char only)
# In Replace mode, typing overwrites existing text
# but Backspace restores the original characters.
# Tabs are preserved with virtual replace (gr/gR).
# Examples:
# abc|def + rx -> abc|xef (cursor advances)
# abc|def + R -> enter Replace mode
# type 'XYZ' -> abcXYZ|f
# Backspace -> abcXY|def (original restored)Special Inserts
:r !cmd inserts shell command output into the buffer — great for pasting command output, dates, file lists. In insert mode, Ctrl-r followed by a register name inserts that register; Ctrl-r = evaluates a Vimscript expression and inserts the result. Ctrl-k inserts a digraph (special character) by typing two ASCII chars.
:r file.txt # insert contents of file below cursor
:r !command # insert output of shell command
:0r !date # insert date at top of file
:r !ls -la # insert directory listing
# Insert with special registers
Ctrl-r = # expression register: evaluate, insert result
Ctrl-r " # insert unnamed register
Ctrl-r % # insert current filename
# Insert from current file path
:i! !pwd # insert pwd output (no autoindent)
# Non-ASCII / digraphs (in insert mode)
Ctrl-k {ch1}{ch2} # digraph: e.g. Ctrl-k e' produces é
:digraphs # list available digraphsInsert Mode Shortcuts
Ctrl-h/w/u work like Backspace/Delete-word/Delete-line within insert mode. Ctrl-t/Ctrl-d adjust indentation without leaving insert mode — invaluable for code. Ctrl-n/Ctrl-p do keyword completion from buffers and tags. Ctrl-o runs a single Normal command then returns to insert (e.g. Ctrl-o zz to recenter). Ctrl-g u breaks the undo chain so a long insert can be undone in pieces.
# In insert mode:
Ctrl-h # delete char before cursor (Backspace)
Ctrl-w # delete word before cursor
Ctrl-u # delete to start of line
Ctrl-t # indent current line (one shiftwidth)
Ctrl-d # dedent current line (one shiftwidth)
Ctrl-n # keyword completion (forward)
Ctrl-p # keyword completion (backward)
Ctrl-o # execute one Normal command, return to insert
Ctrl-r {reg} # insert register contents
Ctrl-a # insert text typed in last insert mode
Ctrl-@ # insert last text + Esc (like Ctrl-a then Esc)
Ctrl-g u # break undo sequence (start new undo block)
Ctrl-] # trigger abbreviation without insertingEditing Commands (Delete/Yank/Paste)
Delete Commands
d{motion} is the universal delete operator — combine with any motion. dd deletes the whole line. x is a shortcut for dl (delete char). Deleted text goes into the unnamed register (and register 1). Use d with text objects (ciw, dib) for powerful edits. J joins the next line onto the current one with a single space.
x # delete char under cursor (also 'dl')
X # delete char before cursor (also 'dh')
dd # delete current line
dw # delete to next word start
de # delete to end of word
d$ / D # delete to end of line
d0 # delete to beginning of line
d^ # delete to first non-blank
dgg # delete from cursor to first line
dG # delete from cursor to last line
J # join next line (removes leading whitespace, inserts space)
gJ # join next line without inserting space
3dd # delete 3 lines
dW # delete WORD (whitespace-separated)Yank (Copy)
y{motion} copies text without deleting. yy yanks the line. Unlike d (which has x as a shortcut), y has no single-character shortcut — you must use a motion. Yank always updates register 0, useful for distinguishing from deletes. Use text objects: ya( yanks around parens, yi" yanks inside quotes.
y # yank {motion} (no character shortcut)
yy / Y # yank current line
yw # yank to next word start
ye # yank to end of word
y$ # yank to end of line
y0 # yank to beginning of line
yG # yank to end of file
ygg # yank to beginning of file
5yy # yank 5 lines
ya( # yank around parentheses
yi" # yank inside double quotes
yt, # yank up to (not including) next comma
# Yanked text goes to:
# " (unnamed register)
# 0 (yank register, only updated by y)Put (Paste)
p pastes after the cursor (or below the line for linewise data); P pastes before. gp/gP move the cursor to the end of the pasted text — useful when chaining operations. ]p/[p reindent pasted lines to match surroundings. The clipboard register (+) shares with the system clipboard if 'clipboard' is set properly.
p # paste after cursor (linewise: line below)
P # paste before cursor (linewise: line above)
gp # paste after, leave cursor on last pasted char
gP # paste before, leave cursor on last pasted char
]p # paste after, reindent to match
[p # paste before, reindent to match
"+p # paste from system clipboard
"*p # paste from primary selection (X11)
# Linewise vs charwise:
# yy + p -> pasted as a new line below
# y$ + p -> pasted after cursor on same line
# Counts: 3p pastes the register 3 times.Changing Case
~ toggles a single character and advances. g~/gu/gU are operators that work with motions or text objects. In visual mode, U/u/~ act on the selection. Use gUiw to uppercase the current word, gUU for the whole line. These are non-destructive — only case changes.
~ # toggle case of char under cursor (advances)
3~ # toggle case of next 3 chars
g~{motion} # toggle case of motion
gu{motion} # lowercase motion
gU{motion} # uppercase motion
g~~ # toggle case of current line
guu # lowercase current line
gUU # uppercase current line
g~~gg # toggle case to first line
# Visual mode:
# U # uppercase selection
# u # lowercase selection
# ~ # toggle case of selection
# Examples:
# hello + gUw -> HELLO
# HELLO + guw -> hello
# hello + g~w -> HELLOJoining & Splitting Lines
J joins lines with a single space; gJ joins without inserting anything. :join works on a range. gq is the format operator — it wraps lines to 'textwidth' (commonly 80), respecting 'formatoptions'. Use gqip to reflow a paragraph, gqq for one line. Set 'textwidth=80' to enable auto-wrapping.
J # join next line with current (inserts space)
gJ # join next line without inserting space
3J # join next 3 lines into one
:[range]join # join lines in range (also :j)
:[range]join! # join without inserting space
# Splitting a line:
# In insert mode, press Enter (autoindent applies)
# In normal mode: r<Enter> replaces char with newline
# Break a long line at column 80 (with 'textwidth'):
gq{motion} # format motion text to 'textwidth'
gqq # format current line
gqip # format paragraph
gvgq # format visual selection
# Set text width:
:set textwidth=80Repeat & Undo
. repeats the last change — the single most useful key in Vim. u/Ctrl-r are undo/redo. U undoes all changes on the current line. :earlier/:later can time-travel by time or count, even across branches (Vim keeps an undo tree). Use :undolist to see branches. @: repeats the last Ex command, & repeats the last substitute.
. # repeat last change (most powerful key in Vim)
u # undo
Ctrl-r # redo
U # undo all changes on current line
:Nu # undo N changes
:earlier 10m # revert to state 10 minutes ago
:later 5 # redo 5 changes
:undolist # show undo tree branches
# Repeat with counts:
5. # repeat last change 5 times
# Repeat Ex commands:
@: # repeat last :command
& # repeat last :s (substitute)
# Repeat macros/marks across files:
:& # repeat last substitute on current lineSearch & Substitute
Basic Search
/ and ? search forward/backward; n/N repeat. * searches for the whole word under the cursor (word boundaries), g* searches for any substring match. :noh clears current highlighting until the next search. Set 'hlsearch' to highlight all matches persistently, 'incsearch' for incremental matching as you type.
/pattern # search forward for pattern
?pattern # search backward
n # repeat search (same direction)
N # repeat search (opposite direction)
* # search forward for word under cursor
# # search backward for word under cursor
g* # search forward (loose, matches substring)
g# # search backward (loose)
# Search history:
# Press / then Up/Down to recall previous searches
# Clear search highlighting:
:nohlsearch # or :noh
# Toggle 'hlsearch' permanently:
:set hlsearch!Search Options
'ignorecase' + 'smartcase' is the recommended combo: case-insensitive unless your pattern contains an uppercase letter. \c/\C override case per-search. \v enables 'very magic' mode where many characters become special without escaping — cleaner regex syntax. Search offsets (e, b) place the cursor relative to the match.
:set ignorecase # case-insensitive search (ic)
:set smartcase # case-sensitive if pattern has uppercase
:set hlsearch # highlight all matches (hls)
:set incsearch # show match as you type (is)
:set wrapscan # wrap around end of file (ws)
:set gdefault # add 'g' flag to all :s by default
# Per-search case override:
/foo\c # case-insensitive search for foo
/foo\C # case-sensitive search for foo
# Use very-magic mode (less escaping):
/foo\v(.+)@<=(bar) # very magic regex
# Search offsets:
/foo/e # cursor on last char of match
/foo/e+2 # cursor 2 chars past match
/foo/b-1 # cursor 1 char before matchSearch Patterns (Regex)
Vim regex differs from PCRE — backslashes are needed for +, =, {, (, |. Use \v prefix for 'very magic' mode where these become special without escaping (closer to PCRE). \< \> mark word boundaries. \d/\w/\s are character classes. Use \(...\) for capturing groups, \1/\2 for backreferences.
# Standard Vim regex (default magic mode)
. # any char
* # zero or more (preceding atom)
\+ # one or more
\= # zero or one (optional)
\{n,m} # between n and m
^ $ # start/end of line
\< \> # word boundaries
[abc] # char class (a, b, or c)
[^abc] # negated char class
\( \) # group
\1 \2 # backreference
\| # or
# Predefined classes:
\d \D # digit / non-digit
\w \W # word char / non-word
\s \S # whitespace / non-whitespace
# Very magic (\v prefix) — less escaping:
/\v\w+\s+\w+ # word, spaces, word (no backslashes needed)
/\v(%d+) # capture one-or-more digitsSubstitute Command
:[range]s/pattern/replacement/flags is the substitute command. :% means the whole file. Without 'g' flag, only the first match per line is replaced; 'g' replaces all. 'c' asks for confirmation (y/n/a/q/l). Use \v for very-magic to simplify grouping syntax. Capture groups are referenced as \1, \2 in the replacement.
:[range]s/old/new/[flags]
# Basic:
:s/old/new/ # replace first 'old' on current line
:s/old/new/g # replace all 'old' on current line
:%s/old/new/g # replace all in whole file
:%s/old/new/gc # confirm each replacement
:%s/old/new/gi # case-insensitive
# Range examples:
:5,10s/old/new/g # lines 5-10
:.,$s/old/new/g # current line to end
:'a,'bs/old/new/g # from mark a to mark b
:'<,'>s/old/new/g # visual selection
:1,$s/old/new/g # whole file (same as %)
# Replace with capture groups:
:%s/\(\w\+\),\(\w\+)/\2,\1/ # swap two comma-separated words
:%s/\v(\w+),(\w+)/\2,\1/ # same with very-magicSubstitute Flags & Special Replacement
Common flags: g (all), c (confirm), i (case-insensitive), n (count only). & in the replacement inserts the matched text. \u/\U/\l/\L modify case of the replacement. Use \r to insert a newline (\n is a null byte in replacement). The 'n' flag is great for counting matches without changing anything.
# Flags:
g # all occurrences on each line (not just first)
c # confirm each substitution
i # ignore case
I # don't ignore case
e # don't error if no match
p # print last substituted line
n # count matches, don't substitute
# Special chars in replacement:
& # the matched text
\0 # the matched text (same as &)
\1..\9 # capture groups
\u # next char uppercase
\U # rest of string uppercase (until \e or \E)
\l \L # lowercase (single / until \e)
\r # split into new line
\n # null byte (use \r for newline)
# Examples:
:%s/foo/&bar/g # foo -> foobar
:%s/\v(\w+)/\U\1/g # uppercase every word
:%s/, /,\r/g # split lines on ', '
:%s/old/new/gn # count matches onlyGlobal Command (:g)
:g runs an Ex command on every line matching a pattern — a powerful 'global' editor. :v (or :g!) runs on non-matching lines. Combine with any Ex command: d (delete), m (move), t (copy), s (substitute), normal (run normal-mode command). Useful for batch operations on tagged lines. Order matters: :g processes top-to-bottom by default.