LOADING

加载过慢请开启缓存 浏览器默认开启

Vim编辑方式的常用命令(2023.11.16)(这辈子都更不完啦!)

2023/11/16 知识分享 Vim

目录

Vim 中文帮助:https://vimcdoc.sourceforge.net/doc/

前言

Vim的模式

普通模式常用命令

前言

本文只是个人在学习和使用Vim编辑方式的笔记,本人暂时没有深入使用过原生的Vim,以下所有内容都是在VSCode和VS的Vim插件下适用。

Vim的模式

Vim分为4种模式,不同按键在不同模式下都用不同的功能,用户可以通过在不同模式之间切换来实现各种各样对文本的快捷操作。

四种模式分别是:

普通模式: –NORMAL–

插入模式: –INSERT–

可视模式: –VISUAL–

命令模式: :

普通模式

普通模式是用户进行移动光标、复制、粘贴等操作的模式。

插入模式

插入模式是我们进行文字编辑的模式,当进入插入模式之后,
Vim的输入操作与普通文本编辑器基本无异。
但Vim仍提供了大量的命令来加快用户输入,
后文将会介绍部分个人常用的命令。

可视模式

该模式类似于使用鼠标进行拖选,当用户在普通模式按下v键后将进入可视模式,
将以用户进入可视模式时光标所在的位置为起始位置,
用户可通过移动光标可以将选择范围内的文本高亮。
用户可以对高亮后的文本进行操作,如复制、删除等。
可视模式下大部分操作与插入模式相同。

命令模式

命令模式下,用户可以输入一些命令来完成一些特殊的操作,如保存、退出、分割窗口等。

普通模式常用命令

普通模式进入插入模式

我们用文本编辑器,最重要的就是要输入文字,vim编辑器中,输入文字需要在插入模式下进行,进入插入模式经常会用到以下命令。

命令 用途
i 在光标前插入
a 在光标后插入
I 在行首插入
A 在行末插入
o 在下一行插入
O 在上一行插入
s 删除当前光标所在字符并插入
S 删除当前当前行并插入

普通模式下移动光标

使用鼠标和方向键来移动光标需要将右手移动很远的距离。
无疑这会降低我们的操作速度。
而vim就是来帮助我们脱离鼠标和方向键的。
我们可以使用以下命令来移动光标。

方向键的替换者

由于我们的右手一直放在hjkl的位置,因此使用hjkl作为新的方向键。

命令 用途
h 向左移动一个字符
j 向下移动一行
k 向上移动一行
l 向右移动一个字符

在命令之前输入数字可以表示命令执行的次数。

命令 用途
nh 向左移动n个字符
nj 向下移动n
nk 向上移动n
nl 向右移动n个字符

单词间移动

首先要先理解vim中对于单词的定义。vim中有两种单词。

word: 只包含字母,数字以及下划线_的字符串(连续的汉字算一个word,汉字word会被其他任何字符分开)

WORD:不包含空格,制表符以及换行的字符串

同样,可以在以下命令开头加上数字表示命令重复的次数。

命令 用途
ne 将光标移动到其所在位置右边第n个word的末尾
nge 将光标移动到其所在位置左边第n个word的末尾
nb 将光标移动到其所在位置左边第n个word的开头
nw 将光标移动到其所在位置右边第n个word的开头
nE 将光标移动到其所在位置右边第n个WORD的末尾
ngE 将光标移动到其所在位置左边第n个WORD的末尾
nB 将光标移动到其所在位置左边第n个WORD的开头
nW 将光标移动到其所在位置右边第n个WORD的开头

行列移动

列移动

命令 用途
0 移动到当前行开头
$ 移动到当前行末尾
^ 移动到当前行第一个非空字符
g_ 移动到当前行最后一个非空字符
n| 移动到第n

行移动

命令 用途
gg 移动到首行
ngg 移动到第n
G 移动到尾行
H 移动到屏幕的第一行
nH 移动到屏幕的第n
M 移动到屏幕的中间行
L 移动到屏幕的最后一行
nL 移动到屏幕的倒数第n

移动到指定的字符

下表中的a可以替换为任意字符。

命令 用途
nfa 将光标移动到其所在位置右边第na
nFa 将光标移动到其所在位置左边第na
nta 将光标移动到其所在位置右边第na之前
nTa 将光标移动到其所在位置左边第na之前
; 重复上一次移动到指定字符操作
, 往相反的方向重复上一次移动到指定字符的操作

移动到指定的字符串

以下命令属于命令模式的命令,并非普通模式

下表中的abcd可以替换为任意字符串(支持中文)

命令 用途
/abcd 将光标移动到其所在处的下一个abcd的开头
?abcd 将光标移动到其所在处的上一个abcd的开头
n 重复上一次移动到指定字符串的操作
N 往相反的方向重复上一次移动到指定字符串的操作

括号匹配

按下 ‘%’ 将会将光标移动到匹配的括号上,
当光标在’()’,’[]’,’{}’上时,会移动到与其匹配的另一半括号上。
当光标不在括号上时,会将光标移动到其之后最近的一组括号上。

位置标记

下表中a可以替换为任意大小写英文字母,
因此,最多可以使用52个位置标记。

小写字母的标记限定在当前文档内使用,而大写字母的位置标记可以跨文件进行跳转

命令 用途
ma 将位置标记a放置在光标所在位置
a 将光标跳转到位置标记a所在的行首
`a 将光标跳转到位置标记a所在的位置
' 将光标跳转到上一次进行跳转的位置的行首
`' 将光标跳转到上一次进行跳转的位置
. 将光标跳转到上一次进行改变的位置的行首
`. 将光标跳转到上一次进行改变的位置
^ 将光标跳转到上一次进行插入的位置的行首
`^ 将光标跳转到上一次进行插入的位置

普通模式下对文本进行操作

在Vim中对文本进行操作满足“动宾”这种语法,
也就是先输入动作,再输入操作对象,Vim就会对对象执行用户输入的动作。
在介绍动作之前,先来介绍一下文本对象吧。

文本对象

在前面的 [单词间移动] 这个小节中,我介绍了Vim的两种单词,这个单词是Vim中的文本对象之一,但是不完整。
Vim的文本对象还需要加上一个修饰语:'a''i'
其中 'a' 表示 "an"'i' 表示 "inner"
使用 'a' 修饰的对象会包括文本开头和结尾的空白或者括号,引号等,
使用 'i' 修饰的对象则不包含这一部分。

常用对象如表所示。

对象 描述
aw 表示一个word,且包含word开头和结尾的各一个空格
iw 表示一个word,不包含空格
aW 表示一个WORD,且包含word开头和结尾的各一个空格
iW 表示一个WORD,不包含空格
a( 或 a) 表示一段由 “()” 包括起来的文字,包含括号
i( 或 i) 表示一段由 “()” 包括起来的文字,不包含括号
a[ 或 a] 表示一段由 “[]” 包括起来的文字,包含括号
i[ 或 i] 表示一段由 “[]” 包括起来的文字,不包含括号
a{ 或 a} 表示一段由 “{}” 包括起来的文字,包含括号
i{ 或 i} 表示一段由 “{}” 包括起来的文字,不包含括号
a< 或 a> 表示一段由 “<>” 包括起来的文字,包含括号
i< 或 i> 表示一段由 “<>” 包括起来的文字,不包含括号
a” 表示一段由 “ “ ” 包括起来的文字,包含引号
i” 表示一段由 “ “ ” 包括起来的文字,不包含引号
a’ 表示一段由 “ ‘ ” 包括起来的文字,包含引号
i’ 表示一段由 “ ‘ ” 包括起来的文字,不包含引号
a` 表示一段由 “ ` ” 包括起来的文字,包含引号
i` 表示一段由 “ ` ” 包括起来的文字,不包含引号

删除/修改/拷贝动作

Vim中,删除/修改/拷贝的动作命令分别是d(delete)/c(change)/y(yank)。

在动作命令后面加上文本对象,即可对对象进行动作,
例如”da(“命令,将会将光标所在位置的括号内的文本及括号删除。

除了文本对象,动作命令后面还可以接上光标移动命令,
例如,
“d3h”会将光标前3个字符删除,
“dgg”会将光标所在位置到首行的所有字符删除,
“d`a”会将光标光标所在位置到位置标志a所在位置之间的字符删除。

总而言之,动作命令之后接着的就是文本范围,Vim会对范围内的字符执行指定的动作。
但有一个例外,当使用可视模式时,是先在可视模式下选定动作执行的范围,最后在输入动作。

由于它们的使用方法基本一致,因此下表中仅列出删除命令,
其他修改和拷贝仅需替换命令即可。

命令 用途
ndd 删除n
ndw 删除n个 word 文本对象
ndiw 删除n个 iw 文本对象(其他文本对象不再列出)
d$ 从光标位置到行尾的文字全部删除
d^ 从光标位置到本行第一个非空字符的文字全部删除
d0 从光标位置到本行行首的文字全部删除
dnfa 从光标位置到本行光标之后第na的文字全部删除(其他移动光标的命令不再列出)

粘贴动作

粘贴动作的命令是 p(paste)。
粘贴命令会将寄存器中存储的内容插入到光标之后,
如果寄存器中存储的内容是一行或多行文字,则会插入到光标所在行的下一行。
粘贴动作之前也可以加上数字表示要把粘贴动作重复几次。

命令 用途
np 将寄存器中存储的内容向光标后插入n
nP 将寄存器中存储的内容向光标前插入n

寄存器

寄存器是Vim提供的一组容器,用户可以往容器中存储和读取内容。
在普通模式下,寄存器的使用规则很简单,首先输入"表示要指定寄存器,
然后输入寄存器的名称,最后再输入普通模式的命令。

例如将一行文字拷贝到寄存器a中的完整命令是:"ayy

Vim中共有9种寄存器,下文将依次介绍。

“” 无名寄存器:

最常用的寄存器,它会自动记录用户最近的删除和拷贝的内容。
无论是否指定寄存器,拷贝和删除的内容都会存储到该寄存器中。

在不指定寄存器时,粘贴动作都是插入该寄存器中存储的内容。

“[0~9] 数字寄存器:

当用户拷贝时未指定寄存器,数字寄存器0会存储用户最近的一次拷贝的内容,
无论拷贝的内容是否包含换行符(这个很重要,和其他的数字寄存器存在区别)

当用户进行删除时指定的寄存器不是黑洞寄存器时,
数字寄存器1~9会存储用户最近9次删除的内容,
寄存器1中存储的一定是最近的一次删除,当用户下一次进行删除时,
寄存器1中的内容会存储到寄存器2,寄存器2中的内容会存储到寄存器3,以此类推直到寄存器9

需要注意的是,只有当删除的内容包含换行时,该内容才会存入数字寄存器1~9
反之则会被存入短删除寄存器-

例如:

eg.1

//删除前

abcdefghijklmn

opqrstuvwxyz

//使用dd删除后

opqrstuvwxyz

该情况删除的内容包含了换行(改变了文章的总行数),
删除的内容会被存入数字寄存器1

eg.2

//删除前

abcdefghijklmn

opqrstuvwxyz

//使用可视模式删除后

abcdefghiuvwxyz

该情况删除的内容也包含了换行(改变了文章的总行数),
删除的内容会被存入数字寄存器1

eg.3

//删除前

abcdefghijklmn

opqrstuvwxyz

//使用0d$删除后

(这里存在一个空行)

opqrstuvwxyz

该情况删除的内容不包含换行(未改变文章的总函数),
删除的内容会被存入短删除寄存器-

“- 短删除寄存器:

在用户删除时,不包含换行的删除内容就会被存储在该寄存器中。

短删除寄存器只有一个,因此在进行短删除时,需要注意,
Vim只会为你存储最近的短删除内容。

“[a~Z] 字母寄存器:

字母寄存器共有26个,大写和小写是同一个寄存器,但是在使用上存在差异。

字母寄存器提供给用户指定来存储内容,当用户在储存内容时指定寄存器时使用小写字母,
则新的内容会覆盖寄存器中原有的内容。

若用户使用的是大写字母,则新的内容会接续在旧内容的后面(包括宏的录制)。

“: “. “% “# 只读寄存器:

寄存器 : 存储用户上一次在命令模式下输入的命令。(目前我还不清楚这个可以做什么)

寄存器 . 存储用户上一次插入的内容。

寄存器 % 存储当前正在编辑的文件名。

寄存器 # 存储跳转到的下一个文件的文件名。
(Vim在打开多个文件后,可以通过:n命令在文件之间进行跳转,VS和VSC的Vim插件目前似乎不支持该命令)

“= 表达式寄存器:

还不会用…

“_ 黑洞寄存器:

器如其名,指定黑洞寄存器进行删除时,删除的内容不会存储到任意一个寄存器中,
会永久删除。

“/ 搜索样式寄存器:

该寄存器存储用户上一次使用/命令进行搜索时的搜索内容。

“* “+ “~ 选择与拖放寄存器寄存器:

当操作系统的剪切板可用时,寄存器"*"+中会存储剪切板中最新的内容。

当通过GUI界面拖拽一段内容到Vim中时,这段文字会存储在寄存器"~中。
(VS和VSC试过好像不行,不知道是不是我操作有问题)

记录与回放命令

Vim中可以使用.来重复上一次修改操作,但是它只能记录最近的一次修改,再复杂的操作便无能为力了。

Vim中提供了宏用来记录命令,使用它很简单,只需要如下四步:

  1. 按下q,再按下任意一个字母寄存器,表示将命令记录到某个字母寄存器中。例如:qa

  2. 开始输入需要记录的命令。

  3. 再次按下q,结束命令的记录。

  4. 按下@,再按下记录命令的字母寄存器,表示执行该字母寄存器中存储的命令。例如:@a

同样的,我们可以在执行命令之前输入需要重复的次数,Vim会自动多次执行记录的命令。
例如:21@a,会将寄存器a中的命令执行21次。

下表中记录了个人常用的和宏相关的命令。

命令 用途
q[a~Z] 开始录制宏
q 当正在录制宏时,结束录制
n@[a~Z] 重复播放n次宏

宏存储在寄存器中,我们录制完宏可以通过指定寄存器粘贴,将录制的内容粘贴出来。

例如下面这一段宏,它的作用是在某一行的行首输入”123”,然后将光标移动到下一行。

I123<Esc>j

我们将宏粘贴出来后,可以对其进行修改后再次存入寄存器中。

例如我将上面那段宏的作用改成在行尾输入123,然后将光标移动到上一行。

A123<Esc>k

然后再普通模式下输入"ayy,将修改过的宏整行存入寄存器中,宏便修改完成了。

接下来我将举几个例子简单演示一下宏的使用。

** eg.1 **
先来个简单的例子。

<!-- 例如下面这段很长的句子,我想用宏让它在每个中文逗号`,`的位置换行 -->

 当用户进行删除时指定的寄存器不是黑洞寄存器时, 数字寄存器`1~9`会存储用户最近9次删除的内容, 寄存器`1`中存储的一定是最近的一次删除, 当用户下一次进行删除时, 寄存器`1`中的内容会存储到寄存器`2`, 寄存器`2`中的内容会存储到寄存器`3`, 以此类推直到寄存器`9`。

<!-- 我们可以使用宏进行如下操作 -->
<!-- 1. 将光标移动至行首,按下 qa ,开始记录命令 -->
<!-- 2. 按下 f ,将输入法切换至中文,输入 ‘,’,光标会跳转至第一个逗号的位置 -->
<!-- 3. 按下 a ,在逗号后插入,按下回车换行 -->
<!-- 4. 按下 <ESC> 键退出插入模式,按下 q 结束命令的录制 -->
<!-- 5. 按下 @a 开始播放命令,按下 @@ 重复上一次播放的命令,也可以输入 5@a ,将命令重复5次 -->

<!-- 完成后句子如下 -->

 当用户进行删除时指定的寄存器不是黑洞寄存器时,
 数字寄存器`1~9`会存储用户最近9次删除的内容,
 寄存器`1`中存储的一定是最近的一次删除,
 当用户下一次进行删除时,
 寄存器`1`中的内容会存储到寄存器`2`,
 寄存器`2`中的内容会存储到寄存器`3`,
 以此类推直到寄存器`9`。

** eg.2 **
再来个比较复杂的例子。

Hexo不支持Markdown原生的目录,因此目录需要一行行手写,很麻烦,
但是我们可以通过Vim的宏来快速的自动写出目录。

<!-- 以下是我的文章的标题的格式,为了生成目录,我需要把它们从全文的各处给复制到文章的开头 -->
<h1 id = "0"> 前言 </h1>
......
<h1 id = "1"> Vim的模式 </h1>
......
<h2 id = "1.1"> 普通模式 </h2>
......
<h2 id = "1.2"> 插入模式 </h2>
......
<h2 id = "1.3"> 可视模式 </h2>
......

为了把文章复制到开头,我需要在文章要插入目录的位置设置位置标记 a
并在第一个标题之前设置位置标记 b。(命令:mamb

然后录制下面这段宏到寄存器a中。(命令:qa

`b/<h
mbyy`aP

录制完后按下q停止录制。

接下来我来解释一下这段命令。

  1. `b 表示将光标跳转到位置标记 b 所在的位置。
  2. /<h 表示向后搜索字符串”<h”,命令换行表示执行搜索命令,将光标跳转到下一个”<h”所在的位置。
  3. mb 表示将在光标所在位置设置位置标记 b
  4. yy 表示复制光标所在位置的这一整行。
  5. `a 表示跳转回位置标记 a 所在的位置,也就是文章开头要插入目录的位置。
  6. P 表示将无名寄存器中存储的标题粘贴到位置标记 a 所在位置的上一行。

于是,通过位置标记和字符串查找,就能依次锁定标题的位置并将它们复制到文章的开头处。

我的文章目前有22个标题,去掉录制宏时已经复制的一个,再输入21@a将宏再执行21次,就成功将所有标题复制到文章开头了。

注意:VSC的Vim插件在宏和位置标记上似乎存在bug。
当你在宏中修改了位置标记,并且批量使用宏时,位置标记并不会发生改变。

具体可以看我向VSCVim插件提交的两篇Issue。Issue_1

在VSC中上述的方法也并非不能使用,只需要再录制一个宏b,在这个宏里只做一件事:播放一次寄存器a中的宏。
嵌套了一层宏之后,批量播放宏b,就不会再有问题了。

现在,我已经成功将标题都复制到了文章的开头,但是还要对其进行修改变成下面这种形式。

[前言](#0)

[Vim的模式](#1)

[普通模式](#1.1)

......

具体做法我就不详细介绍了,在这只展示一下我录制的宏了。

0f""cyi""byit0d$i[<Esc>"bpA(#<Esc>"cpo<Esc>j

未完待续…

图片来源:http://cenalulu.github.io/linux/all-vim-cheatsheat/