Skip to content

Vim 速查表

高度可配置的文本编辑器,用于高效文本编辑。

01

入门

模式与基本移动

Vim 是模式编辑器——不同模式服务于不同目的。Esc 可从任何其他模式返回普通模式。hjkl 键让双手保持在主键盘行。w/b 按单词移动,0/$ 移动到行首/行尾,gg/G 跳到文件开头/末尾,:N 跳到第 N 行。

vim
# 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 42

退出与保存

ZZ 和 ZQ 是最快的退出方式——无需按冒号。:x 仅在文件已修改时写入(保留 mtime),而 :wq 总是写入。:w! 在文件系统权限允许时覆盖只读标记。使用 :qa! 退出多个已修改的缓冲区。

vim
: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)

帮助系统

Vim 的帮助全面且带超链接。Ctrl-] 跟随标签(链接),Ctrl-T 返回。帮助记法:i_CTRL-N 表示插入模式下的 Ctrl-N,'number'(带引号)表示选项。:helpgrep 一次搜索所有帮助文件——用 :cn/:cp 在匹配间导航。K 调用光标下关键词的 man 手册(可通过 'keywordprg' 配置)。

vim
: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 directory

单词与字符移动

小写移动(w/b/e)将标点视为单词分隔符——适合代码。大写移动(W/B/E)仅将空白视为分隔符——适合散文。e/E 移动到下一个单词的结尾;ge/gE 移动到上一个单词的结尾(少用但很方便)。

vim
# 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)

行移动

0 和 $ 跳到绝对行首/行尾(包括空白)。^ 和 g_ 跳过行首/行尾的空白——通常正是你想要的。当 'wrap' 开启时,g0/g$ 在屏幕行上操作,而 0/$ 在逻辑行上操作。| 跳转到指定列号。

vim
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]

文件与屏幕移动

H/M/L 在可见屏幕内跳转。Ctrl-d/u 滚动半页(比整页 Ctrl-f/b 更不易迷失方向)。zz/zt/zb 将当前行滚动到屏幕中间/顶部/底部而不移动光标——在编辑时保持上下文极为有用。N% 跳到文件的百分比位置。

vim
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 bottom
02

插入文本

基本插入 (i, I)

i/I 在光标前/行首插入。a/A 在光标后/行尾插入。gi 返回上次插入位置(标记 '^)——当你按 Esc 做了点别的又想继续输入时很方便。gI 跳到绝对第 1 列,忽略前导空白。

vim
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.

新开行 (o, O)

o 和 O 在光标下方/上方新开一行并进入插入模式。这是添加行的最快方式——无需将光标定位到行尾。缩进从周围行自动复制(由 'autoindent' 控制)。

vim
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 three

修改命令 (c, C, s, S)

c{motion} 删除 motion 目标并进入插入模式——最灵活的编辑命令。C 是 c$(修改到行尾),S 是 cc(修改整行)。s 删除一个字符并进入插入;可与计数结合如 3s。ciw/caw 使用文本对象(见专门章节)。

vim
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)

替换模式 (r, R, gr)

r 替换单个字符并立即返回普通模式(无模式切换)。R 进入替换模式,输入会覆盖字符——退格键会恢复原字符,不像插入模式那样只是删除。处理制表符时使用 gr/gR(虚拟替换)——它保留制表符对齐而不是移动文本。

vim
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)

特殊插入

:r !cmd 将 shell 命令输出插入缓冲区——非常适合粘贴命令输出、日期、文件列表。在插入模式下,Ctrl-r 后跟寄存器名插入该寄存器;Ctrl-r = 计算 Vimscript 表达式并插入结果。Ctrl-k 通过输入两个 ASCII 字符插入二合字符(特殊字符)。

vim
: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 digraphs

插入模式快捷键

Ctrl-h/w/u 在插入模式下相当于退格/删除单词/删除行。Ctrl-t/Ctrl-d 在不离开插入模式的情况下调整缩进——对代码至关重要。Ctrl-n/Ctrl-p 从缓冲区和标签中完成关键词。Ctrl-o 执行单个普通命令后返回插入(如 Ctrl-o zz 居中)。Ctrl-g u 打断撤销链,让长插入可分段撤销。

vim
# 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 inserting
03

编辑命令(删除/复制/粘贴)

删除命令

d{motion} 是通用的删除操作符——可与任何 motion 结合。dd 删除整行。x 是 dl(删除字符)的快捷方式。被删除的文本进入无名寄存器(以及寄存器 1)。将 d 与文本对象(ciw、dib)结合可实现强大编辑。J 将下一行合并到当前行,插入一个空格。

vim
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)

y{motion} 复制文本而不删除。yy 复制行。与 d(有 x 作为快捷方式)不同,y 没有单字符快捷方式——必须使用 motion。复制总是更新寄存器 0,便于与删除区分。使用文本对象:ya( 复制括号周围,yi" 复制引号内部。

vim
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)

p 在光标后粘贴(行向数据则粘贴到下方);P 在前面粘贴。gp/gP 将光标移到粘贴文本的末尾——在链式操作时很有用。]p/[p 重新缩进粘贴的行以匹配周围。剪贴板寄存器(+)在 'clipboard' 设置正确时与系统剪贴板共享。

vim
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.

大小写转换

~ 切换单个字符并前进。g~/gu/gU 是可与 motion 或文本对象配合的操作符。在可视模式下,U/u/~ 作用于选区。使用 gUiw 将当前单词大写,gUU 将整行大写。这些是非破坏性操作——只改变大小写。

vim
~      # 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  ->  HELLO

合并与拆分行

J 用单个空格连接行;gJ 连接但不插入任何内容。:join 作用于范围。gq 是格式化操作符——它将行折行到 'textwidth'(通常为 80),并尊重 'formatoptions'。使用 gqip 重排段落,gqq 重排单行。设置 'textwidth=80' 启用自动折行。

vim
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=80

重复与撤销

. 重复上一次修改——Vim 中最有用的键。u/Ctrl-r 是撤销/重做。U 撤销当前行上的所有修改。:earlier/:later 可按时间或计数穿越,甚至跨分支(Vim 保留撤销树)。使用 :undolist 查看分支。@: 重复上一个 Ex 命令,& 重复上一次替换。

vim
.        # 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 line
04

查找与替换

基本查找

/ 和 ? 向前/向后搜索;n/N 重复。* 搜索光标下的整个单词(带单词边界),g* 搜索任何子串匹配。:noh 清除当前高亮直到下次搜索。设置 'hlsearch' 持久高亮所有匹配,'incsearch' 在输入时增量匹配。

vim
/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!

搜索选项

'ignorecase' + 'smartcase' 是推荐的组合:除非你的模式包含大写字母,否则不区分大小写。\c/\C 按搜索覆盖大小写。\v 启用 'very magic' 模式,许多字符无需转义即成为特殊字符——更简洁的正则语法。搜索偏移(e、b)将光标相对于匹配定位。

vim
: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 match

搜索模式(正则)

Vim 正则与 PCRE 不同——+、=、{、(、| 需要反斜杠。使用 \v 前缀启用 'very magic' 模式,这些字符无需转义即成为特殊字符(更接近 PCRE)。\< \> 标记单词边界。\d/\w/\s 是字符类。使用 \(...\) 作为捕获组,\1/\2 作为反向引用。

vim
# 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 digits

替换命令

:[range]s/pattern/replacement/flags 是替换命令。:% 表示整个文件。不带 'g' 标志时每行只替换第一个匹配;'g' 替换所有。'c' 要求确认(y/n/a/q/l)。使用 \v 启用 very-magic 简化分组语法。捕获组在替换中引用为 \1、\2。

vim
:[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-magic

替换标志与特殊替换

常用标志:g(全部)、c(确认)、i(不区分大小写)、n(仅计数)。替换中的 & 插入匹配的文本。\u/\U/\l/\L 修改替换的大小写。使用 \r 插入换行(\n 在替换中是空字节)。'n' 标志非常适合只计数匹配而不做任何更改。

vim
# 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 only

全局命令 (:g)

:g 在每行匹配模式的行上运行 Ex 命令——强大的'全局'编辑器。:v(或 :g!)在不匹配的行上运行。可与任何 Ex 命令结合:d(删除)、m(移动)、t(复制)、s(替换)、normal(运行普通模式命令)。:g 默认从上到下处理。

vim
:[range]g/pattern/cmd    # run cmd on each matching line
:[range]g!/pattern/cmd   # run cmd on NON-matching lines
:v/pattern/cmd           # same as :g! (inverse)

# Common uses:
:g/^$/d                  # delete all blank lines
:g/TODO/d                # delete all lines with TODO
:g/pattern/p             # print all matching lines
:g/pattern/normal @a     # run macro a on each matching line
:g/pattern/m0            # move matching lines to top (reverse order)
:g/pattern/t$            # copy matching lines to end
:g/pattern/s/old/new/g   # substitute on matching lines only

# Combined with Ex range:
:g/^#/s/foo/bar/g        # in lines starting with #, replace foo with bar

# Multiple commands per match:
:g/pattern/d|t0          # delete then copy (chained with |)
05

可视化模式

可视化模式类型

v/V/Ctrl-v 选择字符/行/块。gv 重新选择上次的可视选择——便于重新应用操作。o 移动到选区的另一端(这样你可以向任一方向扩展)。对于块模式,O 移动到同一行的另一个角。

vim
v       # charwise visual (select characters)
V       # linewise visual (select whole lines)
Ctrl-v  # blockwise visual (select rectangle)
gv      # reselect last visual selection

# In visual mode:
o / O   # move cursor to other end of selection (corner for block)
Esc / v # exit visual mode (toggle)

# Quick selections from normal mode:
vap     # select around paragraph
vip     # select inside paragraph
i"      # then press v... actually ciw etc. work directly

# Switching between types:
#   v then V   ->  linewise (extends to whole lines)
#   V then v   ->  charwise
#   Ctrl-v     ->  blockwise

可视化操作

一旦有了选区,d/c/y/x 按预期工作。= 自动缩进(非常适合代码重排)。>/< 缩进/取消缩进一个 shiftwidth。! 通过 shell 命令过滤选区并用输出替换。在可视模式下按 : 会自动填充 '<,'> 作为范围,因此 :s/old/new/g 只在选区上运行。

vim
# In visual mode, press:
d / x    # delete selection
c / s    # change selection (delete, enter insert)
y        # yank (copy) selection
~        # toggle case of selection
u / U    # lowercase / uppercase selection
>        # indent selection
<        # dedent selection
=        # auto-indent selection (reformat)
!cmd     # filter through external command
:r !cmd  # (after yank) paste command output

# Replace entire selection with a char:
r{x}     # replace every char with x

# Print line numbers of selection:
g Ctrl-g  # count words/chars/lines in selection

# Search within selection (after selecting, type :):
# '<,'>s/old/new/g  -- range auto-filled

块可视化技巧

可视块(Ctrl-v)是 Vim 最强大的功能之一。I 和 A 在块前/后插入——文本在 Esc 后复制到每一行。这是为多行添加注释前缀或追加后缀的最简单方法。c 修改整个块;r{x} 用 x 替换每个字符。当行长度相似时块编辑才能正常工作。

vim
# Enter block visual: Ctrl-v
# Select a column rectangle, then:

I         # insert BEFORE block (text typed appears in front)
          # Type text, then Esc -> inserted on every line of block
A         # insert AFTER block (text appears at end of each line)
          # Esc propagates the insert to all lines

c         # change block (delete, enter insert; Esc propagates)
r{x}      # replace every char in block with x

# Common use cases:
# 1. Add a comment to a column of lines:
#    Ctrl-v select first column, I, type '# ', Esc

# 2. Add semicolons at end of selected lines:
#    Ctrl-v select end column, $A;, Esc

# 3. Delete a column:
#    Ctrl-v select, d

# 4. Replace a column with same char:
#    Ctrl-v select, r;

行可视化模式

V 选择整行。V 之后常用操作是 d/y/>/</= 来操作它们。:m 移动选区(如 :'<,'>m0 移到顶部)。:t 复制。'< 和 '> 是上次可视选区开始/结束的标记——在脚本中很有用。gv 重新选择上次的可视范围。

vim
V         # enter linewise visual
5V        # select 5 lines (current + 4 below)
ggVG      # select entire file
:'<,'>    # range referring to last visual selection

# After selecting lines (V), useful commands:
d         # delete lines
y         # yank lines (linewise)
>         # indent lines
<         # dedent lines
=         # auto-indent lines
:m +2     # move selection down 2 lines
:m -3     # move selection up 3 lines
:t .      # duplicate lines (copy below selection)
:g/^/m0   # reverse line order (whole file)

# Reselect last visual:
gv        # reselect last visual selection
'<        # mark: start of last visual
'>        # mark: end of last visual

可视化与文本对象

文本对象在可视模式下也工作——它们扩展当前选区。vi( 选择括号内部;va( 包括括号。重复文本对象(vawaw)进一步扩展。这通常比手动定位光标更快。与操作符结合:ciw 修改内部单词,无论光标在其内的哪个位置。

vim
# In visual mode, text objects EXTEND the selection:
#   After pressing v (charwise), type:

aw   # extend selection to include next 'a word'
iw   # extend selection to inner word
ab   # around brackets () 
ib   # inside brackets ()
aB   # around braces {}
iB   # inside braces {}
a"   # around double-quoted string
i"   # inside double-quoted string
ap   # around paragraph
ip   # inside paragraph

# Repeated text objects extend selection:
#   vaw   ->  select word + trailing space
#   vawaw ->  extend to next word + space

# Switch between inner and around:
#   vi( ->  select inside parens
#   va( ->  extend to include the parens

可视化查找与编号

在可视模式下按 : 会预填充 '<,'> 作为范围——替换只在选区上运行。要搜索可视选区,使用 y/ 然后 Ctrl-r " Enter(将寄存器粘贴到搜索中)。可视块中的 g Ctrl-a 创建递增序列——从一列相同数字生成编号列表(1、2、3、...)极为有用。

vim
# Search within visual selection:
# 1. Visually select (v/V/Ctrl-v)
# 2. Press :  ->  :'<,'> appears
# 3. Type s/old/new/g  ->  substitute only in selection

# Search for current visual selection (forward):
#  In visual mode, type:
#   \V then /  (very-nomagic, escapes regex chars)
# Or use a plugin (e.g. vim-visual-star-search)
# Manual method:
#   y/ then Ctrl-r " Enter  ->  search for selection

# Increment/decrement numbers (visual block):
#  Select a column of numbers (Ctrl-v)
Ctrl-a    # increment every number in selection by 1
Ctrl-x    # decrement every number in selection by 1
g Ctrl-a  # increment with running counter (1,2,3,...)
g Ctrl-x  # decrement with running counter

# Example: turn 0,0,0,0 into 1,2,3,4:
#  Ctrl-v select column, g Ctrl-a
06

缓冲区

缓冲区基础

缓冲区是内存中的文件。:e 将文件打开到新缓冲区。:ls 列出所有缓冲区及状态标志(% = 当前,# = 交替,+ = 已修改,h = 隐藏)。:b N 按编号切换;:b name 按唯一前缀切换。Ctrl-6(或 :b#)在当前和交替缓冲区之间切换——极为常用。

vim
:e file.txt     # open file in new buffer (current window)
:enew           # new empty buffer
:ls / :buffers  # list all buffers
:b N            # go to buffer N (by number from :ls)
:b name         # go to buffer by name (unique prefix)
:bn             # next buffer
:bp             # previous buffer
:b# / Ctrl-6    # alternate buffer (last edited)
:ball           # open all buffers in horizontal splits
:vertical ball  # open all buffers in vertical splits

# Buffer list symbols:
#   %   current window's buffer
#   #   alternate buffer
#   a   active (loaded and visible)
#   h   hidden (loaded but not visible)
#   -   not modifiable
#   =   read-only
#   +   modified

缓冲区导航

:bn/:bp 循环缓冲区。Ctrl-6 在两个最近的缓冲区之间切换——来回切换的最快方式。设置了 'hidden' 时,从已修改缓冲区切换不会强制保存——Vim 会隐藏它。如果忘了缓冲区编号,使用 :ls 然后 :b N。fzf.vim 等插件让模糊缓冲区切换快得多。

vim
:bn / :bnext      # next buffer (wraps around)
:bp / :bprev      # previous buffer
:bf / :bfirst     # first buffer
:bl / :blast      # last buffer
:b#               # alternate buffer (Ctrl-6)
:N b              # go to buffer number N (e.g. 3b)
:b name<tab>      # tab-complete buffer name
:ls then :b N     # two-step navigation

# Faster navigation (set in vimrc):
:nmap <C-n> :bn<CR>
:nmap <C-p> :bp<CR>

# Jump to a buffer with fuzzy matching (with fzf.vim):
:Buffers          # fuzzy-select from buffer list

# Hide current buffer without saving (if 'hidden' is set):
:hide             # or :bn with 'set hidden'

缓冲区操作

:bd 从列表中移除缓冲区(文件关闭)。:bw 还清除标记和缓冲区局部变量。:%bd | e# 删除除当前外的所有缓冲区——常见的清理模式。:bufdo 在每个缓冲区中运行 Ex 命令——对全局替换很强大,但要注意错误(用 ! 忽略,或给 :s 加 'e' 标志)。

vim
:bd / :bdelete       # delete current buffer
:bd N               # delete buffer N
:bd name            # delete buffer by name
:bw / :bwipeout     # wipe buffer (also clears marks, vars)
:N,N bd             # delete buffers in range
:%bd                # delete all buffers
:%bd | e#           # delete all but current

:bn!                # force switch (don't save current)
:wall               # write all buffers
:bufdo cmd          # run cmd in every buffer
:bufdo! cmd         # same but ignore errors

# Useful patterns:
:bufdo %s/old/new/ge   # substitute in all buffers
:bufdo setlocal nowrap # apply setting to all buffers

# Save and quit current buffer:
:wq                 # write current and quit (closes window)

隐藏缓冲区

设置 'set hidden'(强烈推荐)后,可以从已修改缓冲区切换而无需保存——Vim 将它们保留在内存中。不设 'hidden' 时,Vim 阻止切换直到你保存或用 ! 丢弃。:ls 显示 'h' 表示隐藏缓冲区,'+' 表示已修改。此设置对高效多文件编辑至关重要。

vim
:set hidden         # allow switching from modified buffers
:set nohidden       # force save/abandon when switching

# With 'hidden' set:
#   :bn             # switch even if current is modified (no save)
#   :q              # if buffer is modified, prompts to save
#   :qa!            # quit all without saving
#   :wa             # write all (hidden + visible)

# Buffer with changes still in memory:
:ls                  # shows 'h' flag for hidden, '+' for modified

# Recover list of hidden buffers:
:ls h               # only hidden buffers

# Save a hidden buffer:
:w                  # save current (if visible)
:N w                # save buffer N
:wall               # save all buffers

# Common workflow with hidden:
#   :e file1  -> make changes -> :e file2 -> :e file1 (changes preserved)

参数列表

参数列表是与缓冲区列表分开的列表——它是命令行传入的文件集合或用 :args 设置的。:n/:prev 在其中导航。:argdo 在每个参数列表文件中运行 Ex 命令——非常适合批量处理如 :argdo %s/foo/bar/ge | update。参数列表通常比缓冲区列表更聚焦。

vim
# Open files in arglist:
vim file1.txt file2.txt    # initial args
:args file1 file2 file3    # replace arglist
:argadd file4              # add to arglist
:arga *.py                 # add all Python files
:argdelete file2           # remove from arglist

# Navigate arglist:
:n / :next         # next file in arglist
:prev / :Next      # previous file
:first             # first file
:last              # last file
:rewind            # rewind to first file

# Process all files:
:argdo %s/old/new/ge    # substitute in every arg file
:argdo set ff=unix | w  # convert line endings in all

# Show current arglist:
:args              # show files (current marked with [ ])

缓冲区列表管理

:ls 列出缓冲区,:ls! 包括未列出的(已删除但被记住的)。使用 :sb 在分割中打开缓冲区而不改变当前窗口的缓冲区。没有内置的'最近关闭缓冲区'撤销,但存在插件。:bufdo vimgrepadd 后跟 :copen 可通过 quickfix 列表搜索所有打开的缓冲区。

vim
:ls                 # list all buffers
:ls!                # list including unlisted buffers
:files              # alias for :ls

# Move current buffer to a specific position:
:N b N              # buffer number N

# Quickly inspect related files:
:b <tab>            # tab-complete buffer names
:b #<tab>           # buffers matching '#'

# Find buffer containing a pattern:
:vimgrep /pattern/ %   # current buffer only
:bufdo vimgrepadd /pattern/ %  # all buffers (then :copen)

# Open buffer in a specific window:
:sb N              # open buffer N in horizontal split
:vertical sb N     # open buffer N in vertical split
:tab split +b N    # open buffer N in new tab

# Restore closed buffers:
# (No built-in, but plugins like vim-molten or
# vim-restore-buffers can help)
07

标记与跳转

设置标记

标记是文件中的命名位置。小写(a-z)是缓冲区局部的;大写(A-Z)是跨 Vim 会话持久化的文件标记。编号标记(0-9)由 Vim 自动设置——0 是 Vim 上次关闭时的位置,1-9 是最近关闭的 9 个文件。:marks 列出它们,:delm 删除。

vim
m{a-zA-Z}    # set mark at cursor position
ma           # set mark 'a' (lowercase, buffer-local)
mA           # set mark 'A' (uppercase, file mark)
m'           # set the unnamed mark (jump before/after)

# Lowercase marks (a-z):
#   Local to current buffer
#   Lost when buffer is unloaded

# Uppercase marks (A-Z):
#   File marks — preserved across sessions
#   Work even after the file is closed

# Numbered marks (0-9):
#   Set automatically by Vim
#   0 = position when Vim was last closed
#   1-9 = positions of last 9 closed files

# View marks:
:marks       # list all marks
:marks a b c # specific marks
:delm! a     # delete mark 'a' (force)

跳转到标记

`(反引号)跳到精确位置(行+列);'(撇号)跳到标记行的第一个非空白字符。`A 打开包含标记 A 的文件,即使它已关闭。`. 跳到你上次修改文本的位置,`[ 和 `] 界定上次复制/删除的区域,`< 和 `> 界定上次可视选区。

vim
\`{a-zA-Z}    # jump to mark (line AND column)
'{a-zA-Z}     # jump to mark (line only, column 1)

\`a          # jump to mark 'a' (exact position)
'a           # jump to line of mark 'a'
\`A          # jump to file mark 'A' (opens file if closed)
\`.          # jump to position of last change in this buffer
\`"          # jump to position when file was last closed
\`[          # jump to start of last yanked/changed text
\`]          # jump to end of last yanked/changed text
\`<          # jump to start of last visual selection
\`>          # jump to end of last visual selection
\`^          # jump to last insert position (also gi)

# Jump to next/prev mark in alphabetical order:
:[count]next  # (no built-in, requires plugin)

跳转列表

Vim 保留重要光标移动的跳转列表。Ctrl-o/Ctrl-i 在其中向后/向前移动。'重要'意味着跳转(G、gg、搜索、标记跳转、标签跳转)——不是 j/k/w 等常规 motion。:jumps 显示列表。跳转列表是按窗口的,并在 viminfo/shada 中跨会话持久化。

vim
Ctrl-o    # jump BACK in jumplist (older positions)
Ctrl-i    # jump FORWARD in jumplist (newer positions)
Tab       # same as Ctrl-i
:ju       # show jumplist (:jumps)

# What counts as a jump?
#   - Movement commands: G, gg, H, M, L, %
#   - Search: /, ?, n, N, *, #
#   - Mark jumps: 'x, `x (NOT mm)
#   - Buffer switch: Ctrl-6, :b, :e
#   - Tag jumps: Ctrl-]
#   NOT counted: j, k, w, b, h, l (regular motions)

# Max jumplist size: 100 entries by default
:set jumpoptions=stack   # treat jumplist like a stack

# Clear jumplist:
:clearjumps   # or :clj

修改列表

修改列表跟踪编辑位置(不是导航)。g; 跳到上一个编辑,g, 跳到下一个。用于回溯你的修改:'我刚才在哪里工作?'。:changes 显示列表。与跳转列表不同,修改列表是按缓冲区的。与 `.` 结合可获取当前缓冲区中最后一次修改。

vim
g;        # go to OLDER position of last change
g,        # go to NEWER position of last change
:changes  # list changelist

# Each entry shows:
#   change number, line, column, text of change

# What counts as a change?
#   - Any insertion, deletion, or substitution
#   - Records the position BEFORE the change

# Differences from jumplist:
#   - Changelist tracks EDITS, not movements
#   - Per-buffer (not per-window)
#   - Limited to 100 entries

# Combine with marks:
\`.       # last change position in current buffer
g;         # previous change position

# Common pattern: g;g;g; to walk back through edits

文件标记 (A-Z)

大写标记(A-Z)是文件标记——它们记住文件和位置,跨 Vim 会话持久化(存储在 viminfo 中)。在 foo.txt 中设置 mA 后,你可以用 `A 从任何地方返回到该位置,即使重启 Vim 后也行。将它们用作项目中重要文件/位置的书签。

vim
# Setting a file mark:
mA          # set mark 'A' at cursor (in current file)
mB          # set mark 'B' (in current file)

# Jumping to a file mark:
\`A          # if file is open: jump to position
\`A          # if file is closed: open it, then jump
'A          # jump to first non-blank of mark's line

# File marks persist across Vim sessions:
#   Stored in viminfo (or shada in Neovim)
#   Restored on next Vim launch

# Use cases:
#   1. Bookmark a function: mA in function definition
#   2. Quick jump between files: `A, `B
#   3. Project navigation: set marks in important files

# Listing file marks:
:marks A-Z   # show only file marks

# Deleting:
:delm A      # delete file mark A (must be in its file)

内置特殊标记

Vim 自动设置特殊标记。`. 是上次修改,`[ 和 `] 界定上次复制/删除的区域,`< 和 `> 界定上次可视选区,`^ 是插入模式上次结束的位置(gi 跳到那里并重新进入插入)。`" 是文件上次关闭时的位置——用于恢复工作。

vim
\`.    # position of last change in current buffer
\`"    # position when file was last exited
\`[    # start of last yanked/changed text
\`]    # end of last yanked/changed text
\`<    # start of last visual selection
\`>    # end of last visual selection
\`^    # position where insert mode was last stopped
\`'    # position before latest jump (where you came FROM)

# Common uses:
\`[\`]  # operate on last yanked range: \`[v\`]U  (uppercase it)
\`<\`>  # re-select last visual: \`<v\`> or gv

# Re-select last visual selection:
gv        # easier than \`<v\`>

# Re-select last pasted text:
\`]v\`[   # or use a mapping: nnoremap gp \`]v\`[

# Return to last insert position:
gi        # jump to `^ and enter insert mode
08

文本对象

单词与句子对象

文本对象让你在语义单元上操作。'aw'(around word)包括尾部空白——daw 闭合间隙。'iw'(inner word)保留空白——diw 留下空白。as/ap 是句子/段落版本。重复对象(dawaw)扩展选区。与 d/c/y/v 操作符配合最为有用。

vim
# Operators (d/c/y/v) + text object:
aw   # 'a word' - word + trailing whitespace
iw   # 'inner word' - word only (no whitespace)
aW   # 'a WORD' - WORD + trailing whitespace
iW   # 'inner WORD' - WORD only
as   # 'a sentence' - sentence + trailing space
is   # 'inner sentence' - sentence only
ap   # 'a paragraph' - paragraph + trailing blank line
ip   # 'inner paragraph' - paragraph only

# Examples:
daw    # delete word + trailing space (closes gap)
diw    # delete word only (leaves whitespace)
cas    # change sentence (with trailing space)
yap    # yank paragraph (with trailing blank line)
gqip   # format (wrap) inner paragraph

# Repeated: dawaw extends to next word

括号对象

括号对象作用于 ()、[]、{}、<>。a(around)包括括号;i(inner)排除括号。ci( 修改括号内的内容而不删除括号——完美用于编辑函数参数。嵌套时作用于最内层一对。at/it 作用于 HTML/XML 标签(如 cit 修改标签内部内容)。

vim
# Brackets (any of ( [ { <):
ab / ib   # around / inside ()
aB / iB   # around / inside {} (also a{ / i{)
a( / i(   # same as ab / ib
a[ / i[   # around / inside []
a{ / i{   # same as aB / iB
a< / i<   # around / inside <>

# Examples:
ci(      # change text inside ( ... )
daB      # delete {...} block including braces
yi[      # yank text inside [...]
vaB      # visually select around {...}

# Special: 'tag' objects (HTML/XML):
at / it  # around / inside <tag>...</tag>

# Tip: works with mismatched brackets too:
#   ci) on (foo bar) changes 'foo bar'

# Nested: ci( in ((a)(b)) changes innermost

引号对象

引号对象(a"/i"、a'/i'、a`/i`)作用于引号字符串。光标不需要在引号内——Vim 会找到最近的一对。ci" 修改字符串内容而不删除引号——对编辑字符串字面量极为有用。'a' 包括引号,'i' 排除引号。

vim
a"   # around double-quoted string (includes quotes)
i"   # inside double-quoted string (no quotes)
a'   # around single-quoted string
i'   # inside single-quoted string
a\`   # around backtick-quoted string
i\`   # inside backtick-quoted string

# Examples:
ci"     # change text inside "..."
da"     # delete "..." (including quotes)
yi'     # yank text inside '...'
va\`    # visually select around \`...\`

# Cursor can be ANYWHERE on the line — Vim finds the
# nearest quote pair containing or following the cursor.

# Special: with counts
3a"     # around 3 consecutive quoted strings

# Tip: ci" when cursor is on the quote itself works too

句子与段落对象

句子以 . ! ? 后跟空白(或段落中断)结束。段落是由空行分隔的文本块。'a' 包括尾部空白,'i' 排除它。与操作符配合很有用:gqap 重排段落,>ip 缩进段落,das 删除句子。( 和 ) motion 在句子之间跳转。

vim
# Sentence: ends with . ! ? followed by whitespace
as / is    # around / inner sentence

# Paragraph: blocks of text separated by blank lines
ap / ip    # around / inner paragraph

# Examples:
das        # delete a sentence
cip        # change inside paragraph
vap        # select around paragraph (with trailing blank)

# Differences:
#   'a' (around): includes trailing whitespace/blank line
#   'i' (inner): no trailing whitespace

# Use cases:
#   gqap    # reformat (wrap) the paragraph
#   >ap     # indent the paragraph
#   vip     # select inside paragraph
#   :'<,'>s/old/new/g   # substitute in selected paragraph

# Sentence motion commands (not objects):
(   # previous sentence start
)   # next sentence start

标签与 URL 对象(需插件)

内置的 at/it 作用于 HTML/XML 标签。cit 修改标签内部内容,dat 删除包括开/闭标签的整个标签。对于嵌套标签,it 选择最内层,然后 vat 向外扩展。插件添加更多对象:vim-textobj-url(au/iu)、vim-indent-object(ai/ii 用于同缩进块——对代码极好)。

vim
# Built-in tag object (HTML/XML):
at / it    # around / inside <tag>...</tag>

# Examples:
cit        # change inner tag text: <p>text</p> -> <p>|</p>
dat        # delete entire tag: <p>text</p> -> (gone)
vit        # visually select inner tag text
yat        # yank entire tag including <tag>...</tag>

# For nested tags, 'it' selects the innermost:
#   <div><p>text</p></div>
#   cit on 'text' changes 'text' only
#   vat extends selection outward

# URL objects (require a plugin like vim-textobj-url):
#   au / iu  # around / inside URL

# Indent objects (require vim-indent-object):
#   ai / ii  # around / inside same-indent block
#   aI / iI  # entire indentation level

# Common plugin combos:
#   vai      # select around indent block (great for code)

使用文本对象(配方)

文本对象与任何操作符(d/c/y/v/>/</=)结合实现强大编辑。ciw 修改单词而无需精确定位光标。ci"/ci(/ci[ 修改引号/括号内部。gqip 重排段落。掌握这些是在 Vim 中以思维速度编辑的关键。

vim
# Common editing recipes using text objects:

# Word:
ciw      # change word (regardless of cursor position in it)
daw      # delete word + space (closes gap)
yiw      # yank inner word (no whitespace)

# Quotes:
ci"      # change inside "..."
ci'\`    # change inside '...' or \`...\`
da"      # delete the entire string (with quotes)

# Brackets:
ci(      # change inside (...) -- great for args
daB      # delete {...} block (function body)
ci[      # change inside [...]

# Sentences & paragraphs:
cas      # change a sentence
dap      # delete a paragraph
gqip     # reformat paragraph

# HTML tags:
cit      # change tag content
vat      # select tag, then vat again to extend

# Combos with counts:
2daw     # delete two words
c2aw     # change two words
09

寄存器

寄存器基础

寄存器是 Vim 的剪贴板。"ayy 将行复制到寄存器 a,"ap 从 a 粘贴。有许多寄存器:命名(a-z)、追加(A-Z)、编号(1-9)和特殊(无名 "、复制 0、剪贴板 + 等)。:reg 列出它们。在插入模式下,Ctrl-r {reg} 插入寄存器。大写字母追加到寄存器。

vim
# Registers store text. 10 named (a-z), 10 append (A-Z),
# 9 numbered (1-9), and several special ones.

"ay   # yank into register 'a' (then motion: "ayy, "ayw)
"ap   # paste from register 'a'
"ad   # delete into register 'a' (e.g. "add)
"Ay   # APPEND to register 'a' (uppercase = append)

# View all registers:
:reg           # list all registers
:reg a b c     # list specific registers
:reg +         # show clipboard register

# Use a register with operators:
"aD            # delete to end of line into 'a'
"byy           # yank line into 'b'
"cp            # paste register 'c'

# Insert mode: Ctrl-r + register
Ctrl-r a       # insert contents of register 'a'
Ctrl-r =       # expression register (compute, insert)

# Search from a register:
/ then Ctrl-r a   # search register 'a' contents

命名与编号寄存器

命名寄存器(a-z)由用户控制;A-Z 追加到 a-z。编号寄存器(1-9)自动跟踪最近的删除——1 是最近的,每次新删除向下移动。寄存器 0 只保存上次的复制(删除不会覆盖它)——对先复制后删除的工作流有用。"- 保存小的(子行)删除。

vim
# Named registers (a-z, A-Z):
"ayy      # yank line into 'a'
"byiw     # yank inner word into 'b'
"Ayy      # append yanked line to 'a' (uppercase)

# Numbered registers (1-9) — auto-filled by deletes:
"1        # last delete (largest)
"2        # second-to-last delete
...       # shift older with each new delete
"9        # oldest tracked delete

# Small delete register:
"-        # stores deletes < 1 line (e.g. dw)

# Yank register (separate from deletes):
"0        # last YANK only (not affected by deletes)

# Examples:
"0p       # paste last yank (won't paste a delete)
"1p       # paste last delete
".        # last inserted text (read-only)
"%        # current file name (read-only)

特殊寄存器

特殊寄存器:". = 上次插入的文本,"% = 当前文件,"/ = 上次搜索,": = 上次命令。"_ 是黑洞——写入它的内容被丢弃(用于不希望污染其他寄存器的删除)。"+ 是系统剪贴板,"* 是 X11 主选区。"= 计算 Vimscript 表达式并插入结果。

vim
# Read-only registers:
".        # last inserted text
"%        # current file name
"#        # alternate file name
":        # last Ex command
"/        # last search pattern

# Read/write registers:
"0        # last yank (not overwritten by deletes)
"-        # last small delete (less than one line)
"_        # black hole register (discard, like /dev/null)
"=        # expression register (evaluate Vimscript)

# Clipboard / selection:
"+        # system clipboard (Ctrl-C / Ctrl-V)
"*        # primary selection (X11 middle-click)

# Examples:
"_dd      # delete line without saving to register
"+y       # yank to system clipboard (then Ctrl-V elsewhere)
"*p       # paste from X11 primary selection

# Expression register (in insert mode):
Ctrl-r =strftime('%Y-%m-%d')<CR>
# inserts today's date

# Black hole: "_ prevents updating other registers.

寄存器中的宏

宏存储在寄存器中——用 qa 录制会将按键写入寄存器 a,@a 播放。因为它们是寄存器,你可以检查它们(:reg a)、通过粘贴/修改/重新复制来编辑它们,或用 :let @a = '...' 保存到 vimrc。在字符串字面量中用 \<Esc> 转义特殊键。

vim
# Macros are stored in registers, just like yanked text.
# Record a macro into register 'a':
qa        # start recording into 'a'
...       # perform actions
q         # stop recording

# Play back the macro:
@a        # play macro 'a' once
@@        # play last macro again (the one just played)
5@a       # play macro 'a' 5 times
:reg a    # view the recorded keystrokes

# Edit a macro:
:let @a = "..."     # set register 'a' directly
# Or: paste the register, edit, yank back:
"ap       # paste macro keystrokes
# ... edit the text ...
"ayy      # yank edited version back into 'a'

# Save a macro to vimrc:
:let @a = "0iHello \<Esc>"   # escaped special keys

# Apply macro to a range:
:%normal @a   # run macro 'a' on every line

追加与黑洞寄存器

大写寄存器字母追加:"Ayy 追加到寄存器 a(对构建文本或多步宏非常有用)。黑洞寄存器 "_ 丢弃写入它的任何内容——删除不会更新编号/无名/复制寄存器。当你想删除文本而不覆盖上次复制时使用它。

vim
# Append to a register (use UPPERCASE letter):
"ayy      # yank line into 'a'
"Ayy      # APPEND another line to 'a'
"Ayiw     # APPEND a word to 'a'

# After: register 'a' contains both.

# Black hole register "_:
"_d       # delete without affecting other registers
"_dd      # delete line, doesn't update "1, "0, "-
"_c       # change without saving deleted text
"_x       # delete char, nothing saved

# Why use black hole?
#   - Keep your yank register ("0) intact while deleting
#   - Avoid polluting numbered registers

# Example: yank a line, then delete another (without
# losing the yank):
"ayy      # yank into 'a'
"_dd      # delete line WITHOUT touching 'a' or '0
"ap       # paste the original yank

# When clipboard=unnamed, "_d still bypasses clipboard.

剪贴板与选区

"+ 是系统剪贴板(GUI 应用中的 Ctrl-C/Ctrl-V),"* 是 X11 主选区(中键粘贴)。设置 :set clipboard=unnamedplus 后,复制和粘贴直接进入系统剪贴板——无需 "+ 前缀。在 Linux 上,你可能需要安装 xclip/xsel(X11)或 wl-clipboard(Wayland)。

vim
# System clipboard register:
"+y       # yank to system clipboard
"+p       # paste from system clipboard
"+dd      # delete line to clipboard
"+P       # paste before cursor from clipboard

# X11 primary selection (Linux, middle-click):
"*y       # yank to primary selection
"*p       # paste from primary selection

# Make the system clipboard the default:
:set clipboard=unnamedplus   # use '+ for unnamed register
:set clipboard=unnamed       # use '* for unnamed register

# Then yank/paste go to clipboard automatically:
yy        # yanks to system clipboard (with above setting)
p         # pastes from system clipboard

# Check what's in the clipboard:
:reg +    # show clipboard contents

# On Linux, install a clipboard tool if missing:
#   xclip or xsel for X11
#   wl-clipboard for Wayland
10

录制宏

q{letter} 开始录制到寄存器;再按 q 停止。录制的按键可用 @{letter} 重放。录制时,使用一致的 motion——优先用 0、^、$、w、e 而不是依赖列的 j/k。在批量应用前先在几行上测试。

vim
q{a-z}    # start recording into register {a-z}
...       # perform actions (keystrokes recorded)
q         # stop recording

# Example: wrap each word in quotes on a line:
qa        # start recording into 'a'
0         # go to first column
i"        # insert opening quote
Esc
e         # go to end of word
a"        # append closing quote
Esc
w         # move to next word
q         # stop recording

# Now @a will wrap one more word.

# Recording tips:
#   - Use 0/^-positioned motions (not j/k)
#   - Use commands that work regardless of line length
#   - Test on a few lines before applying broadly

# View the recorded macro:
:reg a    # see keystrokes in register 'a'

播放宏

@a 播放宏一次,@@ 重放上次的宏,N@a 播放 N 次。要将宏应用到多行,可视选择它们并运行 :normal @a,或用 :%normal @a 对整个文件。宏在出错时停止(如搜索失败),所以 999@a 可安全处理到文件末尾。:g/pattern/normal @a 只在匹配行上运行。

vim
@a        # play macro 'a' once (in current buffer)
@@        # replay the LAST macro played
5@a       # play macro 'a' 5 times
100@a     # play 'a' up to 100 times (stops on error)

# Play across multiple lines:
# 1. Visually select lines (V)
# 2. :normal @a    # play 'a' on each selected line

# Or:
:%normal @a        # play on every line in file
:5,20normal @a     # play on lines 5-20
:g/pattern/normal @a  # play on lines matching pattern

# Recursive macros:
#   If macro 'a' contains @a, it'll recurse.
#   Add a 'j' at the end to move to next line.

# Common pattern: process all matching lines:
#   qa0f:x...jq        # record macro moving down 1 line
#   :%s/pattern//gn    # count matches
#   100@a               # play up to 100 times

编辑宏

因为宏存储在寄存器中,你可以编辑它们。粘贴寄存器("ap),修改文本,然后重新复制回去("ay$ 或 "ayy)。或使用 :let @a = '...' 加键转义(\<Esc>、\<CR>)。当你犯了小错误时,这比从头重新录制复杂宏容易得多。

vim
# Macros are just text in registers — you can edit them.

# Method 1: paste, edit, yank back:
"ap       # paste register 'a' into buffer
# ... edit the pasted text (modify keystrokes) ...
0"ay$    # yank the line back into 'a'

# Method 2: set the register directly with :let
:let @a = "0iHello \<Esc>"
# Note: special keys need \<Esc> notation in strings

# Method 3: edit in a scratch buffer:
:edit
"ap       # paste macro
# edit
"ayy      # yank back to 'a'
:bdelete

# Common edits:
#   - Add 'j' at the end to advance to next line
#   - Remove a step that you don't need
#   - Replace a hard-coded search with a generic one

# Show the macro as keystrokes:
:reg a    # view current 'a' contents

递归宏

递归宏调用自身直到出错——通常在搜索失败时。关键:录制前先清空寄存器(let @a = ''),否则宏内的第一次 @a 调用会播放旧内容。当失败的搜索停止递归时,宏自动终止。非常适合全局查找替换模式。

vim
# A macro can call itself, enabling recursion.

# Example: change every 'foo' to 'bar' in a column:
#   (search-based macro)

# Steps to set up recursive macro 'a':
#   1. Clear register 'a':
:let @a = ''
#   2. Start recording:
qa
#   3. Search for 'foo':
/foo<CR>
#   4. Replace with 'bar' (ciwbar<Esc>):
ciwbar<Esc>
#   5. Call itself:
@a
#   6. Stop recording:
q

# Now @a runs until search fails (no more 'foo').
# The recursion stops automatically on the first error
# (when /foo finds nothing).

# Tip: clear the register first, otherwise the FIRST
# call also replays the old contents of 'a'.

# Run:
@a    # processes all 'foo' in file

带计数的宏

N@a 播放宏 N 次——但它在任何错误时提前停止,所以 999@a 可安全处理到文件末尾。:bufdo normal @a 在每个缓冲区中运行宏,:argdo 在每个参数列表文件中运行。:g/pattern/normal @a 只在匹配行上运行。宏内的计数(如 5j)按每次播放重复,而不是按宏。

vim
# Apply a count to a macro:
5@a       # play macro 'a' 5 times
N@a       # play up to N times (stops on error)

# Useful: large count to process until end-of-file
999@a     # play 'a' up to 999 times (errors stop it)

# Macros with counts INSIDE:
#   If your macro starts with '5j', each play jumps 5 lines

# Combine with visual selection (block):
#   Ctrl-v to select, then :normal @a
#   plays 'a' on each line in the block

# Process every Nth line:
:let i = 0
:g/^/if i % 3 == 0 | normal @a | endif
# (plays 'a' on every 3rd line)

# Apply with a delay (avoid blocking):
#   No built-in, but plugins can defer

# Repeat across buffers:
:bufdo normal @a   # play 'a' in every buffer
:argdo normal @a   # play 'a' in every arg file

跨文件与跨行宏

:[range]normal @a 在范围内每行运行宏——将宏应用到多行的最常见方式。在宏结尾加 'j' 让每次迭代前进到下一行。:bufdo/:argdo 跨缓冲区/文件运行。添加 | update 保存每个修改的文件。使用 ! 在错误后继续(如 :bufdo!)。

vim
# Apply a macro to every line in a range:
:[range]normal @a
:%normal @a           # whole file
:5,20normal @a        # lines 5-20
:'<,'>normal @a       # visual selection
:g/pattern/normal @a  # only matching lines

# Apply across buffers:
:bufdo normal @a      # every buffer
:argdo normal @a      # every arglist file

# Save modified buffers after macro run:
:argdo normal @a | update   # save each that changed

# Best practice: include 'j' (move down) at end of macro
# so it advances to the next line for :normal usage.

# Example macro for processing CSV rows:
#   qa 0f,dt,j q    # cut field at comma, move down
#   :%normal @a     # apply to every line

# Abort on error vs. continue:
:bufdo! normal @a     # continue even if a buffer errors

# Debug: play macro step-by-step (no built-in, but
# :set lazyredraw off + slow key timing helps).
11

折叠

折叠方法

foldmethod 控制折叠的创建方式。'manual' 让你用 zf 创建折叠。'indent' 按缩进自动折叠(适合代码)。'syntax' 使用语法规则(语言感知)。'marker' 使用文件文本中的 {{{ }}}。'expr' 用于自定义逻辑。手动折叠在关闭文件时会丢失,除非你 :mkview 保存。

vim
:set foldmethod=manual    # fold by hand (zf)
:set foldmethod=indent    # fold by indent level
:set foldmethod=expr      # fold by 'foldexpr'
:set foldmethod=syntax    # fold by syntax rules
:set foldmethod=marker    # fold by {{{ }}} markers
:set foldmethod=diff      # fold unchanged text in diff

# Recommended defaults:
:set foldmethod=indent    # great for code
:set foldlevel=2          # show first 2 levels

# Per-method notes:
#   manual  - zf to create, zd to delete
#   indent  - automatic, based on shiftwidth
#   syntax  - automatic, requires syntax file support
#   marker  - persistent (saved in file as {{{ }}})
#   expr    - custom logic via 'foldexpr'

# Persistent manual folds across sessions:
:mkview    # save folds (and other view state)
:loadview  # restore them
# Or set:
:set viewoptions=folds   # what :mkview saves

手动折叠

在手动模式下,zf 创建折叠(如 zf3j 折叠接下来 3 行)。zd 删除一个折叠,zD 递归删除,zE 消除窗口中的所有折叠。可视模式 + zf 折叠选区。使用 foldmethod=marker 时,在注释中放置 {{{ 和 }}} 创建存储在文件中的持久折叠——对在版本控制中共享折叠结构有用。

vim
# Manual fold commands (foldmethod=manual):
zf{motion}    # create fold over motion (e.g. zf3j)
zF            # create fold over current line(s)
zd            # delete fold at cursor
zD            # delete fold recursively (nested)
zE            # eliminate ALL folds in window
zj / zk       # move to next/prev fold start
[z / ]z       # move to fold start/end

# Select then fold (visual):
V  zf         # visually select lines, then fold

# Create fold for a range:
:5,20fold     # fold lines 5-20

# Fold using a marker (manual mode):
# Place markers in the file (with foldmethod=marker):
function Foo() " {{{
  echo "hi"
endfunction " }}}

# When foldmethod=marker, the {{{ }}} create persistent
# folds stored in the file text itself.

折叠打开/关闭命令

zo/zc 打开/关闭折叠;za 切换。zO/zC/zA 递归作用于嵌套折叠。zr/zm 全局调整折叠级别(一次一级)。zR 打开所有,zM 关闭所有——快速重置的方法。zj/zk 在折叠之间跳转,[z/]z 跳到当前折叠的开始/结束。

vim
zo    # open fold (one level)
zc    # close fold (one level)
za    # toggle fold (open <-> closed)
zO    # open fold recursively (all levels)
zC    # close fold recursively (all levels)
zA    # toggle fold recursively
zr    # reduce fold level by 1 (open one level everywhere)
zm    # increase fold level by 1 (close one level everywhere)
zR    # open ALL folds (reduce to level 0)
zM    # close ALL folds (fold everything)

# Navigation in/around folds:
zj    # next fold start (down)
zk    # previous fold end (up)
[z    # start of current open fold
]z    # end of current open fold

# Move with folds intact (don't open):
#   Use j/k normally — they may skip closed folds
#   Set 'foldopen' to control what opens folds

折叠导航与编辑

j/k 可能根据 'foldopen' 跳过关闭的折叠。zj/zk 在折叠边界之间导航。折叠行上的 yy/dd 将所有折叠行作为单元操作(行向)。在关闭的折叠内输入会打开它。配置 'foldopen' 控制哪些 motion 打开折叠(如移除 'search' 在搜索时保持折叠关闭)。

vim
# Fold-related motions:
zj    # next fold start (move down)
zk    # previous fold end (move up)
[z    # start of current open fold
]z    # end of current open fold

# What opens a fold when triggered?
:set foldopen=block,hor,mark,percent,quickfix,
             search,tag,undo
# Defaults open folds on most motions.
# To make j/k NOT open folds:
:set foldopen-=block

# Editing a closed fold:
#   Typing inside a closed fold opens it.
#   'o' or 'O' on a fold line opens a new line.

# Search inside closed folds:
#   Default behavior depends on 'foldopen'.
#   :set foldopen-=search  # don't open on search

# Yank/paste on folded lines:
#   yy yanks ALL lines in the fold (linewise)
#   dd deletes ALL lines in the fold

# Paste over a fold:
#   p with a linewise register replaces the folded region

折叠选项

foldlevel 控制折叠保持打开的深度(越高=越开放)。foldlevelstart=99 启动时全部打开。foldcolumn 显示带折叠状态指示器的侧边栏。foldminlines 防止微小折叠。要跨会话持久化折叠,在 BufWinLeave 时 :mkview,在 BufWinEnter 时 :loadview——常见的 autocmd 模式。

vim
:set foldmethod=indent     # folding method
:set foldlevel=2           # show folds deeper than this
:set foldlevelstart=99     # start with all folds open
:set foldminlines=3        # min lines to form a fold
:set foldcolumn=4          # 4-char column showing fold state
:set foldenable            # enable folding (default on)
:set foldclose=all         # auto-close folds when cursor leaves
:set foldopen=block,hor,mark,percent,quickfix,search,tag,undo
:set foldtext=foldtext()   # text to show on a closed fold
:set fillchars=fold:\ ,   # chars for fold display

# Common combo for code:
:set foldmethod=indent
:set foldlevelstart=10
:set foldnestmax=10        # max nesting depth

# Custom fold text:
:set foldtext=MyFoldText()
function MyFoldText()
  return '+++ ' . (v:foldend - v:foldstart + 1) . ' lines +++'
endfunction

# Persistent folds across sessions:
:set viewoptions=folds,cursor
:autocmd BufWinLeave * mkview
:autocmd BufWinEnter * silent loadview

嵌套折叠与折叠级别

折叠分层嵌套。'foldlevel' 决定保持打开的最深级别——foldlevel=0 关闭所有,99 打开所有。zr/zm 按 1 调整,zR/zM 是极端值。'foldnestmax' 限制嵌套深度以避免深度嵌套代码中的失控折叠。自定义 foldtext 函数让你控制关闭折叠上显示的内容。

vim
# Folds can nest. 'foldlevel' controls which are open:
:set foldlevel=0   # all folds closed
:set foldlevel=1   # only top-level open
:set foldlevel=99  # all open

# Display current fold level:
:echo &foldlevel

# Increase/decrease by 1:
zr    # reduce (open one level everywhere)
zm    # more (close one level everywhere)
zR    # open ALL folds (set foldlevel to 99)
zM    # close ALL folds (set foldlevel to 0)

# Limit nesting:
:set foldnestmax=10

# Each indent level adds a fold level.
# Useful for deeply-nested code:
#   function outer() {
#     function inner() {       <- foldlevel 2
#       function deep() {      <- foldlevel 3
#       }
#     }
#   }

# Custom foldtext for nested folds:
:set foldtext=NeatFoldText()
function! NeatFoldText()
  let line = getline(v:foldstart)
  let n = v:foldend - v:foldstart + 1
  return line . ' (' . n . ' lines)'
endfunction
12

窗口分割

分割窗口

:split/:vsplit 分割当前窗口——默认都显示同一个缓冲区(更改同步)。:split file 在新分割中打开文件。:new/:vnew 创建空缓冲区。Ctrl-w s/v 是键盘快捷方式。加数字前缀设置新窗口的大小(:10split 创建 10 行高的窗口)。

vim
:split / :sp        # horizontal split (current file)
:vsplit / :vs       # vertical split (current file)
:split file.txt     # split + open file
:vsplit file.txt    # vsplit + open file
:new                # horizontal split with empty buffer
:vnew               # vertical split with empty buffer
:5split / :5vsplit  # split with 5 rows/cols

# Quick splits (Normal mode):
Ctrl-w s    # split horizontally (same as :split)
Ctrl-w v    # split vertically (same as :vsplit)
Ctrl-w n    # new horizontal split (empty)
Ctrl-w q    # close window (same as :q)

# Split with size hints:
:10split file.txt    # split with height 10
:vert 30split file   # vertical split with width 30

# Reopen current file in a new split:
Ctrl-w s    # same buffer, two views (sync scroll optional)

窗口导航

Ctrl-w 后跟 h/j/k/l 在窗口之间导航(记忆法:hjkl 与移动一样)。Ctrl-w w 循环到下一个窗口。为了速度,许多用户将 Ctrl-h/j/k/l 直接映射到窗口导航。Ctrl-w p 返回之前使用的窗口——在两个窗口之间来回切换时很有用。

vim
Ctrl-w h    # left window
Ctrl-w j    # below window
Ctrl-w k    # above window
Ctrl-w l    # right window
Ctrl-w w    # cycle to next window (wraps)
Ctrl-w W    # cycle to previous window
Ctrl-w t    # top-left window
Ctrl-w b    # bottom-right window
Ctrl-w p    # previous (last accessed) window

# Faster (with mapping):
:nmap <C-h> <C-w>h
:nmap <C-j> <C-w>j
:nmap <C-k> <C-w>k
:nmap <C-l> <C-w>l

# In terminal mode:
Ctrl-w N    # exit terminal mode (to Normal)
Ctrl-w h/j/k/l  # navigate (after exiting terminal mode)

# List windows in current tab:
:ls        # also shows windows' buffers

窗口调整大小

Ctrl-w +/-/</> 按 1 调整大小;加计数前缀(10 Ctrl-w +)进行更大步进。Ctrl-w _ 最大化高度,Ctrl-w | 最大化宽度。Ctrl-w = 均衡所有窗口。精确调整使用 :resize N(高度)和 :vertical resize N(宽度)。设置 'mouse=a' 后可直接拖动状态行和分隔符。

vim
Ctrl-w =    # equalize all window sizes
Ctrl-w +    # increase height by 1
Ctrl-w -    # decrease height by 1
Ctrl-w >    # increase width by 1
Ctrl-w <    # decrease width by 1
Ctrl-w _    # maximize height (current window)
Ctrl-w |    # maximize width (current window)
10 Ctrl-w + # increase height by 10

# Ex commands for precise resize:
:resize 20        # set height to 20 (horizontal split)
:resize +5        # increase height by 5
:resize -5        # decrease height by 5
:vertical resize 80   # set width to 80 (vertical split)
:vertical resize +10  # increase width by 10

# Mouse: drag the status line / vertical separator
# (with :set mouse=a)

# Auto-resize on split:
:set equalalways    # default: auto-equalize on split/close
:set noequalalways  # don't auto-resize

窗口移动

Ctrl-w H/J/K/L 将当前窗口移到远端边缘(大写 = 该侧全屏)。Ctrl-w r/R 在布局内旋转窗口。Ctrl-w x 与下一个窗口交换。Ctrl-w T 将窗口移到新标签页。一个巧妙的技巧:Ctrl-w K/H 还可在水平和垂直方向之间转换。

vim
Ctrl-w H    # move current window to far LEFT (full height)
Ctrl-w J    # move current window to BOTTOM (full width)
Ctrl-w K    # move current window to TOP (full width)
Ctrl-w L    # move current window to far RIGHT (full height)
Ctrl-w r    # rotate windows DOWN/RIGHT
Ctrl-w R    # rotate windows UP/LEFT
Ctrl-w x    # exchange current window with next
Ctrl-w T    # move current window to a NEW tab page

# Move split orientation:
Ctrl-w K    # if vertical, become horizontal (and move to top)
Ctrl-w H    # if horizontal, become vertical (and move to left)

# Example: convert a vertical split to horizontal:
:vsplit        # creates vertical split
Ctrl-w H       # moves it to the left, becoming horizontal

# Reopen current buffer in a new tab:
Ctrl-w T       # current window moves to a new tab

窗口排列

Ctrl-w = 均衡窗口大小。:ball 为每个缓冲区打开一个窗口——一次查看所有内容很有用。Ctrl-w r/R 在布局内旋转窗口。要保存和恢复完整的窗口布局(包括缓冲区、折叠等),使用 :mksession 和 :source。每个标签页维护自己的窗口布局。

vim
# Equalize window sizes:
Ctrl-w =      # equalize height and width

# Stack layouts:
# Vertical splits side by side, horizontal splits stacked
# Example setup:
#   :vsplit        -> | win1 | win2 |
#   :split         -> | win1 | win3 |
#                       |     | win2 |

# Cycle through layouts (with plugins like golden-ratio,
# or use built-in rotation):
Ctrl-w r      # rotate down/right
Ctrl-w R      # rotate up/left

# Distribute buffers across windows:
:ball         # one window per buffer (horizontal)
:vertical ball  # one window per buffer (vertical)

# Layouts persist within a tab page.
# Save current layout to a session:
:mksession mylayout.vim
# Restore:
:source mylayout.vim

# Tab page = a collection of windows
:tabnew       # new tab with one window

关闭窗口

:q 关闭窗口(如果是最后一个则退出 Vim)。:close(或 Ctrl-w c)关闭窗口但不会关闭最后一个。:only(Ctrl-w o)关闭所有其他窗口。设置了 'hidden' 时,关闭带已修改缓冲区的窗口会隐藏它而不是强制保存决定。:only! 即使已修改也强制关闭其他窗口。

vim
:q / :quit       # close current window (errors if last)
:q!              # close without saving
:wq              # save and close
:close / Ctrl-w c   # close window (won't close last)
:only / Ctrl-w o    # close all OTHER windows
:hide            # hide current buffer in window (close window)

# Save and close splits:
:xa              # save all and exit (close all windows)
:wqa             # same as :xa

# Closing a window with modified buffer:
#   - :q  prompts: "Save changes?"
#   - :q! discards
#   - :wq saves and closes
#   - :hide (with 'hidden' set) keeps buffer in memory

# Close every window except the current:
:only            # or Ctrl-w o
# Errors if other windows have unsaved changes:
:only!           # force-close other windows

# When you close the last window, Vim exits.
# To prevent accidental exit:
:q! on last window still exits Vim.
13

标签页

标签页创建

:tabnew/:tabedit 创建新标签页。vim -p file1 file2 在启动时将每个文件打开在自己的标签页中。每个标签页包含一个或多个窗口——标签是布局,不是单个文件。Ctrl-w T 将当前窗口移入新标签页。标签非常适合分隔不相关的工作上下文(如每个项目区域一个标签)。

vim
:tabnew             # new tab with empty buffer
:tabedit file.txt   # new tab opening file
:tab split          # new tab with current buffer in a split
:tabfind file.txt   # new tab, find file in 'path'

# Open multiple files in tabs at startup:
vim -p file1.txt file2.txt file3.txt

# From inside Vim, open files in tabs:
:args *.py | argdo tabe %

# Or one-by-one:
:tabe file1.txt
:tabe file2.txt

# Keyboard shortcut (common mapping):
:nmap <C-t> :tabnew<CR>

# Open current buffer in a new tab (move window):
#   Ctrl-w T    (move current window to a new tab page)

# Each tab page has its own window layout
# (multiple splits/windows per tab allowed)

标签页导航

gt/gT 切换标签页(普通模式)。Ngt 直接跳到标签 N(从 1 开始)。:tabs 列出所有标签页及其窗口。常见映射 Ctrl-Tab/Ctrl-S-Tab 或 <leader>1/2/3 用于快速标签切换。标签默认循环。每个标签维护自己的窗口布局(分割等)。

vim
:tabnext / :tabn   # next tab
:tabprevious / :tabp  # previous tab
:tabfirst / :tabr     # first tab
:tablast / :tabl      # last tab
gt                   # next tab (Normal mode)
gT                   # previous tab
3gt                  # go to tab 3
:tabn 3              # go to tab 3 (Ex command)

# Common mappings (Vim default):
:nmap <C-Tab> :tabnext<CR>
:nmap <C-S-Tab> :tabprevious<CR>

# Or use leader keys:
:nmap <leader>1 1gt
:nmap <leader>2 2gt
:nmap <leader>3 3gt

# Cycle behavior:
#   gt at last tab wraps to first
#   gT at first tab wraps to last
# To disable wrap:
:set tabpagemax=50  # max tabs (not wrap-related)

# Show all tabs:
:tabs   # list tabs with their windows

标签页操作

:tabc 关闭当前标签;:tabo 关闭其他所有标签。:tabm N 将当前标签移到位置 N(0 = 第一个)。:tabdo 在每个标签中运行 Ex 命令——对跨标签工作区的全局操作有用。关闭标签会关闭其所有窗口;设置了 'hidden' 时已修改缓冲区会被保留。

vim
:tabclose / :tabc   # close current tab
:tabclose! / :tabc!  # force close (discard changes)
:tabonly / :tabo     # close all OTHER tabs
:tabonly! / :tabo!   # force close others

# Reorder tabs:
:tabmove / :tabm 0   # move current tab to position 0 (first)
:tabm              # move to last position
:tabm 2            # move to position 2
:tabm +1           # move right by 1
:tabm -1           # move left by 1

# Quick keyboard reordering (no default, common mapping):
:nmap <leader>tm :tabmove<Space>

# Run a command in every tab:
:tabdo cmd         # run cmd in every tab page
# Example:
:tabdo %s/old/new/ge   # substitute in every tab's first window

# Each tab can hold multiple windows/splits.
# 'Tabclose' closes all windows in the tab.

标签页布局与多窗口

每个标签页维护独立的窗口布局(分割、大小、位置)。每个标签可以有多个窗口。Ctrl-w T 将窗口移入自己的新标签页。:set showtabline=2 始终显示标签栏。标签最适合用作工作区分隔符(如每个项目区域一个标签),每个都有自己的布局。

vim
# Each tab page has its own window layout.

# Create a tab with a 3-window layout:
:tabnew            # new tab
:vsplit            # split vertically (2 windows)
:split             # split horizontally (3 windows)
# Now tab has: [win1] [win2]
#                      [win3]

# Add a buffer to current tab:
:e file.txt        # replaces current window's buffer

# Move a window to its own tab:
Ctrl-w T           # window becomes a new tab page

# Show tab line (always):
:set showtabline=2  # 0=never, 1=multi-tab, 2=always

# Tab page-local options:
:setlocal ...       # option applies only to current tab's windows
:tabdo setlocal ... # apply across all tabs

# Tab label customization (status line):
:set guitablabel=%t  # show filename in GUI tab label

# Common use case: one tab per project area:
#   Tab 1: source files
#   Tab 2: tests
#   Tab 3: docs
# Each with its own split layout.

标签页选项与标签

showtabline 控制标签栏(0=从不,1=仅多标签时,2=始终)。你可以用返回格式字符串的函数完全自定义 tabline——用于显示修改状态、缓冲区名、标签编号。guitablabel/guitabtooltip 自定义 GUI Vim。标签从 1 开始编号。

vim
:set showtabline=2    # always show tab bar (0=never, 1=auto)
:set tabpagemax=50    # max tabs creatable
:set hidden           # allow switching tabs with unsaved changes

# Custom tab labels (terminal Vim):
:set tabline=%!MyTabLine()
function! MyTabLine()
  let s = ''
  for i in range(tabpagenr('$'))
    let n = i + 1
    let s .= '%' . n . 'T'
    let s .= (n == tabpagenr() ? '%#TabLineSel#' : '%#TabLine#')
    let s .= ' [' . n . '] '
    let buflist = tabpagebuflist(n)
    let bufname = bufname(buflist[0])
    let s .= empty(bufname) ? '[No Name]' : fnamemodify(bufname, ':t')
    let s .= ' '
  endfor
  let s .= '%T%#TabLineFill#'
  return s
endfunction

# GUI tab labels:
:set guitablabel=%t   # show filename
:set guitabtooltip=%f # show full path on hover

# Quickly switch with number:
:nmap <leader>t :tabs<CR>

标签页命令

:tabs 列出标签及其窗口。:tabdo 在每个标签中运行 Ex 命令。:tab ball 为每个缓冲区打开自己的标签。<C-w>gf 在新标签中打开光标下的文件。使用标签分组相关文件(每个功能/区域一个标签)。对于跨标签操作,:tabdo 是主力——与 windo 配合用于多窗口标签。

vim
:tabs                # list all tabs and their windows
:tabdo cmd           # run cmd in every tab
:tab ball            # open each buffer in its own tab
:tab help subject    # open help in a new tab
:tab split           # split current window into new tab

# Quickly duplicate a tab's layout:
# (no built-in, but possible with sessions)
# Or manually: create a new tab, re-open buffers

# Open file under cursor in a new tab:
:nmap gf :tabe <cfile><CR>
# (or use the built-in <C-w>gf)

# Common workflow:
# 1. Group related files in tabs
# 2. Use gt/gT to switch
# 3. Use :tabdo for cross-tab operations

# Save all tabs' buffers:
:tabdo windo w   # write every window in every tab
# (or simpler)
:wall            # write all buffers (across all tabs)

# Close all tabs but keep buffers:
# (no single command — use :tabo + :b then :bd)
14

配置(.vimrc)

.vimrc 基础

$MYVIMRC 是你的 vimrc 文件路径——:edit $MYVIMRC 打开它,:source $MYVIMRC 在编辑后重新加载。runtimepath 控制 Vim 在哪里查找语法文件、插件等。使用 :verbose set option? 查看选项最后在哪里设置的——对调试冲突插件极为有用。

vim
# vimrc location:
#   Unix:    ~/.vimrc       (or ~/.vim/vimrc)
#   Windows: ~/_vimrc       (or ~/vimfiles/vimrc)
#   Neovim:  ~/.config/nvim/init.vim

# Reload vimrc after editing:
:source $MYVIMRC    # or :so %
# Edit vimrc quickly:
:edit $MYVIMRC      # $MYVIMRC = path to your vimrc

# Runtime path (where Vim looks for scripts):
:set runtimepath?
# Add a custom path:
:set runtimepath+=~/.vim/custom

# Conditional config (only in terminal Vim):
if !has('gui_running')
  set mouse=a
endif

# Per-filetype settings (in vimrc or ftplugin/):
:autocmd FileType python setlocal expandtab shiftwidth=4

# Check where an option was set:
:verbose set tabstop?

基本设置

这些是常见的生活质量设置。relativenumber + number 显示混合编号——相对于光标的行号便于跳转,当前行显示绝对行号。showmatch 闪烁匹配的括号。wildmenu 用菜单增强 :命令 补全。scrolloff 在光标周围保持可见上下文——永不在屏幕边缘编辑。

vim
set nocompatible      # Vim mode (not Vi)
set number            # show line numbers
set relativenumber    # relative line numbers (great for jumps)
set cursorline        # highlight current line
set cursorcolumn      # highlight current column
set showcmd           # show partial commands in status
set showmode          # show current mode (insert, visual...)
set showmatch         # highlight matching brackets
set wildmenu          # better command-line completion
set wildmode=longest:full,full  # completion style
set laststatus=2      # always show status line
set ruler             # show cursor position
set backspace=indent,eol,start  # backspace through everything
set encoding=utf-8    # default encoding
set scrolloff=5       # keep 5 lines above/below cursor
set sidescrolloff=5   # keep 5 cols left/right of cursor
set mouse=a           # enable mouse (all modes)

缩进

tabstop 控制制表符显示为多少列。shiftwidth 是 <<、>> 和自动缩进使用的缩进量。softtabstop 使退格键在 expandtab 关闭时也删除正确的列数。expandtab 将制表符转换为空格——推荐以保持一致性。:retab 在更改设置后转换现有的制表符/空格。

vim
set autoindent       # copy indent from previous line
set smartindent      # smart auto-indent for code (basic)
set cindent          # C-style indenting (more language-aware)
set expandtab        # use spaces instead of tabs
set tabstop=4        # tab key inserts 4-column tabs visually
set shiftwidth=4     # indent/outdent amount (<<, >>, autoindent)
set softtabstop=4    # backspace removes 4 cols even with tabs
set shiftround       # round indent to multiple of shiftwidth
set smarttab         # tab key respects shiftwidth at line start

# Toggle tabs vs spaces:
:set expandtab!     # toggle
:retab              # convert existing tabs<->spaces

# Filetype-specific indent:
:filetype plugin indent on
# Then in ~/.vim/ftplugin/python.vim:
#   setlocal expandtab shiftwidth=4

# Quick indent/dedent in Normal mode:
>>     # indent current line
<<     # dedent current line
5>>    # indent 5 lines

搜索设置

ignorecase + smartcase 是推荐的组合:除非你的模式包含大写字母,否则不区分大小写。hlsearch 高亮所有匹配;incsearch 在输入时增量显示匹配。常见映射:<leader>h 切换 hlsearch,或 :noh 清除当前高亮。Vim 8.2+ 可通过 shortmess 显示搜索计数。

vim
set ignorecase       # case-insensitive search
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 search around end of file (ws)

# Toggle search highlight:
:nmap <leader>h :set hlsearch!<CR>
# Or clear current highlight:
:nmap <leader>nh :nohlsearch<CR>

# Show search count (Vim 8.2+):
:set shortmess-=S
:set shortmess+=S    # search stats in command line

# Search in visual selection:
:vmap * y/\V<C-R>"<CR>

# Use very-magic mode by default (advanced):
# No built-in, but a search mapping can prepend \v

# Improved '*' (don't jump on first press):
:nmap * *N

# Disable highlighting until next search:
:nohlsearch

显示设置

wrap + linebreak 在单词边界(而非词中)折行以提高可读性。list + listchars 显示制表符、尾随空白和不间断空格。colorcolumn 标记一列(如 80 表示行长度限制)。termguicolors 在现代终端中启用真 24 位颜色。splitright/splitbelow 控制新分割出现的位置。

vim
set wrap             # wrap long lines
set linebreak        # wrap at word boundaries (not mid-word)
set textwidth=80     # auto-wrap at 80 chars when typing
set showbreak=+++    # marker for wrapped lines
set list             # show invisible chars
set listchars=tab:>-,trail:.,extends:>,precedes:<
set fillchars=fold:-,vert:\|   # chars for folds/vsplits
set colorcolumn=80   # highlight column 80
set signcolumn=yes   # always show sign column (LSP)
set conceallevel=0   # don't conceal chars
set splitright       # new vsplit on the right
set splitbelow       # new split below
set termguicolors    # 24-bit color (if terminal supports)
set background=dark  # or 'light' — adjusts color scheme

# Color scheme:
:colorscheme desert
# Or with a plugin (e.g. vim-colors-solarized):
:colorscheme solarized

映射按键

始终使用 *noremap 变体(nnoremap、vnoremap 等)作为个人映射——它们不递归,避免意外行为。<leader> 是可自定义的前缀键(默认反斜杠;许多人设为逗号或空格)。<CR> 是回车,<Esc> 是退出,<C-x> 是 Ctrl-X。为常见命令如保存/退出映射 leader 快捷方式。

vim
# Map families:
:map      # normal, visual, select, operator-pending
:nmap     # normal mode only
:imap     # insert mode only
:vmap     # visual + select mode
:xmap     # visual mode only
:smap     # select mode only
:omap     # operator-pending mode only
:cmap     # command-line mode only
:tmap     # terminal mode only

# Non-recursive (RECOMMENDED):
:nnoremap    # normal mode, non-recursive
:vnoremap    # visual mode, non-recursive
:inoremap    # insert mode, non-recursive
:cnoremap    # command-line mode, non-recursive

# Examples:
nnoremap <leader>w :w<CR>         # save with leader-w
nnoremap <leader>q :q<CR>         # quit
inoremap jk <Esc>                 # Esc with jk
nnoremap <C-h> <C-w>h              # window nav

# Leader key (default is \):
:let mapleader = ','
:let maplocalleader = '\\'

# Special keys:
# <CR> <Esc> <Tab> <Space> <C-x> <A-x> <M-x> <F1>-<F12>
# <Up> <Down> <Left> <Right> <leader> <localleader>
15

插件(vim-plug)

vim-plug 基础

vim-plug 是最流行的现代插件管理器。在 call plug#begin 和 plug#end 之间声明插件。每行 Plug 指定一个 GitHub 仓库(user/repo)。添加插件后,:source $MYVIMRC 然后 :PlugInstall 下载它们。你可以固定到标签、分支或提交以保证稳定性。插件安装到 'plugged' 目录。

vim
# Installation (Unix):
# curl -fLo ~/.vim/autoload/plug.vim --create-dirs \
#     https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim

# In vimrc:
call plug#begin('~/.vim/plugged')
  Plug 'junegunn/fzf.vim'           " GitHub repo: user/repo
  Plug 'tpope/vim-surround'         " another plugin
  Plug 'morhetz/gruvbox'            " colorscheme
  Plug 'preservim/nerdtree'         " file explorer
call plug#end()

# After editing vimrc:
:source $MYVIMRC    # or restart Vim
:PlugInstall        # install all declared plugins

# Plugin directory structure:
#   ~/.vim/plugged/<plugin-name>/

# Pin to a specific commit/tag/branch:
Plug 'tpope/vim-fugitive', { 'tag': 'v3.0' }
Plug 'user/repo', { 'branch': 'dev' }
Plug 'user/repo', { 'commit': 'abc123' }

插件管理命令

:PlugInstall 安装新插件,:PlugUpdate 更新所有,:PlugClean 移除未列出的,:PlugStatus 显示它们的状态。'do' 钩子在安装后运行命令——用于需要构建或安装二进制文件的插件。'on'/'for' 选项启用延迟加载(仅在命令/文件类型触发时加载)——改善启动时间。

vim
:PlugInstall        # install newly added plugins
:PlugUpdate         # update all plugins
:PlugClean          # remove plugins no longer in vimrc
:PlugStatus         # show plugin status (loaded, errored)
:PlugUpgrade        # update vim-plug itself
:PlugDiff           # review changes from last update
:PlugSnapshot       # generate a snapshot of plugin versions

# Post-install hooks:
Plug 'user/repo', { 'do': ':GoInstallBinaries' }
Plug 'user/repo', { 'do': './install.sh' }
Plug 'user/repo', { 'do': 'npm install' }

# Lazy loading (load only when needed):
Plug 'user/repo', { 'on': 'CommandName' }    " on command
Plug 'user/repo', { 'for': 'python' }        " for filetype
Plug 'user/repo', { 'on': ['Cmd1', 'Cmd2'] }

# Frozen plugin (don't update):
Plug 'user/repo', { 'frozen': 1 }

# Disable a plugin temporarily:
# (no built-in, but comment out the Plug line and :PlugClean)

常用必备插件

常用必备:fzf 用于模糊查找文件/缓冲区/文本(比 :find 快得多),vim-surround 用于编辑文本周围的引号/括号/标签,vim-fugitive 用于 Git 集成,vim-commentary 用于快速注释(gc 操作符),NERDTree 用于文件浏览器侧边栏,vim-airline 用于更好的状态行,以及像 gruvbox 这样的好配色方案。

vim
" Fuzzy finder (requires fzf binary):
Plug 'junegunn/fzf', { 'do': { -> fzf#install() } }
Plug 'junegunn/fzf.vim'
"   :Files     :GFiles     :Buffers    :Rg

" Surround (cs/ds/ys for surroundings):
Plug 'tpope/vim-surround'
"   cs"'    change " to '
"   ds"     delete surrounding "
"   ysiw)   surround word with ()

" Fugitive (Git integration):
Plug 'tpope/vim-fugitive'
"   :G       :Gstatus   :Gcommit   :Gpush

" Commentary (gc to toggle comments):
Plug 'tpope/vim-commentary'
"   gcc      comment line
"   gc{motion}  comment motion

" NERDTree (file explorer sidebar):
Plug 'preservim/nerdtree'
"   :NERDTree  :NERDTreeToggle

" Airline (status line):
Plug 'vim-airline/vim-airline'

" Gruvbox (colorscheme):
Plug 'morhetz/gruvbox'

插件配置

使用 'on'/'for' 延迟加载插件——它们仅在触发时加载,改善启动时间。插件特定配置通常使用 vimrc 中设置的 g:pluginname_var 变量,或 ~/.vim/after/plugin/ 中的文件。'do' 钩子在安装/更新后运行构建步骤。在 vimrc 中设置 g:loaded_pluginname=1 可完全阻止插件加载。

vim
" Lazy load on command:
Plug 'tpope/vim-fugitive', { 'on': 'Git' }

" Lazy load on filetype:
Plug 'fatih/vim-go', { 'for': 'go' }

" Multiple triggers:
Plug 'junegunn/fzf.vim', { 'on': ['Files', 'Buffers', 'Rg'] }

" Post-install hook:
Plug 'dense-analysis/ale', { 'do': 'npm install' }

" Configuration AFTER plugin loads:
Plug 'preservim/nerdtree'
let g:NERDTreeShowHidden = 1
let g:NERDTreeWinSize = 30
nmap <leader>n :NERDTreeToggle<CR>

" Configuration in AFTER/plugin-name.vim:
" (alternative to setting g: vars in vimrc)
" ~/.vim/after/plugin/nerdtree.vim:
"   let g:NERDTreeShowHidden = 1

" Disable plugin entirely:
let g:loaded_nerdtree = 1   " prevent loading

插件加载与运行时

Vim 在启动时按目录顺序加载 plugin/*.vim 文件,然后最后加载 after/plugin/*.vim(用于覆盖)。:scriptnames 列出所有加载的脚本——对调试极为有用。:verbose map <key> 显示映射在哪里定义;:verbose set option? 对选项做同样的事。autoload/ 文件在其函数首次调用时延迟加载。

vim
" Plugin loading sequence:
" 1. Vim starts, reads vimrc
" 2. plug#begin / plug#end registers plugins
" 3. Runtime path is updated to include plugin dirs
" 4. plugin/*.vim files are sourced (in directory order)
" 5. after/plugin/*.vim files are sourced last

" Check if a plugin is loaded:
:echo exists('g:loaded_fugitive')
:scriptnames        # list all sourced scripts in order

" Manually source a plugin:
:source ~/.vim/plugged/foo/plugin/foo.vim

" Find where a mapping was defined:
:verbose map <leader>w

" Find where an option was set:
:verbose set formatprg?

" Plugin runtime files:
"   plugin/      - loaded at startup
"   ftplugin/    - loaded for specific filetypes
"   syntax/      - syntax highlighting
"   indent/      - indentation rules
"   autoload/    - lazily-loaded functions
"   after/       - sourced last (overrides)

" Helptags for plugin docs:
:Helptags   (vim-plug generates these on install)

替代插件管理器

除 vim-plug 外,选项包括 Vundle(类似 API,功能较少)、Pathogen(最简单——只是将 bundle 目录添加到 runtimepath)、Dein.vim(快速、专注延迟加载)和 Vim 8+ 的内置包(无需插件管理器——将插件放在 pack/<name>/start/)。对于 Neovim 用户,Packer(基于 Lua)很受欢迎。内置包适合极简主义者。

vim
" 1. Vundle (older, similar to vim-plug):
" set rtp+=~/.vim/bundle/Vundle.vim
" call vundle#begin()
"   Plugin 'tpope/vim-surround'
" call vundle#end()

" 2. Pathogen (just adds dirs to runtimepath):
" Execute: 
"   git clone https://github.com/tpope/vim-pathogen ~/.vim/bundle
" In vimrc:
"   execute pathogen#infect()
" Then clone plugins into ~/.vim/bundle/

" 3. Dein.vim (modern, lazy-loading focused):
" set runtimepath+=~/.cache/dein/repos/github.com/Shougo/dein.vim
" if dein#load_state('~/.cache/dein')
"   call dein#begin('~/.cache/dein')
"     call dein#add('tpope/vim-surround')
"   call dein#end()
"   call dein#save_state()
" endif

" 4. Vim 8+ built-in packages (NO plugin manager needed):
" Place plugins in:
"   ~/.vim/pack/<name>/start/<plugin>/    " loaded at startup
"   ~/.vim/pack/<name>/opt/<plugin>/      " loaded on demand
" Load an opt plugin: :packadd <plugin>

" 5. Packer (Neovim Lua):
"   require('packer').startup(function()
"     use 'tpope/vim-surround'
"   end)
16

搜索与导航

内置搜索

内置搜索使用 / 和 ? 加 Vim 正则(用 \v 启用 very-magic)。搜索偏移(/foo/e 将光标放在匹配末尾)在 n/N 之间持久——用于在每次匹配后将光标放在正确位置。\%V 将搜索限制在当前可视选区内。/ 然后 Up 调出搜索历史;:history / 列出它。

vim
/pattern      # search forward
?pattern      # search backward
n / N         # next / previous match
* / #         # search word under cursor (forward / backward)
g* / g#       # search word under cursor (substring match)

# Search with offsets:
/foo/e        # cursor on last char of match
/foo/e+1      # cursor 1 char past match
/foo/b-1      # cursor 1 char before match
/foo/s+2      # cursor 2 chars after start of match

# Search in current visual selection:
# 1. Visually select (v/V/Ctrl-v)
# 2. Press /  ->  search forward
# 3. Pattern auto-confines to selection? NO.
#    Workaround: /\%Vpattern  (\%V = inside visual selection)

# Repeat last search:
/ then Up arrow    # recall from history
:history /         # list search history

# Search offset persists with n/N.

grep 与 Quickfix

:grep 运行外部 grep(可通过 'grepprg' 配置)并将结果放入 quickfix 列表。:copen 显示列表;:cn/:cp 导航。设置 grepprg=rg --vimgrep 使其使用 ripgrep——比 grep 快得多,默认设置更好。:cdo 在每个 quickfix 条目上运行 Ex 命令——非常适合跨匹配的批量替换。

vim
# Built-in :grep (uses external grep):
:grep pattern *.py        # search Python files
:grep -r pattern .        # recursive search
:grepadd pattern *.c      # append to existing quickfix list

# After :grep, results appear in the quickfix window:
:copen / :cw   # open quickfix window (cw only if non-empty)
:cnext / :cn   # next match
:cprev / :cp   # previous match
:cfirst / :clast  # first / last match
:cc N          # jump to match N
:cclose        # close quickfix window
:clist         # list all matches (no window)

# Customize the grep program:
:set grepprg=rg\ --vimgrep   " use ripgrep instead
:set grepformat=%f:%l:%c:%m,%f:%l:%m

# With ripgrep, super fast:
:grep pattern   # uses grepprg

# Multi-file edit from quickfix:
:cdo s/old/new/ge | update

vimgrep 命令

:vimgrep 使用 Vim 的内部搜索引擎——比外部 grep 慢但可移植(在任何操作系统上无需外部工具即可工作)并使用 Vim 正则语法。模式放在 // 分隔符之间;**/*.py 递归匹配 Python 文件。结果填充 quickfix 列表——:copen 查看,:cn/:cp 导航。使用 :vimgrepadd 追加到现有列表。

vim
# :vimgrep uses Vim's internal search (slower than
# external grep, but uses Vim regex and works everywhere).

:vimgrep /pattern/ **/*.py     # search recursively
:vimgrep /pattern/ *.txt       # current directory
:vimgrep /pattern/ file.txt    # single file
:vimgrep /\cFoo/ **/*          # case-insensitive

# Append to existing list:
:vimgrepadd /pattern/ *.c

# Open results in quickfix:
:copen

# Notes:
#   - ** means recursive
#   - Pattern uses Vim regex (same as /search)
#   - Slower than external grep (loads each file into Vim)
#   - Works without external tools (portable)

# Limit search to current buffer only:
:vimgrep /pattern/ %

# Search in visual selection across files: not directly,
# use :grep or external tool instead.

# Jump directly to first match (skip quickfix):
#   :vimgrep /pattern/ **/* | cfirst

ctags 集成

ctags 生成标识符(函数、类等)索引,Vim 用它进行代码导航。Ctrl-] 跳到光标下的定义;Ctrl-T 返回。:ts 在有歧义时列出匹配的标签。在项目根目录运行 ctags -R . 生成标签。添加 `set autochdir` 或配置 'tags' 路径以实现项目感知导航。

vim
# Generate tags file (run in shell):
#   ctags -R .
# (or with Universal Ctags for more languages)

# In Vim, the tags file is auto-read from 'tags' option:
:set tags?        # default: ./tags,./TAGS,tags,TAGS

# Jump to definition (uses tags):
Ctrl-]            # jump to tag under cursor
:tag funcname     # jump to specific tag
:ts funcname      # list matching tags, pick one
:tn / :tp         # next / previous matching tag
:tl               # last tag
Ctrl-T            # pop back from tag jump
:tags             # show tag stack
:pop              # same as Ctrl-T

# Split window before jumping:
Ctrl-W ]          # split, then jump to tag
:stag funcname    # split, then jump

# Preview tag (open in preview window):
Ctrl-W }          # preview tag under cursor
:ptag funcname    # preview specific tag

# Regenerate tags from inside Vim:
:!ctags -R .

cscope 集成

cscope 提供比 ctags 更多的代码导航——它可以查找调用者、被调用者和包含关系,而不仅仅是定义。用 `cscope -Rb` 构建。在 Vim 中,:cscope add 链接数据库,然后 :cs find X symbol 查询。结合 cscopequickfix,结果填充 quickfix 列表便于导航。

vim
# Build cscope database (shell):
#   cscope -Rb       # creates cscope.out
# Or:
#   find . -name '*.c' > cscope.files
#   cscope -b

# In Vim, add the database:
:cscope add cscope.out
:cscope add /path/to/cscope.out /path/to/project

# Query types (use :cs find X symbol):
#   0 or s: symbol (this token)
#   1 or g: definition (same as Ctrl-])
#   2 or d: functions called by this function
#   3 or c: functions calling this function
#   4 or t: string (text)
#   6 or e: egrep pattern
#   7 or f: file
#   8 or i: includes (files #including this file)

:cs find 0 main         # find symbol 'main'
:cs find 3 myfunc       # find callers of myfunc
:cs find e pattern      # egrep search

# Auto-jump on result:
:set cscopequickfix=s-,c-,d-,i-,t-,e-,g-,f-

# Reset connection:
:cscope reset
:cscope kill <connection>

Netrw 文件浏览器

Netrw 是 Vim 的内置文件浏览器——无需插件。:Explore 在当前窗口打开它;:Lexplore 切换左侧边栏。在里面,用 Enter 打开,'-' 上级,'D' 删除,'R' 重命名,'d' 创建目录,'%' 创建文件。用 g:netrw_* 变量自定义(树形视图、隐藏横幅、窗口大小)。

vim
# Netrw is Vim's built-in file explorer.

:Explore / :E       # open explorer in current window
:Sexplore / :Sex    # explorer in horizontal split
:Vexplore / :Vex    # explorer in vertical split
:Lexplore / :Lex    # explorer in left-side toggle

# Inside Netrw:
<CR>      # open file/directory
-         # go up one directory
D         # delete file
R         # rename file
d         # create directory
%         # create new file
i         # cycle view (thin/long/wide/tree)
r         # reverse sort
s         # select sort key
x         # open with system viewer
gh        # toggle hidden files
q         # close explorer

# Useful settings:
:let g:netrw_liststyle = 3      # tree view by default
:let g:netrw_winsize = 25       # window size 25%
:let g:netrw_banner = 0         # hide banner

# Toggle explorer:
:nmap <leader>e :Lexplore<CR>
17

高级移动

字符查找 (f/F/t/T)

f/F 在当前行查找下一个/上一个字符(光标落在其上);t/T 移动到其前/后。; 在相同方向重复上次的 f/F/t/T,, 在相反方向。与操作符结合:dt, 删除到(不包括)下一个逗号,df. 删除到并包括下一个句号。快速精确的行内导航。

vim
f{x}    # find next char 'x' on line (cursor ON x)
F{x}    # find previous char 'x' (cursor ON x)
t{x}    # till next char 'x' (cursor BEFORE x)
T{x}    # till previous char 'x' (cursor AFTER x)
;       # repeat last f/F/t/T (same direction)
,       # repeat last f/F/t/T (opposite direction)

# Examples (cursor on 'a' in "hello world abc"):
#   fw  ->  cursor on 'w' (world)
#   tw  ->  cursor on space before 'w'
#   Fo  ->  cursor on 'o' (hello) - backwards
#   ;   ->  next 'w'... or next match of last

# Combined with operators:
dt,    # delete up to (not including) next ','
df.    # delete up to AND including next '.'
ct;    # change up to next ';'
yt:    # yank up to next ':'

# Find on a long line (no built-in multi-line f):
#   Use :s or search for multi-line patterns.

# Set 'hlsearch' to highlight f/F/t/T targets:
:set hlsearch   " doesn't highlight f, but / does.

句子与段落移动

( 和 ) 按句子移动(句子以 . ! ? 加空白结束);{ 和 } 按段落移动(空行分隔)。对于折行的行,gj/gk/g0/g$ 在视觉屏幕行上操作,而 j/k/0/$ 在逻辑行上操作——'wrap' 开启时至关重要。将 ( ) { } 与操作符结合:das 删除句子,yap 复制段落。

vim
(     # previous sentence start
)     # next sentence start
{     # previous paragraph start (blank-line delimited)
}     # next paragraph start

# Sentence boundaries: ends with . ! ? followed by
# whitespace or end of paragraph.

# Paragraph: block of lines separated by blank lines.

# Examples:
#   Hello world.  This is a sentence.
#   ^         ^  ^
#   (         )  ( moves to sentence starts

# Combine with operators:
das   # delete a sentence
cis   # change inside sentence
yap   # yank a paragraph
dip   # delete inside paragraph

# Move by visual line (with 'wrap' on):
gj    # down one screen line (wrap-aware)
gk    # up one screen line
g0    # start of screen line
g$    # end of screen line
g_    # last non-blank of screen line

# These respect wrapping where j/k don't.

匹配括号 (%)

% 在匹配的括号之间跳转(默认:()、[]、{})。如果 'matchpairs' 设置适当,它也作用于 #if/#endif 和 /* */。与操作符结合:d% 从光标删除到匹配,v% 选择它。[( [{ ]) ]} 跳到不匹配的括号——用于查找外层函数/类块。

vim
%    # jump to matching bracket: ( ) [ ] { }
     # also works on #if/#endif, /* */ with matching pairs

# In Normal mode:
%    # if cursor on (, jumps to matching )
%    # if cursor on ), jumps to matching (

# Combine with operators:
d%    # delete from cursor to matching bracket
c%    # change from cursor to matching bracket
y%    # yank from cursor to matching bracket
v%    # visually select from cursor to matching bracket

# Extend matching to other pairs:
:set matchpairs+=<:>
:set matchpairs+=«:»
# Default: (:),[:],{:}

# Move to enclosing bracket:
#   [(    # back to unmatched (
#   ])    # forward to unmatched )
#   [{    # back to unmatched {
#   ]}    # forward to unmatched }
#   [} / ]{  similar for braces

# Example: jump to outer function's opening brace:
[{   " previous unmatched {

屏幕移动

H/M/L 跳到可见屏幕的顶部/中间/底部。Ctrl-f/b 滚动整页;Ctrl-d/u 半页(不那么令人迷失);Ctrl-e/y 滚动一行而不移动光标。zz/zt/zb 将光标行重新定位到屏幕中间/顶部/底部——保持上下文的关键。'scrolloff' 确保光标永不到达屏幕边缘。

vim
H    # top of screen (High) — first non-blank
M    # middle of screen
L    # bottom of screen (Low) — first non-blank
N H  # Nth line from top (e.g. 5H = 5 lines from top)
N L  # Nth line from bottom

# Scrolling:
Ctrl-f   # forward (down) full screen
Ctrl-b   # backward (up) full screen
Ctrl-d   # down half screen
Ctrl-u   # up half screen
Ctrl-e   # scroll down 1 line (cursor stays)
Ctrl-y   # scroll up 1 line (cursor stays)

# Reposition cursor on screen:
zz   # cursor line to middle of screen
zt   # cursor line to top of screen
zb   # cursor line to bottom of screen
z.   # cursor to middle, first non-blank
z+   # cursor to bottom, scroll up (like zb but accepts count)

# Keep cursor off edges:
:set scrolloff=5   # always 5 lines above/below cursor
:set sidescrolloff=5  # 5 cols left/right of cursor

滚动绑定与同步

:set scrollbind 同步窗口间的滚动——在你想同步的每个窗口中设置它。:vimdiff 以 scrollbind 开启、语法高亮差异的方式打开两个文件,并用 [c/]c 在差异块间导航。do(diff obtain)从另一个窗口拉取更改,dp(diff put)推送它们。非常适合比较文件版本。

vim
# Bind windows to scroll together:
:set scrollbind      # current window syncs scroll
:set syncbind        # force immediate sync
:scrollbind          # same as :set scrollbind

# In each window you want synced:
#   :set scrollbind

# Unbind:
:set noscrollbind

# Common use: diff two files side by side
:vimdiff file1 file2    # opens both, scrollbound, diff highlighted
:diffthis               # mark current window as a diff
:diffoff                # turn off diff mode for current window
:diffupdate             # refresh diff highlighting

# Vertical diff split:
:vert diffsplit file2.txt

# In diff mode:
[c    # previous diff hunk
]c    # next diff hunk
do    # diff obtain (pull from other window)
dp    # diff put (push to other window)

# Bind but at different starting positions:
#   Set scrollbind AFTER scrolling to desired positions.

跳转到位置

定位光标的多种方式::N 跳到第 N 行,N% 按百分比,`.`/`"`/`[`/`<`/`>` 用于特殊标记(上次修改、上次退出、上次复制、上次可视)。Ctrl-o/Ctrl-i 在跳转列表中导航。`" 恢复文件上次关闭时的位置——结合 viminfo 设置,这跨会话持久化。

vim
:N          # go to line N (e.g. :42)
Ng / N G    # go to line N (e.g. 42G or 42gg)
gg          # first line
G           # last line
N%          # go to N% of file (e.g. 50% = middle)
:-N         # N lines before current (e.g. :-5)
:+N         # N lines after current (e.g. :+10)

# Marks and jumps:
\`.            # position of last change in this buffer
\`"            # position when file was last closed
\`[            # start of last yanked/changed region
\`]            # end of last yanked/changed region
\`<            # start of last visual selection
\`>            # end of last visual selection
\`^            # last insert position (gi goes here + insert)

# Recent jumps:
Ctrl-o      # back in jumplist
Ctrl-i      # forward in jumplist
\`'            # position before last jump (where you came from)

# File position on save:
:set viminfo^=%   # remember buffer positions across sessions

# Cursor hold (no movement for 'updatetime' ms):
# CursorHold event triggers (advanced)
18

命令行模式

范围命令

范围指定命令影响哪些行。:% 是整个文件(1,$)。. 是当前行,$ 是最后一行。'<,'> 是上次可视选区(在可视模式下按 : 时自动填充)。'a,'b 是从标记 a 到标记 b。你还可以使用搜索模式作为范围边界::/foo/,/bar/d 从匹配 foo 的行删除到匹配 bar 的行。

vim
# Range syntax: :{start},{end}command

# Range forms:
:%          # whole file (1,$)
:1,10       # lines 1-10
:.,$        # current line to end of file
:1,.        # first line to current
:'a,'b      # from mark a to mark b
:'<,'>      # last visual selection
:.          # current line only
:N          # line N only
:.,+5       # current line + 5 below
:.,-2       # current line - 2 above

# Examples:
:1,10d          # delete lines 1-10
:%s/old/new/g   # substitute whole file
:'<,'>s/old/new/g  # substitute visual selection
:.,$j           # join from current to end into one line
:5,10y a        # yank lines 5-10 into register a
:g/pattern/m$   # move matching lines to end of file

# Search-based range:
:/start/,/end/d    # delete from 'start' to 'end' patterns

文件命令

:w 保存,:e 打开新文件,:r 插入另一个文件的内容(或通过 :r !cmd 插入 shell 命令输出)。:saveas 保存并切换到新文件。:e! 重新加载文件丢弃更改。++enc/++ff 显式指定编码/行尾——用于转换文件。:browse e 打开 GUI 文件对话框(在 GUI Vim 中)。

vim
:w / :write          # save current file
:w file.txt          # save as new file
:w! file.txt         # overwrite existing file
:w >> file.txt       # append to file
:saveas / :sav file  # save as new file and switch to it
:e / :edit file      # open file (replace current buffer)
:e!                  # reload current file (discard changes)
:e #                 # edit alternate file
:r / :read file      # insert file below cursor
:r !command          # insert command output below cursor
:0r !date            # insert date at top of file

# Multiple files:
:wa / :wall          # write all buffers
:xa / :xall          # write all and quit
:qa!                 # quit all without saving

# Browse file system:
:browse e            # GUI file open dialog
:Explore             # Netrw file browser

# File encoding:
:w ++enc=utf-8       # save as UTF-8
:e ++enc=latin1 file # open with specific encoding

# File format (line endings):
:w ++ff=unix         # save as Unix line endings

缓冲区与窗口命令

所有多缓冲区/窗口/标签操作都有 Ex 命令形式。:b 切换缓冲区,:sp/:vs 分割窗口,:tabnew 创建标签。:*do 系列(bufdo、argdo、tabdo、windo、cdo)跨多个目标运行命令——对批量操作极为强大。:ls 显示缓冲区;:tabs 显示标签;:args 显示参数列表。

vim
# Buffer commands:
:b N / :buffer N     # switch to buffer N
:b name              # switch by name (tab-completes)
:bd / :bdelete       # delete buffer
:bw / :bwipeout      # wipe buffer (clears marks)
:ls / :buffers       # list buffers
:bn / :bp            # next / previous buffer
:b#                  # alternate buffer

# Window commands:
:sp / :split         # horizontal split
:vs / :vsplit        # vertical split
:new                 # split with empty buffer
:close               # close window
:only                # close all other windows
:qa / :qa!           # quit all windows

# Tab page commands:
:tabnew              # new tab
:tabe file           # new tab with file
:tabc                # close tab
:tabo                # close other tabs
:tabs                # list tabs
:tabn / :tabp        # next / previous tab

# Argument list commands:
:args                # show arg list
:n / :next           # next arg file
:prev                # previous arg file
:argdo cmd           # run cmd on each arg file

设置选项

:set 用 ! 切换布尔选项,用 = 设置值,用 +=/-= 追加/移除,用 & 重置为默认值。:set option? 显示当前值。:setlocal 设置缓冲区/窗口局部值(不影响其他缓冲区)。不带参数的 :set 列出所有已更改的选项;:set all 显示所有。Tab 补全选项名。

vim
:set option           # turn boolean option on
:set nooption        # turn boolean option off
:set option!         # toggle boolean
:set option?         # show current value
:set option=value    # set value
:set option+=value   # append to value
:set option-=value   # remove from value
:set option&         # reset to default
:set all             # show all options
:set                 # show changed options

# Local vs global:
:setlocal option     # buffer/window-local only
:setglobal option    # global value

# Examples:
:set number          # show line numbers
:set nonumber        # hide line numbers
:set number!         # toggle
:set tabstop=4       # set tabstop to 4
:set formatoptions+=r  # add 'r' to formatoptions
:set ignorecase&     # reset ignorecase to default

# View all set options:
:set                 # shows only non-default values

# Find option with substring:
:set ic<tab>         # tab-complete (ignorecase)

映射命令

个人映射使用 *noremap 变体——它们不递归,防止意外行为。<leader> 是可自定义的前缀(默认反斜杠;许多人设为空格或逗号)。<buffer> 使映射局部于当前缓冲区。特殊键使用 <记法> 如 <CR>、<Esc>、<C-x>(Ctrl-X)、<A-x>(Alt-X)。

vim
:map <key> <action>        # generic map (most modes)
:nmap <key> <action>       # normal mode
:imap <key> <action>       # insert mode
:vmap <key> <action>       # visual mode
:xmap <key> <action>       # visual mode only
:smap <key> <action>       # select mode
:omap <key> <action>       # operator-pending mode
:cmap <key> <action>       # command-line mode
:tmap <key> <action>       # terminal mode

# Non-recursive (RECOMMENDED for personal mappings):
:nnoremap <key> <action>   # normal mode
:vnoremap <key> <action>   # visual mode
:inoremap <key> <action>   # insert mode
:onoremap <key> <action>   # operator-pending
:cnoremap <key> <action>   # command-line
:tnoremap <key> <action>   # terminal

# Buffer-local mappings:
:nnoremap <buffer> <key> <action>

# Clear a mapping:
:nunmap <key>   # or :nmapclear (clears all normal maps)

# Special keys in mappings:
# <CR> <Esc> <Tab> <Space> <BS> <C-x> <A-x> <M-x>
# <F1>-<F12> <Up> <Down> <Left> <Right>
# <leader> <localleader> <C-r> <C-w>
:let mapleader = ' '
:let maplocalleader = '\\'

实用 Ex 命令

:!cmd 运行 shell 命令;:r !cmd 将其输出插入缓冲区。:sort 排序行(u=唯一,n=数字,!=反向)。:retab 在制表符和空格之间转换。:normal 作为 Ex 运行普通模式按键——加 ! 忽略自定义映射。:%!cmd 通过外部命令管道整个文件并用输出替换它。

vim
:!command        # run shell command
:r !command     # insert command output below cursor
:!!              # repeat last :! command
:sh              # open a shell (exit to return)
:terminal / :term  # open terminal in a window
:read file       # insert file contents below cursor
:sort            # sort selected lines (visual mode)
:sort u          # sort and remove duplicates
:sort n          # numeric sort
:sort!           # reverse sort
:retab           # convert tabs to spaces (and vice versa)
:retab!          # also convert in strings
:normal {cmds}   # run normal-mode commands
:normal! {cmds}  # ignore mappings (use raw keys)

# Execute Ex commands from a register:
:@a              # run Ex commands in register 'a'
:@@              # repeat last :@

# Range + command examples:
:5,20s/old/new/g  # substitute in lines 5-20
:%!sort           # pipe whole file through sort
:'<,'>!column -t  # format visual selection as columns

# Help:
:help :command   # help on a specific command
19

Vimscript 基础

变量

Vimscript 用 let 赋值,unlet 删除。变量有作用域,由前缀指示:g:(全局)、b:(缓冲区)、w:(窗口)、t:(标签)、s:(脚本)、l:(函数局部)、v:(内置)。const 创建不可变值(Vim 8+)。exists() 检查变量是否已定义。Vim 8.2+ 支持字符串插值 $"...{expr}..."。

vim
let var = value          # assignment
let var = "hello"        # string
let var = 42             # number
let var = 3.14           # float
let var = [1, 2, 3]      # list
let var = {"key": "val"} # dictionary
unlet var                # delete a variable

# Variable scopes (prefix with letter + colon):
let g:global_var = 1     # global (everywhere)
let b:buffer_var = 1     # buffer-local
let w:window_var = 1     # window-local
let t:tab_var = 1        # tab-local
let s:script_var = 1     # script-local (this file only)
let l:local_var = 1      # function-local
let v:count              # built-in Vim variable

# Constants (Vim 8+):
const PI = 3.14159
let g:CONFIG = #{name: "vim"}  # locked dictionary

# Check if a variable exists:
if exists('var')
if exists('g:plugin_var')

# String interpolation (Vim 8.2+):
let name = "World"
echo $"Hello, {name}!"

函数

function! Name(args) ... endfunction 定义函数——! 允许重定义。a: 访问参数;a:000 是可变参数列表。默认参数(Vim 8+)使用 =value。Lambda(Vim 8+)是 {args -> expr}。function('Name') 返回可传递的函数引用。使用 :call 调用函数以利用其副作用。

vim
# Define a function:
function! MyFunc(arg1, arg2)
  echo a:arg1 . ' ' . a:arg2
  return a:arg1 + a:arg2
endfunction

# The ! in function! overrides an existing function
# (without ! Vim errors if function already exists).

# Call a function:
call MyFunc("hello", "world")
let result = MyFunc(1, 2)

# Variadic arguments (...):
function! Sum(...)
  let total = 0
  for i in a:000
    let total += i
  endfor
  return total
endfunction

# Default arguments (Vim 8+):
function! Greet(name, greeting="Hello")
  echo a:greeting . ', ' . a:name
endfunction

# Lambda / anonymous functions (Vim 8+):
let Square = {x -> x * x}
echo Square(5)   " 25

# Function as a value:
let Fn = function('MyFunc')
echo Fn(1, 2)

条件与循环

if/elseif/else/endif 用于条件。for 循环遍历列表(range(N) 生成 0..N-1),并可以从 items() 解包 [key, val] 对。while/endwhile 用于条件循环。break/continue 按预期工作。常见条件检查:has() 检查特性,exists() 检查变量/命令,filereadable() 检查文件,empty() 检查空值,type() 检查类型。

vim
# Conditionals:
if x > 10
  echo "big"
elseif x > 5
  echo "medium"
else
  echo "small"
endif

# Ternary:
let result = (x > 0) ? "positive" : "non-positive"

# Loops:
for i in range(5)       # 0,1,2,3,4
  echo i
endfor

for [key, val] in items({'a': 1, 'b': 2})
  echo key . '=' . val
endfor

while x > 0
  let x -= 1
endwhile

# Loop control:
break       # exit loop
continue    # skip to next iteration

# Match-related conditionals:
if has('python3')          # feature check
if exists(':Command')      # command exists
if filereadable('file')    # file is readable
if empty(var)              # variable is empty
if type(var) == type([])   # type check

字符串

. 连接字符串。Vim 8.2+ 添加 $"...{expr}..." 插值。len/strlen 给出字节长度;strcharlen 给出字符数(对多字节很重要)。split/join 在字符串和列表之间转换。substitute() 是编程式的 :s 命令。matchstr() 提取正则匹配。=~ 是正则匹配操作符。

vim
# String concatenation:
let s = "Hello" . " " . "World"

# String interpolation (Vim 8.2+):
let name = "Vim"
echo $"Welcome to {name}!"

# Common functions:
len("hello")              # 5
strlen("hello")           # 5 (byte length)
strcharlen("hello")       # 5 (char length)
tolower("HELLO")          # "hello"
toupper("hello")          # "HELLO"
strpart("hello", 1, 3)    # "ell" (substring)
matchstr("foo123bar", '\d\+')  # "123"
substitute("foo", 'o', '0', 'g')  # "f00"
split("a,b,c", ',')       # ['a', 'b', 'c']
join(['a', 'b', 'c'], '-')  # "a-b-c"
trim("  hello  ")         # "hello"

# printf-style formatting:
printf("%d + %d = %d", 1, 2, 3)  # "1 + 2 = 3"
printf("%05.2f", 3.14159)        # "03.14"

# Regex matching:
if "hello" =~ '^h'
  echo "starts with h"
endif

列表与字典

列表是有序的(如数组);字典是键值映射。列表切片使用 [start:end]。add/insert/remove 就地修改列表。sort/reverse 也变异。对于字典,.key 和 ["key"] 访问都有效;keys()/values()/items() 返回列表。has_key() 检查成员资格。#{...}(Vim 8+)是更简洁的字典字面量,无需引用键。

vim
# Lists (like arrays):
let nums = [1, 2, 3, 4, 5]
echo nums[0]              # 1 (first)
echo nums[-1]             # 5 (last)
echo nums[1:3]            # [2, 3, 4] (slice)

# List operations:
call add(nums, 6)         # append 6
call insert(nums, 0, 0)   # insert 0 at index 0
call remove(nums, 0)      # remove index 0
echo len(nums)            # length
call sort(nums)           # sort in place
call reverse(nums)        # reverse in place
call extend(nums, [7, 8]) # concatenate

# Iterate:
for n in nums
  echo n
endfor

# Dictionaries (like hash maps / objects):
let d = {"name": "Vim", "version": 9}
echo d.name               # "Vim"
echo d["version"]         # 9
let d.lang = "Vimscript"  # add a key
call remove(d, "version") # remove a key
echo keys(d)              # ['name', 'lang']
echo values(d)            # ['Vim', 'Vimscript']
echo items(d)             # [['name', 'Vim'], ['lang', 'Vimscript']]
echo has_key(d, "name")   # 1 (true)

# Dictionary literal (Vim 8+, cleaner):
let d2 = #{key: "value", count: 42}

自动命令与自定义命令

autocmd 在 Vim 事件(BufRead、BufWritePre、FileType、VimEnter、CursorMoved 等)上运行代码。始终将 autocmd 包装在 augroup 中并使用 autocmd! 在重新加载时清除旧定义——防止重复。自定义命令使用 :command! -nargs=N -range -bang Name action。<args>、<line1>、<line2>、<bang> 在命令定义中访问用户的输入。

vim
# Autocmds: run code on events
:autocmd BufWritePre *.md :%s/\s\+$//e   " trim trailing whitespace

# Group autocmds (recommended - prevents duplicates on reload):
augroup markdown_settings
  autocmd!
  autocmd BufRead,BufNewFile *.md setlocal spell
  autocmd FileType markdown setlocal textwidth=80
  autocmd BufWritePre *.md :%s/\s\+$//e
augroup END

# Common events:
# BufReadPost   - after loading a file
# BufWritePre   - before saving
# BufWritePost  - after saving
# FileType      - when filetype is set
# VimEnter      - after Vim startup
# CursorMoved   - cursor moved in normal mode
# InsertEnter   - entering insert mode

# Custom Ex commands:
command! -nargs=1 Grep !grep <args> %
command! -range Sum echo <line1> + <line2>
command! -bang Q quit<bang>

# Use them:
:Grep pattern
:Sum

# Show all autocmds:
:autocmd
# Show autocmds for a specific event:
:autocmd BufWritePre

这篇内容对您有帮助吗?