0%

Vim 实用技巧(04):打开、保存文件 & 用动作命令在文档中移动

这篇文章介绍 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
2
set nocompatible
filetype plugin on

结识 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
2
:!mkdir -p %:h
:write

这里需要使用 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键。对于水平方向的移动而言,可以使用面向单词的命令,或者使用字符查找动作命令来进行更快地移动。一般,只是用 hl 键解决差一错误。

技巧 47:区分实际行与屏幕行

Vim 区分实际行与屏幕行。当 wrap 设置被启用时,每个超出窗口宽度的文本行都会被回绕显示。因此文件中的一行也会被显示为屏幕上的若干行。

要知道实际行与屏幕行之间的不同,最简单的方法是启用 number 设置。启用之后,以行号开头的行对应着一个实际行,它们会占据着屏幕上的一行或几行。回绕行的前面不会显示行号。

Vim提供了不同的动作命令来操作两者:

  • j:向下移动一个实际行
  • gj:向下移动一个屏幕行
  • k:向上移动一个实际行
  • gk:向上移动一个屏幕行
  • 0:移动到实际行的行首
  • g0:移动到屏幕行的行首
  • ^:移动到实际行的第一个非空白字符
  • g^:移动到屏幕行的第一个非空白字符
  • $:移动到实际行的行尾
  • g$:移动到屏幕行的行尾

技巧 48:基于单词移动

Vim 提供一组动作命令,让我们每次可以把光标正向或反向移动一个单词的距离。

  • w:正向移动到下一单词的开头
  • b:反向移动到当前单词/上一单词的开头
  • e:正向移动到当前单词/下一单词结尾
  • ge:方向移动上一单词结尾

wb 都是以词首为目标,而 ege 命令都是以词尾为目标。所以通过 ea 命令,我们可以很快在当前单词结尾添加新的内容,而 gea 命令则可以在上一个单词结尾添加内容。

Vim 对单词(word)和字串(WORD)有不同的定义。可以使用 :h word:h WORD 获得更多帮助信息。简单来说,一个单词由字母,数字、下划线字符序列构成,而一个字串由非空白字符序列构成。所以,在如下语句中,包含10个单词、5个字串:

1
e.g. we'are going too slow

之前的 wbe 命令等都是基于单词的移动命令,而大写的 WBE 命令都是基于字串的移动命令:

  • 如果想更快地移动,可以使用面向字串的动作命令
  • 如果想更细粒度的移动,可以使用面向单词的动作命令

技巧 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
2
This phrase takes time but
eventually gets to the point.

如果想删除 takes time but eventually。可以直接使用 d/ge<CR>(假设当前光标在 t 上)。当然通过可视模式进行查找,然后在执行 d 命令进行删除也是可行的。但是直接使用 d{motion} 让你更加 cool。

技巧 51:用精确的文本对象选择选区

文本对象就是基于结构定义的文本区域,它允许我们操作括号、被引用的文本,XML标签以及其它文本中常见的结构。

Vim 的文本对象由两个字符构成,第一个字符永远是 ia 。一般来说,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,可以很容易地给选中的文本添加分隔符。