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)时,
winheight
和winwidth
分别定义窗口的最小高度与宽度。举个例子:由于 Vim 在分割时的默认行为是平均分配,因此假设平均分割后,两个窗口的宽度都为 55 列。假设把 winwidth 调整为 80,则每次将光标移动到某个窗口时,该窗口的宽度自动调整为80,另一个窗口则为30。这种行为非常方便,在切换窗口、切换文件时自动增加窗口的尺寸。Vim 把这两个选项值视为硬性规定,窗口尺寸永远不允许小于这两个值 equalalways
让 Vim 在分隔或关闭窗口后,把窗口调整为相同尺寸eadirection
定义equalalways
的方向,可使用的值为hor、vor、both,分别在水平、垂直、两个方向上都调整窗口尺寸为相等的cmdheight
:设置命令行的高度。之前已经讲过,在只打开一个窗口的情况下,减少窗口的高度将增加命令行的高度,设置此选项,即可维持命令行的高度
缓冲区及其与窗口的交互
Vim 使用缓冲区作为工作对象的容器。Vim 提供很多控制缓冲区以及在缓冲区中移动的命令
- 使用
:ls
或:files
或buffers
命令列出缓冲区。每个文件对应一个缓冲区,每个缓冲区都有一个唯一的、不会改变的编号。如果在上述命令后面加上感叹号,还能列出每个缓冲区额外的信息。该命令首先列出缓冲区编号,然后是状态标志。
状态代码 | 说明 |
---|---|
% | 表示当前窗口所用的缓冲区 |
# | 表示候选缓冲区,使用 :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]
:使用缓冲区 nsbuffer[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
:关闭除当前窗口以外的所有窗口