Vim
- Description: Vim modal editor — modes, motions, operators, text objects, search/substitute, windows/tabs, buffers, registers, marks, ex commands, configuration
- My Notion Note ID: K2B-1-3
- Created: 2023-04-10
- Updated: 2026-05-15
- License: Reuse is very welcome. Please credit Yu Zhang and link back to the original on yuzhang.io
Table of Contents
- 1. Overview
- 2. Modes
- 3. Motions
- 4. Operators and Text Objects
- 5. Editing Essentials
- 6. Search and Substitute
- 7. Windows, Tabs, Buffers
- 8. Registers and Marks
- 9. Ex Commands and Ranges
- 10. Configuration
- 11. Pitfalls
- 12. References
1. Overview
- Origin. Bram Moolenaar, 1991. Clone of Bill Joy's
vi(exwork started 1976; first shipped asviin 2BSD, 1979). - Name. 1991 release "Vi IMitation" → renamed "Vi IMproved" in 1992 (v1.22) once feature set surpassed original.
- License. Charityware — donate to ICCF Holland (children in Uganda).
- Maintainer. Bram passed 2023 → Christian Brabandt + contributors, github.com/vim/vim.
- Ubiquity. Every Unix-like system (BSD, Linux, macOS). POSIX mandates
vi;vim(orvim.tiny) satisfies it. Core motions portable across every SSH'd server. - Modality. Keystrokes mean different things per mode — Normal
d= delete operator, Insertd= letter d. Trades discoverability for keystroke efficiency → edits become composable phrases (diw= "delete inner word") instead of mouse selections. - Neovim. Fork since 2014. Lua scripting, async jobs, built-in LSP. ~99% config-compatible — everything here applies to both unless noted.
2. Modes
:help vim-modes lists seven basic modes. Mode transitions are the first thing to internalize.
| Mode | Enter From Normal | Purpose |
|---|---|---|
| Normal | <Esc> from any mode |
Navigation, operators, the default |
| Insert | i, a, o, I, A, O, s, S, c{motion} |
Type text |
| Visual (char) | v |
Select characters |
| Visual (line) | V |
Select whole lines |
| Visual (block) | <C-v> |
Rectangular selection |
| Select | gh (or mouse drag with :set selectmode=mouse) |
Visual mode that replaces selection on any printable key — used by snippet plugins, rare otherwise |
| Command-line | :, /, ? |
Ex commands and search |
| Ex | Q, gQ |
Multi-line ex prompt (:visual to leave) |
| Replace | R |
Overwrite text |
| Terminal-Job | :terminal (Neovim, Vim 8+) |
Embedded shell |
The three Visual variants are sub-flavours of one mode; counting Visual once gives the canonical seven (Normal, Visual, Select, Insert, Command-line, Ex, Terminal-Job).
- Return to Normal.
<Esc>or<C-[>(canonical, easier reach). - Why modal. Alphabet stays free as commands. Modeless editors (Emacs, VS Code) burn
Ctrl/Alt/Cmdon every action; Vim reserves them for orthogonal axes (windows, completion). Cost: learning curve. Payoff: common edits collapse to 2-3 keystrokes.
3. Motions
Motions move the cursor. Count-prefixable: 5j → down 5 lines; 3w → forward 3 words.
3.1 Character and Line Motions
| Keys | Action |
|---|---|
h j k l |
Left, down, up, right |
0 |
Start of line (column 0) |
^ |
First non-blank character |
$ |
End of line |
g_ |
Last non-blank character |
gg |
First line of file |
G |
Last line of file |
{count}G |
Go to line {count} |
{count}gg |
Same as above |
3.2 Word Motions
| Keys | Action |
|---|---|
w / W |
Next word / WORD start |
b / B |
Previous word / WORD start |
e / E |
End of word / WORD |
ge / gE |
End of previous word / WORD |
Lowercase splits on punctuation; uppercase WORD treats whitespace as the only separator. foo.bar = two words, one WORD.
3.3 Paragraph, Sentence, Section
| Keys | Action |
|---|---|
{ / } |
Previous / next blank-line-separated paragraph |
( / ) |
Previous / next sentence |
[[ / ]] |
Previous / next section (or { in column 0) |
3.4 Find Within Line
| Keys | Action |
|---|---|
f{char} |
Forward to {char} |
F{char} |
Backward to {char} |
t{char} |
Forward till before {char} |
T{char} |
Backward till after {char} |
; |
Repeat last f/t/F/T in same direction |
, |
Repeat last f/t/F/T in opposite direction |
3.5 Search-Based Motions
| Keys | Action |
|---|---|
/pattern<CR> |
Forward search |
?pattern<CR> |
Backward search |
n / N |
Next / previous match |
* / # |
Search forward / backward for word under cursor |
% |
Jump to matching () [] {} |
4. Operators and Text Objects
Verb-noun grammar — Vim's central abstraction. Operator = verb; motion or text object = noun. d3w = delete (d) three words (3w).
4.1 Operators
| Key | Action |
|---|---|
d |
Delete |
c |
Change (delete and enter Insert mode) |
y |
Yank (copy) |
> / < |
Indent right / left |
= |
Auto-indent (formatprg) |
gu / gU |
Lowercase / uppercase |
g~ |
Toggle case |
gq |
Format text |
! |
Filter through external command |
Doubled operator → current line: dd deletes line, yy yanks line, >> indents one line.
4.2 Text Objects
Pair with operators. Two flavors: i (inner, excludes delimiters) and a (around, includes delimiters).
| Object | Inner | Around |
|---|---|---|
| word | iw |
aw |
| WORD | iW |
aW |
| sentence | is |
as |
| paragraph | ip |
ap |
"..." |
i" |
a" |
'...' |
i' |
a' |
`...` |
i` |
a` |
(...) |
i( or ib |
a( or ab |
[...] |
i[ |
a[ |
{...} |
i{ or iB |
a{ or aB |
<...> |
i< |
a< |
| XML/HTML tag | it |
at |
Examples:
diw " delete inner word
daw " delete a word (with surrounding whitespace)
ci" " change text inside quotes
ca( " change parens and content
dap " delete a paragraph
yit " yank inside HTML tag
Grammar composes uniformly. d/c/y + a handful of motions + text objects → large combinatorial vocabulary.
5. Editing Essentials
5.1 Insert Variants
| Key | Action |
|---|---|
i |
Insert before cursor |
I |
Insert at first non-blank of line |
a |
Append after cursor |
A |
Append at end of line |
o |
Open new line below |
O |
Open new line above |
s |
Substitute character (delete + insert) |
S |
Substitute line |
5.2 Undo and Repeat
| Key | Action |
|---|---|
u |
Undo |
<C-r> |
Redo |
U |
Undo all changes on line |
. |
Repeat last change |
. ("dot") — most underused key. Replays last text-modifying action including its count. After cw{new}<Esc>, . repeats the change on the next word cw lands on.
5.3 Copy, Paste, Join, Case
| Key | Action |
|---|---|
yy |
Yank line |
p / P |
Paste after / before cursor |
J |
Join line below with current (single space) |
gJ |
Join without inserting space |
~ |
Toggle case of character |
gU{motion} |
Uppercase |
gu{motion} |
Lowercase |
gUU / guu |
Upper/lower entire line |
5.4 Indent and Format
| Key | Action |
|---|---|
>> / << |
Indent / dedent line |
=G |
Auto-indent to end of file |
gg=G |
Auto-indent entire file |
gqap |
Format paragraph to textwidth |
6. Search and Substitute
6.1 Search
/pattern<CR> → forward; ?pattern<CR> → backward. n/N repeat in original / opposite direction. 'incsearch' highlights matches as you type; 'hlsearch' keeps all matches highlighted until :noh.
6.2 Substitute
:s general form:
:[range]s/{pattern}/{replacement}/{flags}
| Form | Effect |
|---|---|
:s/old/new/ |
First occurrence on current line |
:s/old/new/g |
All occurrences on current line |
:%s/old/new/g |
All occurrences in whole file (% = entire buffer) |
:%s/old/new/gc |
Same, with confirmation per match |
:5,20s/old/new/g |
Lines 5 through 20 |
:.,+10s/old/new/g |
Current line through 10 below |
:'<,'>s/old/new/g |
Last visual selection (auto-inserted after : in Visual mode) |
Flags: g = global within line, c = confirm, i = ignore case, I = match case, n = count matches without replacing.
6.3 Regex Flavor
Default "magic" mode: . * ^ $ \| \( \) \{ \} \+ \?. \v "very magic" → PCRE-like, every metacharacter except letters/digits/_ is special.
:%s/\v(\w+)\s+(\w+)/\2 \1/g " swap two adjacent words
:%s/\v<TODO>/DONE/g " word boundaries with < and >
Capture groups: \(...\) (magic) or (...) (very magic). Back-references in replacement: \1–\9. Entire match: \0 or &.
6.4 Global Command
:g/pattern/cmd → run cmd on every matching line. :v/pattern/cmd → on non-matching lines.
:g/^$/d " delete blank lines
:g/TODO/p " print all lines with TODO
:v/^#/d " delete lines not starting with #
7. Windows, Tabs, Buffers
Three display layers, often confused:
- Buffer. File loaded into memory. Independent of any window.
- Window. Viewport onto a buffer. Multiple windows can share one buffer.
- Tab. Collection of windows. Workspace layout — not a document tab (vs. browser tabs).
7.1 Buffers
| Command | Action |
|---|---|
:e {file} |
Edit file in current window (load into buffer) |
:ls or :buffers |
List buffers |
:b {n} or :b {name} |
Switch to buffer n or matching name |
:bn / :bp |
Next / previous buffer |
:bd |
Delete (unload) buffer |
:bufdo {cmd} |
Run command across all buffers |
7.2 Windows
| Command | Action |
|---|---|
:sp {file} |
Horizontal split |
:vsp {file} |
Vertical split |
<C-w>s / <C-w>v |
Split current window |
<C-w>h/j/k/l |
Move to window in direction |
<C-w>w |
Cycle windows |
<C-w>c or :close |
Close current window |
<C-w>o or :only |
Close all other windows |
<C-w>= |
Equalize sizes |
<C-w>_ / ` |
` |
<C-w>r |
Rotate windows |
7.3 Tabs
| Command | Action |
|---|---|
:tabnew {file} |
New tab |
:tabclose |
Close current tab |
gt / gT |
Next / previous tab |
{n}gt |
Go to tab n |
:tabmove {n} |
Reorder tab |
:tabdo {cmd} |
Run command in every tab |
Vim tabs ≠ browser tabs — a tab holds a whole window layout. For a file picker, use a buffer-based plugin (fzf.vim, telescope.nvim), not tabs.
8. Registers and Marks
8.1 Registers
Named clipboards. Prefix any yank/delete/paste with "{reg}:
"ayy " yank line into register a
"ap " paste from register a
"+y " yank to system clipboard
"+p " paste from system clipboard
| Register | Contents |
|---|---|
"" |
Unnamed (last delete or yank) |
"0 |
Last yank only |
"1–"9 |
Numbered: rotating history of line-or-bigger deletes, plus deletes by search/section motions (/, ?, n, N, %, (, ), {, }) regardless of size |
"- |
Small-delete register (deletes shorter than one line; populated in addition to "1 when the deleting motion is one that fills "1 anyway) |
"a–"z |
Named, lowercase overwrites, uppercase appends |
"+ |
System clipboard (X11 CLIPBOARD selection) |
"* |
Primary selection (X11) / clipboard on macOS/Windows |
"_ |
Black hole — discards content |
"% |
Current file name |
"# |
Alternate file name |
". |
Last inserted text |
": |
Last ex command |
"/ |
Last search pattern |
"= |
Expression register (evaluate vim expression) |
View all with :reg.
Black hole "_. Use for deletions that shouldn't clobber the unnamed register. "_dd deletes a line without overwriting your last yank.
8.2 Marks
Named cursor position. Set with m{a-zA-Z}; jump with `{mark} (exact position) or '{mark} (line, first non-blank).
| Mark | Scope |
|---|---|
ma–mz |
Per-file (lowercase) |
mA–mZ |
Global, including file name (uppercase) |
`` |
Position before last jump |
`. |
Last change |
`^ |
Last insert |
`[ / `] |
Start / end of last change or yank |
`< / `> |
Start / end of last visual selection |
:marks lists all. Global marks (mA) survive across files and sessions if 'shada' (Neovim) / 'viminfo' (Vim) is configured.
9. Ex Commands and Ranges
Commands typed after : — inherited from ed/ex, the line editors that predate vi.
9.1 Ranges
Range prefixes most ex commands. Forms:
| Range | Meaning |
|---|---|
:5 |
Line 5 |
:. |
Current line |
:$ |
Last line |
:% |
Whole file (alias for 1,$) |
:1,10 |
Lines 1 through 10 |
:.,+5 |
Current line and 5 following |
:.,$ |
Current to end |
:'a,'b |
From mark a to mark b |
:'<,'> |
Last visual selection |
:/foo/,/bar/ |
From line matching foo to line matching bar |
9.2 Common Ex Commands
| Command | Action |
|---|---|
:w |
Write (save) |
:w {file} |
Write to another file |
:q |
Quit (fails if unsaved) |
:q! |
Force quit, discard changes |
:wq or :x |
Write and quit (:x writes only if modified) |
ZZ |
Same as :x (Normal mode) |
ZQ |
Same as :q! (Normal mode) |
:e! |
Reload current file, discard changes |
:r {file} |
Insert file below cursor |
:r !cmd |
Insert output of shell command |
:!cmd |
Run shell command, show output |
:[range]!cmd |
Filter range through command |
:set {option} |
Set option |
:set {option}? |
Query option value |
:help {topic} |
Open help (e.g., :help :s) |
9.3 Filtering Through Shell
:%!sort -u " sort whole file, dedupe
:5,20!python -m json.tool " pretty-print JSON in lines 5-20
:r !date " insert current date
10. Configuration
Vim → ~/.vimrc. Neovim → ~/.config/nvim/init.vim or init.lua.
10.1 Minimal Sane Defaults
" ~/.vimrc
set nocompatible " disable vi compatibility
syntax on " syntax highlighting
filetype plugin indent on " filetype detection and indent files
set number " absolute line numbers
set relativenumber " relative numbers for motion counts
set cursorline " highlight current line
set expandtab " spaces, not tabs
set tabstop=4 " a tab is 4 spaces
set shiftwidth=4 " indent step is 4 spaces
set softtabstop=4 " <BS> deletes 4 spaces
set smartindent
set ignorecase " case-insensitive search
set smartcase " ...unless query has caps
set incsearch " show matches as you type
set hlsearch " highlight all matches
set hidden " allow buffer switching without saving
set mouse=a " enable mouse in all modes
set clipboard=unnamedplus " yank to system clipboard
set undofile " persistent undo
set undodir=~/.vim/undo
set scrolloff=8 " keep 8 lines visible above/below cursor
set sidescrolloff=8
let mapleader = " " " space as leader key
nnoremap <leader>w :w<CR>
nnoremap <leader>q :q<CR>
nnoremap <leader><space> :noh<CR>
10.2 Plugin Managers
| Manager | Style | Notes |
|---|---|---|
| vim-plug | Vimscript | Single file, simple syntax — Plug 'tpope/vim-surround' |
| Vundle | Vimscript | Older, still works |
| packer.nvim | Lua | Neovim-only, declarative |
| lazy.nvim | Lua | Current default for Neovim |
| Native packages | Built-in | ~/.vim/pack/{group}/start/{plugin} — no manager needed |
Near-essentials (work in both Vim and Neovim):
tpope/vim-surround— operate on surrounding pairs:cs"'→"foo"becomes'foo'.tpope/vim-commentary—gcccomments a line,gc{motion}for a range.tpope/vim-repeat— makes.repeat plugin actions.junegunn/fzf.vim— fuzzy file/buffer/line picker.
11. Pitfalls
- Caps Lock as Escape on macOS. Touch Bar removed physical Escape on some models; corner reach is awkward regardless. Remap Caps Lock → Escape in System Settings → Keyboard → Modifier Keys. Or use
<C-[>(identical to<Esc>). :wqvs:xvsZZ.:wqwrites unconditionally (bumps mtime even if unchanged → confuses build tools);:x/ZZwrite only when modified. Prefer:xfor files watched bymake/incremental builds.- Stuck in command-line history.
q:(instead of:q) opens the command-line window — looks broken. Type:q<CR>inside it to close. - Recursive
:e %:p:h.%:p:h→ current file's directory;:e %:p:hopens it in netrw. Useful, but:e .from$HOME→ giant tree. Prefer:Exploreor a file plugin. - Leader key conflicts. Many plugins map
<leader>{key}. Settingmapleaderafter sourcing plugins → plugins captured the old leader. Setmapleaderat top of config, before plugin loading. hlsearchlingering. Matches stay highlighted after the search.:nohclears for current view; next search restores highlighting. Common binding:<leader><space>→:noh.- Accidental macro recording.
qin Normal mode starts recording into the register named by the next keypress. Tell:recording @aat the bottom. Pressqagain to stop. - Caps Lock typing. With Caps Lock on,
j/k/l/hbecomeJ/K/L/H—Jjoins lines,Kopens man page,Ddeletes to end of line. Many a confusedddstarts here. :q!chain on:qa. Multiple modified buffers →:qarefuses.:qa!discards everything. Check:lsfirst to see what's modified.
12. References
- Vim user manual:
:help usr_tocinside Vim - Vim reference manual:
:help reference_toc - Vim documentation online: vimdoc.sourceforge.net
- Neovim documentation: neovim.io/doc/user
- Vim source: github.com/vim/vim
- Neovim source: github.com/neovim/neovim
- Learn Vimscript the Hard Way: learnvimscriptthehardway.stevelosh.com
- Vim Adventures (browser game for motions): vim-adventures.com
:ssubstitute reference (Chinese): cnblogs.com/oddcat/articles/9807846.html