nvim 学习

按键

插入模式

按键 说明
<C-A> 插入上次在插入模式下输入的文本
<C-@> 插入上次在插入模式下输入的文本并结束插入
<C-D> 减少缩进
<C-T> 增加缩进
0<C-D> 删除所有缩进
^<C-D> 删除所有缩进但在下一行(按下回车后)恢复
<C-E> 插入光标上一行的字符
<C-Y> 插入光标下一行的字符
<C-X><C-E> 屏幕上移一行
<C-X><C-Y> 屏幕下移一行
<C-G>U 下一次在同一行移动的时候不会开启新撤销点
<C-G><C-J> 光标下移一行且保持在进入插入模式时所在列
<C-G><C-K> 光标上移一行且保持在进入插入模式时所在列
<C-J> <CR>
<C-M> <CR>
<C-Q> <C-V>
<C-R><C-P>{register} 原封不动插入寄存器的内容且修复缩进
<C-[> 等价于 <Esc>
<C-]> 展开缩写
<C-_> 正向输入和反向输入之前切换(需设置 allowrevins
<C-Home> 跳转到文件开头
<C-End> 跳转到文件结尾
<C-Left> 类似普通模式的 b
<C-Right> 类似普通模式的 w
<S-Left> <C-Left>
<S-Right> <C-Right>
<S-Up> <PageUp>
<S-Down> <PageDown>
<F1> 打开帮助文档
<Insert> 切换 Insert/Replace 模式

普通模式

按键 说明
m{a-zA-Z} 创建一个标记
ga 显示光标下字符编码
gi 跳转到上次插入位置并进入插入模式
gf 跳到光标下文件的开头
gr 进入 Virtual Replace 模式进行单字符替换
gR 进入 Virtual Replace 模式进行连续替换
<C-E> 向上滚动 N
<C-Y> 向下滚动 N
<C-G> 显示当前文件名、光标位置和文件状态
<C-W>c 关闭当前窗口
<C-W><C-O> 关闭其他窗口
<C-W>R 向下旋转窗口布局 N
<C-W><C-R> 向右旋转窗口布局 N
<C-W><C-X> 和下一个窗口交换或和 N 号窗口交换
<C-W>_ 最大化窗口高度或设置为 N 行高
<C-W>| 最大化窗口宽度或设置为 N 列宽
gT N 个标签页
gt 下一个标签页或 N 号标签页
g; N 次修改位置
g, N 次修改位置
z= 拼写建议
zg 将当前单词加入词典
zw 把当前单词从词典中移出
zug 撤销对当前单词的 zg 或者 zw 操作
CTRL-] 跳到光标下标识符的定义处
~{motion} 切换大小写
g~{moition} 切换大小写
gu{moition} 小写
gU{moition} 大写
!{motion} 过滤
={motion} 自动缩进
g?{motion} ROT13 编码
zf{motion} 创建折叠
g@{motion} 调用用户设置的 operatorfunc
& :&&
g& :%s//~/&
@@ 重复上次执行的宏
q: 打开命令行窗口
q/ 打开搜索历史命令行窗口
q? 打开反向搜索历史命令行窗口

可视模式

按键 说明
o 切换光标位置
<C-G> 切换到选择模式

命令行模式

按键 说明
<C-P> 向上浏览历史命令,或选择上一个补全项
<C-N> 向下浏览历史命令,或选择下一个补全项
<C-F> 打开命令行窗口

多模式可用

按键 模式 说明
<C-H> C I 删除一个字符
<C-W> C I 删除一个单词
<C-U> C I 删除至开头
<C-K>{char1}{char2} C I 插入一个特殊字符
<C-R>{register} C I 依次输入寄存器的内容但不触发按键绑定和缩写
<C-R><C-R>{register} C I 原封不动插入寄存器的内容
<C-R><C-O>{register} C I 原封不动插入寄存器的内容且不自动缩进
<C-V> C I 直接插入非数字或者插入编码对应的字符
<C-^> C I 切换 imsimi 的值
<C-C> C I N O V <Esc> 类似
<C-\><C-G> C I N O T V 回到普通模式
<C-\><C-N> C I N O T V 回到普通模式
ib O V inside block
ab O V around block
iB O V inside Block
aB O V around Block
is O V inside sentence
as O V around sentence
ip O V inside paragraph
ap O V around paragraph
it O V inside tag
at O V around tag
gp N V 类似 p 但是将光标放在粘贴内容的结尾处
gP N V 类似 P 但是将光标放在粘贴内容的结尾处
H N O V 移动到屏幕顶部行的第一个非空字符
M N O V 移动到屏幕中间行的第一个非空字符
L N O V 移动到屏幕底部行的第一个非空字符
gM N O V 移动到当前行中间
gm N O V 移动到当前折行屏幕中间
g_ N O V 移动到当前行最后一个非空字符
g<End> N O V 移动到当前折行最后一个非空字符
( N O V 移动到上 Nsentence 的开头
) N O V 移动到下 Nsentence 的开头
{ N O V 移动到上 Nparagraph 的开头
} N O V 移动到下 Nparagraph 的开头
[s N O V 移动到上 N 个拼写错误
]s N O V 移动到下 N 个拼写错误
[[ N O V 移动到上 Nsection 的开头或上 N 个行首 {
]] N O V 移动到下 Nsection 的开头或下 N 个行首 {
[] N O V 移动到上 Nsection 的开头或上 N 个行首 }
][ N O V 移动到下 Nsection 的开头或下 N 个行首 }
[m N O V 移动到上 N 个方法的开头
]m N O V 移动到下 N 个方法的开头
[M N O V 移动到上 N 个方法的结尾
]M N O V 移动到下 N 个方法的结尾
[( N O V 移动到上 N 个未匹配的 (
]) N O V 移动到下 N 个未匹配的 )
[{ N O V 移动到上 N 个未匹配的 {
]} N O V 移动到下 N 个未匹配的 }
[# N O V 移动到上 N 个未匹配的 #if#else
]# N O V 移动到下 N 个未匹配的 #if#else
[* N O V 移动到上 NC 语言风格的注释开头 /*
[/ N O V 移动到上 NC 语言风格的注释开头 /*
]* N O V 移动到下 NC 语言风格的注释结尾 */
]/ N O V 移动到下 NC 语言风格的注释结尾 */
- N O V 移动到上 N 行第一个非空字符
+ N O V 移动到下 N 行第一个非空字符
<cr> N O V 移动到下 N 行第一个非空字符
_ N O V 移动到下 N-1 行的第一个非空字符
| N O V 移动到第 N

命令

命令 说明
:changes 显示更改列表
:ju 显示跳转列表
:cle 清空跳转列表
:e 打开一个文件
:up 保存当前缓冲区(如果有修改)
:ls 列出所有缓冲区
:vim /{pattern}/ {file} 在文件中搜索 pattern 并将结果放入 quickfix 列表
:mak 编译当前文件并将错误放入 quickfix 列表
:cope 打开 quickfix 列表窗口
:lop 打开 location 列表窗口
:col [N] N 次的 quickfix 列表
:lol [N] N 次的 location 列表
:cnew [N] N 次的 quickfix 列表
:lnew [N] N 次的 location 列表
:ar [arglist] 列出参数列表或重新定义参数列表
:arga {pattern} 向参数列表中添加匹配 pattern 的文件
:argd {name} 从参数列表中删除 name
:[range]< [N] range 结尾开始的 N 行向左缩进,输入多个 < 可以缩进多次
:[range]> [N] range 结尾开始的 N 行向右缩进,输入多个 > 可以缩进多次
:[range]c range 中的内容替换成输入的内容
:[range]d [x] range 删除到寄存器 x
:[range]y [x] range 复制到寄存器 x
:[line]pu [x] x 寄存器内容粘贴到 line 之后
:[line]pu! [x] x 寄存器内容粘贴到 line 之前
:[range]t {address} range 复制到 address 下一行
:[range]m {address} range 移动到 address 下一行
:[range]j range 连接成一行
:[range]norm {cmd} range 执行普通模式的命令
:[range]g/[pattern]/[cmd] range 上匹配 pattern 的行执行 Ex 命令
:[range]v/[pattern]/[cmd] range 上不匹配 pattern 的行执行 Ex 命令

设置项

wrapscan 选项控制搜索时是否从文件头/尾重新开始搜索,其默认值为开启。


nrformats 决定了 <C-A><C-X> 增减数字时所识别的数字格式,其默认值为 bin,hex 表示会将 0b0B 开头的数字识别成二进制,0x0X 开头的数字识别成十六进制。也可以增加 octal 来识别 0 开头的八进制数字。


scroll 选项可以设置 <C-U><C-D> 每次翻多少行。值得注意的是如果我们在按 <C-U><C-D> 之前输入了一个数字那么这个数字就会覆盖 scroll,后续翻页操作都会以这个数字为准。


tildeop 选项关闭时(默认关闭),~ 在普通模式下只会切换光标下字符的大小写;当开启时,~ 在普通模式下和 g~ 效果相同。


makeprg 决定了执行 :mak 时会运行的命令,默认值为 make。我们可以根据需要将其设置为其他编译命令,例如 gcc % -o %<

errorformat 规定了如何解释 :make 命令的输出,我们可以使用一些插件来自动设置它。这些插件会编写各种文件的 compiler 文件,我们借助这些插件就可以通过 :compiler {name} 来开启一个特定的 compiler 设置。


gpreprg 决定了执行 :gr 命令时会运行的命令。grepformat 决定了如何解释 :gr 命令的输出。

使用 :vim 进行过滤时需要使用 nvim 中的正则,而使用 :gr 进行过滤的时候则需要使用外部命令的正则。这是两者的主要不同,再者,通常 :gr 的性能会更好一些。

补充知识

Vim 中在可视模式下 *# 不会直接搜索选中的内容,而在 nvim 中则会直接搜索选中的内容。


在启动 nvim 的时候我们可用通过 nvim file +9 直接跳转到 file 文件的第 9 行;也可以使用 nvim file +/pattern file 来直接跳转到 file 文件中第一个匹配 pattern 的位置。


quickfix 列表全局只有一份,而 location 列表全局可以有多份,每个窗口都有自己的 location 列表。我们只需要在会创建 quickfix 的命令前加上 l 就可以将结果放入 location 列表中,例如 :lvim /pattern/ file 会在 file 中搜索 pattern 并将结果放入当前窗口的 location 列表中。


窗口通常是从左到右、从上到下进行编号的。使用使用 1<C-W>x 将当前窗口和一号窗口进行交换,但是 nvim 的窗口并没有那么“智能”:如果当前窗口是在一个横向布局里面,那么它会和一号窗口所在的横向布局进行交换,而不是直接和一号窗口进行交换;坚向布局也是类似的。这是为什么 <C-W><C-R><C-W>R 不是顺时针或逆时针旋转窗口布局的原因。

在使用窗口旋转操作时,如何旋转方向上的窗口已经被逆向分隔了,nvim 就会直接给出报错:

E443: Can't rotate when another window is split

ROT13 是一种简单的字母替换加密算法,其将字母表中的每个字母替换成其后第 13 个字母,例如 A 会被替换成 NB 会被替换成 O,以此类推。对于字母表后半部分的字母则会循环回到开头,例如 N 会被替换成 AO 会被替换成 B。使用两次 ROT13 会还原回原始文本。


可视模式下可以通过 g<C-A> 来格式化 Markdown 中的列表。选择多行时,每一行将会比上一行多加 N。例如:

1.
1.
1.
1.

如果选择后三行并按下 g<C-A>,则会变成:

1.
2.
3.
4.

如果要给一段文字的每一行进行编号,可以使用 Vip 进入可视模式并选择整段文字,然后再使用 :norm I0. 给每一行的开头插入 0. ,接着使用 gv 重新选择刚刚的内容,最后用 g<C-A> 进行编号。


tag 指的是成对出现的标签,例如 <div>...</div>inside tag 会选择标签内的内容,而 around tag 则会选择包括标签本身在内的内容。在 HTMLXML 文件中,itat 非常有用。


我们也可以使用 <C-R>. 来实现和 <C-A> 相同的功能;而 <C-@> 在大多数终端里面和 <C-Space> 是等价的。


在插入模式下直接移动光标(例如 <Left><S-Left> 等)会开启新的撤销点,这会影响普通模式下 . 的效果,例如:

abc<Left>d

在退出插入模式后使用 . 会插入 d 而不是 abdc 要想让 . 插入 abdc 我们需要使用 <C-G>U

abc<C-G>U<Left>d

像是 <C-@><C-A> 这样的按键也会因为撤销点的变化而被影响。

直接对指针进行上下移动而一定会开启新的撤销点,我们无法避免这一点。


使用 <C-R>{register} 插入寄存器内容的时候,如果寄存器内容有控制字符(例如退格、回车等),这些控制字符会被解析并执行;使用 <C-R><C-R>{register}<C-R><C-O>{register}<C-R><C-P>{register}(仅插入模式,命令模式是插入光标下的文件名并用 path 展开)则不会解析这些控制字符;值得说明的是如果在插入模式下使用 <C-R><C-O>{register}<C-R><C-P>{register} 且寄存器内容是 line-wise 的时候会插入到当前行的上方,这点和普通模式下的 P 类似。


<C-V> 后面可以跟一个非数字字符来插入对应的控制字符,例如 <C-V><C-M> 可以插入回车符。也可以用来插入指定编码的字符:

按键 模式 数字长度 最大值
<C-V>{digit} 十进制 3 255
<C-V>u{digit} 十六进制 4 0xFFFF
<C-V>U{digit} 十六进制 8 0x7FFFFFFF
<C-V>o{digit}/<C-V>O{digit} 八进制 3 377
<C-V>x{digit}/<C-V>X{digit} 十六进制 2 0xFF

使用 <C-X><C-E><C-X><C-Y> 的时候,按一次 <C-X> 后可以按多次 <C-E><C-Y> 进行滚动;但是使用 <C-G><C-J><C-G><C-K> 的时候每次都需要重新按下 <C-G>


ga 输出格式如下:

<}>  125,  Hex 7d,  Oct 175, Digr !)

<}> 是该字符本身,125 是该字符的十进制编码值,Hex 7d 是十六进制编码值,Oct 175 是八进制编码值,Digr !) 表示在输入该字符可以使用 <C-K>!)


<Up><Down> 在已经输入了一部分命令时只会过滤以输入内容开头的历史命令。


选择模式往往是用于实现 Snippets 相关功能,实际使用中不太需要自己主动进入选择模式。

选择模式和可视模式的一个主要区别是:在选择模式下输入的内容会替换选中的文本,而在可视模式下如果要替换文本需要先使用 c 进入插入模式。


当使用 :e {file} 打开一个不存在目录下的文件时,nvim 会给出报错但是依然会打开文件供我们编辑,只是在使用 :w 命令进行保存时会提示无法保存,这时我们先可以使用 :!mkdir -p %:h 来保证目录全部被创建,再使用 :w 即可正常保存文件;也可以直接使用 :w ++p 在保存时自动创建目录。


:w 命令可能会遇到需要 root 权限的情况,这时可以直接使用 :w !sudo tee % > /dev/null 来进行保存。


:&& 中的 :& 代表 :s,末尾的 & 代表上一次的替换使用的标志,而 :%s//~/& 中的 ~ 代表上一次使用的替换串。


Virtual ReplaceReplace 模式的主要区别在于:Virtual Replace 模式是按照屏幕上显示宽度进行替换,Replace 模式是按照实际字符进行替换的。这在替换包含制表符的文本时非常有用,例如在 Virtual Replace 模式下,如果当前位置是一个显示宽度为多个空格的制表符,那么输入的字符会插入到制表符之前,当输入的字符数量达到制表符的显示宽度时,制表符才会被替换掉。


使用 / 或者 ? 进行搜索的时候,我们其实是需要在输入一个模式串后还需要输入 / 或者 ? 来进行结尾的,只是在大部分的时候它们可以被省略(在使用 :norm 命令进行操作的时候不能被省略)。我们也可以使用类似 /word/e 的方式来指定搜索结果的光标位置,这与 /word\zs 会有所不同,后者不会高亮整个匹配的内容。

如果向后搜索的时候 / 则需要使用 \ 来进行转义;在向前搜索的时候 ? 则需要使用 \ 来进行转义。向后搜索时可以使用 \?? 来表示 ? 的两种意思,但向前搜索时只能通过 \? 来表示 ? 的一种意思,不过 =\= 也可以表示匹配 0 次或者 1 次。


在使用 :argdo {cmd}:bufdo {cmd} 或者 :windo {cmd}时,其会按照如下的逻辑进行执行:

  • 跳到第一个文件/缓冲区/窗口
  • :{cmd}
  • 跳到下一个文件/缓冲区/窗口
  • :{cmd}
  • ……

当执行过程中 :{cmd} 进行了修改但是没有保存的时候,:next:bn 就会执行失败,我们可以通过设置 hidden 选项(默认开启)来允许切换未保存修改的缓冲区。

当然我们也可以在 :{cmd} 结尾加上 | update 来保存修改,例如::bufdo %s/var/let/g | update 会对所有缓冲区进行替换并保存修改。


命令行模式下可以用 | 来连接多个命令,例如::w | !gcc % -o %<; ./%< 会先保存当前文件,然后编译该文件并运行生成的可执行文件。

在执行 SHELL 命令的时候,% 会被解析成当前文件名,%< 解析成当前文件名去掉扩展名后的名称。可以通过 :h cmdline-special 来查看所有可用的特殊符号。


可以通过 :!{cmd} 的方式来执行外部命令,当直接运行 :!ls 时,会回显命令的输出,当命令的输出比较长时,可以用 :r !{cmd} 的方式来将命令的输出读取到当前光标所在位置。

当然我们也可以通过 :enew | r !{cmd} 的方式来在一个新的缓冲区中读取命令的输出。

:[range]w !{cmd} 则做相反的操作,它会将当前缓冲区的指定内容通过管道传递给外部命令 cmd 的标准输入。如果不指定范围则默认是整个缓冲区的内容。


每个窗口有自己的工作目录,可以使用 :pwd 来查看当前窗口的工作目录。使用 :cd {path} 可以全局改变工作目录,而使用 :lcd {path} 则只会改变当前窗口的工作目录。


在使用与 quickfix 相关的命令(如 :gr:vim:mak)或使用与缓冲区列表、参数列表有关的命令(如 :args:argdo)前设置一个全局标记可以在操作后快速返回。

一些自带的位置标记:

标记 说明
` 上次跳转位置
. 上次修改位置
" 上次退出文件位置
^ 上次插入位置
[ 上次修改或复制的开始位置
] 上次修改或复制的结束位置
< 可视模式下选区的开始位置
> 可视模式下选区的结束位置

word 是由 iskeyword 中的字符组成,可以简单认为数字、下划线、字母属于单词的一部分。

WORD 则由非空字符组成。

sentence 则是以.?! 结尾加上至少一个空白字符的文本块。在 sentence 的标点和空格之间可以有多个 )]"`。需要注意的是 paragraphsection 的边界也是 sentence 的边界。

paragraph 则可以简单地认为是以一个或多个空行作为分隔符的文本块。需要注意的是 section 的边界也是 paragraph 的边界。

section 则必须以 sections 中定义的标记作为分隔符,这个概念平时用得不多,在写相关文档的时候可能会用到。

block 是指 () 围成的区域。

Block 则是指 {} 围成的区域。


path 变量会控制一些命令的查找路径,默认值为 .,, 其中的 . 代表当前文件所在目录,, 是分隔符,两个逗号一起表示增加一个空路径(当前工作目录)。

subffixesadd 变量则会控制一些命令在查找文件时所使用的后缀名列表。其往往会根据打开的文件类型被自动设置。


需要注意的是 CTRL-I<Tab> 是等价的,这意味着你绑定 <Tab> 键为其他功能后将无法使用 CTRL-I 进行跳转。


d 进行删除时会将删除的内容放入匿名寄存器中,如果想要在删除时不放入任何寄存器,则可以使用 "_d 来进行删除。

我们有时候在使用 p 的时候可能会发现匿名寄存器中的内容已经被覆盖了,这个时候我们可以使用 "0p 来粘贴最近一次使用 y 进行复制的内容。0 寄存器专门用于保存最近一次复制的内容。

字母寄存器是不区分大小写的,我们只能使用小写字母来操作它们,当我们使用大写字母时则表示将内容追加到对应的小写字母寄存器中。比如我们使用 qa 给寄存器 a 录制一个宏,然后使用 qA 则会将后续录制的内容追加到寄存器 a 中。

一些寄存器的说明:

寄存器 说明
0 最近一次复制的内容
_ 黑洞寄存器
% 当前文件名
# 轮换文件名
/ 上次搜索的模式
: 上次命令行模式下的命令
. 上次插入的文本
- 最近删除的少于一行的文本
= 可以获取一个表达式的结果

例如我们可以使用 @: 来重复上一次执行的命令行模式下的命令;使用 @/ 来执行上一次搜索的模式,不过我们更常用的是直接按 n


可视模式使用 p 实际上会将选中的内容替换掉并将其放入匿名寄存器中,也正是因为这个特性我们可以使用如下的步骤来交换两段内容:

  1. 选择第一段内容并使用 d 删除,使用 mm 来创建一个标记 m 记录删除位置
  2. 选择第二段内容并使用 p 进行粘贴
  3. 使用 `m 跳转到标记 m 处并使用 p 进行粘贴

可视模式中使用 P 则不会将选中的内容放入匿名寄存器中。不过这一点只对匿名寄存器有用,对命名寄存器不起作用。


当使用一些可以接 {motion} 的按键时,我们往往可以用对应按键的最后一个字符来表示当前行,例如 gUU 会让当前行的内容变成大写。而在可视模式下使用普通模式下需要 {motion} 的按键时会直接作用在所选的内容上不再需要 {motion} 了,例如可视模式下 gU 会将选中的内容变成大写。


过滤是指将指定范围内的内容通过管道传递给外部命令 {filter} 的标准输入,并将外部命令的输出替换掉指定范围内的内容。例如普通模式下输入 !G 后会进命令行模式且会自动填入 :.,$! 这个时候我们可以将其补充完整 :.,$!sort -t',' -k2 会对当前行到最后一行的内容按逗号分隔的第二列进行排序。


Operator-pending 的阶段可以先输入 vV 、或者 CTRL-V 来将本次的 motion 操作强制设置成 charwiselinewise 或者 blockwise 模式。在使用 v 的时候如果后续 motion 已经是 charwise 模式则会在 insidearound 语义间切换,而如果后续 motionlinewise 则始终是以 inside 的语义进行执行。


我们有时候可能会需要频繁地在多行内依次使用 ;.,我们可以按照以下的步骤来实现:

  1. 通过 qq;.q 来录制一个宏到寄存器 q 中;
  2. 在可视模型下选择需要执行的行;
  3. 使用 :normal @99q 来对选中的每一行执行多次宏命令。

当我们想要对一个宏进行修改的时候,我们可以在插入模式下通过 <C-R>{register} 先读取出宏的内容到当前的位置进行修改,修改完成后我们可以直接使用 "{register}D 来将修改后的内容重新保存到寄存器中。

实际上宏里面存储的就是一个字符串,我们当然可以使用内置的函数对其进行修改,例如:

:let @q = substitute(@q, ';', ',','g')

会将寄存器 q 中的所有分号替换成逗号。


nvim -u NONE -N 表示以不加载任何配置文件且设置 nocompatible 的方式启动。


即使我们设置了 ignorecasesmartcase 选项为特定的值,我们在搜索的时候还是可以使用 \c\C 来忽略或者区分大小写,例如 /pattern\C 会区分大小写地搜索 pattern,而 /pattern\c 则会忽略大小写地搜索 pattern\c\C 可以出现在搜索模式的任意位置,也就是说我们输入到一半的时候突然想要切换大小写敏感性也是可以的。


搜索模式可以分为四种类型:

  • magic:这是开了 magic(默认开启)选项后的默认模式,也可以使用 \m 开头强制开启,.^$*~\/ 有特殊含义;
  • nomagic:关闭 magic 后的默认模式,可以使用 \M 开头强制开启,这个模式很少使用,^$\/ 有特殊含义;
  • verymagic:这是使用 \v 开头的模式,除了 a-zA-Z0-9_ 之外的所有字符都有特殊含义;
  • verynomagic:这是使用 \V 开头的模式,只有 \/ 有特殊含义。

在使用 ydc 进行相关操作的时候,内容往往会被放入匿名寄存器中,我们可以通过设置 set clipboard+=unnamedplus 来让这些操作将内容放入系统剪切板中。


在命令行窗口中,我们可以像编辑普通文件一样编辑命令历史,当你在任意一行上按下 <CR> 的时候,该行命令将会被执行;<C-C> 会返回到命令行模式;:q 则可以关闭命令行窗口。


在搜索时可以使用的字符集:

字符集 说明
\w 代表单词字符 [A-Za-z0-9_]
\a 代表字母字符 [A-Za-z]
\l 代表小写字母字符 [a-z]
\u 代表大写字母字符 [A-Z]
\s 代表空白字符 [ \t]
\d 代表数字字符 [0-9]
\o 代表八进制数字字符 [0-7]
\x 代表十六进制数字字符 [0-9A-Fa-f]

我们可以使用大写字母来表示取反,例如 \D 代表非数字字符。


在搜索的时候可以使用 <> 来表示单词的边界,例如 /\<word\> 会匹配独立的 word,而不会匹配 sword 或者 words。我们在使用 #* 的时候也会自动在单词前后添加 \<\>g#g* 则不会添加。


在使用 :set 命令时如果我们在结尾增加 ? 则会显示该选项的当前值;结尾增加 & 则会将该选项重置为默认值;结尾增加 ! 则会切换布尔选项的值。

在进行替换时可以用 \0& 来代表整个匹配的内容,\1\2 等等则代表第 12 个捕获组匹配的内容。如果不想捕获某个组可以在左括号前添加 %,例如 \v%(pattern) 则不会捕获 pattern

在进行替换的时候我们也可以使用 \= 来获取一个表达式的结果作为替换内容,例如下面的命令会将所有数字扩大两倍:

:%s/\v\d+/\=submatch(0) * 2/g

如果在使用 :s 进行替换过程中使用 \= 时想要获取被捕获的文本我们则必须使用 submatch() 函数,例如:

:%s/\v(word)/\=submatch(1) . '_suffix'/g

常用的地址(范围)表示方法:

地址 说明
$ 最后一行
. 当前行
% 当前文件的所有行
0 虚拟行,位于第一行之前
N N
+N 当前行向下 N
-N 当前行向上 N
/pattern/ 向下搜索匹配模式的行
?pattern? 向上搜索匹配模式的行

对于搜索的范围可以写出 /<html>/,/<\/html>/ 来表示从第一个 <html> 标签所在行到第一个 </html> 标签所在行的范围。

搜索范围可以和 +N-N 结合使用,例如::/<html>/+1,/<\/html>/-1d 表示删除从第一个 <html> 标签下一行到第一个 </html> 标签上一行的内容。

对于这些命令我们也可以在 Visual 模式下选中内容后使用 : 进入命令行模式,此时命令行中会自动插入 '<,'> 来表示选中的范围,我们只需要在后面添加命令即可。

这里给出几个例子:

  • :%norm A; 在每一行的末尾添加分号
  • :%norm I// 在每一行的开头添加注释符号 //

小写字母标记是每个缓冲区局部可见的,而大写字母的标记则是全局可见的。我们可以使用 '{mark} 来跳转到 {mark} 所在行的第一个非空字符上,使用 `{mark} 则会跳转到定义 {mark} 的精确位置。


:ls 列出的缓冲区中 % 符号表示当前缓冲区,# 符号表示轮换文件。开头的数字则表示缓冲区的编号。CTRL-^ 可以在当前文件和轮换文件之间切换。

轮换文件:在一个窗口打开一个新的缓冲区时,当前缓冲区就对应的文件就成为了轮换文件,如果使用 :bd 将一个轮换文件对应的缓冲区从缓冲区列表中移除,那么当前窗口就没有对应的轮换文件了,轮换文件是相对于每个窗口独立的。


在命令行模式下可以进行下列展开:

符号 说明
% 当前文件的完整路径
# 轮换文件的完整路径

在进行展开的时候可以使用下面的修饰符:

符号 说明
:p 转换成完整路径
:~ ~ 代替用户主目录
:. 用当前工作目录代替路径前缀
:h 去掉文件名,保留目录路径
:t 去掉目录路径,保留文件名
:r 去掉文件扩展名
:e 只保留文件扩展名
:s?p?s? 替换路径中的第一个匹配模式 ps
:gs?p?s? 替换路径中所有匹配模式 ps
:S 对路径进行 shell 转义

可以一次使用多个修饰符,但是需要注意顺序,例如 %:p:~:h 会先将当前文件转换成完整路径,然后用 ~ 代替用户主目录,最后去掉文件名只保留目录路径。在 :s?p?s?:gs?p?s? 中,? 可以替换成任意未在 ps 中出现的字符。


:s 命令中可以使用下面的标志:

  • g:全局替换;
  • c:替换前需要确认;
  • e:忽略错误;
  • n:只显示替换的数量;
  • i:忽略大小写;
  • I:区分大小写。

参数列表是打开 nvim 时指定的所以文件。我们可以在打开后重新设置参数列表,例如 :arga *.c 会将当前目录下的所有 c 文件添加到参数列表中;使用 :argd {name} 则可以从参数列表中删除指定的文件;:ar *.js 则会将参数列表设置成当前目录下的所有 js 文件。也可以使用反引号将命令的输出作为参数列表,例如下面的命令可以将参数列表设置成 ~/repo 下的所有 js 文件:

:ar `find ~/repo -name '*.js'`

nvim 中虽然可以设置很多的按键但是并不是所以的按键都可以被终端识别,比如在使用 Wezterm 的时候:

  • <C-Left>/<C-Right>os X 上无法触发;
  • <S-ScrollWheelUp>/<S-ScrollWheelDown>os X 上按住 Shift 并滚动鼠标会被识别成 <S-ScrollWheelLeft><S-ScrollWheelRight>
  • <Insert>os X 上无法被正确识别。

许多类似于搜索的命令都会使用到 pattern,它们往往在不使用 pattern 的时候会使用搜索历史进行填充,所以在我们开始执行命令之前先进行一次搜索以检查搜索模式是一个好习惯,这样不会直接影响内容。但是也有些命令不会使用搜索历史(例如 :vim),不过我们可以通过 <C-R>/ 来手动插入上一次搜索的模式。


<C-R><C-W> 不只在搜索开始的时候可以使用,我们在输入了部分内容后再使用则会将第一个高亮的匹配内容补全到命令行中。不过需要注意的是如果搜索以 \v 开始,使用 <C-R><C-W> 则不是进行补全而是直接将完整单词插入。

Vim Script

Vim Script 中我们可以使用 let 来声明会给一个变量进行赋值;使用 @ 来访问寄存器的内容;例如我们可以使用下面的命令将上一次搜索的内容替换成系统剪切板中的内容:

:%s//\=@+/g

使用下面的命令可以直接修改寄存器中的内容:

:let @a = "New content"

Vim Script 里面的字符串可以用单引号包围也可以用双引号包围,二者的区别在于单引号包围的字符串不会解析其中的转义字符,而双引号包围的字符串会解析转义字符,例如:

:let str1 = 'Line1\nLine2I''m here'
:let str2 = "Line1\nLine2I\"m here"

str1 的值为 Line1\nLine2,而 str2 的值会被解析成两行。在单引号中要插入单引号本身可以使用两个单引号来表示一个单引号字符:'';而在双引号中要插入双引号本身则需要使用反斜杠来进行转义:\"

在双引号中支持使用 #{name} 来对变量进行替换。

. 可以进行字符串的拼接操作。


escape 函数可以对字符串中的指定字符进行转义,例如:

let @u = escape('path/to/file (1).txt', ' /()')

上面的命令会将寄存器 u 的内容设置为 path\/to\/file\ \(1\).txt

插件

netrw 是自带的一个文件管理插件,可以使用 :Explore 来打开,在这样进行操作时,其与传统的文件管理器有一些不同,其会直接在当前的窗口打开一个目录,当我们使用 ENTER 选择一个文件时,其会在当前的窗口打开该文件,如果我们打开错了,我们可以使用 CTRL-^ 在当前文件和 netrw 之间进行切换。

netrw 的强大之处在于其支持通过 scpftpcurlwget 来访问远程文件系统,例如我们可以使用 :e scp://user@host//path/to/file 来编辑远程主机上的文件。




    Enjoy Reading This Article?

    Here are some more articles you might like to read next:

  • 奇怪的知识增加了
  • 文学摘抄
  • Go 学习笔记
  • 鞋带公式
  • 多重背包优化