跳到正文

Vim 的编辑语法 ​

编辑命令是动词,移动是名词,两者相乘才是 Vim 的表达力。

动词作用于名词 ​

d 删除、c 修改、y 复制,后面都跟一个名词表示范围。p 粘贴、u 撤销、<C-r> 重做是独立动作,不接名词。

按键作用等价写法
d{w}删除到范围末尾—
c{w}删除后进入 Insertd 加 i
y{w}复制范围—
x删除光标下字符dl
s删除字符并插入cl
p粘贴到光标后—
o/O下方/上方开新行—
u撤销—
<C-r>重做—
~切换大小写—
J把下一行接到本行尾—
vim
dw     " 删一个词
d$     " 删到行尾
d0     " 删到行首
cw     " 改一个词,改完停在 Insert
yy     " 复制整行
3dd    " 删三行

dd 是 d 加 d 的特殊形式,删整行;同理 cc 改整行,yy 复制整行。理解成「动词重复一次等于作用于整行」就够用了。

计数与文本对象 ​

计数放在动词前:3w 前进三个词,5j 下移五行,7dw 删七个词,2dd 删两行。

文本对象是更精确的名词。i 表示内部,a 表示连同边界一起。

对象范围
iw/aw当前词,含或不含空格
i(/a(圆括号内部,含或不含括号
i[/a[方括号内部,含或不含括号
i"/a"双引号内部,含或不含引号
i{/a{花括号内部
it/atHTML 标签内部
ip/ap当前段落
vim
ci(    " 改括号里的内容,括号不动
ci[    " 改方括号里的内容
ci"    " 改双引号里的内容
da'    " 连引号一起删掉单引号字符串
di{    " 清空花括号内部

光标已经在括号里时,ci( 作用于包住它的那一对;光标不在括号内时,Vim 在本行向前找第一对左括号。这比先用 f( 定位再删要稳。

修一段真实的代码 ​

讲义里的 fizz buzz 有三个 bug:main 没被调用、范围从 0 开始、5 的倍数打印成 fizz。从 Normal 模式开始,全程不碰鼠标。

python
def fizz_buzz(limit):
    for i in range(limit):      # 应从 1 开始
        if i % 3 == 0:
            print("fizz", end="")
        if i % 5 == 0:
            print("fizz", end="")   # 应为 buzz
        if i % 3 and i % 5:
            print(i, end="")
        print()

def main():
    fizz_buzz(20)               # main 从未被调用
  1. G 到文件末尾,o 开新行,输入 if __name__ == "__main__": main(),<ESC> 返回
  2. /range 回车定位到 range,ww 移到 limit,i 插入 1,,<ESC> 返回
  3. e 到词尾,a 追加 + 1
  4. :6 跳到第六行,ci" 把 fizz 改成 buzz

i % 3 and i % 5 这种写法只在 Python 里成立,因为它把 0 当假。别的语言里要换成显式的两个取模比较。

重复与批量化 ​

. 重复上一次修改,包括插入的文本和删除的范围。3. 连做三次,比重新输入快得多。

vim
:%s/foo/bar/g     " 全文替换
:%s/foo/bar/gc    " 每次替换前确认
qa                " 开始录制到寄存器 a
q                 " 停止录制
@a                " 回放
@@                " 再次回放

批量改 20 个函数签名时,宏比手写脚本快:录一遍,20@a 一次跑完。跑之前先 u 确认一次录制本身没问题。

改完一个文件的习惯是先 :w,再 u 和 <C-r> 来回看几遍。练组合时先用 vimtutor 过一遍基础,再拿 VimGolf 的题目数按键:同一处修改从 12 键压到 6 键,说明你开始按「动词加名词」思考,而不是逐字敲。