写点什么

一篇文章吸取 Vim 全部精华(上)

2019 年 10 月 18 日

一篇文章吸取Vim全部精华(上)

本文翻译自“History and effective use of Vim”,翻译已获得原作者 Joe Nelson 授权。


这篇文章研究了 Vim 的发展历史,并吸取了 Vim 用户手册的全部精华。希望文中内容可以帮你发现(或者再次想起)这个编辑器的核心功能,让你可以摆脱安装包中自带的配置文件 vimrc,更有创意地使用各种插件。



如果想以这篇文章的内容为基础进行进一步研究,我建议准备一份纸质版的用户手册,或者一本比较好的口袋书。我没能找到官方 Vim 用户手册的纸质版。随编辑器发布的$VIMRUNTIME/doc/usr_??.txt系列文件中有个打印出来读得更舒服的PDF版,最后我只好通过printme1.com把它打印了出来。如果只是想要个方便的命令列表,我推荐《vi and Vim Editors Pocket Reference》。


内容列表

  • 历史

  • 配置文件架构

  • 第三方插件

  • 备份与撤销

  • 包含与 path

  • 编辑与编译周期

  • 对比与补丁

  • Buffer I/O

  • 文件类型

  • 不要忘了鼠标

  • 各种编辑


历史

Vi 的诞生

Vi 命令及各种功能的发展要从 QED 编辑器说起,至今已经有 50 多年了。下面是一些关键的时间点:


  • 1966 年:伯克利分时系统中发布了 QED(“Quick EDitor”),即快速编辑器

  • 1969 年 7 月:人类第一次登上月球(仅供对比时间用)

  • 1969 年 8 月:在 AT&T,QED 发展成了 ed

  • 1976 年 2 月:在伦敦玛丽女王大学,ed 发展成了 em(“Editor for Mortals”,普通人的编辑器)

  • 1976 年:在加州大学伯克利分校,em 发展成了 ex(“EXtended”)

  • 1977 年 10 月:ex 有了可视化模式,即 vi


如果你去读读QEDex的用户手册,就会发现它们的许多相似之处。它们使用的语法相近,都以行为单位进行各种操作。


QED、ed 和 em 之类的编辑器都是为实体版终端设计的,基本上就是指连接了调制解调器的电子打字机。实体版终端会直接把系统输出打印在纸上。很明显,一旦打印就无法修改了,因此编辑的过程就是用许多用户命令去修改并手动打印一行行的文本。



实体版终端


在 1976 年出现了 ADM-3A 之类的可视终端。于是 ex 编辑器引入了“开放模式”,支持在可视终端上进行行内编辑。还引入了可视化模式,可以在屏幕上用移动焦点的方式进行编辑。可视化模式用 vi 命令激活,会在屏幕上保持文件的最新视图,屏幕的最下方就是 ex 的命令行。有趣的是,ADM-3A 的 h、j、k、l 键上面也标记了方向箭头,因此 vi 对方向键的选择也是与键盘上的按键布局匹配的。



要想知道更多关于 ex 和 vi 的故事,可以看看对 Bill Joy 的访谈。他谈到了他是如何创造 ex 和 vi 的,以及对它们还有哪些不满意之处。


最初的 vi 只是 ex 的另一个版本——二进制文件是相同的,用不同的命令运行起来,就会进入 ex 模式或者 vi 模式。从这段发展历史可以看出,ex 和 vi 是在使用的过程中不断演进的,它们需要的系统资源极少,操作所要占用的带宽也极少。它们在大多数系统上都可用,并在 POSIX 上得到了全面推广


从 vi 到 vim

作为 ed 的衍生物,ex 和 vi 编辑器的知识产权都属于 AT&T。要在 Unix 之外的平台上使用 vi,大家只能自己写克隆版。


下面是一些克隆版的列表:


  • nvi:1980 年为 4BSD 设计

  • calvin:1987 年为 DOS 设计

  • vile:1990 年为 DOS 设计

  • stevie:1987 年为 Atari ST 设计

  • elvis:1990 年为 Minix 和 386BSD 设计

  • vim:1991 年为 Amiga 计算机设计

  • viper:1995 年为 Emacs 设计

  • elwin:1995 年为 Windows 设计

  • lemmy:2002 年为 Windows 设计


我们只关注中间的 vim。Bram Moolenaar 想在 Amiga 计算机上使用 vi,因此他着手从 Atari 上把 Stevie 移植过来,并继续演进。他把这次移植行动称为“仿制 Vi”(Vi IMitation)。从自由软件杂志对 Bram 的访谈中可以获得更多第一手信息。


到了 1.22 版,Vim 的意义被重新解释成了“改进版 Vi”(Vi IMproved),功能与原版 Vi 相比甚至有所超越。下面是发布了重要功能的一些重大版本列表:


  • 1991 年 11 月 2 日,Vim 1.14 版:首发版

  • 1992 年,Vim 1.22 版:移植回 Unix。现在 Vim 已经可以与 Vi 相提并论了

  • 1994 年 8 月 12 日,Vim 3.0:支持多个缓冲区和窗口

  • 1996 年 5 月 29 日,Vim 4.0:图形用户界面(主要归功于 Robert Webb)

  • 1998 年 2 月 19 日,Vim 5.0:增加了语法着色和高亮显示

  • 2001 年 9 月 26 日,Vim 6.0:折叠、插件和垂直分割

  • 2006 年 5 月 8 日,Vim 7.0:引入了拼写检查、万能补全、分枝撤销、标签页编辑等功能

  • 2016 年 9 月 12 日,Vim 8.0:引入了任务、异步 I/O、原生包等


想了解更多有关各个版本的信息,可以直接查看:help vim8命令的执行结果。想了解未来的规划和已知缺陷等,可以查看:help todo.txt的输出。


竞争对手 NeoVim 的开发者希望可以直接在编辑器中运行其网页脚本语言的调试器和 REPL。在这个压力下,Vim 8.0 发布了异步任务支持功能。


Vim 的可移植性非常好,而且由于支持的平台越来越多,它的可移植性就保持得更好了。它可以运行的平台包括 OS/390、Amiga、BeOS 和 BeBox、Macintosh、Atari MiNT、MS-DOS、OS/2、QNX、RISC-OS、BSD、Linux、OS X、VMS 和 MS-Windows 等。不管你用的是哪种计算机,你都能找到可用的 Vim。


不管怎样演进,最初版的 ex/vi 源码都是在 2002 年基于 BSD 自由软件协议ex-vi.sourceforge.net发布的。


接下来我们回到正题。在讨论各种奇技淫巧之前,我们先了解一下 Vim 是如何组织并解析它的配置文件的。


配置文件结构

我以前总是错误地以为,Vim 只从~/.vimrc 这一个文件中读取所有的设置和脚本。随便查看一下各种其它配置文件,你会更加坚定这个想法。人们总喜欢发布一些非常大的.vimrc 配置文件,以控制编辑器的方方面面。这些大配置文件常被叫成“Vim 发行版”。


事实上 Vim 的配置是有简单结构的,.vimrc 文件只是多个输入之一。你也可以查看它到底加载了哪些脚本。你可以随便打开一个源文件,然后运行命令:


:scriptnames
复制代码


花点时间仔细读读输出的列表,猜猜每个脚本的功能,并留意一下它们的存放位置。


列表是不是比你想像中要长?如果你还安装了一些插件的话,编辑器要做的事就更多了。如果你想了解是什么原因造成编辑器启动变慢了,可以运行如下命令,并查看它创建的start.log文件:


vim --startuptime start.log name-of-your-file
复制代码


如果不使用你现有的配置,可以比较一下看 Vim 的启动能有多快:


vim --clean --startuptime clean.log name-of-your-file
复制代码


要确定在启动或加载缓冲区的时候该运行哪些脚本,Vim 会遍历“运行时路径”。这个路径是一个由逗号分割的目录列表,每个目录下面都可以包含一套配置结构。按在列表中出现的顺序,Vim 检查每个目录下面的结构,找到要运行的脚本。


运行以下命令可以得到你的系统里的运行时路径:


:set runtimepath
复制代码


在我的系统里,运行时路径的默认配置如下。并不是列表中的每个目录都必须存在,但如果存在,就一定会被检查到:


  • ~/.vim:根目录,存放个性化配置

  • /usr/local/share/vim/vimfiles:系统级的 Vim 目录,存放系统管理员的个性化配置

  • /usr/local/share/vim/vim81:即众所周知的 $VIMRUNTIME,存放随 Vim 发布出来的文件

  • /usr/local/share/vim/vimfiles/after:系统级 Vim 目录里面的 after 目录。这是让系统管理员对发行版默认配置进行覆盖和补充的

  • ~/.vim/after:根目录中的 after 目录。用于对发行版进行个性化的覆盖和补充


因为目录是按它们出现在列表中的顺序处理的,因此 after 目录的唯一不同之处仅仅在于它们出现在了列表的末尾。after 这个词并没有什么特殊功能。


处理各个目录时,Vim 也会查找有特定名字的子目录。要了解得多,请查看:help runtimepath。我们对下面这些简单介绍一下。


  • plugin/:编辑任何文件时都会自动加载的脚本,称为“全局插件”

  • autoload/:为了与 plugin 相区别,只有在其它脚本需要时,这个目录下的脚本里包含的功能才会被加载

  • ftdetect/:用于检测文件类型的脚本。可以根据文件扩展名、位置或文件内容的不同,而产生不同行为

  • ftplugin/:编辑已知类型的文件时要运行的脚本

  • compiler/:定义如何运行各种不同的编辑器、静态分析器等,以及如何解析它们的输出。在多个 ftplugin 之间也可以共享。而且它也不是自动应用的,需要通过:compiler调用

  • pack/:存放 Vim 8 原生程序包的地方,是 Pathogen 风格包管理的继任者。原生程包系统不需要依赖任何第三方代码


最后,~/.vimrc是所有通用编辑器设置的大杂烩。用它进行默认配置,配置项会进一步被具体文件类型的配置覆盖。要了解在.vimrc 中支持的所有配置项,请运行:options命令。


第三方插件

插件只是一些 Vim 脚本而已,要想能运行,就要把它们放到运行时路径里的正确位置。安装过程用一句话就可以概括:把文件放入正确目录。但难点在于删除或更新某些插件,因为脚本会在运行时路径的子目录里到处乱放文件,很难辨别哪个文件是属于哪个插件的。


于是各种“插件管理器”就出来救驾了。根据记载,从 2003 年起 Vim.org 就已经有了这种插件注册表了,但直到 2008 年才真正有插件管理器流行起来。


这类工具可以把各个插件的路径加到 Vim 的运行时路径中,并编译出插件文档的帮助标签。大多数插件管理器也可以从互联网上安装或更新插件代码,更新过程有时是并行的,或者有彩色进度条。


下面按时间顺序列出了各种插件管理器的信息。起止时间表示每种插件管理器的最早和最新发布时间。如果找不到正式的发布消息,也可能是最早和最新的代码提交时间。


  • 2006 年 3 月-2014 年 7 月:Vimball(一种分发格式,并与 Vim 命令相关联)

  • 2008 年 10 月-2015 年 12 月:Pathogen(出现原生 vim 包之后就废弃了)

  • 2009 年 8 月-2009 年 12 月:Vimana

  • 2009 年 12 月-2014 年 12 月:VAM

  • 2010 年 8 月-2010 年 11 月:Jolt

  • 2010 年 10 月-2012 年 11 月:tplugin

  • 2010 年 10 月-2014 年 2 月:Vundle(被 NeoBundle 敲了竹杠之后就没有继续了)

  • 2012 年 3 月-2018 年 3 月:vim-flavor

  • 2012 年 4 月-2016 年 3 月:NeoBundle(出现 dein 之后就废弃了)

  • 2013 年 1 月-2017 年 8 月:infect

  • 2013 年 2 月-2016 年 8 月:vimogen

  • 2013 年 10 月-2015 年 1 月:vim-unbundle

  • 2013 年 12 月-2015 年 7 月:Vizardry

  • 2014 年 2 月-2018 年 10 月:vim-plug

  • 2015 年 1 月-2015 年 10 月:enabler

  • 2015 年 8 月-2016 年 4 月:Vizardry 2

  • 2016 年 1 月-2018 年 6 月:dein.vim

  • 2016 年 9 月-今:native in Vim 8

  • 2017 年 2 月-2018 年 9 月:minpac

  • 2018 年 3 月-2018 年 3 月:autopac

  • 2017 年 2 月-2018 年 6 月:pack

  • 2017 年 3 月-2017 年 9 月:vim-pck

  • 2017 年 9 月-2017 年 9 月:vim8-pack

  • 2017 年 9 月-2019 年 5 月:volt

  • 2018 年 9 月-2019 年 2 月:vim-packager

  • 2019 年 2 月-2019 年 2 月:plugpac.vim


大家首先可以注意到的就是这些工具之间的千差万别,其次是每种工具平均活跃 4 年,然后就慢慢退出舞台。


使用 Vim 8 内置的插件管理功能是最稳定的,而且不需要任何第三方代码。下面我们来了解一下。


首先在你的运行时路径下的 pack 目录里,创建 opt 和 start 两个目录。


mkdir -p ~/.vim/pack/foobar/{opt,start}
复制代码


请注意占位符“foobar”,这个名字完全由你决定,它用来将里面的包分类。许多人直接把所有插件都归入一个没有描述的分类中,这样也是可以的。你喜欢什么名字,就用什么名字,我接下来会继续使用 foobar。理论上你也可以创建多个分类,比如~/.vim/pack/navigation 和~/.vim/pack/linting。注意 Vim 并不会对分类进行去重检查,如果有重复的,它只会直接再加载一次。


“start”目录中的包会被自动加载,而“opt”目录中的包只会在 Vim 中用:packadd命令请求之后才会加载。对于使用频率较低的包,放在 opt 里是个不错的选择,这样 Vim 就可以不运行不必要的脚本,从而运行得更快。请注意没有:packadd的逆操作,即无法取消对包的加载。


下面举个例子,我们把模糊查询插件“ctrlp”加到 opt 里。用下面的命令下载并解压最新版:


curl -L https://github.com/kien/ctrlp.vim/archive/1.79.tar.gz \  | tar zx -C ~/.vim/pack/foobar/opt
复制代码


这个命令会创建一个~/.vim/pack/foobar/opt/ctrlp.vim-1.79 目录,然后这个包就可用了。回到 Vim 中,为新的包创建一个帮助标签索引:


:helptags ~/.vim/pack/foobar/opt/ctrlp.vim-1.79/doc
复制代码


这条命令会在包的 doc 目录中创建一个名为“tags”的文件,这样在 Vim 的内部帮助系统中浏览时就可以看到相应的主题了。你也可以在包被加载后运行:helptags ALL命令,它会处理运行时目录中的所有文档。


当你想要使用这个包时,直接加载就可以了。另外请记住插件名是可以用 tab 键自动补齐的,所以不必打出完整的名字:


:packadd ctrlp.vim-1.79
复制代码


packadd 会在运行时路径中包含包的根目录,并加载插件和 ftdetect 脚本。加载完毕后,敲下 Ctrl+P 按键,就会弹出模糊查找匹配器了。


有些人会对~/.vim 目录做版本控制,并用 git 来管理各个包。我习惯于直接把 tar 包解压到自己的仓库中。如果你使用的包比较稳定,就不需要经常升级,而且脚本通常很小,不要把 git 的修改历史搞得太长。


备份与撤销

用户可以进行设置,防备四种类型的数据丢失:


  1. 两次保存之间,编辑过程中的崩溃:Vim 可以周期性地将未保存的修改写入一个交换文件,来防止这类数据丢失。

  2. 打开的两个 Vim 实例编辑了相同的文件,彼此之间的改动相互覆盖:交换文件也可以防止这类数据丢失。

  3. 在保存的过程中崩溃,即在目标文件已经清空而新的内容还没有完全写入时崩溃:Vim 用“写备份”的方式进行保护,即先写入一个新文件,在成功之后再与旧文件相切换,这要依赖“backupcopy”选项。

  4. 已经保存了新内容,但又想把旧内容找回来:Vim 会在写入之后保存旧文件的副本,以此来支持这项功能。


在查看更多重要设置之前,要不要先来点好玩的放松一下?下面是 GitHub 上对 vimrc 文件的一些留言:


  • 别创建什么交换文件了,用版本控制来管理吧。

  • 还搞什么备份啊,用版本控制不就得了。

  • 赶紧 TMD 用版本控制!

  • 我们生活在充满版本控制的世界里,所以别用交换文件和备份了。

  • 别写备份文件了,版本控制够用了。

  • 我到现在都没真的用过 Vim 的备份文件……用版本控制吧。

  • 反正大多数东西都用上版本控制了。

  • 禁用掉备份文件,你就用上版本控制系统了。:)

  • 版本控制驾到,Git 来拯救大家了!

  • 禁用交换文件和备份,一直使用版本控制,一直!

  • 关掉备份!什么东西我都用版本控制来管理。


在上面谈到的四种情况里,这些留言其实只与第四种有关(第三种勉强相关)。笔者本人一般也是禁用交换文件的,对前两种情况不设防。


我推荐使用下面的配置,来保证可以安全地进行编辑:


" Protect changes between writes. Default values of" updatecount (200 keystrokes) and updatetime" (4 seconds) are fineset swapfileset directory^=~/.vim/swap//
" protect against crash-during-writeset writebackup" but do not persist backup after successful writeset nobackup" use rename-and-write-new method whenever safeset backupcopy=auto" patch required to honor double slash at endif has("patch-8.1.0251") " consolidate the writebackups -- not a big " deal either way, since they usually get deleted set backupdir^=~/.vim/backup//end
" persist the undo tree for each fileset undofileset undodir^=~/.vim/undo//
复制代码


这些设置开启了写入过程中的备份,但在写入成功之后并不会保留备份文件,因为有版本控制。注意你要自己创建目录mkdir ~/.vim/{swap,undodir,backup},不然 Vim 就会去偏好列表里找下一个可用的目录了。你也可以考虑修改目录权限,让里面的内容对外不可见,因为交换文件和撤销历史里可能会包含着敏感信息。


有一点要注意的是,配置文件中的路径是以两个斜杠结尾的。这样就启用了一个功能,可以让交换和备份文件与其它目录中的同名文件相区别。比如/foo/bar的交换文件会被保存成~/.vim/swap/%foo%bar.swp(斜杠转义成了百分号)。Vim 有个缺陷,用双斜线来表示备份目录会有问题,这个缺陷直到最近的补丁才修复,而用上面的方法就可以避开这个缺陷。


Vim 也保留了每个文件的撤销历史,这样即使关闭了文件,当你再次打开时,仍然可以继续使用撤销功能。尽管听起来好像和交换文件的功能有些冗余,但实际上撤销历史是交换文件的补充,因为仅当文件被写入时,才会同时写入撤销历史。为什么要保持和文件写入相同的频率,而不是更频繁呢?原因在于发生崩溃时,撤销历史的状态和磁盘上文件的状态很可能不匹配,这不是 Vim 期望的。


说起撤销,Vim 实际上维护了完整的编辑历史。这意味着你可以修改一下,再撤销修改,再做一次别的修改,这三个状态都是可恢复的。通过:undolist命令可以看到修改的次数和重要性,但从这个列表中很难将树形结构用可视化的方式表达出来。你可以跳到这个列表中的某些具体修改的位置;也可以用:earlier:later命令带上 5m 这类表示时间的参数,来按时间单位跳转;或者用文件的保存次数跳转,比如 3f。不过我觉得,展示撤销历史树这个功能应该由undotree之类插件来完成。


打开灾难恢复设置可以让你获得心理上的安全感。以前我编辑了一阵之后,或者从电脑前走开时,总是会习惯性地保存一下。但现在做了这些设置之后,我可以一连编辑几个小时也不保存了,因为我知道交换文件的工作机制了。


最后要提醒的一点是,请留意这些灾难恢复文件,它们可能会堆满你的.vim 目录,慢慢地耗尽磁盘空间。而且在磁盘空间剩余不多又要保存大文件时,就有必要设置 nowritebackup,否则 Vim 有可能会把整个文件临时再拷贝一份。默认设置中的“backupskip”选项会禁止在系统 temp 目录下备份任何东西。


Vim 的“patchmode”是个与备份相关的功能。如果你的目录还没有做版本管理,就可以用用它。比如你想下载个源码包,修改一下形成一个补丁,再通过电子邮件发送出去,而不是通过 git 提交。那么就运行:set patchmod=.orig命令,这样在你修改任何文件之前,原文件都会被加上.orig 后缀备份起来。然后,你就可以在命令行上创建.orig 文件和新文件之间的补丁了。


请继续阅读“Vim 发展历史及高级用法(下)”来了解本文剩余内容。


原文链接


History and effective use of Vim


2019 年 10 月 18 日 22:103570

评论

发布
暂无评论
发现更多内容

淘宝网 Java 千亿级并发系统架构设计笔记(全彩版小册开源)

云流

Java 程序员 架构 并发编程 计算机

网络 IO 服务器模型 Reactor 与 Proactor

赖猫

Linux reactor

2021腾讯Android面试题精选,复习指南

欢喜学安卓

android 程序员 面试 移动开发

新工科师资培训 |深度推进校企合作 新工科产学研联盟华为技术

科技汇

优先考虑 nameof

喵叔

7月日更

马拉松还是骇客松 Hackathon?

escray

极客时间 学习笔记 朱赟的技术管理课 7月日更

个性化联邦学习算法框架发布,赋能AI药物研发

华为云开发者社区

联邦学习 药物研发 算法框架

模块三-学生管理系统详细架构设计

kk

架构训练营

Python OpenCV 图像处理之 图像运算和图像位运算知识补充

梦想橡皮擦

7月日更

架构训练营第 1 期 模块三作业

高远

Linux之free命令

入门小站

Linux

2021年最新大厂Android面试笔试题目,威力加强版

欢喜学安卓

结对编程,到底是双剑合璧还是脚趾抠地?

华为云开发者社区

编程 软件 敏捷 敏捷开发 结对编程

FIL云算力挖矿平台系统开发案例

橙子区块链l53o56oloo3

云算力挖矿系统开发详解 云算力模式系统开发源码 filecoin矿机哪家好? fil挖矿

Vue进阶(幺捌伍):应用 qs 插件实现参数格式化

No Silver Bullet

Vue 7月日更 qs

倒数1天

IT蜗壳-Tango

7月日更

在线诺基亚短信图片生成器工具

入门小站

工具

2021年中国DevOps现状调查报告发布!

华为云开发者社区

DevOps 敏捷 安全 华为云DevCloud 信通院

🏆「作者推荐!」【Java 技术之旅】彻底你明白什么是JIT编译器(Just In Time编译器)

李浩宇/Alex

Java 编译器 JIT compiler 即时编译器

「SQL数据分析系列」12. 事务

数据与智能

sql 事务

发现了一个电子书仓库,分享给大家,值得收藏!

C语言与CPP编程

Java c++ Python C语言 数据结构与算法

Building deep retrieval models

毛显新

自然语言处理 深度学习 tensorflow 推荐系统 keras

校友卡微信小程序开发总结

CC同学

Vue进阶(幺柒陆):CSS 预编译语言 Sass、Scss、Less 和 Stylus

No Silver Bullet

CSS less SASS scss 7月日更

不愧是阿里内部“SpringCloudAlibaba学习笔记”竟然在GitHub霸榜

云流

Java 程序员 架构 微服务 计算机

一句话木马该怎么实现?现在就带你了解

网络安全学海

Java 网络安全 信息安全 渗透测试 漏洞分析

Taking advantage of context features

毛显新

自然语言处理 tensorflow 推荐系统

如何制定音视频编解码学习路线

hanaper

图像识别 音视频开发 图形处理 语言 & 开发 音视频sdk

网络攻防学习笔记 Day90

穿过生命散发芬芳

网络攻防 7月日更

Confluence 7 如何修改启动内存

HoneyMoose

带你看清梦饷集团如何成为上海在线新经济四小龙

华为云开发者社区

MySQL 数据库 mongodb 电商 华为云数据库

低代码的认知误区与落地实践

低代码的认知误区与落地实践

一篇文章吸取Vim全部精华(上)-InfoQ