VIM8 文本实战学习

目录

第一章 开始Vim 之旅

1.1 模式界面与无模式界面对比

1.2 通过.vimrc文件来配置Vim

1.3 Vim 交换文件

1.4 通过:help阅读Vim手册

本章小结

第二章 高级编辑和文本浏览

主要内容:

2.1 安装插件

2.2 组织工作区

2.2.1 缓冲区

2.2.2 插件——unimpaired

2.2.3 窗口

2.2.4 标签页

2.2.5 折叠

2.3 文件树浏览

2.3.1 目录浏览器Netrw

2.3.2 支持文件菜单的:e 命令

2.3.3 插件—— NERDTree

2.4.4 插件——Vinegar

2.4.5 插件——CtrlP

2.4 文本的浏览

2.4.1 切换到插入模式

2.4.2 用/和?搜索

2.4.3 利用文本对象

2.4.4 插件——EasyMotion

2.6 使用寄存器进行复制和粘贴

2.6.1 寄存器

2.6.2 从外部复制文本到Vim中

本章小结

第三章 使用先导键——插件管理

主要内容

3.1 插件管理

3.1.1 插件管理——vim-plug

3.1.2 其他推荐

3.1.3 分析运行慢的插件

3.2 模式详解

3.2.1 正常模式

3.2.2 命令行模式和 ex 模式

3.2.3 插入模式

3.2.4 可视模式和选择模式

3.2.5 替换模式和虚拟替换模式

3.2.6 终端模式

3.3 命令的重映射

3.4 先导键

3.5 插件的配置

本章小结

第四章 理解文本

主要内容:

4.1 代码补全功能

4.1.1 内置自动补全

4.1.2 YouCompleteMe 插件

4.2 撤销树和Gundo

本章小结

第五章 构建、测试和执行

主要内容

5.1 版本控制

5.1.1 git基本操作

5.1.2 Git 与 Vim 的整合(vim-fugitive)

5.2 用vimdiff解决冲突

5.2.1 比较两个文件

5.2.2 vimdiff和Git组合

5.3 其他终端工具(Tmux、Screen 和 Vim 的终端模式)

本章小结

第六章 用正则表达式和宏来重构代码

主要内容

6.1 用正则表达式来搜索和替换

61.1 搜索和替换

6.2 宏的录制和回放

6.2.1 宏的编辑

6.3 用插件来实现代码重构

本章小结

第七章 定制自己的Vim

主要内容

7.1 Vim用户界面

7.1.1 配色

7.1.2 常见问题

7.2 状态栏

7.2.1 Powerline

7.2.2 Airline

7.3 配置文件的同步

7.4 健康的Vim定制习惯

第八章 卓尔不凡的Vimscript

第九章 Neovim

9.1 为什么需要另外一种VIM

9.2 Neovim的安装和配置

9.2.1 检查健康状态

9.2.2 合理的默认选项

9.3 Oni

第十章 延伸阅读

10.1 高效文本编辑的7个习惯


第一章 开始Vim 之旅

1.1 模式界面与无模式界面对比

“无模式”指的是每个界面的元素都只有一个功能,每个按钮都对应于屏幕上的一个字母或某种其他操作,每个按键(或组合键)总是做同样的事:此应用程序总是以单一模式来执行操作。如:Windows下的文本文档、word等

”模式“ 根据上下文的不同,每个行为可能对应不同的操作。Vim就是一款模式编辑器,即在不同的上下文,单击一个按钮会产生不同的行为结果。

编辑速度快并不是Vim的卖点。Vim让用户置身于文本处理的流程中,不需要因为找鼠标而打乱节奏;也不需要按17次放方向键到达页面中的某个位置;更不需要在复制粘贴时通过鼠标操作来小心翼翼地选择文本。

当使用无模式编辑器时,工作流程总是会被打断。而对于模式编辑器,特别是Vim,文本处理就像是与编辑器进行了一次亲密交谈,而且是用一种一致的语言与编辑器进行交流,比如删除3个单词(命令为d3w)、改变引号内文本(命令为ci“)。通过Vim,文本编辑变成一种更从容的操作。

1.2 通过.vimrc文件来配置Vim

Vim会从一个名为.vimrc的文件中读取配置信息。Vim安装好了后就可以使用,但是通过对.vimrc的配置会让Vim更好使用。

.vimrc一般位于home目录中用户目录下,如果没有可自行创建。

配置示例:

syntax on                   "支持语法高亮显示
filetype plugin indent on   "启动根据文件类型自动缩进
​
set autoindent              "开始新行时处理缩进
set expandtab               "将制表符Tab展开为空格,这对于Python尤其有用
set tabstop=4               "要计算的空格数
​
set backspace               "在多数终端上修正退格键Backspace的行为"
​
colorscheme murphy          "修改配色

Vim 配置 Tip:

当编写Vim配置文件时,可以先在Vim编辑界面中尝试相应的设置,然后再写到.vimrc文件中去。

操作过程为先输入冒号,然后执行相应的命令,再按Enter键即可。比如:set autoindent(按 Enter 键执行)。如果想知道某种设置 当前的值,可以在命令后面加上问号,比如执行:set tabstop?命令会 显示出当前的 tabstop 的值。

Vim 自带如下主题配色

blue、darkblue、default、delek、desert、elflord、evening、 industry、koehler、morning、murhpy、pablo、peachpuff、ron、 shine、slate、torte、zellner。读者可以尝试其中一种配色主题, 方法是输入:colorscheme,然后按 Enter 键;也可以在所有可 用的配色主题之间循环切换,方法是输入:colorscheme,然后输入一个 空格,并多次按 Tab 键。第 7 章中有更多关于 Vim 的配置和配色的介绍, 届时读者可以拥有完全属于自己的 Vim

1.3 Vim 交换文件

默认情况下,Vim用交换文件跟踪文件的变化情况。当用户编辑文件的时候,Vim会自动产生交换文件。交换文件的作用时恢复文件内容,以防用户的VIM、SSH会话或系统崩溃。一旦出现上述问题,或者由于其他失误意外地退出Vim,再次用Vim打开同一个文件时,就会出现恢复界面。

这时可以输入r从交换文件中恢复文件,或者输入d直接忽略交换文件。

默认情况下,Vim会在原始文件所在目录下生成类似于filename.swp

或.filename.swp 的文件。为避免这些交换文件污染文件系统,可以修改这个默认 行为,使 Vim 将所有交换文件都统一存放在同一个目录中。要实现这个设置,可以 在.vimrc 文件中加入如下内容。

set directory=$HOME/.vim/swap//

或者,也可以选择完全禁止交换文件,在.vimrc 中加入 set noswapfile 即可。

  • Vim 中有两种单词对象:狭义单词(word)和广义单词(WORD)。在 Vim的世界里,狭义单词指的是由空白字符(比如空格、制表符或换行符)分隔的字母、数字和下划线组成的序列,广义单词则是由空格分隔的任何非 空字符组成的序列。

1.4 通过:help阅读Vim手册

Vim 提供了一个学习工具:help 命令。

Vim 手册中附带了大量的资源和教程。通过翻页键(PageUp 和 PageDown)可以浏 览手册的内容(注意,Ctrl + b 组合键和 Ctrl + f 组合键也能起到翻页的效果), 信息极其丰富。 如果读者使用 Vim 时遇到问题,或者希望深入了解某个命令,可尝试使用:help(其 缩略版为:h)来搜索这个命令。比如,读者可以搜索一下 cc。

h cc

:help 命令可用于浏览所有这些帮助文件。在阅读帮助文件时会发现,某些词是高 亮显示的。它们是标签,即可用于:help 命令的关键字。不过,不是每个标签名都很直 观。如果想知道如何在 Vim 中搜索一个字符串,用户可能会尝试使用如下命令。

:h search 

然而,输入这条命令后进入了表达式评估(expression evaluation)的页面,这并不 是读者想要的

为找到正确的词条,输入:h search(先不要按 Enter 键),然后按 Ctrl + D 组合键。这时会得到一个包含字符search 的标签列表。其中一项为search-commands,这正是读者需要的。接下来继续补充命令行。

:h search-commands

本章小结

原来的 Vi 版本是针对远程终端开发出来的,那时的带宽和网速都有限。但正是这些 限制使 Vi 的文本编辑流程变得高效而专业,从而演变为如今的 Vim(改进版 Vi,Vi Improved)的核心。

本章介绍了如何在主流平台上安装和更新 Vim,以及它的图形界面版本 gVim(介绍 了太多方法,有些可能根本就不需要)。

然后介绍了如何通过修改.vimrc 文件来配置 Vim,这个过程在今后可能会反复进 行,因为读者需要根据自己的需求定制这个编辑器。

另外,还介绍了处理文件、在 Vim 中移动光标和修改内容等基本操作。Vim 中的文 本对象的概念(字母、单词和段落)和复合命令(如 d2w 会删除两个单词)可以帮助读 者做出精确的文本操作。

如果读者能从本章中学会一件事,那一定是:help。Vim 内置的帮助系统极其详细, 它几乎可以回答所有关于 Vim 的问题。

第二章 高级编辑和文本浏览

主要内容:

  • 一种粗暴但快捷地Vim插件安装方式

  • 介绍如何使用缓冲区、窗口、标签页和折叠来处理多个文件或长文件,从而使工作区更为整洁

  • 介绍插件Netrw、NERDTree、Vinegar 和 CtrlP,通过这些插件,读者可以在不退 出 Vim 的情况下浏览复杂的文件树。

  • 介绍文件中的高级文本浏览方式、极其高效的光标移动插件EasyMotion 以及多 种文本对象,介绍通过 grep 和 ack 实现跨文件的搜索

  • 介绍如何利用寄存器来复制和粘贴

2.1 安装插件

  1. 创建一个存储插件的目录,执行下列命令

    mkdir -p ~/.vim/pack/plugins/start

  2. 使Vim能够自动加载每个插件的文档(Vim默认不会这么做)。在~/.vimrc文件中添加下列代码

    packloadall " 加载所有插件
    silent! helptags ALL " 为所有插件加载帮助文档

然后,每次安装插件都可按照下列步骤进行。

  1. 在GitHub上找到想要安装的插件。比如,读者想安装scrooloose/ nerdtree (注意,这里的 scrooloose/nerdtree 为该 GitHub 仓库的唯一标识,实际地址为 GitHub - preservim/nerdtree: A tree explorer plugin for vim.)。假设读者已经安装了 Git, 则可以找到此 Git 仓库的克隆地址,然后运行如下命令。

    $ git clone https://github.com/scrooloose/nerdtree.git ~/.vim/pack/plugins/start/nerdtree 

  2. 重启Vim之后,即可使用插件进行相关操作

2.2 组织工作区

到目前为止,还只是用Vim处理单个文件。但是在编写程序时,经常需要同时处理多个文件,涉及到来回切换、跨文件编辑或到其他界面查询资料等操作。幸运的是,Vim提供了一个能够处理多个文件的插件。

  • Vim内部用缓冲区来表示文件;通过缓冲区,用户可以在不同文件之间快速切换

  • Vim用多个窗口在同一屏幕中显示多个文件

  • Vim用标签页对窗口进行分组

  • Vim用折叠效果来隐藏或展开一个文件的部分内容,从而让读者可以更容易地浏览文件的内容

2.2.1 缓冲区

缓冲区是文件的内部表示,每个打开的文件都有一个缓冲区。比如,通过命令行vim main.c 打开一个文件,然后可以用:ls命令看到现有的缓冲区列表。

缓冲区信息含义

  • 1为缓冲区编号,在整个Vim会话中,它的值保持不变

  • %表示该缓冲区位于当前窗口中

  • a 表示该缓冲区处于活动状态,即它已被加载并可见

  • “main.c” 为文件铭

  • line 30 表示当前光标位置

Vim 通过数字和名称来标识每个缓冲区,在同一个 Vim 会话中,它们都是唯一的(除 非退出 Vim)。为了在不同的缓冲区之间切换,可使用:b 命令,其参数为缓冲区的编号 数字。

:b1
2.2.2 插件——unimpaired

Tim Pope的vim-unimpaired是一个Vim插件,它为很多内置命令(以及一些新的命令) 添加映射。本书作者每天都会使用这个插件,因为它提供了更为直观的映射,比如]b 和 [b 用于循环遍历缓冲区,]f 和[f 用于遍历目录中的文件。该插件可以在 GitHub 仓库 tpope/vim-unimpaired 中找到(安装方法参见本章 2.1节)。

下面是 vim-unimpaired 提供的部分映射。

  • ]b 和[b 循环遍历缓冲区

  • ]f 和[f 循环遍历同一目录中的文件,并打开为当前缓冲区

  • ]l 和[l 遍历位置列表(参见第 5 章)

  • ]q 和[q 遍历快速修复列表(参见第 5 章)

  • ]t 和[t 遍历标签列表(参见第 4 章)

此插件还支持用少数几次按键来切换某些选项,比如 yos 切换拼写检查,或 yoc 切换光标行高亮显示。更多功能参见:help unimpaired 中 vim-unimpaired 所提供的 完整映射和功能清单。

$ git clone https://github.com/tpope/vim-unimpaired.git ~/.vim/pack/plugins/start/nerdtree 
2.2.3 窗口

Vim将缓冲区加载到窗口中。一个屏幕上可以同时显示多个窗口,它们将屏幕分割成几块。

  1. 窗口的创建、删除和跳转

    • 窗口的创建 首先,打开 main.c 文件 然后,使用如下命令将窗口垂直分割成两个,其中一个显示新的文件。

      :split test.c

      也可以使用下面的命令按水平方向分割窗口

      :vsplit farm.py 

      这两个命令都可以简化成sp、vs

    • 窗口的跳转 为了使光标能在不同窗口间 移动,先按 Ctrl + w 组合键,然后输入一个方向键:h、j、k、l 中的一个或键盘方向键。

      如果经常使用窗口,读者可以按照如下配置绑定快捷键。

      " 使用<Ctrl> + hjkl 快速在窗口间跳转 noremap <c-h> <c-w><c-h> noremap <c-j> <c-w><c-j> noremap <c-k> <c-w><c-k> noremap <c-l> <c-w><c-l>

    然后,就可以用 Ctrl + h 组合键跳到左边的窗口,用 Ctrl + j 组合键跳到底 部的窗口,依此类推

    • 窗口的删除

      1. 使用 Ctrl + w,q 组合键关闭当前窗口

      2. 使用:q 命令关闭窗口并卸载缓冲区;不过,当只有一个窗口打开的时候,这会 导致退出 Vim

      3. 使用:bd 命令删除当前缓冲区,并关闭当前窗口

      4. 使用 Ctrl + w,o 组合键(或:only,或:on 命令)关闭除当前窗口之外的所 有窗口

      5. 当打开了多个窗口时,可通过:qa 命令关闭所有窗口并退出。也可以结合:w 命令,即:wqa,它会先保存所有打开的文件,再退出 Vim

      如果只想关闭缓冲区,而保留它所在的窗口,则可以在.vimrc 文件中加入如下配置。 " 关闭缓冲区而不关闭窗口 command! Bd :bp | :sp | :bn | :bd 用户就可以使用:Bd 来关闭缓冲区,而保留分割窗口。

    • 窗口的移动

      窗口也可以移动、交换或改变大小。因为 Vim 中没有鼠标拖曳的功能,所以只能记 住一些命令了。

      并不需要记住所有这些命令,只要知道 Vim 支持哪些窗口操作,剩下的 操作可以通过查看文档。使用:help window-moving 和:help windowresize 打开 Vim 手册中相应的条目,即可找到所有相关的快捷键

      窗口命令的快捷键都要先按 Ctrl + w 组合键,后面跟一个大写的方向键(H、J、 K 和 L 中的一个),当前窗口会被移动到相应的位置

      使用 Ctrl + w,H 组合键将当前窗口移动到屏幕的最左边。
      使用 Ctrl + w,J 组合键将当前窗口移动到屏幕的底部。
      使用 Ctrl + w,K 组合键将当前窗口移动到屏幕的顶部。
      使用 Ctrl + w,L 组合键将当前窗口移动到屏幕的最右边。

      Vim 内部用数字来标识窗口。不过,与缓冲区不同,窗口的编号是随着布 局变化而改变的,而且并没有直接的方法来修改窗口编号。有些窗口管理 命令以窗口编号为参数,但本书不会涉及这部分内容。有一条原则仅供参 考,窗口编号顺序为由上至下、由左至右递增。

    • 改变窗口的大小 Vim 窗口默认的宽高比为 50/50,这可能并不满足读者的需求,因此窗口的大小可以通过一些方法来改变。 快捷键 Ctrl + w,=(按 Ctrl+w 后再按=键)能够将所有打开窗口的宽和高调整 为一致。如果不恰当地调整了窗口大小,这个命令将非常有用。 :resize 命令会增加或减少当前窗口的高度,而:vertical resize 将调整窗口 的宽度。读者还可以使用如下命令。

      :resize +N 用于将当前窗口的高度增加 N 行。
      :resize -N 用于将当前窗口的高度减少 N 行。
      :vertical resize +N 用于将当前窗口的宽度增加 N 列。
      :vertical resize -N 用于将当前窗口的宽度减少 N 列。

      :resize 和:vertical resize 可分别简写为:res 和:vert res。

      另 外,还有将窗口高度和宽度改变一行/列的快捷键

      :Ctrl + w,-和 Ctrl + w,+用于调整高度,

      :Ctrl+w,>和 Ctrl + w,<用于调整宽度。

      两种命令都可以将宽度/高度设置为具体的行数/列数。 :resize N 用于将窗口高度设置为 N。

      :vertical resize N 用于将窗口宽度设置为 N。

2.2.4 标签页

在很多现代编辑器中,标签页(Tabs)用于表示不同的文件。在 Vim 中自然也是如 此,但用户需要考虑其原始目的。

Vim 用标签页来组织一个窗口的集合,进而支持在不同的窗口集合之间切换,这让 用户方便地拥有了多个工作区。标签页通常用来在同一个 Vim 会话中区分不同的问题或 者文件集合。标签页功能不一定是一个频繁使用的功能,但如果希望在不同项目或同一 项目的不同上下文之间切换,那么标签页将是一个不错的选择。

用户愿意使用标签页的另一个原因可能与 Vim 的 diff 功能有关,因为 diff 作用于一个标签页内。更多详情请参考第 5 章中关于 vimdiff 的介绍。

在一个新标签页中打开一个空缓冲区的命令如下。

:tabnew

在新标签页中打开一个已有文件的命令为:tabnew <文件名>。

标签页显示在屏幕的顶部。在标记为 3 main.c 的标签页中打 开了三个窗口及一个活动缓冲区main.c。[No Name]标签页则是刚才打开过的空缓冲区。

在一个标签页中,可以通过常用的方式(:e <文件名>)来加载文件,也可以用:b 命令在不同缓冲区之间切换。 为了在不同标签页之间跳转,可以使用如下命令。

快捷键 gt 或:tabnext 命令用于切换到下一个标签页。
快捷键 gT 或:tabprevious 命令用于切换到上一个标签页。

标签页可通过:tabclose 命令来关闭,标签页关闭也会导致其中的窗口关闭(如 果只剩一个标签页,则需要用:q 来关闭)。 :tabmove N 命令将当前标签页移动到第 N 个标签页之后(如果 N 为 0,则变成第 一个标签页)。

2.2.5 折叠
  1. 开启折叠

Vim 为浏览大型文件提供的一个强大工具是折叠。折叠功能支持文件部分内容的隐 藏,隐藏的依据既可以是预定义的规则,也可以是手动添加的折叠标记。 ​ 设置折叠方式的方法为在.vimrc 文件中加入 set foldmethod=<折叠 方法>

set foldmethod=indent

设置 foldmethod 为 indent,使 Vim 基于缩进来折叠代码。

命令作用
zo【将光标移动到其中一个折叠行上】打开当前折叠
zc【只要光标在一个潜在的折叠文本中】将此折叠关闭
za切换折叠状态(打开关闭的折叠或关闭打开的折叠)
zR同时打开所有折叠
zM同时关闭所有折叠

将 foldmethod 设置为自动类型(如 indent)会默认将所有文件折叠。 这只是一种偏好,读者也可能会选择在打开新文件时打开折叠。在.vimrc 文件中添加 autocmd BufRead * normal zR 会在打开新文件时令折叠 处于打开状态,即 Vim 在读取新的缓冲区时执行 zR 命令(打开所有折叠)。

  1. 折叠的类型

从某种意义上来说,Vim 在折叠代码方面是比较智能的,而且支持多种折叠方式。 折叠的方法由.vimrc 文件中的 foldmethod 选项来指定。Vim 支持如下折叠方式。

  • manual:手动折叠,这种方法对于长文本而言并不适用。

  • indent:基于缩进的折叠,这对于依赖缩进的编程语言非常合适(不管哪种语 言,标准的编码风格中总是会采用某种一致性的缩进。因此,当读者想要快速隐 藏不关心的代码时,indent 折叠方式不失为一种高效率的选择)。

  • expr:基于正则表达式的折叠。如果读者想要用复杂的规则来定义折叠,那么 可以选择这种方式。

  • marker:使用文本中特殊的标记来定义折叠,比如{{{和}}}。这种方法对于 管理很长的.vimrc 文件非常有效,但是在 Vim 之外不常用,因为这种方式需 要修改文件内容。

  • syntax 提供了可识别语法的折叠,但它并非对所有语言都开箱即用(不支持 Python)

  • diff:当 Vim 处于 diff 模式时会自动采用这种折叠方式,diff 模式下需要展示 两个文件的不同之处,而相同之处往往需要隐藏起来(参见第 5 章)。

2.3 文件树浏览

软件项目往往包含大量的文件和目录,能够利用 Vim 快速浏览和展示这些文件和目 录将是一件很方便的事。本节介绍 5 种不同的文件浏览方式,它们分别是内置的 Netrw文件管理器、启用了 wildmenu 的:e 命令、NERDTree、Vinegar 和 CtrlP 插件。这些方 式都可用于处理文件,并可按需求组合使用。

2.3.1 目录浏览器Netrw

Netrw 是 Vim 的内置文件管理器(用技术语言来说,它是 Vim 自带的一个插件)。 Netrw 支持对目录和文件的浏览,这和操作系统下的文件管理器类似。

使用方法为执行命令:Ex(完整命令为:Explore),它会打开文件浏览窗口

  • 因为 Netrw 与 Vim 集成在一起,所以编辑一个目录时(如用:e .命令打开 当前目录),实际上打开的是 Netrw

Enter 键用于打开文件和目录
-键进入上一层目录
D 键删除一个文件或目录
R 重命名一个文件或目录
​
Netrw 的窗口也可以在分割窗口或标签页中打开。
:Vex 以左右分割方式打开 Netrw
:Sex 以上下分割方式打开 Netrw
:Lex 以左右分割方式打开 Netrw,当前 Netrw 窗口位于最左边,且高度占满整
个屏幕

Netrw 非常强大,它还支持远程编辑。比如,通过下列命令可以列出 SFTP 下远程目 录的内容

:Ex sftp://<domain>/<directory>/ 
2.3.2 支持文件菜单的:e 命令

另一种浏览文件树的方式为在.vimrc 文件中设置 set wildmenu。此选项将在增 强模式下产生一个自动补全的文件名菜单(wildmenu),并在状态栏中快速按 Enter 键 呈现所有可能的自动补全选项。启用 wildmenu 之后,输入:e 命令(不按 Enter 键, 输入一个空格),然后按 Tab 键。这时,状态栏中会显示一个文件列表,读者可以用 Tab 键遍历选择这些文件,或者用 Shift + Tab 组合键反向遍历(左方向键和右方向键可 以起到同样的作用)

选择完成后再按 Enter 键,就打开相应的文件或目录了。按向下方向键将进入光标 选中的目录,而向上方向键则进入上一级目录。

wildmenu 也支持部分路径,输入:e <文件名开始的字符>,然后按 Tab 键,会触 发 wildmenu。 .vimrc 中常用的 wildmenu 设置如下所示。

set wildmenu " 启用增强的 Tab 自动补全
set wildmode=list:longest,full " 补全为允许的最长字符串,然后打开 wildmenu 

上述设置允许读者在第一次按 Tab 键时将部分路径补全为最长的匹配字符串(并且 还会展示所有的可能项);第二次按 Tab 键时遍历 wildmenu 中的文件。

2.3.3 插件—— NERDTree

NERDTree 是一种模拟现代 IDE 行为的优秀插件,它在屏幕边缘用一个分割的缓冲 区来展示文件树。NERDTree 可从 GitHub 仓库 scrooloose/nerdtree 中找到.

安装 NERDTree 之后可通过:NERDTree 命令启动 NERDTree。将在左 边窗口中列出了一个目录以及其中的文件。

使用 h、j、k 和 l 键或方向键可以浏览文件树结构,按 Enter 键或 o 键可打开选 中的文件。还有一些其他有用的快捷键,可以用 Shift + ?组合键查看。

NERDTree 有一个不得不提的功能:支持书签。读者可通过:Bookmark 命令来收藏 当前光标在 NERDTree 中选择的目录。在 NERDTree 窗口中,按 B 键可以在窗口顶部列出 所有书签。

读者可以在 NERDTree 窗口中使书签保持显示,方法是在.vimrc 文件中设置 NERDTreeShowBookmarks 选项,如下所示。

let NERDTreeShowBookmarks = 1 " 启动 NERDTree 时显示书签

隐藏 NERDTree 窗口的命令是:NERDTreeToggle。如果读者希望在编辑时令 NERDTree 窗口保持打开,则可以在.vimrc 中加入如下设置。

autocmd VimEnter * NERDTree " Vim 启动时打开 NERDTree 

当 NERDTree 窗口变成最后一个窗口时并不会自动关闭,读者可以通过.vimrc 文 件中的如下设置来改变这种行为。

" 当 NERDTree 窗口是最后一个窗口时自动关闭
autocmd bufenter * if (winnr("$") == 1 && exists("b:NERDTree") && 
 \ b:NERDTree.isTabTree()) | q | endif

NERDTree 的优点是可以在工作区中显示一个项目文件的概览,很多人习惯使用图 形化用户界面,因此会喜欢 NERDTree。但是,当用户的编辑习惯被 Vim 改变时,可能 会觉得文件列表容易让人分心,转而又会返回到更简洁的 Netrw。

2.3.4 插件——Vinegar

Tim Pope 的 vinegar.vim 是一个小插件,用于解决项目侧边栏与分割窗口无法 同时工作的问题。当打开了多个分割窗口时,诸如 NERDTree 这样的插件会让人更加 分心。

假设已经打开了 3 个窗口(第四个 NERDTree 窗口在最左边)。如果在 NERDTree 窗口中按 Enter 键,则光标选中的文件将在右边哪个窗口中打开呢?

这个文件将在左下方打开,但用户无法预知。NERDTree 总是在最后一个 创建的窗口中打开文件

Tim Pope 用一个称为 Vinegar 的小插件解决了这个问题,使 Netrw 拥有无缝集成的 用户体验。此插件可在 GitHub 仓库 tpope/vim-vinegar 中找到

如果读者同时安装了 NERDTree 和 Vinegar,则 Vinegar 会使用 NERDTree 窗 口,而不是 Netrw。为了避免 NERDTree 取代 Netrw(而且使-键之类的命令 保持可用),读者需要在.vimrc 文件中设置 let NERDTreeHijackNetrw = 0。

Vinegar 提供了一种新的快捷键映射:-键(短横线)用于在当前目录中打开 Netrw

此插件隐藏了 Netrw 顶部的帮助栏,这可能会让读者有些迷惑。按 I 键(大写的 i), 帮助栏又会重新出现。另一个快捷键是 Shift + ~组合键,它将打开用户主目录,很 多人习惯在主目录中保存项目。

2.3.5 插件——CtrlP

CtrlP 是一个模糊补全插件,可帮助读者在只知道部分关键字时快速打开所需文 件。CtrlP 可在 GitHub 仓库 ctrlpvim/ctrlp.vim 中找到

安装此插件,然后在 Vim 中按下 Ctrl + p 组合键。屏幕下方会 出现 CtrlP 窗口,其中列出了项目目录中的文件列表。然后,输入文件名或目录中的关 键字,列表中将只剩下匹配项。读者可以用 Ctrl + j 组合键和 Ctrl + k 组合键来 上下遍历列表,并用 Enter 键来打开选中的文件。按 Esc 键退出 CtrlP 窗口

除当前目录下的文件之外,CtrlP 还支持在缓冲区或最近使用的文件之间切换。在打 开 CtrlP 窗口的情况下,使用 Ctrl + f 组合键和 Ctrl + b 组合键可以在 3 种不同的 搜索模式之间的循环切换。

读者可以用命令来选择一种搜索模式,:CtrlPBuffer 命令会列出缓冲区供读者选 择,:CtrlPMRU 命令会列出最近使用的文件,而:CtrlPMixed 命令则同时显示文件、 缓冲区和最近使用的文件。

读者也可以在.vimrc 文件中绑定其他快捷键。比如,将:CtrlPBuffer 命令绑定 到 Ctrl + b 组合键,可以使用如下设置。

nnoremap <C-b> :CtrlPBuffer<cr> " 将 CtrlP 缓冲区模式绑定到 Ctrl + b 组合键

2.4 文本的浏览

2.4.1 切换到插入模式

前面已经介绍过,在 Vim 中使用 i 键进入插入模式,而插入文本的位置为光标的 位置。

除此之外,还有其他进入插入模式的便捷方法。

  • a键用于在光标后面进入插入模式

  • A 键用于在当前行行尾进入插入模式(等价于$a)

  • I 键用于在当前行行首(在缩进之后)进入插入模式(等价于_i)

  • o 键用于在光标下面新增一行,在新的一行里进入插入模式

  • O 键用于在光标上面新增一行,在新的一行里进入插入模式

  • gi 用于在最后退出的位置进入插入模式

第 1 章已经介绍了如何通过修改命令 c 来删除文本,然后进入插入模式。还有一些 其他的先修改再插入的组合命令,介绍如下

  • C 用于删除光标右边的文字(直到行尾),然后进入插入模式

  • cc 或 S 用于删除当前行的内容,然后进入插入模式,但会保留缩进

  • s 用于删除单个字符(如果前面加了数字,则会删除多个字符),然后进入插入 模式

2.4.2 用/和?搜索

大多数情况下,Vim 中最快的文本浏览方式是搜索指定的字符串,搜索方法是按/ 键(从正常模式进入命令模式),然后输入待搜索的字符串。按 Enter 键之后,光标将 移动到第一个匹配的地方。

按 n 键可以在同一个缓冲区中循环遍历所有匹配的位置,而按 N 键则可以开始反向 遍历

搜索过程中常用到的一个选项是 set hlsearch(可以考虑添加到.vimrc 文件中), 因为这个选项会在屏幕上高亮显示每个匹配项。

使用 set incsearch。这个选项会在读者还未完整输入 搜索命令时,就将光标动态跳转到第一个匹配处。

如果想要反向搜索,则将/换成?即可,这时 n(跳转到下一个匹配)和 N(跳转到 上一个匹配)的行为也会发生变化。

  1. 跨文件搜索

Vim 提供了两个命令来实现跨文件搜索,分别是:grep 和:vimgrep。

  • :grep 调用的是系统命令 grep,这是一个非常强大的工具,特别是对于熟悉 grep 用法的人而言

  • :vimgrep 则属于 Vim 的一部分,如果读者还不是太熟悉 grep,则:vimgrep 命令会更容易掌握

:vimgrep 的语法为:vimgrep <模式> <路径>。其中<模式>既可以是一个字符 串,也可以是 Vim 风格的正则表达式。<路径>通常为一个通配符,当<路径>为**时, 表示对目录递归搜索(或者使用**/*.py 搜索具体的文件类型)。

比如,使用下面的命令在本书的 Python 工程中搜索 calc 字符串。

:vimgrep animal **/*.py 

然后,Vim 会跳转到第一个匹配处,并在屏幕底部显示匹配的个数

然后可以用:cn:cp 来逐个浏览各匹配项。读者也可以执行:copen 命令,这时 会在底部出现一个新窗口,其中有可视化显示的快速恢复(quickfix)列表

在快速恢复列表中,可以用 j 和 k 来上下移动光标,然后按 Enter 键跳转到相应的匹配处。这个快速恢复窗口和其他窗口一样,也可以用:q 命令或 Ctrl + w,q 组合 键退出。

  1. ack

在 Linux 操作系统中,Vim 可以结合 ack 工具来搜索代码库。ack 继承了 grep 的 思想,它更关注于搜索代码。读者可以用自己喜欢的包管理器来安装 ack,下面是使用 apt-get 的安装示例。

sudo apt-get install ack-grep 

比如,读者可以在命令行中用 ack 来递归搜索所有包含单词 Animal 的 Python 文 件,命令如下。

 ack --python Animal 

Vim 中包含一个集成 ack 的插件,可以将 ack 的结果显示在快速恢复窗口中(参考 5.5.1 节关于快速列表的介绍)。这个插件的地址为 GitHub 仓库 mileszs/ack.vim。安装此插件之后,可以在 Vim 中执行如下:ack 命令。

:ack --python Animal 

此命令会运行 ack,然后显示快速恢复窗口(参考 5.5.1 节中关于快速恢复窗口的 详细介绍)

2.4.3 利用文本对象

文本对象是 Vim 中额外增加的对象类型,比如括号内或引号内的文本都可以称作文 本对象。通过文本对象,读者可以更方便地处理代码。文本对象只能与其他操作符组合 起来使用,比如修改命令、删除命令或可视模式。

可以尝试使用文本对象,如下,将光标置于括号内

    def print_contents(self):
        print("We've got some animals on the farm:",
              ', '.join(animal.kind for animal in self.animals) + '.')

然后输入di)(删除括号内的文本),括号内的文本就会被删除

    def print_contents(self):
        print("We've got some animals on the farm:",
              ', '.join() + '.')

上面的删除命令和修改命令类似。用 u 来撤销上面的修改

执行 c2aw(修改两个单词)会删除光标后的两个单词(包括周围的空格),然后进 入插入模式

    def print_contents(self):
        print("We've got some animals on the farm:",
              ', '.join(animal.kind in self.animals) + '.')

从上面的两个例子中可以看到,文本对象有两种风格:内对象(以 i 开头)和外对 象(以 a 开头)。内对象不包含空白字符(或周围其他字符),而外对象是包含的。

文本对象的完整列表可通过:help text-objects 命令来查看,这里列出一些有 趣的文本对象。

  • w 和 W 分别表示狭义单词 word 和广义单词 WORD。

  • s 表示句子。

  • p 表示段落。

  • t 表示 HTML/XML 标签。

编程语言中常用的成对的字符全部可以表示为文本对象,包括`、''、""、()、[]、<> 和{},因而读者可以选择这些字符之间的文本。

读者可以将文本对象的使用方式理解为句子的构造

2.4.4 插件——EasyMotion

EasyMotion 一直是作者工具箱中的核心组成部分。它简化了文本浏览过程,可以准 确且快速地跳转到指定位置。插件的网址为 GitHub 仓库 easymotion/vimeasymotion

启动方式:安装完成之后,连续按两次先导键\,然后按某个移动键,可以启动此插件。

Vim 插件常用先导键来实现额外的快捷键映射。默认情况下,Vim 的先导 键为\。关于先导键的更多介绍请参考第 3 章

\\w 命令(按两次反斜划线,然后按 w 键)将触发逐个单词的移动方式

屏幕上每个单词的开头都被替换为一个字母(或两个字母, EasyMotion 命令用完了所有英文字母之后就会使用两个字母)。按一个字母(或依次按 两个字母),光标将瞬间跳转到屏幕上的指定位置.

EasyMotion 默认支持下列移动命令(所有命令都要加上两个先导符)

  • f 用于向右查找一个字符,F 则用于向左查找字符

  • t 用于向右移动直到找到该字符,T 则用于向左移动直到查找该字符

  • w 用于向后跳过一个狭义单词(W 向后跳过一个广义单词)

  • b 用于向前跳过一个狭义单词(B 向前跳过一个广义单词)

  • e 用于向前跳到狭义单词末尾(E 向前跳到广义单词末尾)

  • ge 用于向后跳到狭义单词的末尾(gE 向后跳到广义单词的末尾)

  • k 和 j 分别用于跳到上一行或下一行的开关

  • n 和 N 分别用于跳到上一个或下一个的搜索结果(在用/或?搜索之后)

EasyMotion 还保留了很多快捷键没有使用,因此读者可以构建自己的快捷键映 射。:help easymotion 命令可查看 EasyMotion 的所有功能

2.5 使用寄存器进行复制和粘贴

读者可以用 y 命令(yank)来复制文本,后面接一个移动命令或一个文本对象。选 择一些文本后也可以在可视模式下使用 y 来复制。

这里的复制和粘贴只限于 Vim 内部,复制是将文本存入相应的 Vim 寄存器中,而粘贴是 从某个 Vim 寄存器中取出文字并插入当前光标位置。如果想要和系统的粘贴板进行交互,需要使用特 殊的寄存器。

  • ye 命令(复制文本直到下一个单词结尾)

  • 删除和修改操作同时也复制了相关的文本,这些文本可以用于后续的粘贴操作中。 另外,粘贴操作 p 的前面可以加数字,从而实现多次粘贴。

2.5.1 寄存器

Vim 中复制和粘贴文本时,文本是储存在 Vim 寄存器里面的。Vim 支持多种寄存 器,每个寄存器用字母、数字或特殊符号来标识。

寄存器的访问方式是引号键",后面接寄存器的标识符,然后是针对指定寄存器的 操作。

a~z 所标识的寄存器用于手动复制数据。比如,将一个单词复制到 a 寄存器,可以 使用"ayw 命令,而粘贴命令为"ap

前面介绍的复制和粘贴操作使用的都是默认的无名寄存器。这个无名寄存器用双引 号"来标识,读者可以用这个标识符来显式访问寄存器。比如,""p 用于从无名寄存中 粘贴文本,等同于 p。

用数字编号的寄存器存储的是最后 10 次删除操作的历史记录。0 寄存器存储的是最 后一次删除的文本,1 寄存器则为上上次删除的文本,依此类推。假设读者记忆力超群, 想起了 7 次粘贴操作之前的粘贴操作,"7p 命令会将那次操作的文本再次粘贴出来。

还有一些很有用的只读寄存器:%存储了当前文件名,#存储了上次打开的 文件名,.中为最后插入的文本,:为最后执行的命令。

读者也可以在正常模式之外与缓冲区进行交互。Ctrl + r 组合键允许读者在插入 模式或命令行模式下粘贴某个寄存器的内容。比如,在插入模式下,按 Ctrl + r 组合 键,"会在光标处粘贴无名寄存器中的内容。

读者可以在任何时候通过:reg <寄存器标识符>命令来访问某个寄存器的内容。比 如,:reg a b 命令可同时得到 a 和 b 寄存器中的内容。

还可以用:reg 命令列出所有寄存器的内容

y 命令会重写寄存器内的内容,而字母命令的寄存器(a~z)是可以附加内容的, 方法是使用大写的寄存器标识符。比如,若想附加一个单词到寄存器 a 中,可以先将光 标置于此单词开头,然后执行"Ayw 命令。

2.5.2 从外部复制文本到Vim中

Vim 中有如下两种内置的寄存器用于和外部世界交互。

  • *寄存器表示系统的主粘贴板(macOS 和 Windows 系统中的默认粘贴板,在 Linux 系统中为终端的鼠标选择内容)

  • +寄存器(只针对 Linux)用于 Windows 风格的 Ctrl + c 组合键和 Ctrl + v 组合键操作[称为粘贴板选择器(Clipboard selection)]

这两个寄存器的使用方式与其他寄存器相同。比如,"*p 命令用于从系统主粘贴板 中将文本粘贴进来,"+yy 命令则将一行文本复制到 Linux 的粘贴板选择器中。

如果读者希望默认使用这些寄存器,可以在.vimrc 文件中设置 clipboard 变量, 将其设置为 unamed 时,表示默认使用*寄存器进行复制和粘贴。

set clipboard=unamed " 复制到系统寄存器(*) 

将 clipboard 设置为 unnamedplus,将默认使用+寄存器

set clipboard=unnamedplus " 复制到系统寄存器(+) 

利用如下命令还可以同时使用这两个寄存器

set clipboard=unamed,unnamedplus " 复制到系统寄存器(*, +) 

在.vimrc 文件中完成设置之后,y 和 p 命令将分别从指定的默认寄存器中复制和 粘贴。

有时,读者希望在插入模式下从系统粘贴板中将文字粘贴到缓冲区中。在 老版本的 Vim 或某些终端模拟器中,这样粘贴会出现问题,因为 Vim 会在 粘贴过程中自动缩进代码或添加注释。为避免这种情况,在粘贴代码之前 先禁用缩进和自动注释,方法是运行:set paste 命令,粘贴完成之后, 恢复的命令为:set nopaste。从 8.0 版本开始,Vim 默认启用括号化粘 贴模式(bracketed paste mode),从而基本解决了这些问题。

本章小结

本章介绍了 Vim 中的一些核心概念,以及如何利用这些概念进行文本浏览,具体包 括使用缓冲区表示文件、分割窗口和用标签页组织多个窗口。另外还介绍了如何通过折 叠来更有条理地浏览大文件的内容。

经过本章的学习,读者应该可以更自信地用 Vim 来处理大型代码库,包括用插件(如 Netrw、NERDTree、Vinegar 和 CtrlP)来浏览文件。本章还介绍了一种安装这些插件的 快速方式(虽然不够自动化)。

本章介绍了几种新的光标移动操作、文本对象、快速切换到插入模式的方法以及如 何用 EasyMotion 插件在文件中快速跳转。

本章还介绍了搜索功能,包括文件内搜索和整 个代码库内文件之间的搜索。如果读者能够尝试本章介绍的 ack 插件,收获会更大。 最后,本章还介绍了寄存器的概念,以及如何用寄存器来复制和粘贴文本。

第三章 使用先导键——插件管理

主要内容

  • 使用手动方法或 vim-plug、Vundle、Pathogen 等管理多个插件

  • 介绍配置慢速插件的方法

  • 深入介绍 Vim 中的主要模式

  • 理解命令重映射的复杂性

  • 介绍先导键及其在定制快捷键时的用处

  • 配置和定制插件

3.1 插件管理

到目前为止,读者应该已经安装了一些插件,而且今后安装的插件会越来越多,特别是其中可能还包含一些用于解决疑难杂症的专用插件。手动让这些插件保持更新还是 比较麻烦的,幸运的是,已经有一些现成的插件管理方案了。 当读者需要在多台机器之间切换,并希望 Vim 插件保持更新时,插件管理就显得非 常重要了。

在多台机器之间同步 Vim 配置的方法,请参见第 7 章。

3.1.1 插件管理——vim-plug

vim-plug 是很有前景的插件管理器。这个插件管理器是轻量级的,它易用且兼容大 量的插件。其 GitHub 仓库地址为 junegunn/vim-plug(它的帮助文件 README 非常 友好,还想进一步偷懒的读者可以直接查看本节中的摘要)。

插件 vim-plug 有如下优点。

  • 轻量级、单个文件且支持一些直观的安装选项

  • 支持并行插件加载(要求 Vim 编译带有 Python 或 Ruby 支持,这几乎已经是现 代 Vim 的标配)

  • 支持大多数插件的延迟加载,即只为特定命令或文件类型触发必要的插件

Linux 系统中的删除 命令为 rm -rf $HOME/.vim/pack

安装 vim-plug :

  • 下载插件文件

  • 保存为$HOME/.vim/autoload/plug.vim

  1. 为了从 GitHub 上下载文件,在 Linux 或 macOS 系统中可以使用 curl 或 wget 命令,或者直接在浏览器中打开链接,然后“右键->另存为”。比如,UNIX 用户可以用 下列命令下载文件。

    $ curl -fLo ~/.vim/autoload/plug.vim --create-dirs 
    https://raw.github.com/junegunn/vim-plug/master/plug.vim 
  2. 修改.vimrc 文件,加入 vim-plug 初始化的代码,如下所示。

    " 使用 vim-plug 管理插件
    call plug#begin() 
    call plug#end() 
  3. 在这两行之间加入一些插件,其中的地址格式为 GitHub 地址的最后两部分 (<用户名>/<仓库>,比如 GitHub - preservim/nerdtree: A tree explorer plugin for vim. 记为 scrooloose/nerdtree),用于唯一标识插件,如下所示。

    " 使用 vim-plug 管理插件
    call plug#begin() 
    Plug 'scrooloose/nerdtree' 
    Plug 'tpope/vim-vinegar' 
    Plug 'ctrlpvim/ctrlp.vim' 
    Plug 'mileszs/ack.vim' 
    Plug 'easymotion/vim-easymotion' 
    call plug#end() 
  4. 保存.vimrc 文件,然后重载(命令为:w | source $MYVIMRC)或重启 Vim, 以使这些修改生效。执行:PlugInstall 来安装这些插件。然后上面提到的插件将会自 动从 GitHub 上下载下来

vim-plug 有两个主要的命令。

  • :PlugUpdate 用于更新所有已安装的插件。

  • :PlugClean 用于删除.vimrc 中已经移除的插件。如果不执行:PlugClean, 则没有激活的插件(.vimrc 中删除或注释掉的那些 Plug...行)将仍然保存 在文件系统中

运行:PlugUpdate 将更新 vim-plug 所管理的插件,但不包括它自己。如 果想要更新 vim-plug,需要运行:PlugUpgrade 命令,然后重载.vimrc 文件(执行:source $MYVIMRC 或重启 Vim)

延迟加载是一种避免插件拖延 Vim 运行速度的有效技术,这一点可通过 Plug 指令 的可选参数来实现。比如,如果想要在:NERDTreeToggle 命令执行时再加载 NERDTree,可以使用 on 参数。

Plug 'scrooloose/nerdtree', { 'on': 'NERDTreeToggle' } 

如果只想对特定文件类型加载某个插件,可以使用 for 参数,如下所示。

Plug 'junegunn/goyo.vim', { 'for': 'markdown' }

由于 vim-plug 采用单文件安装方式,因此它的帮助文档并未安装到 Vim 中。 如果想要用:help vim-plug 来查看文档,则需要将 Plug 'junegunn/ vim-plug'添加到插件安装列表中,然后运行:PlugInstall 命令。

在 vim-plug 的 GitHub 仓库 junegunn/vim-plug 中,读者可以在它的 README 文 件中找到 vim-plug 支持的所有参数列表。

对于 Linux 或 macOS 系统用户(以及 Cygwin 用户),可以将下列代码添加到.vimrc 文件中,当该.vimrc 被移植到新机器上时,它会自动安装 vim-plug。

" 如果没安装过 vim-plug,则下载安装
if empty(glob('~/.vim/autoload/plug.vim')) 
 silent !curl -fLo ~/.vim/autoload/plug.vim --create-dirs 
 \https://raw.GitHub.com/junegunn/vim-plug/master/plug.vim 
 autocmd VimEnter * PlugInstall --sync | source $MYVIMRC 
endif 

再次打开 Vim 时,vim-plug(以及所有用 Plug 指令列出的插件)就安装好了。

3.1.2 其他推荐

除了 vim-plug,还有很多其他的插件管理方案。本节的介绍并不全面,而是侧重展 示不同的插件管理风格。读者可以选择一款适合自己的,或者从网上搜索其他方案。

  1. Vundle Vundle 是 vim-plug 的前身(也许只是提供了灵感),它们有着相似的工作方式。比 如,插件安装是同步的(不是后台运行),只不过 Vundle 的插件打包方式更重量级一些。 另一个不同之处在于插件的搜索方式,Vundle 支持直接从 Vim 中搜索插件。此外,Vundle 还支持用户在安装插件之前先进行测试。Vundle 及其安装指令可以在它的 GitHub 仓库 VundleVim/Vundle.vim 中找到。

    Vundle 和 vim-plug 的工作方式类似, :PluginInstall 、 :PluginUpdate 和:PluginClean 这几个命令的作用相同。

  2. 手动方式 由于大部分插件都可以在 GitHub 上找到,因此使插件保持更新的主要方法是将它们 安装成 Git 的子模块。如果读者熟悉 Git,可以在.vim 目录中初始化一个 Git 仓库,然 后在此仓库中安装插件,作为 Git 仓库的子模块。

    Vim8 提供了一种原生的插件加载方式,即在.vim/pack 目录中搜索插件目录树。 Vim8 支持如下目录结构。

  • .vim/pack/<目录名>/opt/中保存的插件用于手动加载

  • .vim/pack/<目录名>/start/中保存的插件用于始终加载

如果读者想要深入了解每个插件目录的组织方式,请参考第 8 章。

首先,需要选择一个好记的名字,作为.vim/pack/的子目录。比如,plugins 就 是一个不错的选择。

然后,<目录名>/start/目录用于存储那些总是需要加载的插件。

opt/目录中的插件只会在执行:packadd <目录名>命令之后再加载,因此可以 在.vimrc 中用 packadd 命令有选择性地加载插件。opt/目录和 packadd 命令可以 实现插件的延迟加载(与 vim-plug 相同)。

" 通过:Ack 命令来加载和运行 ack.vim 插件
command ! -nargs=* Ack :packadd ack.vim | Ack <f-args> 
" 当打开 Markdown 文件时加载并运行 Goyo 插件
autcmd! filetype markdown packadd goyo.vim | Goyo 

此外,在.vimrc 中加入如下代码,可以加载所有插件的文档。

packloadall " 加载所有插件
silent! helptags ALL " 为所有插件加载帮助文档

执行 packloadall 命令使 Vim 加载 start/目录中的所有插件(Vim 会在加 载.vimrc 之后自动执行这个步骤,这里是提前执行)。helptags ALL 加载所有插件的 帮助文档,而 silent!前缀则是为了隐藏加载帮助文档过程中的所有输出和错误信息。

读者可以通过 Git 子模块来自行管理插件(稍微有点麻烦),即用 Git 下载插件并保 持更新

首先,在.vim 目录中初始化一个 Git 仓库(只需要操作一次)

$ cd ~/.vim 
$ git init 

然后,添加一个插件,比如 nerdtree,作为子模块。

$ git submodule add https://github.com/scrooloose/nerdtree.git 
pack/plugins/start/nerdtree 
$ git commit -am "Add NERDTree plugin"

每次更新插件时,执行下列命令。

$ git submodule update --recursive 
$ git commit -am "Update plugins" 

若要删除插件及相应的子模块,可执行下列命令。

$ git submodule deinit -f -- pack/plugins/start/nerdtree 
$ rm -rf .git/modules/pack/plugins/start/nerdtree 
$ git rm -f pack/plugins/start/nerdtree
  1. Pathogen

本节可能更像是对 Pathogen 光荣历史的回顾

从定义看,Pathogen 更像是一种 runtimepath 管理器,而非插件管理器。但在 实践中,runtimepath 管理可以相当好地过渡为插件管理。在 Vim 8 之后,已经不再 需要通过操作 runtimepath 来安装插件。不过,如果读者还在使用 Vim 8 之前的版 本 Vim(或者不愿意使用重量级的包管理器),则 Pathogen 可以作为简便的插件管理器 来使用。

Pathogen 是插件管理的先行者之一,并在很大程度影响了后来者。许多 Vim 用户至 今仍在使用 Pathogen。

Pathogen 可以在它的 GitHub 仓库 tpope/vim-pathogen 中找到

3.1.3 分析运行慢的插件

Vim 使用得越久,安装的插件可能也会越多。有时候,这些插件会导致 Vim 运行速 度减慢。Vim 运行速度慢的根源通常是某个未优化的插件,原因可能是插件的缺陷或插 件与系统之间独特的交互方式。为此,本节将介绍 Vim 内置的插件分析功能,它可以用 于检测这些问题。

  1. 启动时间分析

Vim 启动时,若加上--startuptime <文件名>选项,则 Vim 会将启动时的每个行 为记录到一个文件中。比如,下列命令将启动日志记录到文件 startuptime.log 中。

$ vim --startuptime startuptime.log
  1. 特定行为的分析

有时候,读者会发现 Vim 中的某个行为比较慢,这时可以对特定几个行为进行分析。

这里设计了一种典型的场景。从 GitHub 下载 Python 和 Vim 仓库,然后尝试运行 CtrlP 插件提供的:CtrlP 命令(参见第 2 章)。:CtrlP 命令将试图从当前目录开始递归为所有文件建立索引,对于文件数众多的这两个仓库而言,速度显然是非常慢的。

为了分析这个行为,在启动 Vim 后,执行如下几条命令。

:profile start profile.log 
:profile func *
:profile file *

Vim 将分析这里执行的每个行为。当一个命令运行速度较慢时,比如这里的:CtrlP 命令(或按 Ctrl + P 组合键)。等待这个操作完成之后,退出 Vim(:q)。 用 Vim 打开 profile.log 文件

3.2 模式详解

Vim 有 7 种主模式,理解每一种模式的用途非常重要

3.2.1 正常模式

用户使用较多的 Vim 模式是正常模式(normal mode)。打开 Vim 时默认为正常模式, 在其他模式中按一次 Esc 键(有时候需要按两次),也会返回正常模式。

3.2.2 命令行模式和 ex 模式

命令行模式(command-line mode)的进入方式是输入冒号(:)(或者用/或?进行 搜索),进入命令行模式之后,读者可以输入一条命令,然后按 Enter 键执行命令。命 令行模式提供了以下几个常用的快捷键。

  • 上下箭头键(或 Ctrl + p 组合键和 Ctrl + n 组合键)可以逐个命令地浏览 历史记录。

  • Ctrl + b 组合键和 Ctrl + e 组合键可以跳转到命令的开头(beginning)和 结尾(ending)。

  • Shift 键和 Ctrl 键结合左右箭头键可以逐个单词移动光标。

  • Ctrl + f 也是一个非常有用的快捷键,它可以打开一个可编辑的命令行窗口,其 中显示的是之前运行过的命令的历史记录

命令历史记录窗口只是普通的缓冲区,读者可以在其中找到曾经执行过的命令,并 进一步编辑(使用 Vim 中正常的文本编辑方式),然后再次执行。按 Enter 键可以执行 光标所在的命令行,按 Ctrl + c 组合键可以编辑此缓冲区。

查看帮助文档:help cmdline-editing 可以了解到更多命令行模式的使用方法。

Vim 还有一个命令行模式变体,称为 ex 模式,进入方式是按 Q 键。ex 模式是为了兼容 Vim 的前身 Ex。ex 模式支持执行多个命令而无须退出,但这个模式现在已经很少使用。

3.2.3 插入模式

插入模式(insert mode)用于输入文本,除此之外没有其他功能。按 Esc 键可返 回到正常模式,大部分工作其实是在正常模式下完成的。在插入模式下,也可以使用 Ctrl + o 组合键来执行正常模式下的命令,然后再回到插入模式。

在状态栏上,插入模式的标识文本为--INSERT--。

3.2.4 可视模式和选择模式

Vim 的可视模式(visual mode)支持文本的任意选择(通常用于执行某些操作)。如 果需要选择的文本不属于已定义的文本对象(单词、句子和段落等),则这个模式非常有 用。进入可视模式有以下几种方式。

  • v 进入字符可视模式(状态栏标文本--VISUAL--)

  • V 进入行可视模式(状态栏标识文本--VISUAL LINE--)

  • Ctrl + v 组合键进入块可视模式(状态栏标识文本--VISUAL BLOCK--)

一旦进入可视模式,读者可以通过常用的移动命令来移动光标,从而扩展选择范围。

Vim 还有一个选择模式(select mode),它模拟的是其他编辑器中的选择模式:输 入任意可打印字符可立即删除选中文本,然后进入插入模式(所以这时常用的移动命令 已经失去作用)。这个模式和 ex 模式相同,只应用于特定和受限的场合。事实上,本书 的作者也从未使用过这个模式,这里只是为了内容的完整性才进行介绍。

读者可以在正常模式下按 gh 键进入选择模式,也可以在可视模式下按 Ctrl + g 组合键,退出方式为按 Esc 键。

3.2.5 替换模式和虚拟替换模式

在其他编辑器里,按 Insert 键进入替换模式,其效果是擦除其他文本。在 Vim 下, 替换模式也类似,输入的文本会覆盖已有的文本(而不像插入模式下,输入文本会移动 已有文本)。当读者不想改变原始文件中的字符数时,这种模式非常有用。

替换模式的进入方式是按 R 键

替换模式在状态栏上的标识文本为--REPLACE--。在替换模式下,读者可以体验 替换文本的效果

按 Esc 键可退出替换模式,并返回到正常模式。

读者可按 r 键进入单字符替换模式,在此模式下,替换单个字符后会马上切换回正 常模式。

Vim 还提供了虚拟替换模式,它和替换模式类似,只不过操作的对象是屏幕上的显 示,而非直接针对文件中的字符。主要的区别包括 Tab 键会替换多个字符(而替换模式 下只会替换单个字符);按 Enter 键不会新增一行,但会移动到下一行。虚拟替换模式 的进入方式是输入 gR,详情参见帮助文档:help vreplace-mode

3.2.6 终端模式

终端模式(terminal mode)出现在 Vim 8.1 版本中,此模式支持在一个分割窗口中运 行一个终端环境。进入终端模式的命令如下。

:terminal
  • :terminal 命令可简化为:term

:terminal 命令会在水平分割窗口中打开系统的命令行系统(Linux 系统中为默认 的 Shell)

终端模式中封装了系统的终端,读者可以像往常一样使用命令行。这个终端窗口与 其他窗口类似(可以用 Ctrl + w 命令进行切换),只不过此窗口中只有插入模式。读 者可以考虑使用 Linux 或 macOS 系统下的 tmux 命令或 screen 命令,与 Vim 的终端模式 配合使用。

3.3 命令的重映射

只要是愿意使用插件的读者,都会希望对 Vim 中的命令进行重映射,以满足自己进 一步的需求。那些插件往往是由不同的人编写的,而每个人的工作流程往往也不同。Vim 本身则是无穷可扩展的,几乎支持每一个操作的重映射,读者可以任意改变默认行为, 从而让 Vim 彻底为己所用。本节介绍的就是命令的重映射。

Vim 支持将某些键映射为其他键,:map 和:noremap 提供了这样的功能

  • :map 用于递归映射

  • :noremap 用于非递归映射

这意味着经:map 重映射的命令可以识别自定义映射,而:noremap 则针对系统默 认映射

当读者决定创建一个新映射时,最好先确认一下此键或按键序列是否已经 被映射为其他用途了。内置的按键绑定列表可通过:help index 命令查 看。而:map 命令则可以查看插件和读者自定义的映射。比如,:map g 将 显示所有以 g 键开头的映射

比如,在.vimrc 文件中将按如下方式自定义映射

noremap ; : " 用分号取代默认的冒号,用于输入命令

模式感知的重映射

:map 命令和:noremap 命令都可用于正常模式、可视模式、选择模式和操作待决 模式(operator pending mode)。不过,Vim 还支持更细粒度的映射控制方式,读者可针 对每一种模式分别定义映射。

  • :nmap 和:nnoremap 用于正常模式

  • :vmap 和:vnoremap 用于可视和选择模式

  • :xmap 和:xnoremap 用于可视模式

  • :smap 和:snoremap 用于选择模式

  • :omap 和:onoremap 用于操作待决模式

  • :map!和:noremap!用于插入和命令行模式

  • :imap 和:inoremap 用于插入模式

  • :cmap 和:cnoremap 用于命令行模式

3.4 先导键

本书前面已经提到过先导键(leader key),它本质上是读者或插件定义的快捷方式 的命名空间。先按先导键,然后按下的任何键都来自于该命名空间。

默认的先导键为反斜线\,但这个键使用起来不太方便。在 Vim 社区中有几个替代的方 案,其中逗号是较流行的。为重新绑定先导键,需要在.vimrc 文件中加入如下设置。

" 将先导键重新映射为逗号
"let mapleader = ',' 

先导键的设置应该放在.vimrc 的顶部,因为新定义的先导键只会在定义之后 生效。

本书推荐的先导键是空格键。因为这个键足够大,而且在正常模式下并没有什么实 际的用途(只不过是在功能上模拟了右箭头键)。

" 将先导键映射为空格键
"let mapleader = "\<space>" 

这里之前的转义符\是必要的,因为 mapleader 变量中不含特殊 字符(如空格符)。对于这个转义来说,只能用双引号,而不能是单引号, 因为单引号中只能存字面量字符串,而不含转义字符。

然后,读者就可以在.vimrc 中使用先导键了,比如下列代码

" 用 leader-w 保存文件
"noremap <leader>w :w<cr>

3.5 插件的配置

Vim 插件通常会提供一些命令来绑定快捷键,或提供一些变量用于控制插件的行为。 因此,在使用一个插件时,查看它提供了哪些命令和选项是一个好习惯。读者可以将命 令绑定到方便记忆的快捷键上,这会有助于记住插件的用法。

Vim 支持全局变量,其主要用途是配置插件。全局变量通常以 g:为前缀。通过:help <插件名>命令查看插件文档,可以找到它提供了哪些配置选项。

本章小结

本章介绍了管理插件的几种不同方式,其中 vim-plug 是一种轻量级的插件管理器, 支持插件的异步安装和更新。vim-plug 的前身 Vundle 也支持新插件的搜索和临时安装。

本章还介绍了一种手动的插件安装方式,因为 Vim 8 引入了一种插件加载方式,从而无 须手动修改每个插件的 runtimepath。对于低于 Vim 8 版本,读者仍然可以使用 Pathogen,它提供了一种自动执行部分 runtimepath 操作的方法。

本章介绍了两种分析 Vim 性能的方法:--startuptime 选项和:profile 命令。

本章介绍了 Vim 的主要模式:正常模式、命令行模式和 ex 模式、插入模式、可视 模式和选择模式、替换模式和虚拟替换模式,以及终端模式。 本章还介绍了如何通过重新映射命令来定制 Vim。不同的用户习惯使用不同的快捷 键组合。Vim 支持根据不同的模式映射不同的快捷键,即同一个按键在不同的模式下可 以有不同的意义。先导键支持创建一个全新的命名空间,可以用于插件和用户自定义的 命令。

本章还介绍了如何通过定制插件的配置选项来充分使用插件,以及通过为插件添加 快捷键来改进工作流程。

第四章 理解文本

当代码库越来越庞大时,代码文件之间的跳转也会变得更加困难。幸运的是,Vim 拥有一些浏览复杂代码结构的功能。

主要内容:

  • 利用 Vim 内置的自动补全功能和相关插件对代码自动补全。

  • 使用 Exuberant Ctags 浏览大型代码库。

  • 使用 Gundo 浏览 Vim 复杂的撤销树。

4.1 代码补全功能

代码自动补全是现代集成开发环境(IDE)的一项极其吸引人的功能。通过自动补 全,读者可以在 IDE 中避免拼写错误,而且无须反复输入和记忆冗长复杂的变量名。

Vim 已经内置了自动补全功能,还有一些插件进一步扩展了这个功能。

4.1.1 内置自动补全

Vim 支持在打开的缓冲区中对单词进行简单的自动补全,这个功能从 Vim 7 开始默 认启用。输入一个函数名的开始几个字符,然后按 Ctrl + n 组合键,Vim 会弹出一 个自动补全列表。然后用 Ctrl + n 和 Ctrl + p 组合键可以在这个列表中上下移动, 以选择合适的备选项。比如,打开 animal_farm.py 文件,进入插入模式,输入 make_animal 的前两个字符 an,然后按 Ctrl + n 组合键,自动补全列表中会出现

如果继续输入,列表则会消失。

本节中仅列出了部分常用命令,其他关于自动补全的完整命令列表请参见 帮助文档:help ins-completion。每个人的工作流程都是独一无二的, 除非亲自操作,否则无法预知哪些命令对自己是最有用的。读者还可以查 看:help 'complete', 'complete'选项用于控制 Vim 补全建议的来 源(默认为缓冲区、标签文件和头文件)。

4.1.2 YouCompleteMe 插件

YouCompleteMe 插件扩展了 Vim 内置的自动补全引擎,而且还增添了大量新功能, 下面是 YouCompleteMe 优于内置自动补全的几个重要功能。

  • 支持程序语言感知的自动语义补全;YouCompleteMe 更能理解代码的语义

  • 补全建议的智能排序和过滤

  • 能够显示代码文档、重命名变量、自动格式化代码以及修正某些类别的输入 错误(这个功能与具体的语言有关,参见官方 GitHub 仓库 Valloric/ YouCompleteMe)

  1. 安装 首先,确保安装了 cmake 和 llvm,因为 YouCompleteMe 需要编译代码

     sudo apt-get install cmake llvm

    如果使用 vim-plug 插件管理器,则可在.vimrc 中加入如下设置

    let g:plug_timeout = 300 " 为 YouCompleteMe 增加 vim-plug 的超时时间
    Plug 'Valloric/YouCompleteMe', { 'do': './install.py' } 

    保存.vimrc,然后执行如下命令

    :source $MYVIMRC | PlugInstall 
  2. 使用 YouCompleteMe YouCompleteMe 并没有新增太多快捷键,因此读者比较容易适应。(进入插入模式, 开始编辑。),自动补全列表会自动弹出来。按 Tab 键会循环遍历所有建议。此外, 如果 YouCompleteMe 能够查找函数定义,并且找到的函数支持文档字符串,则会在窗口 顶部显示一个预览窗口.

预览窗口只在 YouCompleteMe 使用语义自动补全引擎时显示。语义自动补 全引擎可通过在插入模式下输入句点.来启动,或者通过 Ctrl +空格组合 键来手动启动

4.2 撤销树和Gundo

大部分现代编辑器都支持撤销栈,其中保存了撤销和重做操作的历史记录。Vim 则 更进一步,采用了撤销树。如果读者执行修改操作 X,然后撤销并执行另一修改操作 Y, X 却仍然被保存起来了。Vim 支持手动浏览撤销树的叶子结点,但还有更好的方式。

Gundo 是一种对撤销树进行可视化的插件,读者可以在 GitHub 上找到。

如果使用 vim-plug 管理插件,请在.vimrc 中添加 Plug 'sjl/gundo.vim'。执行:w | so $MYVIMRC | PlugInstall,即 可成功安装 Gundo。

本章小结

本章介绍了 Vim 中的一些高级工作流程。首先,本章介绍了如何利用 Vim 的内置自 动补全功能来进行代码自动补全。然后,本章推荐了插件 YouCompleteMe,它能够通过 识别语法来辅助自动补全。此外,对于更复杂的代码库,可以使用 Exuberant Ctags 工具。 最后,本章介绍了撤销树和 Gundo 插件,可以让读者更直观地浏览撤销树。

第五章 构建、测试和执行

主要内容

本章重点介绍版本控制及相关流程和代码的构建及测试,具体包括如下几个方面

  • 介绍版本控制的相关知识(主要是 Git)

  • 介绍如何高效地将 Git 和 Vim 结合起来

  • 用 vimdiff 来比较和合并文件,以及处理 Git 冲突

  • 用 Tmux、Screen 或 Vim 终端模式来执行多任务和 Shell 命令

  • 用快速提示列表和位置列表捕获警告和错误

  • 用内置的:make 命令或插件构建和测试代码

  • 手动或用插件来运行语法检查器

5.1 版本控制

5.1.1 git基本操作
  1. 安装

    $ sudo apt-get install git 
  2. 配置用户名和邮件地址

    $ git config --global user.name 'Your Name' 
    $ git config --global user.email 'your@email'
  3. Git 文档查看

    $ git help 
  4. 初始化仓库

    $ cd animal_farm/ 
    $ git init
  5. 将文件添加到暂存区

    $ git add 
  6. 创建初始提交

    $ git commit -m "初始提交" 
  7. 添加远程仓库

    $ git remote add origin <url>
  8. 将本地仓库推送(push)到远程仓库

    $ git push -u origin master
  9. 克隆已有仓库

    $ git clone <url> 
  10. 查看状态

    $ git status 
  11. 创建分支

    $ git checkout -b <branch-name> 
  12. 查看分支

    $ git branch -a
  13. 切换分支

    $ git checkout <branch-name> 
  14. 合并分支

    # 切换回主分支
    $ git checkout master 
    # 合并分支
    $ git merge <branch-name>
5.1.2 Git 与 Vim 的整合(vim-fugitive)

Tim Pope 的 vim-fugitive 插件可以使用户在不离开 Vim 的情况下进行 Git 操作。读 者在使用 Vim 编辑文件时,也可以同时利用版本管理系统记录编辑的位置

如果使用 vim-plug 安装插件,则可以在.vimrc 中添加 Plug 'tpope/vim-fugitive',并执行命令:w | source $MYVIMRC | PlugInstall。

5.2 用vimdiff解决冲突

在开发过程中,经常需要比较不同文件之间的差异,比如比较不同的输出,或者比 较一个文件的不同版本,或者处理合并时的冲突。Vim 提供了一个独立的可执行程序 vimdiff,非常适用于对比文件差异。

5.2.1 比较两个文件

用 vimdiff 比较两个文件是非常简单的。以 animal_farm 目录中的 animals/ cat.py 和 animals/dog.py 为例,下面用 vimdiff 比较它们之间的差异。

用 vimdiff 打开多个文件的命令行,如下所示

$ vimdiff animals/cat.py animals/dog.py 
  1. 可以用]c 和[c 分别在多个修改处之间向前和向后跳转

  2. 可以在不同文件之间推送修改。 do 或:diffget(do 表示 diff obtain,即获得差异的意思)将文件修改应用于当 前窗口中的文件。 dp 或:diffput(dp 表示 diff put,即推送差异的意思)将当前窗口中的文件修 改推送给另一个文件。

5.2.2 vimdiff和Git组合

个人感觉不适用,不记录

5.3 其他终端工具(Tmux、Screen 和 Vim 的终端模式)

  1. 推荐使用Tmux

  2. Screen 是 Tmux 的思想先驱,但它现在仍然有很多用户。Screen 的可扩展性不如 Tmux, 事实上,若不经过一番配置,Vim 和 Screen 是不能和谐共处的。不过,如果已经习惯使用 Screen,又不想改变现有的工作流程,那么本节的一些技巧可让两者更融洽地相处。

  3. 在 8.1 版本中,Vim 中有了终端模式。终端模式是 Vim 会话中运行的一个终端模拟 器。和 Tmux 不同,终端模式是 Vim 自带的。当一个程序需要运行很长时间时,终端模 式是很有帮助的,因为用户可以同时使用 Vim 进行其他操作。 终端模式可以用命令:term 启动。它会打开一个上下分割的窗口,然后在其中运行 默认的 Shell 解释器

本章小结

本章介绍了(或回顾了)Git 的使用方法,包括 Git 的核心要领、如何新建/克隆工 程以及几个常用的命令。同时还介绍了 vim-fugitive 插件,这个工具让用户在 Vim 中以 一种更具有交互性的方式使用 Git。

本章介绍了 vimdiff,它是 Vim 自带的工具,用于比较多个文件的差异或在文件之间 消除差异。此外,本章中还展示了如何解决 Git 分支合并时的冲突,优化了用户体验。

本章介绍了在 Vim 中运行 Shell 命令的几种不同方式,包括 Tmux、Screen 和 Vim 终端模式。

本章还介绍了(全局的)快速恢复列表和(局部的)位置列表,它们都用于保存文 件中某些行的引用。将它们与:grep 和:make 命令的输出结合起来,可以方便用户浏览。 然后,本章解析了:make 命令是如何调用外部编译器的、使用 vim-dispatch 插件来扩 展:make 命令功能的方法,以及利用 vim-test 来更流畅地运行测试的技巧

最后,本章还推荐了几种在 Vim 中检查语法的方案,包括为 Pylint 定制自己的检查 方案。另外还有两个插件,异步语法检查器 ALE 和流行的 Syntastic。

第六章 用正则表达式和宏来重构代码

主要内容

本章重点关注 Vim 提供的一些代码重构功能,包括如下几个方面。

  • 使用:substitute 进行搜索或替换

  • 使用正则表达式进行更智能化的搜索和替换

  • 使用参数列表(arglist)对多个文件进行操作

  • 提供代码重构操作的示例,比如方法的重命名和参数的重排序

  • 介绍宏的用法,宏可用于录制键盘动作,并回放

6.1 用正则表达式来搜索和替换

61.1 搜索和替换

Vim 通过:substitute 命令实现搜索和替换功能,大部分时候都会将其简写为:s。 默认情况下,:s 命令将当前行中的一个子字符串替换为其他字符串,其命令形式如下。

:s/<find-this>/<replace-with-this>/<flags>

<选项>参数是可选的。打开 animal_farm.py 文件,体验此命令。跳转到包含 cat 的行(如用搜索命令/cat),然后执行:s/cat/dog 命令。

:substitute 的一些选项。

  • g 表示全局替换,即将匹配到的所有项都替换掉,而不仅仅是第一个

  • c 表示每次替换前需要确认,即弹出一个界面供用户确认是否替换。

  • e 表示没有匹配项时不显示错误。

  • i 表示忽略大小写,即搜索时不关心大小写。

  • I 表示区分大小写

这些选项可以根据需求结合起来使用(除了 i 和 I 之外)。比如,命令:s/cat/dog/gi 会将字符串 cat.Cat()替换为 dog.dog()

:substitute 命令可以作用于一个区间范围,即哪些内容中的匹配项会被替换掉。 常用的范围是%,它使:s 命令作用于整个文件。 如果要将一个文件中的所有 animal 替换成 creature,则只需要执行:%s/ animal/creature/g 命令。

:substitute 还支持其他区间范围,常用的有以下几种。

  • 数字,表示行号

  • $表示最后一行

  • %表示整个文件(最常用的一种)

  • /search-pattern/,即在下一个搜索结果所在的行操作

  • ?backwards-search-pattern?,与/search-pattern/功能类似,只不 过是反向搜索

6.2 宏的录制和回放

宏是一种非常强大的工具,它支持录制一系列动作,然后用于回放。

使用方法:

用命令 q 进入宏录制模式,后面是任意寄存器,即 qa 命令(这里用的是寄存器 a)。然后,我们可以在状态栏中看到 recording @a 的字样,表示宏已经开始录制。

之后的每一个移动操作或编辑操作都会在宏模式下被记录下来,在回放时被重复。 这也是需要录制宏的原因,在宏模式下的每一个移动或编辑操作都需要考虑到后续的 回放。

录制过程完成。按 q 键退出宏录制过程,可以看到状态栏中的 recording @a 字样 已经消失了。

我们可以用@a 命令回放这个宏

@@是一个不错的快捷方式,它可以回放最后一次运行的宏

6.2.1 宏的编辑

宏被保存在寄存器中,与复制和粘贴操作使用的寄存器基本没有区别。可以 用:reg 命令来查看每个寄存器中的内容。

我们可以在列表的中间位置看到"a,即保存宏的寄存器。查看这个寄存器内容的命 令为:echo @a

事实上,宏和寄存器没有区别,只不过,读者可以用 q 命令往寄存器中附加按键, 而@命令则重复执行寄存器中的按键序列。

既然宏也相当于一个寄存器,读者可以用 p 命令来将其粘贴出来。用:new 命令打 开一个新的缓冲区,并用"ap 命令将寄存器中的宏粘贴出来

用户就可以自由编辑宏的内容,而无须将整个过程重新操作一遍

编辑完成之后,可以将其复制回原来的寄存器,命令为"ay$。表示回到行首, "a 表示使用寄存器 a 来复制,而 y$表示复制整行,直到行尾为止。

总之,我们用"ap 将寄存器 a 中的内容粘贴出来,编辑完成后再用_"ay$将修改后 的内容重新放回这个寄存器。

6.3 用插件来实现代码重构

本章小结

本章主要介绍了:substitute 命令和宏,这是代码重构中会用到的两种强大的 工具。

首先,本章中介绍了:substitute 命令及其选项。同时还介绍了参数列表,它可 以用于在多个文件中执行一条命令。

:substitute 命令还支持正则表达式,这是一种比简单字符串匹配更强大的工具, 能够使用户获得更轻松的使用体验。

本章中介绍了正则表达式的基础知识和 Vim 的魔法 模式(解析含正则表达式字符串的 3 种不同方式)。

然后,本章中还介绍了宏,宏用于录制用户的键盘操作,读者可以在必要时回放一 个宏。宏和寄存器的编辑方式一样,还可以递归调用,实现任意次数的重复执行。

第七章 定制自己的Vim

主要内容

本章介绍 Vim 定制,以及提高 Vim 易用性的方法。每个人的需求是不同的,本章的 目标就是帮助读者拥有自己的风格

  • Vim 的配色和界面美化

  • 定制 Vim 的状态栏,以显示更多信息

  • 针对 gVim 的图形用户界面的配置

  • 定制工作流程时的健康习惯

  • 组织.vimrc 的方法学

7.1 Vim用户界面

Vim 用户界面是可扩展的,读者可以改变 Vim 的主题,修改某些界面元素的显示方 式,并且增强状态栏中的信息显示。此外,gVim 还有更多其他可定制选项。

7.1.1 配色

Vim 中可以使用多种配色,一部分是 Vim 自带的,一部分则由社区成员提供

读者可以通过在.vimrc 中设置 colorscheme 来设置配色,如下所示

:colorscheme elflord 

通过执行:colorscheme Ctrl + d 命令,可以看到当前安装的配色列表(这里 的 Ctrl + d 表示快捷键)

上面的例子中,使用的是:colorscheme PaperColor,它来自于 GitHub 仓库 NLKNguyen/papercolor-theme。

读者可以进一步定制配色,修改其背景色,即将 background 选项设置为 light 或 dark(必须在设置 colorscheme 之前完成)。

7.1.2 常见问题

有时候,读者会发现自己正在试用的配色并没有示例图片看起来那么漂亮,而且颜 色数目好像也不够多。

最可能的原因是终端模拟器错误地告诉 Vim 它只支持 8 种颜色,而现代终端模拟器 普遍支持 256 种颜色。为解决这个问题,读者需要正确地设置$TERM 环境变量。

这种情况极有可能发生在 Tmux 和 GNU Screen 中,因为它们错误地汇报了颜色数目。

为查看环境变量$TERM 当前的值,可运行如下命令。

$ echo $TERM 

如果使用 Tmux,则在.tmux.conf 中添加如下设置。

set -g default-terminal "xterm-256color"

如果使用 GNU Screen,则在.screenrc 中添加如下设置。

term "xterm-256color" 

如果上述方法还不奏效,则在.bashrc 中添加如下设置。

TERM=xterm-256color 

不过在.bashrc 中修改$TERM 绝不是什么好主意。读者可以自行研究擅自修改 $TERM 会有什么后果

7.2 状态栏

状态栏是屏幕底部用于显示信息的一个区域。通过下列简单的设置,可以使状态栏 更加符合用户要求。

" 总显示状态栏(默认情况下,有时会隐藏)
 set laststatus=2 
 
" 在状态栏中显示最后执行的命令
 set showcmd 

如果读者想深度定制状态栏,则可以使用插件。这里介绍两款插件(Powerline 和 Airline),其中 Powerline 是强大的“全家桶”,而 Airline 则更为轻量级。

7.2.1 Powerline

powerline 为 Vim 提供了增强版的状态栏,而且还有其他功能,比如扩展 Shell 命令 提示符或 Tmux 状态栏。此插件的 GitHub 仓库为 powerline/powerline

Powerline 提供的状态栏中包含了大量的信息,包括当前模式、Git 分支、 当前文件状态、文件类型、编码,以及光标所在的位置。这个状态栏是可定制的,需要 显示多少信息可由读者自行决定。

不过,这个插件的安装有些麻烦,因为它不仅仅是一个 Vim 插件。首先,需要通过 pip 安装 Python 包 powerline-status。

$ python3 -m pip install powerline-status 

然后,还需要确保$HOME/.local/bin 目录在系统 PATH 路径列表中,即 在.bashrc 中添加以下代码。

最后,将 laststatus 设置为 2(确保状态栏不会隐藏),并在~/.vimrc 中加载 Powerline。

" 总显示状态栏(默认情况下,有时会隐藏)
set laststatus=2 
" 加载 Powerline 
 python3 from powerline.vim import setup as powerline_setup 
 python3 powerline_setup() 
 python3 del powerline_setup 
7.2.2 Airline

如果读者不需要过多的功能,也不希望 Python 程序一直在后台运行,那么 Airline 是更好的选择

Airline 的 GitHub 仓库为 vim-airline/vim-airline,它不需要任何依赖项。

7.3 配置文件的同步

正常情况下,我们不会在十年后使用同一台计算机,每个人也有可能同时使用多台 计算机。因此,有必要在多个环境之间同步 Vim 的配置文件。

一般而言,文件的同步方式有很多,人们通常会将文件存储在一个 Git 仓库中[这 些配置文件常常称为点文件(dotfiles),这是因为 Linux 系统中的配置文件常常是主目录 下以句点开头的隐藏文件],并在主目录中用符号链接文件指向点文件目录中的相应文 件。读者只需要正常地执行 Git 操作(如 commit/push/pull),就可以使不同机器上的配 置文件保持同步。

比如,下面是一种常用的修改配置文件的流程(Linux 和 macOS 系统中的点文件存储 在$HOME/.dotfiles 中,Windows 系统中则存储在%USERPROFILE%_dotfiles 中)。

$ cd ~/.dotfiles 
$ git pull --rebase 
# 执行修改操作,比如编辑.vimrc 
$ git commit -am "某个重要更新" 
$ git push 

比如,~/dotfiles 中有一个配置文件的仓库,其中包含.vimrc 文件和.gVimrc 文件,还有一个.vim 目录。然后,读者可以手动创建符号链接,命令为 ln -s ~/dotfiles/.vimrc .vimrc。也可以用如下 Python 脚本实现。

import os 
dotfiles_dir = os.path.join(os.environ['HOME'], 'dotfiles') 
for filename in os.listdir(dotfiles_dir): 
 os.symlink( 
 os.path.join(dotfiles_dir, filename), 
 os.path.join(os.environ['HOME'], filename)) 

7.4 健康的Vim定制习惯

使用 Vim 的时间越长,就会发现配置文件的改动也越来越频繁。以至于.vimrc 文 件最后变成了也许用不到的别名、函数和插件,用户再也无法思考真正的需求。

因此,定期查看.vimrc,并清理掉多余的函数、插件以及快捷键绑定是一件有必 要的事。如果不清楚一个配置项的目的,最好将它删除

每个人的工作流程都是独一无二的,没有两个人能同时走出一条完全相同的 Vim 轨 迹。可以想办法填补读者的 Vim 使用风格的缺陷,并改善自己使用 Vim 的工作方式

第八章 卓尔不凡的Vimscript

Vimscript,这是 Vim 内置的一种强大的脚本语言

第九章 Neovim

Neovim 是 Vim 的一个分支,它的目标是使 Vim 的核心开发者更便捷地维护 Vim,同 时使插件更易于开发和集成。本章主要包括以下内容。

  • Neovim 的重要性

  • Neovim 如何安装和配置

  • Neovim 和 Vim 配置文件的同步

  • Neovim 独有的插件

9.1 为什么需要另外一种VIM

  • Vim 的代码库已经有多年的历史,维持向后兼容是非常困难的。

  • 编写某些 Vim 插件比较困难,特别是异步操作,这是 Vim 长期以来的痛点(从 Vim 8 开始,Vim 开始支持异步操作,但这已经是 Neovim 出现之后的事了)。

  • 事实上,不仅是异步操作,编写 Vim 插件也不太容易,而且要求开发者熟悉 Vimscript

  • 在现代操作系统中,如果没有一个编写好的.vimrc,那么 Vim 使用起来会比较 困难

Neovim 从如下几个方面来解决这些问题。

  • 大刀阔斧重构 Vim 的代码库,包括使用单一的编码风格和增加测试的覆盖率。

  • 放弃对旧系统的支持

  • 使用适应于现代系统的默认设置

  • 提供丰富的插件开发 API,支持与外部程序的通信,包括对 Python 和 Lua 脚本 的支持

Vim 被安装到了大量各式各样的系统中,因此向后兼容且适应各种特殊情况需要重 点考虑。而在新的分支上,Neovim 就没有这个负担,可以不断进行各种尝试和改进,而 且能够促使 Vim 变得更好。

Neovim 的重要性体现在它可以更容易地增加新功能,插件的开发也更方便,因此 有希望吸引更多的开发者,并吸收更多新的思考和想法,从而让这个编辑器有更好的 发展

9.2 Neovim的安装和配置

Neovim 及其安装方法可以在其 GitHub 仓库 neovim/neovim 中找到。读 者既可以下载二进制安装包,也可以通过包管理器安装。由于网上的安装 过程已经非常详尽,而且更新非常快,因此建议读者安装之前先浏览该仓 库中的 wiki/Installing-Neovim 文件。 对于 Debian 系列的 Linux 发行版,可以通过$ sudo apt-get install neovim 安装 Neovim,并用 Python3 -m pip install neovim 来实 现 Neovim 对 Python 3 的支持。

安装好 Neovim 之后,启动 Neovim 的命令为$ nvim。我们可以看到 Vim 界面,看起来与 Vim 并没有太大区别。

所有 Vim 命令都可以在 Neovim 中使用,Neovim 的配置文件格式也与 Vim 相同。不过,.vimrc 在 Neovim 中不会自动加载。

Neovim 的配置文件遵守 XDG 基本目录结构,即所有的配置文件都放在~/.config 目录中。Neovim 的配置文件被保存在~/.config/nvim 中。

 ~/.config/nvim/init.vim 对应于~/.vimrc。
 ~/.config/nvim/对应于~/.vim/。

读者可以直接将 Neovim 的配置文件链接到 Vim 的配置文件。

$ mkdir -p $HOME/.config 
$ ln -s $HOME/.vim $HOME/.config/nvim 
$ ln -s $HOME/.vimrc $HOME/.config/nvim/init.vim

做好上述软连接之后,Neovim 就可以加载原来的.vimrc 文件了

9.2.1 检查健康状态

Neovim 欢迎界面中,Neovim 提示用户运行:checkhealth 命令.Neovim 的健康检查会汇报当前 Neovim 设置中的所有错误,并给出修复的建议。读 者应该查看检查结果,并修复相关的错误。

读者可通过查看:help nvim-defaults 了解 Neovim 的默认选项。

9.2.2 合理的默认选项

Neovim 的默认选项与 Vim 有很大的不同。在现代的计算机世界里,文本编辑器的 默认值需要对用户比较友好。默认情况下 Vim 的.vimrc 文件并不包含任何默认设置, 而 Neovim 默认已经设置好语法高亮、合理的缩进设置、wildmenu、高亮显示搜索结果 和增量搜索(incsearch)等

如果读者想要同步 Vim 和 Neovim 的配置文件,则最好是在~/.vimrc 中加入如下 设置(然后再将其链接为~/.config/nvim/init.vim)。

if !has('nvim') 
 set nocompatible " 与 Vi 不兼容
 filetype plugin indent on " 对现在的插件是必须的
 syntax on " 语法高亮
 set autoindent " 沿用上一行缩进
 set autoread " 从磁盘自动重载文件
 set backspace=indent,eol,start " 现代编辑器的退格键行为
 set belloff=all " 禁用错误报警声
 set cscopeverbose " 详细输出 cscope 结果
 set complete-=i " 补全时,不要对当前被包含的文件进行扫描
 set display=lastline,msgsep " 显示更多消息文本
 set encoding=utf-8 " 设置默认编码
 set fillchars=vert:|,fold: " 分隔字符
 set formatoptions=tcqj " 更直观的自动格式化
 set fsync " 调用 fsync()实现更健壮的文件保存
 set history=10000 " 最大的历史记录数
 set hlsearch " 搜索结果高亮显示
 set incsearch " 搜索时边输入边搜索、并移动光标
 set langnoremap " 避免出现映射崩溃的情况
 set laststatus=2 " 总是显示状态栏
 set listchars=tab:>\ ,trail:-,nbsp:+ " :list 时一些特殊字符的显示
 set nrformats=bin,hex " 对<c-a>和<c-x>的支持
 set ruler " 在状态栏角落里显示当前行位置信息
 set sessionoptions-=options " 不同会话不共享选项
 set shortmess=F " 文件信息少显示一些
 set showcmd " 在状态栏中显示最后一条命令
 set sidescroll=1 " 更平滑的侧边滚动条
 set smarttab " 更智能的<Tab>键响应方式
 set tabpagemax=50 " -p 选项能够打开的最大数目的标签页
 set tags=./tags;,tags " 用于搜索标签的那些文件名
 set ttimeoutlen=50 " 按键序列中等待下一个的时间,单位为毫秒
 set ttyfast " 要求实现快速的终端连接
 set viminfo+=! " 为多个会话保存全局变量
 set wildmenu " 增强命令行补全功能
endif

9.3 Oni

Oni 是基于 Neovim 实现的跨平台图形用户界面编辑器, GitHub 仓库为 onivim/oni。该编辑器为 Neovim 增加了集成开发环境(IDE)的功能,包括一个内置 的浏览器、原生支持的自动补全和模糊搜索、一个命令菜单以及多种教程,还有明显提 升用户体验的其他功能,而且它还沿用了 Neovim 的配置文件和键盘绑定。

第十章 延伸阅读

本章作为总结,旨在为读者提供一些思考,主要包括以下内容

  • 健康的文本编辑习惯(来自于 Bram Moolenaar 的讲稿)

  • 在 Vim 之外使用基于模式的界面,包括集成开发环境(IDE)、浏览器,等等

  • 推荐几个 Vim 社区和一些参考资料

10.1 高效文本编辑的7个习惯

本节为 Bram Moolenaar 在 2000 年发表的文章及讲稿的摘要,介绍了高效编辑的 7 个习惯。

  1. 快速移动光标

  2. 避免重复输入

  3. 发现错误马上修改

  4. 学会同时处理多个文件

  5. 学会组合使用多种工具

  6. 用结构化思想去理解文本

  7. 坚持好的做法并养成习惯

因为开发者需要花大量时间阅读和编辑代码,所以 Bram 的 7 个习惯实际上可以进 一步总结为改进文本编辑能力的三步法。

1.发现低效

2.提高效率

3.形成习惯

1.发现低效:移动光标需要花费很多时间。

2.提高效率:通常,用户移动光标是为了找到某些已经存在的文本。读者可以通过 搜索文本来移动光标,或者进一步采用如下策略。

  • 用*来搜索光标下的单词

  • 用:set incsearch 实现输入即搜索

  • 用:set hlsearch 高亮显示每个匹配项

3.形成习惯:练习学到的技能,在.vimrc 设置 incsearch 和 hlsearch。需要 用/搜索光标附近的单词时,改用*