前一篇只是 vim 的极简使用,现在正式进入 vim 的学习笔记。

参考:【Vim】可能是B站最系统的Vim教程

工作模式

vim 包括三种主要模式

  • Normal 模式
  • Insert 模式
  • Command 模式

以及一个临时的 Visual 模式。

启动 vim 会直接进入 Normal 模式,此时的按键不会被视作输入字符,而是视作命令,例如输入i会切换到 Insert 模式,进行正常的文本编辑状态;输入英文冒号:会切换到 Command 模式,此时光标会留在最底行,可以输入复杂的命令。

几个模式的转换如下图,Normal 模式作为转换的中枢,在其他模式中可以通过 <Esc> 键可以返回 Normal 模式,也可以使用 <Ctrl+[> 替代(默认情况下,它和<Esc>键功能完全等价)。

从 Normal 模式出发

光标移动基础

在 Normal 模式中,为了保证操作的高效连贯,我们不希望双手离开主键区,主要使用按键 hjkl 来移动光标:(当然四个方向键也可以)

  • h:左移
  • j:下移
  • k:上移
  • l:右移

整个文件尺度的移动:

  • gg:移动到当前文件的第一行
  • G:移动到当前文件的最后一行

按照行号定位:

  • {lineno}gg/{lineno}G:跳转到第 lineno
  • {percent}% 移动到当前文件对应比例的位置,例如30%

在 Command 模式中,还有如下的命令可以按行跳转(需要回车执行):

  • :0 移动到当前文件的第一行;
  • :{lineno} 移动到当前文件的第 lineno 行;
  • :$ 移动到当前文件的最后一行;

屏幕尺度下的移动:

  • H:移动到屏幕顶端的行;
  • M:移动到屏幕中部的行;
  • L:移动到屏幕底部的行;

翻页:(尽量保持光标在屏幕中的相对位置不变)

  • <Ctrl-u>:(up) 将文本上移半页
  • <Ctrl-d>:(down) 将文本下移半页
  • <Ctrl-b>:将文本上移一页
  • <Ctrl-f>:将文本下移一页

光标行定位:

  • zz:光标行居中
  • zt:光标行置于屏幕第一行
  • zb:光标行置于屏幕最后一行

进入 Insert 模式

从 Normal 模式进入 Insert 模式,常见方法如下:

  • 插入:(insert)
    • i:在光标所在字符前开始插入(光标所处的字符会被挤到后面);
    • I:在光标所在行的行首开始插入,如果行首有空格则在空格之后插入。
  • 添加:(append)
    • a:在光标所在字符后开始插入(光标会自动后移一格);
    • A:在光标所在行的行尾开始插入。
  • 替换:(substitude)
    • s:删除光标所在的字符并开始插入;
    • S:删除光标所在行并开始插入
  • 插入新行:
    • o:在光标所在行的下面创建新行插入;
    • O:在光标所在行的上面创建新行开始插入。

大小写命令对应的操作不同,但是普遍具有相关性:

  • 如果小写的操作是字符尺度的,那么大写的操作通常是行尺度的;
  • 如果小写的操作具有方向性,那么大写通常是相反方向的操作。

进入 Command 模式

Normal 模式下,可以通过 : 进入 Command 模式,此时可以输入命令,例如:

  • :help:h 显示帮助(:h xxx显示对应条目的帮助)
  • :w 保存
  • :q 退出
  • :wq 保存并退出
  • :!{cmd}:利用shell执行外部命令,支持传递参数,提供一个临时界面展示命令的输出。

在 Command 模式,输入单词开头部分,即可可以使用<Ctrl-d>获取补全提示,也可以使用tab获取补全建议。

进入 Visual 模式

Normal 模式下,输入 v/V 进入 Visual 模式,常见操作:

  • 移动光标选中文本进行操作;(v以字符为单位,V以整行为单位)
  • x:剪切
  • y:(yank) 复制
  • d:(delete) 删除
  • p:(paste) 粘贴
    • 可以退出到 Normal 模式进行
    • 如果直接在选中一部分内容的状态下进行粘贴,会直接覆盖掉当前选中的内容

除了 <Esc>,再次输入 v 或者某些操作完成后也会自动退出 Visual 模式。

移动 (Motion)

vim 在 Normal 模式下提供了各种快捷键用于快速移动光标,这些按键所绑定的行为称为移动 (Motion)。

基于单词的移动

  • w/W:(word) 移动到(下一处)单词的首字母;
  • b/B:(back) 移动到(上一处)单词的首字母;
  • e/E:(end) 移动到(下一处)单词的尾字母;
  • ge:移动到(上一处)单词的尾字母; (e的反向操作)

大小写版本的 W/B/E 区别在于分词逻辑:大写操作只按照空格分词,例如 open-source 在小写操作中视作三个单词(- 也视作一个单词),但是在大写操作中视作一个单词。

基于单词的操作对于中文并不友好,因为单词的分词是简单地基于空格等符号所进行的,中文需要根据语义分词,这一点很难做到,可能会把整个句子视作一个单词。

行内搜索与移动

行内搜索:

  • f{char}/F{char}:跳转到本行的下一个/上一个 {char} 字符位置;
  • t{char}/T{char}:跳转到本行的下一个/上一个 {char} 字符位置之前;
  • ;:继续(或切换为)向后搜索
  • ,:继续(或切换为)向前搜索

注:

  • 行内搜索只能基于指定字符,除此之外,vim 还提供了一套支持模式匹配的全局搜索命令,见下文。
  • ;/, 在跳转时仍然限于本行内部,但是可以通过插件实现跨行跳转。

基于标记的移动

vim 提供了一套标记系统,可以在特定位置打上标记,从而基于标记快速移动:

  • m{mark}:在当前位置打一个标记 markmark 要求是小写字母,例如 mm
  • `{mark}:跳转到到标记 mark

内置的特殊标记:

  • ``:上次跳转前的位置
  • `.:上次修改的位置
  • `^:上次插入的位置

在 Normal 模式中,还可以使用 gi 来直接跳转到 Insert 模式中的光标最后位置,并且直接进入 Insert 模式。

基于文本的移动

  • (/):向前/向后跳转到一个句子的开头
  • {/}:向前/向后跳转到一个段落的开头
  • %:如果光标位于括号等配对符,会跳转到匹配的符号,否则向后跳转到下一个配对符

实用的局部跳转

  • 0/$:跳转到当前行开头/末尾(含空格)
  • ^/g_:跳转到当前行的第一个/最后一个非空白字符
  • +/-:跳转到上一行/下一行的第一个空白字符

粘贴

Vim 的粘贴命令为 p/P,它们会根据复制内容的类型自动选择进行“字符粘贴”或“行粘贴”,两者的区别是在光标后或光标前:

  • 如果复制的是字符或词:
    • p:粘贴在光标后
    • P:粘贴在光标前
  • 如果复制的是整行(如使用 yydd):
    • p:将复制内容插入到当前行的下方
    • P:将复制内容插入到当前行的上方

重复与撤销

  • .:重复上一次修改(批量操作常用)
  • u:撤销上一次修改
  • <Ctrl-r>:重做上一次修改(撤销的相反)

u 不同,大写的 U 对应的撤销更加彻底:恢复某一行进入编辑时的状态,这也意味着重复按 U 无效。

全局搜索匹配

文件中搜索:(pattern 可以是正则表达式)

  • /{pattern}:搜索模式 {pattern},并跳转到匹配的下一个位置;
  • ?{pattern}:搜索模式 {pattern},并跳转到匹配的上一个位置;
  • n:继续(或切换为)向后搜索
  • N:继续(或切换为)向前搜索
  • *:自动以当前光标所在单词为 pattern,向后搜索

进阶

重复移动

在移动前面加上数字就可以重复多次移动。

1
[count]{motion}

例如

  • 5j:向下移动 5 行;
  • 5k:向上移动 5 行;
  • 2w:向右移动 2 个单词;

Operator + Motion = Action

操作符和移动可以组成的一个完整编辑动作,移动前后的光标所组成的就是这次编辑所对应的范围。

1
{operator}{motion}

常见的重要操作符:

  • c:(change) 修改(删除并进入 Insert 模式)
  • d:(delete) 删除
  • y:(yank) 复制
  • v:(visual) 选中文本,进入 Visual 模式

这里并没有剪切操作,因为 vim 实现的删除操作其实就是剪切,它会把删除内容记录下来,可以被用于粘贴。

在操作符后面加上移动即可组成完整命令,例如:

  • dgg:删除到第一行
  • ye:复制到单词结尾
  • d$:删除到行尾
  • dt;:删除到遇到分号;

如果不确定{motion}的作用范围,可以先尝试v{motion},再执行c/d/y操作。

重复两次操作符通常是作用在当前行:

  • cc:修改当前行(删除并进入 Insert 模式)
  • dd:删除当前行
  • yy:复制当前行

但是 vv 会进入 Visual 模式然后退出。

由于历史兼容性等原因,这几个操作符的大写含义为:

  • C = c$:修改到行尾
  • D = d$:删除到行尾
  • Y = yy:复制整行(注意不是复制到行尾)

Count + Action

[count]{action} 代表重复 {action} 行为 [count] 次,这里的 {action} 也可以进一步展开,变成

1
[count]{operator}{motion}

例如:

  • 5j:向下移动5行
  • 3dw:删除3个单词
  • 2yy:复制2行
  • 4p:粘贴4次

这里的组合方式其实非常灵活,例如 {motion} 通常也可以换成 [count]{motion},例如:

  • 3dw 解释为 dw (删除到单词末尾)且重复3次;
  • d3w 解释为为按照 3w 的移动范围进行删除。

两者效果是一样的。

文本对象及其操作

vim 将如下文本结构视作文本对象:

  • 单词:w/W (word)(两者区别是单词的定义)
  • 句子:s (sentence)
  • 段落:p (paragraph)
  • 配对符定义的对象:(/)[/]{/}</>'/"

有两种基本格式:

  • i:(inner) 内部
  • a:(around) 完整对象,额外包括边界的空格或两边的配对符

⽂本对象使⽂本具有了结构化的语义,进而允许我们以语义对象为操作单元,进行更具体的操作。

1
[count]{operator}{textobjects}

例如:

  • diw:删除单词
  • ci(:修改小括号内部
  • yi{:复制大括号内部

但是这里的 {textobjects} 不能独立使用,而且重复数量只能加在 {operator} 前面。

如果不确定 {textobjects} 的作用范围,可以先尝试 v{textobjects},再执行 c/d/y 操作。

补充

命令补充:

  • ~:大小写翻转
  • J:拼接两行
  • r:快速替换单个字符
  • R:进入字符替换的模式(<Esc 退出)

操作符补充:(操作符不能单独使用,这里的 g 也不能单独使用)

  • gu/gU:转换为小写/大写
  • g~:大小写翻转
  • </>:调整缩进(例如>><<调整当前行的缩进)

vim 支持 bash 中常见的几个删除操作(甚至是在 Insert 模式下也支持!):

  • <Ctrl+u>:删除至行首
  • <Ctrl+w>:删除光标前的单词
  • <Ctrl+h>:删除光标前的字符(与 <Backspace> 相同)

vim 有着非常详细的操作历史记录:

  • 使用 q: 可以查看 Command 模式的历史记录。
  • 使用 q/ 可以查看 Search 模式(正则匹配)的历史记录。

这些历史记录以及寄存器内容等会被存储在 .viminfo 文件中,再次打开 vim 时,这些内容会自动恢复。