0%

Vim 实用技巧(05):在文件间跳转 & 复制粘贴

这篇文章介绍 Vim 中在文件间跳转的技巧,以及如何通过寄存器实现各种复制粘贴操作。

在文件间跳转

技巧 55:遍历跳转列表

Vim 会记录前后跳转的位置,并提供一些命令让我们能够沿原路返回。

通常来说,动作命令在一个文件内移动,而跳转命令则可以在文件间移动(但是有些移动命令也被归为跳转)。任何改变当前窗口中活动文件的命令,都可以被称为跳转命令。此外,使用 [Count]G 跳转到指定行的命令,也可以被称为跳转。面向句子的动作及面向段落的动作都算跳转。一句话来概括,大范围的动作命令可能会被当成跳转,小范围的动作命令则只能算移动

以下选了一些跳转命令:

  • [count]G:调到指定行
  • /pattern<CR>?pattern<CR>nN:跳转到下一个/上一个模式出现之处
  • %:跳转到匹配括号之处
  • ():跳转到上一句/下一句的开头
  • {}:跳转到上一段/下一段的开头
  • HML:跳转到屏幕最上方/正中间/最下方
  • gf:跳转到光标下的文件名
  • Ctrl-]:跳转到光标下的定义之处
  • '{mark}、`{mark}:跳转到位置标记

使用 :jumps 命令,可以查看跳转列表的内容。使用 <Ctrl-o> 命令可以在跳转列表路径上后退,而 <Ctrl-i> 命令则可以在跳转列表上前进。

另外,Vim 可以同时维护多份跳转列表,实际上,每个单独的窗口都有一份自己的跳转列表。

技巧 56:遍历改变列表

每当对文件作出修改后,Vim 都会记录下当时光标所在的位置。Vim 在编辑会话期间维护一张表,里面记载我们对每个缓冲区所做的修改。这个表就是改变列表。使用 :changes 命令可以查看改变列表的内容。

可以使用 g;g, 命令反向或正向遍历改变列表。要想跳转到上次文档被改过的地方,可以使用 g;

之前也讲过,Vim 会自动创建一些标记。'. 总是跳转到上次修改过的位置,而 '^ 则跳转到退出插入模式时光标所在位置(另外,使用 gi 命令也是可行的,而且它会自动切换到插入模式)。

Vim 会为编辑会话的每个单独缓冲区维护一个改变列表。

技巧 57:跳转到光标下的文件

Vim 可以将文档中的文件名当成一个超级链接,在选择了正确的配置后,就可以使用 gf (可以记忆成 go to file)命令跳转到光标下的文件了。

除了直接打开一个指定的文件名外,也可以省略文件的扩展名。suffixesadd 选项允许指定一个或多个文件扩展名。Vim 使用 gf 命令搜索文件名时会尝试使用这些扩展名。

每次使用 gf 命令时,Vim 都会在跳转列表中增加一条记录。因此可以通过之前的跳转命令进行来回跳转。

path 选项包含一个目录列表,使用 gf 命令时,Vim 会检查 path 目录列表中的每个目录,看该目录是否包含一个匹配光标下文本的文件名。使用 :set path? 可以查看 path 选项的当前值:

  • . 代表当前文件所在的目录
  • 空字符串(由两个连着的逗号界定)则代表工作目录

技巧 58:用全局位置标记在文件间快速跳转

之前在学习位置标记时已经讲过,使用 m{letter} 命令设置位置标记时,如果使用小写字母,会创建局部于缓冲区中的标记,而大写字母则创建全局标记。全局标记在两次编辑会话之间仍然保留。

当需要浏览一些文件,然后再快速跳回原处时,全局标记会非常有用。

复制粘贴

技巧 59:用无名寄存器实现删除、复制与粘贴操作

在 Vim 的操作里,剪切、复制与粘贴这三组动作,操作的都是寄存器,而非剪贴板。

  • 调换字符: 使用 xp 命令可以快速调换当前光标下的字符和其之后的一个字符。因为 x 命令会删除一个字符,并且把该字符的内容放到无名寄存器中,最后 p 命令把无名寄存器的内容粘贴到光标后面

  • 调换文本行: 使用 ddp 命令可以快速交换当前光标所在的行和其之后的一行。同样,dd 命令会删除一行,并将该行内容放到无名寄存器中,p 命令把无名寄存器中的内容粘贴到光标后

  • 创建文本行的副本:使用 yyp 命令可以创建一行的副本。其原理也是类似。

由于复制(yank)、删除(delete)等命令,默认都是将内容保存到无名寄存器,因此有时候一些操作会不经意覆盖无名寄存器的内容,造成预料之外的结果。为了解决这个问题,需要深入了解 Vim 寄存器的工作原理。

技巧 60:深入理解 Vim 寄存器

Vim 的删除、复制、粘贴操作都可以使用众多寄存器中的某一个。可以使用 "{register} 的方式引用某一个寄存器。如果不指明寄存器,默认使用无名寄存器

除了普通模式下的命令,Vim 也提供了用于删除、复制、粘贴的Ex命令,其形式分别为::delete {register}:yank {register}:put {register}

以上操作,如果没有指定寄存器,Vim默认使用无名寄存器。无名寄存器用 " 表示,因此可以通过 "" 双引号,显式引用无名寄存器。

当使用 y{motion} 命令时,要复制的文本不仅会拷贝到无名寄存器,而且也会拷贝到复制专用寄存器。复制专用寄存器可以使用数字 0 来引用。注意,复制寄存器只对复制命令有效,xsc{momtiion}d{motion} 命令不会覆盖复制寄存器中的内容。

Vim 提供了一组以 26 个英文字母命名(a-z)的 有名寄存器。使用小写字母引用 有名寄存器 时,会覆盖寄存器中的原有内容。而如果使用大写字母的话,则将新的内容添加到寄存器原有内容之后。

黑洞寄存器是个有去无会的地方,可以使用 _ 引用它。如果想删除一段内容,同时该内容不保存到无名寄存器中,则可以使用 "_d{motion} 命令(其实是将内容保存到黑洞寄存器)。

如果想从 Vim 复制文本到外部程序(反之亦然),则必须使用系统剪贴板。Vim 的加号寄存器与系统剪贴板等效:

  • 如果在外部程序中复制了文本,可以使用 "+p 命令或者在插入模式下使用 <Ctrl-r>+ 命令将其粘贴到Vim内部
  • 相反,如果使用 "+ 引用寄存器,则相应的文本也会捕获到系统剪贴板。

Vim的寄存器通常被认为是保存文本的容器。但是,通过 "= 引用的表达式寄存器确是个例外。当我们从表达式寄存器中获取内容时,Vim 会跳到命令行模式,并显示提示符 =,这时可以输入一段 Vim 脚本并执行。

Vim 提供了几组被隐式赋值的寄存器,它们被称作只读寄存器:

  • "%:当前文件名
  • "#:轮换文件名
  • ".:上次插入的文本
  • "::上次执行的Ex命令
  • "/:上次查找的模式

可以使用 :reg 命令查看指定所有寄存器或指定寄存器中的内容,在插入模式下,可以使用 <Ctrl-r> 来访问寄存器。

技巧 61:用寄存器中的内容替换高亮选区的文本

在可视模式下使用 p 命令时,Vim 会用我们指定的寄存器内容来替换高亮选区中的文本。但是其背后的步骤也需要注意,在可视模式下使用 p 命令时:

  • Vim 是首先将无名寄存器的内容取出并粘贴到高亮选区
  • 然后将高亮选区中的内容存入无名寄存器

利用这条技巧,如果需要交换如下 chips 和 fish 两个单词的顺序(假设光标在 chips 上):

1
I like chips and fish
  • diw:删除单词
  • mm:设置位置标记m
  • ww:移动到目标单词
  • ve:选中当前单词
  • p:使用无名寄存器中的内容替换高亮选区
  • 'm:重新回到高亮选区
  • P:粘贴新的无名寄存器内容。

技巧 62:把寄存器的内容粘贴出来

普通模式下的粘贴命令,根据要插入文本的性质不同,执行结果也不同。

  • p 命令将寄存器中的文本粘贴到光标之后
  • P 命令将寄存器的文本粘贴到光标之前

但是光标前后的位置具体在哪,要根据寄存器中的内容决定:

  • 面向行的复制或删除操作,将创建面向行的寄存器
  • 而面向字符的复制或删除操作,将创建面向字符的寄存器

对于面向行的区域,pP 命令行为就非常直观了。需要注意,这两个命令将光标位置移动被粘贴文本的开头。而 gpgP 命令行为类似,但是将光标位置移动到被粘贴文本的结尾,当复制多行文本时,gp 命令尤为管用。

当粘贴面向字符的区域时,pP 命令使用起来并不直观(非常容易犯差一错误)。可以尝试在插入模式下使用 <Ctrl-r>{register} 的方式来粘贴寄存器中的内容。这种方式,寄存器的文本总是在当前光标之间插入。

技巧 63:与系统剪贴板进行交互

在插入模式下使用系统粘贴命令时,就如同手动输入。如果 autoindent 选项被开启时,这就意味着每当创建新行,都讲保持同级缩进,进而导致空格增多(在自动缩进基础上又保持原有空格)。GVim 能够捕获系统剪贴板粘贴文本的事件,从而可以相应地调整行为。但是在终端运行 Vim 时,这些信息无法获取。

使用 paste 选项允许我们手动通知 Vim 要使用系统粘贴命令了。paste 选项启用后,Vim 将禁用所有插入模式下的映射项与缩写,并重置很多选项。当使用完 paste 选项后,可以使用 :set paste! 回到普通模式。

为避免切换 paste 选项,也可以使用加号寄存器进行粘贴。