0%

学习 vi 和 Vim 编辑器(7):多窗口功能

Vim 默认是在一个窗口中编辑所有文件,在文件间移动或者移动到文件的不同部分时只显示一个缓冲区。但 Vim 也提供了多窗口编辑功能,可以简化复合的编辑任务。这篇文章将学习如何在 Vim 中使用多窗口,包括多窗口编辑工作的初始化/启动、多窗口的 ex 命令、在窗口间移动光标、在显示区中移动窗口等。

启动多窗口编辑

可以在打开Vim时启动多窗口编辑,也可以在编辑会话中分割窗口。

  • 从命令行(shell)启动多窗口

默认情况下,Vim 只为一个会话打开一个窗口,即使打开时已经指定打开多个文件。如果想从命令行中打开多个窗口,需要使用Vim的 -o 选项,此时 Vim 会试着为命令行上的每个文件参数打开一个窗口,如下图所示:

也可以在 -o 后面附上数量(即 -o[N]),指定打开窗口的数量。

  • Vim 启动后多窗口编辑

在启动 Vim 后,可以使用 :split 创建新窗口。该命令将当前窗口在水平方向分成两半,均显示相同缓冲区中的内容,因此可以在两个窗口中浏览相同的文件。类似地,使用 vsplit 命令,可以创建全新的垂直分隔窗口。以 :split:vsplit 分隔窗口时,可以接收文件参数,此时将在新的窗口中打开所指定的文件。

可以通过选项精确地控制分隔窗口行为:

1
:[n]split [++opt] [+cmd] [file]
  • n:为 Vim 指定新窗口中显示的行数,新窗口位于画面顶端
  • opt:给新的窗口会话传递指定的 Vim 选项
  • cmd:新窗口会话中所要执行的命令
  • file:新窗口会话中所要编辑的文件

上述命令形式同样适用于 :vsplit。除了使用 ex 命令来分隔窗口,Vim 也支持快捷键对窗口进行分隔,所有 Vim 窗口命令都以 ^W 开始(W 代表 Window 的意思):

  • ^Ws: 等价于 :split 命令
  • ^Wv:等价于 :vsplit 命令

相比于快捷键方式,使用 ex 命令可以让我们提供可选参数。如下两个命令也可以水平划分窗口来打开文件,适用于不同场景:

  • :sview filename:水平划分窗口以打开新文件,但是缓冲区设置为 readonly
  • :sfind [++opt][+cmd] filename:与 :split 命令类似,但是会首先在 path 中寻找 filename。如果未找到,则不分割窗口

窗口导航

使用 gVim 和 Vim 时,可以方便地在窗口间移动。gVim 默认支持鼠标点击,而 Vim 则可通过 :set mouse=a 命令来打开 mouse 选项,激活鼠标。Vim 也提供全套导航命令,可在窗口间快速移动。这些窗口间的移动命令类似于编辑模式下的相应命令:

  • ^Wj^W<DOWN>:移动到下一个窗口,这个命令不会循环移动,如果已经是最底端的窗口,则该命令无效。而且该命令在往下移动时,还会跳过同一行中的其他窗口
  • ^Wk^W<UP>:移动到上一个窗口,刚好与 ^Wj 命令相反
  • ^Wh^W<LEFT>:移动到当前窗口左边的窗口
  • ^Wl^W<RIGHT>:移动到当前窗口右边的窗口
  • ^Ww^W^w:移动到下一个窗口,即下方的窗口或右边的窗口。这个命令会在所有 Vim 窗口间循环移动。移动顺序为:从左到右,从上到下
  • ^Wt^W^t:移动到最左上角的窗口(t 代表top)
  • ^Wb^W^b:移动最右下角的窗口(b 代表bottom)
  • ^Wp^W^p:移动到前一个(最后访问的)窗口(p 代表previous)

移动窗口本身

Vim 有两种移动窗口本身的方式:一种是简单地在屏幕上轮换窗口,另一种则是改变窗口的实际布局。第一种情况:虽然窗口在屏幕上的位置有变,但尺寸维持不变,第二种情况下,窗口不仅移动,还调整尺寸,以填充它们移向的位置。

移动窗口本身(轮换或交换)

接下来的命令能够移动窗口,但不会调整布局。即窗口以轮换的形式进行移动。这些命令的对象只限当前窗口所在的行或列上。

  • ^Wr:向右或向下轮换窗口,即当前行或当前列的每一个窗口按指定方向移动一位,最末尾的窗口移动到第一位
  • ^WR:与 ^Wr 类似,只是轮换方向相反。如果没有其它窗口与当前窗口同行或同列,则这些命令不会有动作。而且轮换窗口时,光标随着窗口移动
  • ^Wx:交换同行或同列窗口的位置。默认是交换当前窗口与它的下一个窗口的位置,如果下方没有窗口,则试着与上一个窗口交换位置。也可以在此命令前加上数量,从而与指定的窗口交换位置。

移动窗口并改变布局

  • ^WK:移动当前窗口至屏幕顶端,使用屏幕的全部宽度
  • ^WJ:移动当前窗口至屏幕地端,使用屏幕的全部宽度
  • ^WH:移动当前窗口至屏幕左端,使用屏幕的全部高度
  • ^WL:移动当前窗口至屏幕右端,使用屏幕的全部高度
  • ^WT:移动当前窗口至新的 tab,如果当前窗口是当前分页里的唯一窗口,则不发生任何操作

在移动并扩展窗口为全屏的宽度/高度后,Vim 会重新以合理的方式分配其他窗口。当然,重新分配时也会受到一些窗口选项设置的影响。

调整窗口尺寸

gVim 中,可以使用鼠标调整窗口尺寸,通过鼠标拖动窗口边界即可。而在 Vim 中,只要开启了 mouse 选项(:set mouse=a),也能够用鼠标调整窗口尺寸。当然 Vim 中更自然的方式还是通过命令调整窗口尺寸。

窗口尺寸调整命令:

  • ^W=:试图调整所有窗口至相同尺寸(此命令受当前的 winwidth 和 winheight 选项值的影响)。如果可用的屏幕块不能平均划分,Vim 也会尽可能地平均分割
  • ^W-:将当前窗口的高度减少一行
  • ^W+:将当前窗口的高度增加一行
  • :resize n:设置当前窗口的的高度为 n 行,设置值为绝对尺寸。另外,:resize +n 命令可以将当前窗口增加 n 行,:resize -n 命令可以将当前窗口减少 n 行
  • ^W|:调整当前窗口至可能的最大宽度
  • ^W<:减少当前窗口的宽度
  • ^W>:增加当前窗口的宽度
  • :vertical resize n:用于改变当前窗口的宽度,同样支持 +n-n 形式

即使不在一个多窗口编辑会话中(即只打开一个窗口),Vim 也能够减少窗口尺寸,此时空出来的屏幕面积都留给命令行窗口。

窗口尺寸调整选项

有些 Vim 选项能够影响窗口尺寸调整命令的行为:

  • 在窗口变为活动中(active)时,winheightwinwidth 分别定义窗口的最小高度与宽度。举个例子:由于 Vim 在分割时的默认行为是平均分配,因此假设平均分割后,两个窗口的宽度都为 55 列。假设把 winwidth 调整为 80,则每次将光标移动到某个窗口时,该窗口的宽度自动调整为80,另一个窗口则为30。这种行为非常方便,在切换窗口、切换文件时自动增加窗口的尺寸。Vim 把这两个选项值视为硬性规定,窗口尺寸永远不允许小于这两个值
  • equalalways 让 Vim 在分隔或关闭窗口后,把窗口调整为相同尺寸
  • eadirection 定义 equalalways 的方向,可使用的值为hor、vor、both,分别在水平、垂直、两个方向上都调整窗口尺寸为相等的
  • cmdheight:设置命令行的高度。之前已经讲过,在只打开一个窗口的情况下,减少窗口的高度将增加命令行的高度,设置此选项,即可维持命令行的高度

缓冲区及其与窗口的交互

Vim 使用缓冲区作为工作对象的容器。Vim 提供很多控制缓冲区以及在缓冲区中移动的命令

  • 使用 :ls:filesbuffers 命令列出缓冲区。每个文件对应一个缓冲区,每个缓冲区都有一个唯一的、不会改变的编号。如果在上述命令后面加上感叹号,还能列出每个缓冲区额外的信息。该命令首先列出缓冲区编号,然后是状态标志。
状态代码 说明
% 表示当前窗口所用的缓冲区
# 表示候选缓冲区,使用 :e # 可以跳到的缓冲区(即后补文件对应的缓冲区)
a 表示活动中的缓冲区,即该缓冲区已经载入且可见
h 表示隐藏缓冲区,隐藏缓冲区虽然已经加载,但没有在任何窗口中显示。:hide 命令可以隐藏缓冲区
u 非列出缓冲区,这个缓冲区不会列出,除非使用!
- 表示缓冲区的 modifiable 选项被关闭
= 表示缓冲区为只读缓冲区
+ 表示缓冲区已经被修改
x 表示缓冲区出现读入错误

Vim的特殊缓冲区

Vim 自身使用的一些缓冲区称为特殊缓冲区,一般而言,这些缓冲区不能被编辑。

  • quickfix:用于包含错误列表或命令列表,可以通过 :cw 打开该缓冲区
  • help:包含 Vim 帮助文件。使用 :help 命令时,帮助文件将存入该缓冲区中
  • directory:包含目录内容,即某个目录中的文件列表
  • scratch:这些缓冲区包含一般用途的文本
  • 隐藏缓冲区:隐藏缓冲区是不显示于任何当前窗口里的 Vim 缓冲区。隐藏缓冲区可使得编辑多个文件变得较为容易

缓冲区命令

虽然 Vim 会自动在打开/关闭多个文件或窗口时管理缓冲区,我们还是可以通过缓冲区命令来对缓冲区进行精细控制。 首先介绍两个缓冲区命令,通过它们能一次对多个文件执行操作

  • windo cmd:该命令是 window do 的简称,这个伪缓冲区命令(其实它是个窗口命令)在每个窗口中执行指定命令。它只在当前分页中动作,若在执行命令时产生错误,会停在产生错误的窗口,产生错误的窗口随即成为新的当前窗口。cmd 支持使用管道符号(|)串联多个命令
  • bufdo [!] cmd:与 windo 命令类似,但操作对象是编辑会话中的所有缓冲区,而不仅仅是当前分页中的可见缓冲区

接下来列出常见的缓冲区命令:

  • :ls[!] / :files[!] / :buffers[!]:列出缓冲区与文件名称,如果加上 !,则包括非列表缓冲区(unlisted buffer
  • :ball / :sball:编辑所有缓冲区,每个缓冲区对应一个窗口
  • :unhide / :sunhide:编辑所有载入缓冲区,每个缓冲区对应一个窗口
  • :badd file:把 file 加入缓冲区列表
  • :bunload[!]: 从内存中卸载缓冲区。如果缓冲区修改后未保存,Vim 不允许卸载缓冲区。加上感叹号,将忽略 Vim 警告,强制卸载缓冲区
  • :bdelete[!]:卸载缓冲区并从缓冲区列表中将其删除,感叹号作用同上
  • :buffer[n]:使用缓冲区 n
  • sbuffer[n]:在一个新窗口中使用缓冲区 n
  • :bnext[n]:使用当前缓冲区后面的第 n 个缓冲区,n 默认为 1
  • :sbnext[n]:类似于 bnext[n],但是在新窗口中编辑
  • :bNext[n] / :bprevious[n] :使用当前缓冲区前面的第 n 个缓冲区
  • :sbNext[n] / :sbprevious[n] :类似于 :bNext[n] / :bprevious[n],但是在新窗口中编辑
  • :bfirst:移到第一个缓冲区
  • :sbfirst:移到第一个缓冲区,但是在新窗口中编辑
  • :blast:移到最后一个缓冲区
  • :sblast:移到最后一个缓冲区,但是在新窗口中编辑
  • :bmod[n]:移到第 n 个修改过的缓冲区
  • :sbmod[n]:移到第 个修改过的缓冲区,但是在新窗口编辑

窗口间的标签跳转

Vim 在多窗口中也支持标签跳转,使得可以在不同窗口中进行标签跳转。

  • :stags[!] [tagname]:在新的窗口中显示所找到的 tag。如果没有找到 tag,则命令执行失败且不会创建新窗口
  • ^W]:等价于 :stag,即查找当前光标所在的 tag,并在新窗口中显示找到的 tag
  • ^Wg]:类似于 ^W],但在新窗口中执行 :tselect tag 命令
  • ^Wg^]:类似于 ^Wg],但在新窗口中执行 :tjump 命令
  • ^Wf:在新的窗口中编辑指定文件,文件名为当前光标所在的字符串
  • ^Wgf:类似于 ^Wf,但是在新的 tab 中编辑指定文件

分页编辑

Vim 允许创建新的分页(也被称为 tab),每个 tab 相互独立。在每个 tab 中,可以分割窗口、编辑多个文件等等。

  • :tabnew filename:在新的 tab 中打开指定文件。如果未指定文件,则 Vim 只打开一个新分页并附上空缓冲区
  • :tabclose:关闭当前的分页
  • :tabonly:关闭除当前分页以外的所有分页
  • :tabnext / gt / Ctrl + PageDown:移动到下一个分页,支持循环移动
  • :[count]tabnext / [count] gt`:移动到第 count 个分页。第一个分页编号为 1
  • :tabprevious / :tabNext/ gT / Ctrl + PageUp:移动到第一个分页,而且为循环移动
  • :[count]tabprevious / :[count]tabNext / [count]gT: 往回移动 count 个分页
  • :tabfirst:移动到第一个分页
  • :tablast:移动到最后一个分页

关于分页,更详细的使用方法可以 :help tab-page-commands

关闭与离开窗口

如下命令可以关闭窗口,这些命令有很多细节需要注意,更详细的说明可以参考 Vim 的帮助文档:

  • :quit/^Wq: 离开当前窗口,如果该窗口是最后一个窗口,则退出 Vim。如果设置了 hidden 选项,而且该窗口是打开该文件的最后一个窗口,则缓冲区隐藏。如果没有设置 hidden 选项,而且该窗口是打开该文件的最后一个窗口,如果缓冲区有未保存的修改,则该命令失败(此时想要强制离开窗口,可在 quit 命令后面加上 !,从而放弃对缓冲区的修改)
  • ^Wc:关闭当前窗口,如果当前窗口是屏幕上的唯一窗口,该命令执行失败
  • :only/^Wo:关闭除当前窗口以外的所有窗口