通过阅读本章,读者将能更加得心应手地在日常任务中使用Vim。本章使用一个Python工程作为示例,模拟了一系列日常编码的场景。当然,读者完全可以拿自己手头的项目练手;只不过,本章中的使用场景不一定都适用于读者自己的代码。
本章包括如下主题。
grep
和ack
实现跨文件的搜索。本章将介绍如何用Vim在一个Python工程上作业。读者可以在异步社区中找到本章用到的代码。
本章会介绍几个Vim插件,但暂时不会涉及较为复杂的插件管理,相关的内容参见第3章。
首先,读者必须要做好准备工作。
1.创建一个存储插件的目录,执行下列命令。
$ mkdir -p ~/.vim/pack/plugins/start
如果在Windows系统中使用gVim,则需要在用户目录(通常是C:Users
<用户名>
&#xff09;下创建vimfiles
目录&#xff0c;然后在其中创建子目录pack plugins start
。
2&#xff0e;使Vim能够自动加载每个插件的文档&#xff08;Vim默认不会这么做&#xff09;。在~/.vimrc
文件&#xff08;在Windows系统中为用户目录下的_vimrc
文件&#xff09;中添加下列代码。
packloadall " 加载所有插件
silent! helptags ALL " 为所有插件加载帮助文档
然后&#xff0c;每次安装插件都可按照下列步骤进行。
1&#xff0e;在 GitHub 上找到想要安装的插件。比如&#xff0c;读者想安装scrooloose/ nerdtree
&#xff08;注意&#xff0c;这里的scrooloose/nerdtree
为该GitHub仓库的唯一标识&#xff0c;实际地址为https://github.com/scrooloose/nerdtree.git
&#xff09;。假设读者已经安装了Git&#xff0c;则可以找到此Git仓库的克隆地址&#xff0c;然后运行如下命令。
$ git clone https://github.com/scrooloose/nerdtree.git
~/.vim/pack/plugins/start/nerdtree
如果读者没有安装Git&#xff0c;或者在Windows系统中使用gVim&#xff0c;则可以在GitHub页面上找到克隆或下载&#xff08;Clone or download&#xff09;按钮&#xff0c;下载ZIP压缩包&#xff0c;然后将其解压到相应的插件目录中&#xff0c;比如在Linux系统中为目录~/.vim/pack/ plugins/start/nerdtree
&#xff0c;而在Windows系统中为用户目录下的子目录vimfiles/pack/plugins/start/nerdtree
。
2&#xff0e;重启Vim之后&#xff0c;即可使用插件进行相关操作。
到目前为止&#xff0c;本书还只是用Vim处理单个文件。但是在编写程序时&#xff0c;经常需要同时处理多个文件&#xff0c;涉及来回切换、跨文件编辑或到其他界面查询资料等操作。幸运的是&#xff0c;Vim提供了一个能够处理多个文件的插件。
图1中展示了上述要点&#xff0c;解释如下。
图1
farm.py
、animal/cat.py
和animal_farm.py
&#xff09;。3 farm.py
和a/dog.py
&#xff09;。&#43;--
开头的行表示折叠&#xff0c;它隐藏了文件的部分内容。本节将详细介绍窗口、标签页和折叠&#xff0c;通过这些功能&#xff0c;读者可以在工作中同时处理任意多个文件。
缓冲区是文件的内部表示&#xff0c;每个打开的文件都有一个缓冲区。比如&#xff0c;通过命令行vim animal_farm.py
打开一个文件&#xff0c;然后可以用:ls
命令看到现有的缓冲区列表。
很多命令都有别名或等价命令&#xff0c;:ls
也不例外&#xff0c;它和:buffers
及:files
实现的是同样的功能&#xff0c;读者可以从中选择一个最容易记的命令。
图2所示为ls
命令的执行结果&#xff08;最下面的那3行&#xff09;。
图2
图2.2中的状态栏显示了已经打开的缓冲区&#xff08;这里只有一个&#xff09;的相关信息&#xff0c;这些信息的含义如下。
1
为缓冲区编号&#xff0c;在整个Vim会话中&#xff0c;它的值保持不变。%
表示该缓冲区位于当前窗口中。a
表示该缓冲区处于活动状态&#xff0c;即它已被加载并可见。animal_farm.py
"为文件名。line 30
表示当前光标位置。现在&#xff0c;用下列命令打开另一个文件。
:e animals/cat.py
然后&#xff0c;可以看到之前打开的文件已经被当前文件所取代。不过&#xff0c;animal_farm.py
仍然存储在某个缓冲区中&#xff0c;读者可以再次用:ls
命令将其显示出来。
现在&#xff0c;可以看到两个文件名都被列出来了&#xff0c;如图3所示。
图3
那么怎样才能跳转到之前的文件呢&#xff1f;
Vim通过数字和名称来标识每个缓冲区&#xff0c;在同一个Vim会话中&#xff0c;它们都是唯一的&#xff08;除非退出Vim&#xff09;。为了在不同的缓冲区之间切换&#xff0c;可使用:b
命令&#xff0c;其参数为缓冲区的编号数字。
:b 1
:b 1
命令中的空格可以省略&#xff0c;得到简化版的命令:b1
。
通过一条很简单的命令就可以回到原来的文件。由于缓冲区还可以用文件名来标识&#xff0c;因此读者可以用文件名的一部分来切换缓冲区。下列命令将打开animals/cat.py
的缓冲区。
:b cat
不过&#xff0c;如果名称匹配了多个缓冲区&#xff0c;则Vim会报错。比如&#xff0c;用下面的命令查找所有文件名包含py
的缓冲区。
:b py
Vim的状态栏中会显示错误&#xff0c;如图2.4所示。
图4
为解决这个问题&#xff0c;可以使用Tab键补全文件名&#xff0c;从而实现在不同缓冲区之间循环切换。输入:b py
&#xff08;先不要按Enter
键&#xff09;&#xff0c;然后按Tab键&#xff0c;在所有匹配的结果之间循环遍历。
读者也可以使用:bn
&#xff08;:bnext
&#xff09;和:bp
&#xff08;:bprevious
&#xff09;命令循环遍历缓冲区。
当不再需要某缓冲区的时候&#xff08;如不再需要编辑该文件&#xff09;&#xff0c;可以将其删除。通过如下命令可以将一个缓冲区从打开的缓冲区列表中删除&#xff0c;而无须退出Vim。
:bd
但如果没有保存当前缓冲区&#xff0c;则Vim会报错。因此&#xff0c;在不小心删除缓冲区之前&#xff0c;读者还有一次保存文件的机会。
Tim Pope的vim-unimpaired是一个Vim插件,它为很多内置命令&#xff08;以及一些新的命令&#xff09;添加映射。本书作者每天都会使用这个插件&#xff0c;因为它提供了更为直观的映射&#xff0c;比如]b
和[b
用于循环遍历缓冲区&#xff0c;]f
和[f
用于遍历目录中的文件。该插件可以在GitHub仓库tpope/vim-unimpaired
中找到&#xff08;安装方法参见本章2.2节&#xff09;。
下面是vim-unimpaired提供的部分映射。
]b
和[b
循环遍历缓冲区。]f
和[f
循环遍历同一目录中的文件&#xff0c;并打开为当前缓冲区。]l
和[l
遍历位置列表&#xff08;参见第5章&#xff09;。]q
和[q
遍历快速修复列表&#xff08;参见第5章&#xff09;。]t
和[t
遍历标签列表&#xff08;参见第4章&#xff09;。此插件还支持用少数几次按键来切换某些选项&#xff0c;比如yos
切换拼写检查&#xff0c;或yoc
切换光标行高亮显示。更多功能参见:help unimpaired
中vim-unimpaired所提供的完整映射和功能清单。
Vim将缓冲区加载到窗口中。一个屏幕上可以同时显示多个窗口&#xff0c;它们将屏幕分割成几块。
本节将介绍Vim窗口的使用方式。首先&#xff0c;打开animal_farm.py
文件&#xff08;在命令行中执行$ vim animal_farm.py
或从Vim中执行:e animal_farm.py
&#xff09;。
然后&#xff0c;使用如下命令将窗口分割成两个&#xff0c;其中一个显示新的文件。
:split animals/cat.py
:split
命令可以简化为:sp
。
可以看到animals/cat.py
被打开&#xff0c;显示在原文件上方的窗口中&#xff0c;而且光标也出现在里面&#xff0c;如图5所示。
图5
也可以使用下面的命令按水平方向分割窗口。
:vsplit farm.py
如图6所示&#xff0c;当前窗口又水平分隔出一个新的窗口&#xff08;光标也随之移动到左边的新窗口中&#xff09;。
图6
:vs
是:vsplit
的简化版。
通过组合:split
和:vsplit
&#xff0c;可以生成任意多个窗口。
目前本书提到的所有命令都适用于窗口&#xff0c;包括切换缓冲区。为了使光标能在不同窗口间移动&#xff0c;先按Ctrl &#43; w
组合键&#xff0c;然后输入一个方向键&#xff1a;h、j、k、l
中的一个或键盘方向键。
按Ctrl &#43; h
组合键&#xff0c;之后再按Ctrl &#43; j
组合键&#xff08;Ctrl
键可以不松开&#xff0c;记为Ctrl &#43; w,j
组合键&#xff09;&#xff0c;光标会进入下面的窗口&#xff0c;而使用Ctrl &#43; w,k
组合键则进入上面的窗口。
如果经常使用窗口&#xff0c;读者可以按照如下配置绑定快捷键。
" 使用
然后&#xff0c;就可以用Ctrl &#43; h
组合键跳到左边的窗口&#xff0c;用Ctrl &#43; j
组合键跳到底部的窗口&#xff0c;依此类推。
读者可以用下列方式来关闭窗口。
Ctrl &#43; w,q
组合键关闭当前窗口。:q
命令关闭窗口并卸载缓冲区&#xff1b;不过&#xff0c;当只有一个窗口打开的时候&#xff0c;这会导致退出Vim。:bd
命令删除当前缓冲区&#xff0c;并关闭当前窗口。Ctrl &#43; w,o
组合键&#xff08;或:only
&#xff0c;或:on
命令&#xff09;关闭除当前窗口之外的所有窗口。
当打开了多个窗口时&#xff0c;可通过:qa
命令关闭所有窗口并退出。也可以结合:w
命令&#xff0c;即:wqa
&#xff0c;它会先保存所有打开的文件&#xff0c;再退出Vim。
如果只想关闭缓冲区&#xff0c;而保留它所在的窗口&#xff0c;则可以在.vimrc
文件中加入如下配置。
" 关闭缓冲区而不关闭窗口
command! Bd :bp | :sp | :bn | :bd
然后&#xff0c;读者就可以使用:Bd
来关闭缓冲区&#xff0c;而保留分割窗口。
窗口也可以移动、交换或改变大小。因为Vim中没有鼠标拖曳的功能&#xff0c;所以只能记住一些命令了。
读者并不需要记住所有这些命令&#xff0c;只要知道Vim支持哪些窗口操作&#xff0c;剩下的操作可以通过查看文档。使用:help window-moving
和:help window- resize
打开Vim手册中相应的条目&#xff0c;即可找到所有相关的快捷键。
窗口命令的快捷键都要先按Ctrl &#43; w
组合键&#xff0c;后面跟一个大写的方向键&#xff08;H、J、K
和L
中的一个&#xff09;&#xff0c;当前窗口会被移动到相应的位置。
Ctrl &#43; w,H
组合键将当前窗口移动到屏幕的最左边。Ctrl &#43; w,J
组合键将当前窗口移动到屏幕的底部。Ctrl &#43; w,K
组合键将当前窗口移动到屏幕的顶部。Ctrl &#43; w,L
组合键将当前窗口移动到屏幕的最右边。比如图 7 所示的窗口布局&#xff08;先打开animal_farm.py
&#xff0c;然后再依次运行:sp animals/cat.py
和:vs farm.py
&#xff0c;可得到这个布局&#xff09;。
图7
注意&#xff0c;图7中光标位于animals/cat.py
文件所在的窗口中。通过前面介绍的几个快捷键&#xff0c;可以让这个窗口朝不同的方向移动。
Ctrl &#43; w,H
组合键将animals/cat.py
移动到最左边&#xff0c;如图8&#xff08;a&#xff09;所示。Ctrl &#43; w,J
组合键将animals/cat.py
移动到底部&#xff0c;而且左右分割变成了上下分割&#xff0c;如图8&#xff08;b&#xff09;所示。Ctrl &#43; w,K
组合键将animals/cat.py
移动到顶部&#xff0c;如图8&#xff08;c&#xff09;所示。Ctrl &#43; w,L
组合键将animals/cat.py
移动到最右边&#xff0c;如图8&#xff08;d&#xff09;所示。&#xff08;a&#xff09;
&#xff08;b&#xff09;
&#xff08;c&#xff09;
&#xff08;d&#xff09;
图8
若想修改一个窗口的内容&#xff0c;则只需要跳转到这个窗口&#xff0c;然后用:b
命令来选择所需的缓冲区。不过&#xff0c;也有一些快捷键可以用于交换两个窗口的内容。
Ctrl &#43; w,r
组合键将当前行或当前列&#xff08;行优先于列&#xff09;中的每个窗口的内容向右或向下移动。使用Ctrl &#43; w,R
组合键则以相反的方向执行类似的操作。Ctrl &#43; w,x
组合键将当前窗口与下一个窗口的内容交换&#xff08;如果当前窗口是最后一个&#xff0c;则与前一个交换&#xff09;。
Vim内部用数字来标识窗口。不过&#xff0c;与缓冲区不同&#xff0c;窗口的编号是随着布局变化而改变的&#xff0c;而且并没有直接的方法来修改窗口编号。有些窗口管理命令以窗口编号为参数&#xff0c;但本书不会涉及这部分内容。有一条原则仅供参考&#xff0c;窗口编号顺序为由上至下、由左至右递增。
Vim窗口默认的宽高比为50/50&#xff0c;这可能并不满足读者的需求&#xff0c;因此窗口的大小可以通过一些方法来改变。
快捷键Ctrl &#43; w,&#61;
&#xff08;按Ctrl&#43;w
后再按&#61;
键&#xff09;能够将所有打开窗口的宽和高调整为一致。如果不恰当地调整了窗口大小&#xff0c;这个命令将非常有用。
:resize
命令会增加或减少当前窗口的高度&#xff0c;而:vertical resize
将调整窗口的宽度。读者还可以使用如下命令。
:resize &#43;N
用于将当前窗口的高度增加N
行。:resize -N
用于将当前窗口的高度减少N
行。:vertical resize &#43;N
用于将当前窗口的宽度增加N
列。:vertical resize -N
用于将当前窗口的宽度减少N
列。:resize
和:vertical resize
可分别简写为:res
和:vert res
。另外&#xff0c;还有将窗口高度和宽度改变一行/列的快捷键&#xff1a;Ctrl &#43; w,-
和Ctrl &#43; w,&#43;
用于调整高度&#xff0c;而Ctrl&#43;w,>
和Ctrl &#43; w,<
用于调整宽度。
两种命令都可以将宽度/高度设置为具体的行数/列数。
:resize N
用于将窗口高度设置为N
。:vertical resize N
用于将窗口宽度设置为N
。在很多现代编辑器中&#xff0c;标签页&#xff08;Tabs&#xff09;用于表示不同的文件。在Vim中自然也是如此&#xff0c;但读者需要考虑其原始目的。
Vim用标签页来组织一个窗口的集合&#xff0c;进而支持在不同的窗口集合之间切换&#xff0c;这让用户方便地拥有了多个工作区。标签页通常用来在同一个Vim会话中区分不同的问题或者文件集合。标签页功能不一定是一个频繁使用的功能&#xff0c;但如果希望在不同项目或同一项目的不同上下文之间切换&#xff0c;那么标签页将是一个不错的选择。
用户愿意使用标签页的另一个原因可能与Vim的diff功能有关&#xff0c;因为diff作用于一个标签页内。更多详情请参考第5章中关于vimdiff的介绍。
在一个新标签页中打开一个空缓冲区的命令如下。
:tabnew
在新标签页中打开一个已有文件的命令为:tabnew <文件名>
。
如图9所示&#xff0c;标签页显示在屏幕的顶部。在标记为3 farm.py
的标签页中打开了三个窗口及一个活动缓冲区farm.py
。[No Name]
标签页则是刚才打开过的空缓冲区。
图9
在一个标签页中&#xff0c;可以通过常用的方式&#xff08;:e <文件名>
&#xff09;来加载文件&#xff0c;也可以用:b
命令在不同缓冲区之间切换。
为了在不同标签页之间跳转&#xff0c;可以使用如下命令。
gt
或:tabnext
命令用于切换到下一个标签页。gT
或:tabprevious
命令用于切换到上一个标签页。标签页可通过:tabclose
命令来关闭&#xff0c;标签页关闭也会导致其中的窗口关闭&#xff08;如果只剩一个标签页&#xff0c;则需要用:q
来关闭&#xff09;。
:tabmove N
命令将当前标签页移动到第N
个标签页之后&#xff08;如果N
为0
&#xff0c;则变成第一个标签页&#xff09;。
Vim为浏览大型文件提供的一个强大工具是折叠。折叠功能支持文件部分内容的隐藏&#xff0c;隐藏的依据既可以是预定义的规则&#xff0c;也可以是手动添加的折叠标记。
如图10所示&#xff0c;animal_farm.py
中的部分代码片断被折叠了&#xff0c;代码中每个方法的具体内容被隐藏了&#xff0c;从而可以在整体上来查看代码。
图10
因为本书以Python编程为例&#xff0c;所以这里只介绍Python示例代码的折叠方式。首先&#xff0c;需要在.vimrc
文件中将foldmethod
设置为indent
&#xff0c;设置代码如下。
set foldmethod&#61;indent
不要忘记重新加载~/.vimrc
&#xff0c;方法是重启Vim或在Vim中执行:source $MYVIMRC
命令。
设置foldmethod
为indent
&#xff0c;使Vim基于缩进来折叠代码。
再次打开animal_farm.py
&#xff0c;可以看到该文件中的部分代码已经被隐藏&#xff0c;如图11所示。
图11
将光标移动到其中一个折叠行上&#xff0c;输入zo
可以打开当前折叠&#xff0c;如图2.12所示。
图2.12
只要光标在一个潜在的折叠文本中&#xff08;本例中为缩进的代码块&#xff09;&#xff0c;输入zc
都会将此折叠关闭。
为了方便看清折叠的位置&#xff0c;可以使用:set foldcolumn&#61;N
命令&#xff0c;其中N
的取值为0&#xff5e;12。这会告诉Vim用从屏幕左边开始的N
列来标记折叠&#xff0c;其中符号-
表示一个打开的折叠&#xff0c;符号|
表示打开的折叠的内容&#xff0c;符号&#43;
表示关闭的折叠。
输入za
可切换折叠状态&#xff08;打开关闭的折叠或关闭打开的折叠&#xff09;。输入zR
和zM
分别用于同时打开和关闭所有折叠。
将foldmethod
设置为自动类型&#xff08;如indent
&#xff09;会默认将所有文件折叠。这只是一种偏好&#xff0c;读者也可能会选择在打开新文件时打开折叠。在.vimrc
文件中添加autocmd BufRead * normal zR
会在打开新文件时令折叠处于打开状态&#xff0c;即Vim在读取新的缓冲区时执行zR
命令&#xff08;打开所有折叠&#xff09;。
从某种意义上来说&#xff0c;Vim在折叠代码方面是比较智能的&#xff0c;而且支持多种折叠方式。折叠的方法由.vimrc
文件中的foldmethod
选项来指定。Vim支持如下折叠方式。
manual
&#xff1a;手动折叠&#xff0c;这种方法对于长文本而言并不适用。indent
&#xff1a;基于缩进的折叠&#xff0c;这对于依赖缩进的编程语言非常合适&#xff08;不管哪种语言&#xff0c;标准的编码风格中总是会采用某种一致性的缩进。因此&#xff0c;当读者想要快速隐藏不关心的代码时&#xff0c;indent
折叠方式不失为一种高效率的选择&#xff09;。expr
&#xff1a;基于正则表达式的折叠。如果读者想要用复杂的规则来定义折叠&#xff0c;那么可以选择这种方式。marker
&#xff1a;使用文本中特殊的标记来定义折叠&#xff0c;比如{{{
和}}}
。这种方法对于管理很长的.vimrc
文件非常有效&#xff0c;但是在Vim之外不常用&#xff0c;因为这种方式需要修改文件内容。syntax
提供了可识别语法的折叠&#xff0c;但它并非对所有语言都开箱即用&#xff08;不支持Python&#xff09;。diff
&#xff1a;当Vim处于diff模式时会自动采用这种折叠方式&#xff0c;diff模式下需要展示两个文件的不同之处&#xff0c;而相同之处往往需要隐藏起来&#xff08;参见第5章&#xff09;。
设置折叠方式的方法为在.vimrc
文件中加入set foldmethod&#61;<折叠方法>
。
软件项目往往包含大量的文件和目录&#xff0c;能够利用Vim快速浏览和展示这些文件和目录将是一件很方便的事。本节介绍5种不同的文件浏览方式&#xff0c;它们分别是内置的Netrw文件管理器、启用了wildmenu
的:e
命令、NERDTree、Vinegar和CtrlP插件。这些方式都可用于处理文件&#xff0c;并可按需求组合使用。
作为全面介绍Vim使用方法的教程&#xff0c;本书介绍了各种常用的文本编辑方法和程序设计中的实用操作&#xff0c;深入Vim内部的数据结构和VimScript脚本编程&#xff0c;内容详实。本书基于Vim 8平台&#xff0c;介绍了前沿分支Neovim&#xff0c;还推荐了更先进的Oni编辑器&#xff0c;兼容并包&#xff0c;集Vim社区典型使用经验和发展趋势于一体。
本书面向的读者群体是所有使用Vim的程序员&#xff0c;书中的示例文本为Python代码&#xff0c;并详细介绍了Git和正则表达式。读者需要对操作系统和程序设计有基本的了解&#xff0c;特别是需要了解Linux操作系统的基本使用。虽然本书尝试兼顾三大操作系统&#xff0c;但毫无疑问书中内容以Linux为主。本书可以帮助读者完善Vim技能&#xff0c;增加程序设计的知识储备。