这篇文章介绍 Vim 中打开/报错文件的各种技巧,以及用动作命令在文档中高效移动。
打开、保存文件
技巧 41:用 :edit
命令打开文件
在 Vim 中,:edit
命令允许通过文件的绝对路径或相对路径来打开文件。
在 Vim 中也有工作目录的概念,当 Vim 启动时,它会采用 shell 的活动目录作为其工作目录。可以在 Vim 中执行 :pwd
命令查看 Vim 的当前工作目录。
如果想打开的文件和当前活动缓冲区中的文件在同一个目录,如果能够采用活动缓冲区作为参考点,可以更方便。%
符号代表活动缓冲区中的完整路径,按下tab键会将其展开。而 :h
修饰符会去除文件名,但保留路径中的其它部分。因此,输入如下命令下,就会得到当前活动缓冲区的目录:
1 | :edit %:h<tab> |
这个技巧除了可以用于 :edit
命令外,也可以用于 :write
、:saveas
等命令。
技巧 42:用 :find
命令打开文件
:find
命令允许我们通过文件名打开一个文件,但无需输入该文件的完整路径。想利用此功能,我们首先要配置 path
选项。 path
选项允许我们指定一些目录,当调用 :find
命令时,Vim 会在这些目录中进行查找。可以使用 :h 'path'
获取更多信息。
例如,通过如下方式将指定目录加入到path中:
1 | :set path+=Pratical_Vim/** |
其中 **
通配符会递归匹配 Pratical_Vim
下的所有子目录。
配置好 path 选项后,通过 find 命令,只需要给出文件名就可以打开指定目录中的文件了。如果文件名不唯一,使用 tab 键会自动补全所有匹配的完整文件路径。如果不进行 tab 选择,则 Vim 会打开第一个满足条件的文件。
技巧 43:使用 netrw 管理文件系统
Vim 发行版中自带的 netrw 插件,允许我们对文件系统进行管理。netrw 是Vim 发行版的标准插件,因此不需要额外安装任何东西。但是必须确保 Vim 已被配置为可加载插件:
1 | set nocompatible |
结识 netrw:Vim原生的文件管理器
当使用目录作为文件名启动 Vim 时,Vim 就会打开一个文件窗口管理器。文件管理器也是一个常规的 Vim 缓冲区,但它代表的是一个目录的内容,而不是文件的内容。
打开文件管理器
使用 :edit {path}
命令打开文件管理器,只不过 path 是相应的目录名。例如使用 :edit .
命令,就可以查看当前 Vim工作目录的内容。
如果想打开当前活动缓冲区中文件所在的目录,可以使用 :edit %:h
。另外,:Explore
命令也可以实现功能。另外,:Sexplore
及 :Vexplore
命令,可以分别在水平切分窗口及垂直切分窗口里打开文件管理器。
与分割窗口协同工作
在执行 Explore
命令时,文件管理器在当前活动窗口打开。如果在调出文件管理器后,又想切回到刚刚正在编辑的那个文件,可以使用 <C-r>
命令。所以 Vim 下,文件管理管理器与文件窗口间更像是翻转的关系。
使用 netrw 完成更多功能
netrw
插件不仅可以浏览文件系统,还可以创建新文件及目录,重命名已有文件及目录,或者删除它们。另外 netrw
插件的杀手级功能是:可以通过网络读写文件。
技巧 44:把文件保存到不存在的目录中
:edit {file}
命令一般用于打开一个已经存在的文件。如果指定的文件名并不存在,Vim 会创建一个新的空白缓冲区,并允许我们编辑(此时可以通过 <Ctrl-g>
命令查看文件状态)。随后执行 :write
命令时,Vim 就会将该缓冲区的内容写到所指定的新文件名中。但是,如果该文件名包含不存在的目录时,保存时 Vim 会提示错误。
此时我们可以通过调用外部程序 mkdir
进行补救:
1 | :!mkdir -p %:h |
这里需要使用 mkdir
的 -p
参数进行递归创建。
技巧 45:以超级用户权限保存文件
有时,我们不得不把修改保存到一个需要 sudo 权限的文件中,无需重启 Vim 就能实现这一功能。
例如,如果以普通用户修改 /etc/hosts
文件。此时,Vim 会把文件标识为 [readonly]
,修改后,即使使用 :write!
命令,也无法将修改保存到文件中,因为没有修改该文件的权限。
此时,可以通过如下方式,对文件进行修改:
1 | :write !sudo tee % > /dev/null |
其中,write 命令将当前缓冲区的内容作为标准输入,传递给 tee 命令,tee 命令又通过读取标准输入,将其保存到当前文件名中(% 代表当前缓冲区对应的文件名,即 /etc/hosts)。因此通过这条命令,就可以用root权限写入修改。
之后,由于 Vim 检测到文件被外部程序修改,通常这就意味着缓冲区中的内容和文件不同步了,因此 Vim 会提示我们做出选择,此时我们可以选择 L(重新加载文件)
。
用动作命令在文档中移动
Vim 提供了很多在文档中移动的方法以及许多在缓冲区间跳转的命令。接下来将重点介绍动作命令(motion),利用这些命令可以在文档中四处移动。
动作命令除了可以在文档中移动,还可以在操作符待决模式中使用。关于动作命令,可以参考 :h motion.txt
技巧 46:让手指保持在本位行上
对于盲打人员,标准姿势是手指始终保留在 home row
(即 ASDGHJKL
这一行)上。此时Vim提供如下命令来移动光标:
- h:左移一列
- j:下移一行
- k:上移一行
- l:右移一列
当然,除了通过这 4 个键,光标键中的上下左右也能够起到移动光标作用,但是并不方便。
可能有人觉得输入 h
命令进行移动时,并不方便,因为需要伸出食指才能按下它。但是,其实我们很少会用到h键。对于水平方向的移动而言,可以使用面向单词的命令,或者使用字符查找动作命令来进行更快地移动。一般,只是用 h
和 l
键解决差一错误。
技巧 47:区分实际行与屏幕行
Vim 区分实际行与屏幕行。当 wrap
设置被启用时,每个超出窗口宽度的文本行都会被回绕显示。因此文件中的一行也会被显示为屏幕上的若干行。
要知道实际行与屏幕行之间的不同,最简单的方法是启用 number
设置。启用之后,以行号开头的行对应着一个实际行,它们会占据着屏幕上的一行或几行。回绕行的前面不会显示行号。
Vim提供了不同的动作命令来操作两者:
- j:向下移动一个实际行
- gj:向下移动一个屏幕行
- k:向上移动一个实际行
- gk:向上移动一个屏幕行
- 0:移动到实际行的行首
- g0:移动到屏幕行的行首
- ^:移动到实际行的第一个非空白字符
- g^:移动到屏幕行的第一个非空白字符
- $:移动到实际行的行尾
- g$:移动到屏幕行的行尾
技巧 48:基于单词移动
Vim 提供一组动作命令,让我们每次可以把光标正向或反向移动一个单词的距离。
- w:正向移动到下一单词的开头
- b:反向移动到当前单词/上一单词的开头
- e:正向移动到当前单词/下一单词结尾
- ge:方向移动上一单词结尾
w
和 b
都是以词首为目标,而 e
和 ge
命令都是以词尾为目标。所以通过 ea
命令,我们可以很快在当前单词结尾添加新的内容,而 gea
命令则可以在上一个单词结尾添加内容。
Vim 对单词(word)和字串(WORD)有不同的定义。可以使用 :h word
和 :h WORD
获得更多帮助信息。简单来说,一个单词由字母,数字、下划线字符序列构成,而一个字串由非空白字符序列构成。所以,在如下语句中,包含10个单词、5个字串:
1 | e.g. we'are going too slow |
之前的 w
,b
,e
命令等都是基于单词的移动命令,而大写的 W
,B
,E
命令都是基于字串的移动命令:
- 如果想更快地移动,可以使用面向字串的动作命令
- 如果想更细粒度的移动,可以使用面向单词的动作命令
技巧 49:对字符进行查找
Vim 的字符查找命令让我们可以在行内快速移动,并且他们能够在操作符待决模式下很好地工作。
查找字符时,可以是包含或是排除目标字符:
- f{char}:正向移动到下一个 {char} 所在之处
- F{char}:反向移动到上一个 {char} 所在之处
- t{char}:正向移动到下一个 {char} 所在之处的前一个字符上
- T{char}:反向移动到上一个 {char} 所在之处的后一个字符上
- ;:重复上次的字符查找命令
- ,:反转方向,重复上次的字符查找命令
通常来说,在当前行内快速移动时,倾向于使用 f{char}
和 F{Char}
。而如果需要和 d{motion}
或 c{motion}
一起使用时,更倾向于使用 t{char}
及 T{char}
命令。
另外,我们使用一次查找命令后,可以使用 ;
进行重复,而当我们重复地忘乎所以而找到错误目标时,可以使用 ,
进行反向查找,因此这又一次体现了 执行-重复-回退
的 Vim 精髓。
另外当我们移动到一个单词时,并不一定是要单词开头作为目标字符。因为有些字符重复频率非常高,这会导致我们移动效率变低,而选择一些不常见的字符,则可以更快移动到目标单词上,之后通过操作文本对象一样可以达到目的。
技巧 50:通过查找进行移动
字符查找命令一次只能查找一个字符,并且只能在当前行内查找。 如果想查找一个以上的字符,或者是移动到当前行之外的话,就需要使用查找命令。
使用 /
命令进行正向查找,?
命令进行反向查找。另外,使用 n
命令可以重复上次查找,而 N
命令则在相反方向上重复上次查找。
当启用了 hlsearch
时,可以高亮匹配项。如果文档中有多个匹配项时,可能会导致结果不太容易辨识。此时可以设置 :set nohlsearch
选项以关闭搜索时高亮匹配。
查找命令不仅可以在普通模式下使用,在可视模式及操作符待决模式下都可以使用。例如,在如下句子中:
1 | This phrase takes time but |
如果想删除 takes time but eventually
。可以直接使用 d/ge<CR>
(假设当前光标在 t 上)。当然通过可视模式进行查找,然后在执行 d
命令进行删除也是可行的。但是直接使用 d{motion}
让你更加 cool。
技巧 51:用精确的文本对象选择选区
文本对象就是基于结构定义的文本区域,它允许我们操作括号、被引用的文本,XML标签以及其它文本中常见的结构。
Vim 的文本对象由两个字符构成,第一个字符永远是 i
或 a
。一般来说,i
开头的文本对象会选择分隔符内部的文本(include),而 a
开头的文本对象会选择包括分隔符在内的整个文本(all)。
如下总结了部分 Vim 内置的文本对象
- a) 或 ab:一对圆括号
- i) 或 ib:一对圆括号内部
- a} 或 aB:一对花括号
- i} 或 iB:一对花括号内部
- a]:一对方括号
- i]:一对方括号内部
- a>:一对尖括号
- i>:一对尖括号内部
- a’:一对单引号
- i’:一对单引号内部
- a”:一对双引号
- i”:一对双引号内部
- a`:一对反引号
- i`:一对反引号内部
- at:一对xml标签
- it:xml标签内部
文本对象自身并不是动作命令,不能用它们在文档中移动。但是可用于可视模型及操作符待决模式,而且在操作符待决模式下,威力更大。凡是在 operator{motion}
中,motion
都可以使用文本对象。
技巧 52:删除周边,修改内部
文本对象通常是成对出现的,一个用于操作对象内部的文本,而另一个则操作周围的文本。上一技巧介绍了操作分隔符的文本对象(分隔符文本对象),这一节将介绍操作文本块(范围文本对象),如单词、句子和段落等。
- iw:当前单词
- aw:当前单词及一个空格
- iW:当前字串
- aW:当前字串及一个空格
- is:当前句子
- aS:当前句子及一个空格
- ip:当前段落
- ap:当前段落及一个空行
之所以会出现两套,是因为 d{motion}
命令和 a
开头文本对象配合效果更好(删除一个空格或空行),而 c{motion}
命令和 i
开头的文本对象配合效果更好(保留原来的空格或空行)。
技巧 53:设置位置标记,以便快速调回
m{a-zA-Z}
命令会用选定的字母标记当前光标所在位置,小写位置标记只在每个缓冲区里局部可见,而大写位置标记则全局可见。
Vim提供了两条普通模式命令,可以用他们跳转到一个位置标记上:
- ‘{martk} 跳转到位置标记所在的行,并把光标置于该行第一个非空白字符上
- `{mark} 跳转到位置标记所在的行,并把光标置于设置位置标记时光标所在之处
Vim 会自动为我们设置一些位置标记:
- ``:当前文件中上次跳转动作之前的位置
- `.:上次修改的地方
- `^:上次插入的地方
- `[:上次修改或复制的起始位置
- `]:上次修改或复制的结束位置
- `<:上次高亮选区的起始位置
- `>:上次高亮选区的结束位置
技巧 54:在匹配括号间跳转
%
命令允许我们在一组开、闭括号间跳转,可用于 ()
、{}
、[]
等括号。每当使用 %
命令时,Vim 会自动为发生跳转的地方设置一个位置标记,因此通过 `` 命令也可以来回在括号间跳转。
Vim 发布时带了一个名为 matchit
的插件,它增强了 %
命令的功能。激活了 matchit 插件后,%
命令可以在匹配的关键字键跳转。尽管 matchit 插件随 Vim 一起发布,但是它缺省未使能。通过如下配置使能该插件:
1 | runtime macros/matchit.vim |
另外再推荐一款插件,surrond.vim
,可以很容易地给选中的文本添加分隔符。