first commit

This commit is contained in:
张乾
2024-10-16 06:37:41 +08:00
parent 633f45ea20
commit 206fad82a2
3590 changed files with 680090 additions and 0 deletions

View File

@ -0,0 +1,76 @@
因收到Google相关通知网站将会择期关闭。相关通知内容
00 导读 池建强Vim 就是四个字“唯快不破”
你好,我是池建强。
操作系统、编程语言和编辑器是程序员永恒的讨论话题,技术发展了几十年,大家聊起这个来依然能争个面红耳赤。今天我就和你聊聊文本编辑器的那些事儿。
在文本编辑器领域Vim 和 Emacs 是永恒的焦点。使用 Emacs 和 Vim 的程序员,平时大家各用各的,各自沿着不同的道路和目标前进,但总会在某个场景下相遇,愣一下就互相扔石头和臭鸡蛋,砸得对方鼻青脸肿,然后擦擦眼泪和口水继续前行。你看,编程也是有宗教信仰的,其实是个危险的工种,当真不是瞎扯。
我工作了二十多年,写程序有十几年,用过各种编程工具,用错过,也用对过,虽然每种优秀的编辑器都有传奇的故事,每个程序员都有自己的脾气,但是,如果让我推荐一款编程工具,那一定是 Vim。
Vim 号称编辑器之神,唯快不破,可扩展,插件遍天下。学习曲线虽然陡峭,但是学成之后,基本上就成肌肉记忆了,写程序双手不离键盘,上下翻飞,可谓快意编程。
我和 Vim 怎么结缘的呢?那得从 2000 年说起。
缘起
当时正值第一波互联网浪潮,我刚毕业不久,一如现在的热血青年,投身到互联网的大熔炉中。我所在的公司叫洪恩教育,公司里聚集了很多清华北大的同学,技术牛人扎堆,大家清一色使用 Vim 在服务器端编程,语法高亮都不设,内部 BBS 也是水木清华那种,通过终端访问,非常极客。走进办公室一眼望去,满目皆是黑漆漆的屏,绿瓦瓦的字,每个人都在那里噼噼啪啪地敲击键盘,韵律十足,我想,这简直酷毙了。
我最初还在使用 Editplus 编程,隶属菜鸟帮。别人的开发、编译和发布环境都在服务器端,而我则需要在本地编写好程序,通过 Editplus 的 ftp 功能上传到服务器端,再进行调试、测试和发布,不仅麻烦,而且不够酷。
那时候不酷是不行的,我这种行为遭到了小伙伴的无情嘲讽,于是我把愤怒都发泄在键盘上,每天在满天星斗的夜色中学习 Vim 技法,在清晨的微光中编译 Linux 内核,上午敲打键盘输出 Perl 程序,中午吃完五又四分之一口米饭之后开始研习 Vim 的多窗口和标签……
那时候我住在公司,时间充裕到让你不好意思不学习,虽然 Vim 资料匮乏,但我周边都是牛人啊,随时随地请教,不断练习,很快小有所成,编码时鼠标锁进抽屉,双手敲击键盘上下翻飞,成就感十足。我对语法高亮情有独钟,经常把自己的界面配置得花花绿绿,没事看看也是一件乐事。
自此以后,我就与 Vim 结下不解之缘十几年过去了工作中一直没有离开过Vim断断续续一直在用。到了2009年我开始把工作环境完全切换到了 Mac 上,记得当时打开 Mac 的终端时,欣喜若狂地想,这不就是 Vim、Shell 和 IDE 的完美集成么?
场景
在不同的场景下应该采用最适合的工具这时就会有人问了Vim 适合什么场景呢?
简单说来Vim 比较适合 Unix/Linux 服务器端编程,如果你使用 Mac 电脑Vim 是直接集成在你的终端环境中的,用起来十分方便。我以前用 Vim 主要用来进行 Shell/Python/C 编程。在 Unix/Linux 服务器端编辑和修改文件也离不开Vim另外由于我个人工作环境是 Mac所以修改文本文件、Code Reiview、批量替换文件、比对文件等工作用 Vim 顺手就做了。
写 Java 程序、前端 HTML/CSS/JS、Objective-C 和 Swift最优方案依次是 IDEA、VS Code、XCode 等,这些优秀的工具可以帮助我们提升效率,减少错误,但是如果你还想更进一步,那么 Vim 绝对值得拥有。
历史
Vim 源于 vi但不是 vivi 作为计算机的文本编辑器历史极为悠远它是由美国计算机科学家比尔·乔伊编写并于1976年发布的同年苹果公司成立。比尔·乔伊是 Sun 公司的联合创始人和首席科学家,一位传奇的技术天才,我个人以为他最伟大的贡献是独立编写 BSD 操作系统,开发 vi 编辑器,创立 Sun 公司,当然,他还是 Java 语言的主要贡献者之一,任何人有幸完成其中一项工作已经足以名垂计算机发展史,而乔伊则通过一己之力完成了这些科技成果,推动了整个计算机科技的发展。
Vim 诞生得要晚一些它的第一个版本由布莱姆·米勒在1991年发布这个兄弟也是一位声名显赫的程序员80 年代买了一台 Amiga 电脑,打开电脑一看,米勒鼻子差点气歪了,居然没有他最常用的 vi 编辑器!对于米勒来说这是不可接受的。
愤怒的米勒决定自己开发一个文本编辑器,完全复制 vi 的功能并起名为Vi IMitation模拟。事实证明优秀的程序员都具备这种品质感到不爽了就会写出个什么东西要么完善一下要么创新一下要么是你写要么是我写于是很多伟大的软件程序就发明出来了。随着 Vim 的不断发展更多更好的功能被加了进来正式名称改成了Vi IMproved增强也就形成了现代的 Vim目前最新的稳定版本是 8.2Vim 的开发语言是 C 和 VimScript。
理念
Vim 是一款完全面向程序员的软件,我很少见到用 Vim 编辑文字的普通用户,如果你是,一定要告诉我。
写过程序的人都知道,编程的时候双手大部分时间都放在键盘上,或编码、或插入、或移动、或定位、或查找,这种连续操作的时间和频率远远大于阅读、翻页、设置字体、摆弄样式等文案工作,而二者往往产生很多停顿和间隙,而编程时的停顿是非常影响编程效率的,所以 Vim 的设计理念就是通过模式的转换、命令的组合和数以万计的插件,保证程序员在编程的过程中,双手尽可能保留在键盘中央的区域,并且,不需要鼠标。
想用好 Vim先要理解 Vim 的模式转换。Vim 常用的模式有四种:
普通模式Vim 启动后的默认模式,用来移动光标、删除文本、覆盖输入文本、恢复操作、粘贴文本等等。
插入模式:输入 i 或 a 进入插入模式,在这个模式下敲击键盘会往文字缓冲区增加文字,相当于普通编辑器的编辑模式。
可视模式:选择文本,可以行选、块选和依次选择,选择后可以进行复制、删除、排序等操作。
命令模式:执行内部和外部命令,通过“:”“/”“?”“:!”可以进入命令模式,分别对应的是:执行内部命令、向上或向下搜索、执行外部命令。
Vim 的模式和普通的编辑器有所不同,而且命令繁多千变万化,所以初期的学习曲线较高,一旦你坚持练习并且度过了最早的平台期,就会领略 Vim 的妙和全键盘的好。事实上 Vim 除了能够快速编辑文本文件之外,还能够通过简单的命令做更多的事情。
如何帮助你度过这个陡峭的学习曲线呢?这就是极客时间开设 Vim 专栏的初衷,吴咏炜老师是前英特尔亚太研发中心资深系统架构师,是《现代 C++ 实战 30 讲》的专栏作者,也是一位深度 Vim 用户。
他会带着你掌握即学即用的 Vim 高频命令了解Vim 高阶用法和技巧,利用 Vim 脚本和插件实现常用功能,以及定制自己专属的 Vim 环境。
一旦你跟着吴老师学完了专栏,真正掌握了 Vim 这个高效率的工具,你会进入一个全新的世界,以前繁琐的编程和文本处理会变得简单有趣,如果你有兴趣,完全可以根据专栏所学的内容,定制一个强大的个性化开发工具,提升自己编程和日常工作效率。
下面我就交棒给吴咏炜老师,让我们一起开始学习吧。

View File

@ -0,0 +1,119 @@
因收到Google相关通知网站将会择期关闭。相关通知内容
00 开篇词 我们为什么要学 Vim
你好,我是吴咏炜。
今年 2 月,我在极客时间的第一门课程《现代 C++ 实战 30 讲》结课了。现在我给你带来了一个全新的课程《Vim 实用技巧必知必会》。
这两门课虽然主题不同,但我准备它们的初衷却是完全一样的。我在 C++ 课程里分享过,我特别喜欢 Larry Wall拉里 · 沃尔所说的程序员的三大美德懒惰急切傲慢laziness, impatience, hubris。正是这些美德驱动我们不断地追求效率和极致而 Vim 就恰恰是这样一个效率利器。我想通过接下来的课程,和你一起享受 Vim 带来的技术乐趣。
Vim 的“前世今生”
说到 Vim就不能不谈一下 vi要说 vi那跟 Unix 就有着千丝万缕的联系。万物起源总有那么点故事,我们这个课程的主题也不例外。这个故事可以讲上很长时间,不过,今天不是故事会时间,我只会花几分钟给你快速梳理一下这段历史,带你了解 Vim 的“前世”,也就能更好地理解它的“今生”。
故事的开头是在 1975 年秋天Unix 诞生之后的第六年。Ken Thompson肯 · 汤普逊)来到了加利福尼亚大学伯克利分校,开始了为期一年的访问教授生活。当然,他也带上了最新版本的 Unix 的补丁。根据传说由于贝尔实验室律师们的阻挠他不能直接把补丁给其他人而只能把装有补丁的磁带“丢”在某个地方然后由别人“正好”捡到……Unix 就以这种“地下”的传播方式流传开了。
同年,年轻的 Bill Joy比尔 · 乔伊)也进入了加利福尼亚大学伯克利分校,学习电子工程和计算机科学。他立刻就迷上了 Unix。在后面几年的硕士生涯里他修正了 Unix 里的 Pascal 系统,使得 Pascal 成了学生编程的缺省选择。他在 1978 年负责发布了第一个伯克利发行版BSD即 Berkeley Software Distribution其中包含了他写的 ex一个编辑器 ed 的改进版本。随即,在 1979 年他发布了第二版的伯克利 Unix2BSD包含了他写的 vi 和 csh。他独立实现了 BSD 中的 TCP/IP 栈。
离开伯克利后,他成了 Sun 的联合创始人和首席科学家,在 Solaris 操作系统、NFS 网络文件系统、SPARC 处理器、Java 语言的开发等多个领域中作出了自己的贡献……
在 Bill Joy 的无数传奇故事里,有一个是,他只花了一个周末就写出了 vi。这当然……不是真的。vi 是演进的结果,前面还有 ed、em、en、ex对于两字母的 Unix 命令,我看得也是有点晕了🤔)等等一系列。只不过,那些都是基于命令的行编辑器,而不是全屏编辑器(部分原因是那时的很多系统仍然使用着电传打字机,而不是 CRT 终端。vi 可以充分使用整个终端屏幕的资源,易用性的提升是毋庸置疑的。
不管怎么说vi 只是一个 Bill 无意插柳柳成荫的结果,是他职业生涯中的一个副产品而已。在 1982 年初Bill Joy 加入 Sun 公司之后vi 就不怎么有人维护了。此外,由于没有得到 AT&T 授权的公司和个人也不能使用 vi 的源码(律师又一次发挥了威力),因此,大量的 vi 克隆版本纷纷出现。
目前大部分 Linux 发行版和 macOS 中的 vi 命令唤起的都是 Vim一个由 Bram Moolenaar布莱姆 · 穆勒纳尔)持续开发维护了三十多年的 vi 克隆(想想,三十年在计算机的发展中,那是经历了多少代技术的演进!)。在这些年里,其他的 vi 克隆诞生又死去,最后只剩下了 Vim好吧“只”是夸张手法。起初Vim 的意思是 Vi IMitation但很快就成了 Vi IMproved。而这就是我们这个课程的主题。
Vim 的优势
vi 有着一个非常老古董的设计,就是它是一个有“模式”的编辑器。其他大部分编辑器都相当于 vi 的插入模式,输入什么字符就会在屏幕上出现什么字符。但 vi 的行为不是如此。
事实上,这种不那么直观的设计,即使在 vi 初次出现的 20 世纪 70 年代,也被认为是违反人机交互的原则的。所有的后续 vi 实现,包括 Vim都继承了 vi 的模式设计。
令人惊讶的恐怕是尽管有这些问题Vim 在程序员群体中的流行程度并没有受到影响。根据 Stack Overflow 的开发者调查2015 年程序员中最流行的编辑器是 Notepad++Vim 的使用比例是 15.2%2019 年最流行的开发环境变成了 Visual Studio Code而 Vim 的使用比例还保持在了 25.4%。
这就是这门课程从头到尾都会试图回答的问题Vim 到底好在哪里?
拿我自己来说,我刚开始使用 Vim 时,不是出于选择,而是在 Linux 上开发的需要。不过,用着用着我就喜欢上 Vim 了——不仅在 Linux 下用,也在 Windows 下用(从十几年前开始,我就一直自己编译和维护着一个 Windows 下的最新 Vim 可执行文件)。自打切换到了 Mac 上之后当然就更不用说MacVim 是日常打开次数最多的工具。这里面最最主要的原因,就是使用 Vim 编辑文件非常高效。
在很大程度上vi 的“高效”是一种历史性的设计要求,当年程序员需要在网速 300 波特大致认为是今天网速的百万分之一吧的环境里编辑文本文件。那个时代人们还不可能拥有自己的计算机大学、政府、公司里的计算机全都通过终端来进行分时共用。因此vi 在命令上不得不非常“经济”。好玩的是,这种经济性,在今天仍然非常有用,它是 vi 及其克隆软件的高效之源。
我已经强调了几遍了,编辑的高效性,就是 Vim 最大的一个特点。除此之外Vim 的优势还有很多,我来给你分享一下我认为最重要的三点。
第一,与 vi 最初只运行在 Unix 平台上不同Vim 是一个完全跨平台的编辑器。
它支持的第一个操作系统是 AmigaOS然后被逐步移植到了大部分其他操作系统上既有我们常见的 Unix/Linux、Windows、macOS也有不常见或者过时的操作系统如 OS/2、BeOS、OpenVMS甚至在 iOS 和 Android 上也能找到 Vim 的移植版本。这可以算是 Vim 的一个重要优点了。这个课程里,我会介绍 Vim 在主流操作系统上的使用,包括 Linux、macOS 和 Windows。
第二Vim 也是一个高度可定制、可扩展的编辑器。
这对热爱折腾的程序员来说,绝对是一种乐趣,同时也是进一步提升效率的源泉。定制 Vim ,大部分情况下,你不需要什么特殊工具,使用 Vim 本身就可以。Vim 有自己的脚本语言,就叫 Vim 脚本Vim script语法相当简单任何一个程序员应该都可以轻松地学会。配置文件和功能扩展都使用 Vim 脚本,使用统一的语法。同时,需要更强大的扩展能力还可以使用 Python、Perl、Ruby、Tcl 等其他通用的脚本语言,或者直接调用外部命令。你可以很容易打造一个你自己专属的开发环境,也很容易把这个环境从一台机器转移到另外一台机器上。
第三作为一个发展了几十年的老牌开源软件Vim 也有着良好的生态环境。
网上可以找到大量的现成脚本和插件,能帮助你打造一个顺手的开发环境。总的来说,像语法检查、自动补全等程序员常用功能,全都可以在 Vim 里实现。你不需要离开 Vim就可以完成从写代码、编译到运行的大部分工作。下面的这张图里就展示了 Vim 的很多扩展一起工作的结果:
你可以看到,左边栏展示了 Vim 相对当前 Git 版本的修改状态(一处增,一处改),波浪线标出了代码中目前有错误的部分,底部显示了错误的原因,下面有个小窗口显示了光标所在处相对 Git 版本的变化状态栏里更是密密麻麻地显示了编辑器模式、Git 分支、文件名、修改状态等信息。这里面用到了好几个扩展,包括颜色主题也是一个扩展。
虽然 Vim 最初是个针对字符界面的应用程序,但它也能支持主流的图形界面,包括 Windows 的图形界面Linux 下的 GTK以及 macOS 下的 Cocoa 和 Carbon等等。作为一个并非“原教旨主义”的 Vim 用户,我个人是绝对赞成图形界面的使用的。因而我会推荐,只要有条件,就使用有图形界面的 Vim 版本。
不过这个课程的绝大部分内容是对图形界面和文本界面都有效的在两者有区别的地方我则会明确指出。换句话说在你只能使用基于字符界面进行远程连接时Vim 的功能仍然大部分有效只是界面的美观程度会受一定的影响而已。也由于这个原因Vim 在后端开发人员中特别受欢迎。
Vim 的模式是 Vim 的高效所在,但同时也是 Vim 学习上的一个难点。略有点搞笑的是Stack Overflow 上有一个目前票数达到 3840 的问题是“如何退出 Vim 编辑器”,按问题票数排名可以进入前 100这可能就是 Vim 的模式造成的困惑了。
反过来,这个反常规的设计使得 Vim 可以使用很逻辑的多个按键来处理文本,比如,在正常模式使用 daw 三个按键代表 delete a word 来删除光标下的一个完整单词,也可以输入 : 进入命令模式使用“make”这样的完整命令来进行项目的构建。
整体来说Vim 会给你一个高效、跨平台、高定制性、易于扩展的开发环境。全面掌握 Vim 需要花费一定的时间进行学习。但这个时间不会白费,因为 Vim 可以在任何地方使用,它会成为你编程道路上一件称手的兵刃,让你成为更加高效的开发者。
课程主要内容
比起很多“开箱即用”的编辑器Vim 是有一定的学习曲线的。虽然学 Vim 比学编程容易多了但对于非英语母语的人来说Vim 又会难上一点点。我会尽量多讲原理,而不是枯燥地讲解命令。不得不讲命令的时候,我会使用图片和动画,让你能对相关内容有一个直观的理解。对于很多 Vim 的命令,我们是需要形成“肌肉记忆”的;我们不需要死记硬背,但需要多看、多练,熟能生巧,在学习过程中自然而然就掌握了使用 Vim 的技巧。
作为一个有历史的编辑器Vim 一直保持着非常良好的向后兼容性。学 Vim 学到的东西不会过时,在你的程序员生涯中一直可以用下去。我个人的 Vim 配置文件始于约 20 年前慢慢地添砖加瓦一直用到了今天。同时Vim 也一直在发展,虽然不快,却也从来没有停下来(从发布 8.0 版本算起,平均每天 3.7 个补丁吧)。
在这个课程中,我会基于目前最新的 Vim 8.2 来讲解 Vim 的功能。你将会学到:
Vim 的安装
Vim 的模式和命令
Vim 的配置
Vim 的使用技巧
Vim 里的重要插件
及最最重要的
如何把 Vim 集成到你的工作流里,让它成为一件称手的工具,来进行高效的编辑
课程学习要求
我对这门课程的定位是零基础课,哪怕你以前没有用过 Vim ,也完全可以上手。这个课程不是 Vim 的百科全书,不会把 Vim 的所有命令选项,不管有用没用,全部都教给你。这是一个“新”教程,里面讲述的版本、很多技巧和插件是最近几年才有的,甚至是我在写专栏的时候才发现的。这也是一个面向实践者的教程,会让一个需要或想要使用 Vim 的开发者,从入门到精通,学会高效地使用 Vim 完成程序或其他文本文件的编辑。
虽然课程定位是零基础,但这并不意味着我对你没有任何要求。我仍然要求你在学习课程前:
熟悉你使用的平台上的包管理器yum、apt、brew 等;仅类 Unix 环境),知道如何完成程序的安装和卸载;
安装了 Git并对 Git 操作有基本的概念(不要求熟练掌握,因为我会给出大部分情况下需要的命令);
有一颗勇于探索的心,愿意花点力气把手里的“武器”打造得更为好用、称心。
使用 Vim 有不同的场景。在我设想的环境里,你是一个程序员,但我不对语言作出要求。课程的大部分内容完全是语言无关的,无论你使用什么编程语言开发,都应该可以获得有用的知识。
不过,如果你使用 C、C++ 或 Python 进行开发你可以得到一些额外的福利因为这些是我主要使用的语言。这三个语言的额外重要性在于Vim 的插件有可能会用到这三种语言。
如果你使用其他语言的话,就多多留言,在留言区介绍你自己使用 Vim 的情况,有啥问题和困惑一定要及时提出来,这样,我也可以更有针对性地解决你的问题;如果说得太晚了,我可能就帮不上忙了哦😁。
好了,闲话就说到这里。下一次,我们就进入正题,从 Vim 的基本安装和配置开始讲起。
我是吴咏炜,我们下一讲见。

View File

@ -0,0 +1,274 @@
因收到Google相关通知网站将会择期关闭。相关通知内容
01 各平台下的 Vim 安装方法:上路前准备好你的宝马
你好,我是吴咏炜。
今天第一讲,我们先来讨论一下 Vim 在 Linux、macOS、Windows 系统下的安装和配置问题。
Vim 在 Linux 和 macOS 上一般是默认安装的,在 Windows 上不是。不过 Vim 的网站上提供了 Windows 下的安装包,自己安装也很容易。所以,今天的课程我不会手把手、一步步地讲,而是挑选一些重点。对于默认安装的情况,主要讨论的是版本老旧或功能不全的问题。对于其他情况,我则会给出一个基本指引,减少你走弯路的可能性。
好了,下面我们就分各个不同的平台,一一来看。
Linux 下的安装
Red Hat 和 CentOS 系列
在 Red Hat Linux 和 CentOS Linux 上,默认安装的 Vim 可能是一个最小功能的版本。虽然这个版本启动速度很快但它缺少了很多对开发有用的功能如语法加亮、Python 集成和图形界面。一般情况下,应至少安装更全功能版本的 Vim如果能使用 X Window 的话,则应安装图形界面版本。
你可以通过下面的命令来查看已经安装的 Vim 版本:
yum list installed | grep vim
如果输出只有下面这样的内容的话,就说明安装的 Vim 版本只有基本功能:
vim-minimal.x86_64 2:8.0.1763-13.el8 @System
此时,我建议使用 sudo yum install vim-X11 来安装图形界面的 Vim或至少使用 sudo yum install vim-enhanced 来安装增强版本的 Vim如果你不在这台机器上进行图形界面登录的话
只要你使用图形界面,一般而言,你都应该安装有图形界面支持的 Vim。总体而言图形界面 Vim 的功能更丰富,并且即使你只在终端里使用 Vim含图形界面支持的 Vim 会带剪贴板支持,跟整个图形环境的交互也就比较容易。当然,如果你只是远程通过 SSH 使用 Vim 的话,那确实图形界面支持就没有意义了。
Debian 和 Ubuntu 系列
在 Debian、Ubuntu 等使用 apt 的 Linux 发行版上Vim 同样有着不同功能版本的区别,而且选择更多。我们可能会看到:
vim
vim-athena
vim-gnome
vim-gtk
vim-gtk3
vim-nox
vim-tiny
它们中有编译进最小功能的 Vim 包vim-tiny有较全功能的文本界面 Vim 包vim-nox有适用于老的 X-Window 界面的版本vim-athena有适用于 KDE 环境的 GTK2 版本vim-gtk等等。
如果你不确定要装什么版本的话,那可以遵循我下面的建议:
如果你使用标准的 GNOME 桌面环境的话(大部分的情况),安装 vim-gtk3 或 vim-gnome。
如果你使用 KDE 桌面的话,安装 vim-gtk。
如果你只使用文本界面的话,安装 vim-nox。
都不是?那你是个爱自己定制的家伙,也就不需要我告诉你该安装什么了。
你可以通过下面的命令来查看已经安装的 Vim 版本:
apt list --installed | grep vim
我们先执行 sudo apt update 来确保更新环境,然后使用 sudo apt install vim-gtk3 安装 GTK3 版本的 Vim或者其他你需要的版本。如果你安装了图形界面的版本不必单独再另外安装其他版本的 Vim因为图形版本的 Vim 也是可以纯文本启动的。事实上,在 Ubuntu 上vim 和 gvim 都是指向同一个应用程序的符号链接,且 gvim 的执行效果和 vim -g 相同。
手工编译
如果你用的 Linux 发行版较老的话,你可能会希望手工编译 Vim 来得到最新的版本。此时需要注意的是Vim 有很多的编译配置选项,有些缺省是不打开的。对于这个课程来讲,我们会希望至少加上 Python 支持和图形界面支持。
你首先需要确保自己已经安装了所需的开发包(可以参考这个网上的回答)。然后,我们可以使用下面的命令来配置 Vim 和编译根据需要“auto”也可以替换成“gtk3”等其他需要的数值
./configure --enable-pythoninterp \
--enable-python3interp \
--enable-gui=auto
make -j
sudo make install
如果上述步骤正常没有出错Vim就被成功安装到 /usr/local 下了。你可以用 which vim 来检查系统是否会自动优先选择你的 vim如果不是的话你可能需要调整 PATH 里的顺序,或者设置别名来优先启动 /usr/local/bin/vim。然后你可以使用 vim --version 命令来输出 vim 的版本信息。我们希望能在输出里看到:
Huge version with … GUI-
+python/dyn-
+python3/dyn
目前 Python 2 已经停止支持,所以你最好可以确保上面的输出中包含“+python3”很多 Vim 的插件已经开始要求 Python 3、不再支持 Python 2 了);没有“+python”即 Python 2则没什么关系有没有“dyn”关系也不大
好了,关于 Linux 环境下的Vim安装和配置要点我们就讲完了接下来继续看在 macOS 上如何安装。
macOS 下的安装
在 macOS 中一般已经内置了 vim并提供了除图形界面外的较完整功能集。如果你希望使用图形界面则需要自行安装 MacVim一个跟现代 macOS 融合较好的独立 Vim 版本。安装 MacVim 有两种常用方式:
使用 Homebrew。我推荐你使用这种方式这样的话以后升级也会比较容易。
使用 MacVim 的独立安装包。如果你之前没有在用 Homebrew 的话,或处于不方便使用 Homebrew 的网络环境中,这种方式也可以。
由于使用 Homebrew 已经足够简单,日后升级也非常方便,我个人觉得我们没必要自己去编译 MacVim。
使用 Homebrew 安装 MacVim
首先,如果你没有 Homebrew那你需要先安装 Homebrew。安装信息可以在 Homebrew 的主页上找到(这个网站是支持中文的)。
在安装了 Homebrew 之后,一般情况下,你需要修改你的 .bash_profile如果使用 Bash 的话)、.zprofile如果使用 Zsh 的话)或是相应的 shell 的配置文件,调整 PATH把 /usr/local/bin 放到 /usr/bin 前面。我个人在 .bash_profile 里是这样配置的:
if [[ $PATH != "$HOME/bin"* ]]; then
PATH=~/bin:/usr/local/bin:/usr/local/sbin:`echo $PATH|sed -e "s!:$HOME/bin!!" -e 's!:/usr/local/bin!!'`
fi
这样,可以确保个人的路径优先于 /usr/local而 /usr/local 下的路径又优先于系统的路径。
如果你这样配置的话,那只要执行 brew install macvim然后在等待安装完成之后你用 vim 启动的就是 MacVim 了。缺省用 vim 运行的仍然是纯文本界面的 Vim但跟 Linux 一样,你可以用 vim -g 或 gvim还有仅用在 Mac 上的 mvim来启动 Vim 的图形界面。
跟 Homebrew 里的其他软件一样,你以后要升级 MacVim 的话,只需要输入命令 brew upgrade macvim 即可。是不是很简单?这就是为什么我比较推荐这种安装方式,后期升级真的更容易。不过我下面还是会介绍下安装包的方式,以满足我们不同的应用需求。
使用安装包安装 MacVim
跟大部分的 Mac 软件一样,你也可以直接使用 DMG 安装包来安装 MacVim。目前可从以下网址下载 MacVim 的安装包:
https://github.com/macvim-dev/macvim/releases
等待下载完成后双击下载的文件然后会打开一个访达Finder窗口。你只需要把 MacVim 拖拽复制到应用程序文件夹即可。
在这种安装方式下,手工键入 vim、gvim 或 mvim 命令是无法启动 MacVim 的。你需要手工创建这些命令的符号链接symlink或别名alias才行。假设你的 MacVim 是直接安装在应用程序文件夹里的话,这些命令本身可以在 /Applications/MacVim.app/Contents/bin 文件夹里找到;使用下面的命令可以在你自己的 bin 目录下创建这些命令的符号链接:
[ -d ~/bin ] || mkdir ~/bin
ln -s /Applications/MacVim.app/Contents/bin/* ~/bin/
Windows 下的安装
最后,我们来看在 Windows 下怎么安装。课程开头我提到了Windows 上缺省是没有 Vim 的。我们可以从 Vim 的网站下载 Windows 下的安装包:
https://www.vim.org/download.php#pc
在 Linux 和 macOS 上64 位应用程序已经成为主流。而与此不同的是,在 64 位Windows上32 位应用程序仍然很常见。默认的 Vim 8 的安装包安装的仍然是一个 32 位的应用程序。不过32 位的 Vim 也足够满足一般需求了,除非你需要编辑 2 GB 以上的大文件。
安装界面会有一个选择组件的步骤,如下图所示:
这个界面中,下面几项我们可以关注一下:
“安装批处理文件”Create .bat files对于用 Vim 的开发者来说,通常命令行是刚需,所以我们一般需要勾上这项。
“创建图标”Create icons for Vim根据你自己的需要进行选择通常我会去掉展开子项里的“桌面图标”On the Desktop不在桌面上创建 Vim 的图标。
“创建默认配置文件”Create Default Config去掉这项——我们马上会创建配置文件。
“安装多语言支持”Native Language Support这项功能使得 Vim 的菜单可以显示中文的命令,但实际上还是有点鸡肋,因为 Vim 的主要功能不是靠菜单驱动的,安装程序安装的帮助文件也只有英文版。所以,这项选和不选关系不大,你可以自由选择。
然后我们点“下一步”Next不需要修改安装目标文件夹完成安装即可。
完成安装后Vim 会缺省打开一个 README 文件。在这个窗口中,我们应当键入“:e ~_vimrc”回车键然后把下面的内容粘贴进去这些配置项的意义我们以后会讨论
set enc=utf-8
set nocompatible
source $VIMRUNTIME/vimrc_example.vim
然后键入“ZZ”大写存盘退出即可。
注意由于历史上的文件系统限制,在 Windows 下 Vim 的配置文件名称是 _vimrc 而不是 .vimrc虽然 Windows 命令行不支持像 Unix 一样用“”代表用户的主目录,在 Vim 里我们仍然可以使用“\_vimrc”或“~/_vimrc”这样的写法。这是 Unix 和 Windows 下的 Vim 配置的区别之一。其他的主要区别是以下两点:
点打头的 Vim 文件都成了“_”打头如 .viminfo 也成了 _viminfo
点打头的 Vim 配置目录 .vim 在 Windows 下则成了 vimfiles
除此之外Vim 的配置在 Windows 下和 Unix 下(如 Linux 和 macOS并没有根本不同。Windows 上的主要麻烦在于,由于 Vim 的生态主要在 Unix 上,某些 Vim 的插件在 Windows 上安装配置需要花费更大的力气。但就一般的文本和程序编辑而言Vim 在 Windows 下和 Linux 下没有本质的不同。甚至 Windows 下还有一个小小的优势Unix 下虽然 Vim 可以编译成支持 Python 2 和 Python 3但在 Vim 里一旦执行了 Python 2 的代码,就不能再执行 Python 3 的代码了反之亦然。Windows 下则没有这个限制。
有没有注意到我只在 Windows 的安装部分讨论了配置?这是因为 Unix 下主流的缺省编码已经是 UTF-8 了,而 Vim 只能在内码是 UTF-8 的情况下才能处理多语言的文本。而我们有自己的配置文件,是为了确保启用一些最为基本的配置选项,来保证基本行为的一致性。
Windows 上可以把 Vim 配置成跟普通的编辑器行为差不多,包括支持 Ctrl-A 全选,选择内容后输入任何内容替换选择的内容,等等。但是,这种行为跟 Vim 的标准行为是冲突的。我们要学习 Vim还是忘了这些 Windows 特有的功能为好,去学习掌握 Vim 的跨平台标准功能。上面的配置文件也同样没有启用 Windows 下的特有行为。
Cygwin/MSYS2
Windows 有 Cygwin 和 MSYS2可以提供类似于 Linux 的 POSIX shell。在这些环境里Vim 都是标准组件,按这些环境的标准方式来安装 Vim 就行。如果你使用 Git Bash 的话,里面就直接包含了 MSYS2 的终端、Bash 和 Vim。唯一需要提一句的是这些类 POSIX 环境里面的 Vim 配置应当参照 Linux 终端来,而不是 Windows 下的标准方式(也就是说,个人配置目录和配置文件是 .vim 和 .vimrc而非 vimfiles 和 _vimrc。我以后对这种情况就不再单独描述了。
远程使用 Vim
还有一种常用的环境恐怕是使用 mintty、PuTTY、SecureCRT 之类的软件在 Windows 上远程连接到 Linux 机器上。在这种情况下,需要特别注意的,是远程终端软件的远程字符集(如 PuTTY 中的“Windows > Translation > Remote character set”应当设置成 UTF-8。这个设定跟具体的软件及其版本有关我就不详细说明了请自行查看你所使用的远程终端软件的设定和相关文档。
学习 Vim
上面我们讲解了 Vim 的安装。如果安装过程中遇到了什么问题,可以留言提问。接下来,我会给你提供一些 Vim 的学习资料,帮助你进入 Vim 的世界。你应该仔细看一下你所使用的平台上的 Vim 安装信息(其他平台的可以略过),并且应该自己打开 Vim 教程练习一遍(除非这些基础知识你都了解了)。键盘配置相关信息属于可选,可以根据自己的兴趣和需要决定是否了解一下。
中文帮助文件
Vim 内置了完整的英文帮助文件。如果你想要中文帮助文件的话,有个好消息是,有网友同步翻译了最新的帮助文件,而且安装过程在 Vim 8 (或将来的版本)里是非常简单的。以 Unix 下为例Windows 下类似,但路径 .vim 需要修改为 vimfiles
cd ~/.vim
mkdir -p pack/my/start
git clone https://github.com/yianwillis/vimcdoc.git pack/my/start/vimcdoc
如果你不需要以后利用 Git 来快速升级文档的话, 也可以在这个 Vim 中文文档计划的下载页面下载 tar 包,然后自行解压到 ~/.vim/pack/my/start 目录下(或 Windows 用户目录下的 vimfiles\pack\my\start 目录下)。
Windows 用户有一个简单的安装程序(当前为 vimcdoc-2.3.0-setup-unicode.exe可以自动帮你完成中文帮助文件的安装任务。如果你的机器上没有 git 和 tar 可执行程序的话,那这个方式最简单。
Vim 教程
Vim 在安装中自带了一个教程,可供快速入手使用。如果你对 Vim 的基本操作不熟的话,建议你完整学习一下,我也就不必多费笔墨介绍一些最基础的用法了。
Vim 教程支持多语言,可使用命令 vimtutor 来启动。如果启动的教程的语言不是你希望的,你可以使用环境变量 LANG 来设定希望的语言。比如,下面的命令可以在 Unix 环境中启动一个中文的 Vim 教程:
LANG=zh_CN.UTF-8 vimtutor
Windows 下你可以在开始菜单里找到 Vim tutor。但我测试下来它有一个问题。虽然我提交的解决方法已经作为补丁8.2.0412合并但目前Vim 8.2)安装程序安装的文件多半仍然是有问题的,你会无法成功地创建一个 tutor 文件的副本供编辑使用。我建议手工创建一个这个教程的副本。可以在命令提示符下输入:
vim --clean -c "e $VIMRUNTIME/tutor/tutor.zh_cn.utf-8" -c "w! TUTORCOPY" -c "q"
这样即可在当前目录下创建一个教程的副本。然后我们可以用 gvim TUTORCOPY 来打开这个副本进行学习。
键盘重配置
最后,有些重度的 Vim 用户会重新配置键盘把使用频度较低的大写锁定键Caps Lock重新映射成 Esc 或 Ctrl 键。对于这个问题,如果你需要的话,网上很容易就能找到攻略,如:
Linux下将大写锁定键(CapsLock)键映射为Ctrl键(Ubuntu, ManjaroCentOS)
mac book更改caps lock键为esc键/ctrl键
windows交换大写锁定键与ESC键注册表修改
在任何操作系统上,如何禁用或者重新分配 Caps Lock键
这当然是一件非常个人化的事情,而且有一个风险,你一旦跑到别人的机器上操作,你的“肌肉记忆”可能会让你常常按错键。鉴于你目前可能只是个 Vim 的初学者,现在不一定需要这么去做。等到你觉得按 Esc 太麻烦了,再想起这个可能性去修改键盘配置也来得及。
内容小结
今天我们讨论了 Vim 在常见平台上的安装过程。顺便说一句,以后在牵涉到环境问题时,我一般也会以上面提到的几种典型情况为例来进行讲解:
LinuxCentOS 和 Ubuntu
macOS
Windows
你可能看着多个平台的安装过程有点晕,这却是我的实际使用环境了——我就是在各个平台下都安装、配置、使用着 Vim 的,这也就是 Vim 的全平台、跨平台优势了。
当然必须得承认Vim 还是最适合类 Unix 环境,它的生态系统也是在类 Unix 环境下最好。鉴于在 Windows 下已经越来越容易接触到类 Unix 环境(像 Git Bash、Docker 和 Windows Subsystem for Linux服务器开发上 Linux 也已经成了主流,在 Windows 上熟悉 Vim 的完整环境对你也应该是件好事——尤其如果你是做服务器或嵌入式开发的话。
下一讲,我们就会进一步学习一下 Vim 的基本概念和配置。
课后练习
如果你之前不常使用 Vim ,请务必花点时间看一下 Vim 教程。在下一讲开始时,我将会假设你已经掌握了 Vim 教程里的基本用法。
当然,如果有任何问题的话,可以在讨论区留言和我进行交流。
我是吴咏炜,我们下一讲再见。

View File

@ -0,0 +1,329 @@
因收到Google相关通知网站将会择期关闭。相关通知内容
02 基本概念和基础命令:应对简单的编辑任务
你好,我是吴咏炜。
这一讲,我们会讨论 Vim 的基本概念和配置。先强调一下,请务必确保你在学习这一讲之前,已经通过 Vim 教程熟悉了 Vim 的基本用法。
Vim 教程的内容概要
上节课我给你留的作业,就是花时间学习一下 Vim 教程,下面我们就来检验一下。只有你自己先对照着教程操作了一遍,今天我再带着你过一遍里面的基本概念和配置,你才能查漏补缺,发现自己遇到的问题,明确自己需要多加练习的地方。
现在请查看下面的键盘图。简单说明一下这张图上展示了一个键盘。图中的“•”表示单个字母不是完整的命令必须再有进一步的输入。比如单个“g”没有意义而“gg”表示跳转到文件开头。对于命令后面明确跟一个动作的如“c”我们不使用“•”。一个键最多有三排内容最底下是直接按键的结果中间是按下 Shift 的结果(变大写),上面偏右的小字是按下 Ctrl 的结果。我们还用了一些特殊符号来表示操作的位置,如果你已经了解了这些命令的功能,你也自然就明白它们的意义了。
请检查一下有颜色的那些键,看看你是否有任何不熟悉的地方。如果看下来有让你感到陌生的内容,请复习 Vim 教程。
这张图里没有写出 Vim 的命令行命令。你现在应该已经掌握了以下这些:
“:q!”:退出 Vim
“:wq”存盘退出
“:s”执行替换
“:!”:执行外部命令
“:edit”一般缩写为 “:e”编辑文件
“:w”写文件
“:r”读文件
“:help”查看帮助
使用键 Ctrl-D 和 Tab 来进行命令行补全
同样,如果你发现上面列举的命令有你不熟悉的,也请重新打开 Vim 教程复习一下——这些属于 Vim 的最基本功能,一定要能熟练运用才行。
Vim 的模式
接下来我们进入本讲的正题,讲述 Vim 的四种主要模式、键描述的体例和 Vim 需要的基本配置选项。掌握了这些内容之后,我们就能应对基本的编辑任务了。下面我们一一来看。
Vim 最特别的地方就是它的模式了。与其他大部分编辑器不同,进入 Vim 后,缺省状态下键入的字符并不会插入到所编辑的文件之中。 Vim 的模式mode可以简单地理解为“状态”是它的麻烦所在但同时也是它的威力所在。
我们需要知道Vim 有以下四种主要模式:
正常normal模式也称为普通模式缺省的编辑模式如果不加特殊说明一般提到的命令都直接在正常模式下输入在任何其他模式中都可以通过键盘上的 Esc 键回到正常模式。
插入insert模式输入文本时使用比如在正常模式下键入 iinsert或 aappend即可进入插入模式。
可视visual模式用于选定文本块教程中已经提到可以用键 v小写来按字符选定Vim 里也提供其他不同的选定方法,包括按行和按列块。
命令行command-line模式用于执行较长、较复杂的命令在正常模式下键入冒号:)即可进入该模式;使用斜杠(/)和问号(?开始搜索也算作命令行模式。命令行模式下的命令要输入回车键Enter才算完成。
此外Vim 也有个选择select模式与普通的 Windows 编辑器行为较为接近,选择内容后再输入任何内容,将会替换选择的内容。在以可视模式和选择模式之一选定文本块之后,可以使用 Ctrl-G 切换到另一模式。这个模式主要是为了模拟 Windows 编辑器的行为,并不是 Vim 的主要用法,使用它反而会给 Vim 里的自动化带来麻烦,所以我们也就不多作介绍了。
关于 Vim 的模式我们重点掌握正常模式就可以了刚刚也说过Vim 里的大部分操作会在正常模式下完成。如果你做编辑工作时有超过几秒的停顿,就应当考虑按下 Esc 键,回到正常模式。记住,正常模式就是正常情况下你应当处于的模式。😄
Vim 的键描述体例
清楚了 Vim 模式之后,我们来对 Vim 里的按键作一下清晰的体例描述毕竟Vim 里的键真的有点多。
从现在开始,我会使用 Vim 里的标准键描述方式来讲解。根据 Vim 的一般习惯,我们使用尖括号来描述特殊的输入序列。下面我会提供一个列表,给出常用键的表示方式及在动图中的显示方式。这部分内容不需要记住,你用的时候作为参考就行。
<Esc> 表示 Esc 键;显示为“⎋”
<CR> 表示回车键;显示为“↩”
<Space> 表示空格键;显示为“␣”
<Tab> 表示 Tab 键;显示为“⇥”
<BS> 表示退格键;显示为“⌫”
<Del> 表示删除键;显示为“⌦”
<lt> 表示 < 显示为“<”
<Up> 表示光标上移键;显示为“⇡”
<Down> 表示光标下移键;显示为“⇣”
<Left> 表示光标左移键;显示为“⇠”
<Right> 表示光标右移键;显示为“⇢”
<PageUp> 表示 Page Up 键;显示为“⇞”
<PageDown> 表示 Page Down 键;显示为“⇟”
<Home> 表示 Home 键;显示为“↖”
<End> 表示 End 键;显示为“↘”
<F1> - <F12> 表示功能键 1 到 12显示为“F1”到“F12”
<S-> Shift 组合键;显示为“⇧”(较少使用,因为我们需要写 ! 而不是 <S-1>;和特殊键组合时仍然有用)
<C-> Control 组合键;显示为“⌃”
<M-> Alt 组合键;显示为“⌥”(对于大部分用户,它的原始键名 Meta 应该只具有历史意义)
<D-> Command 组合键显示为“⌘”Mac 键盘)
现在回到前面的模式部分,我们提到的 Esc、Enter、v、V 和 Ctrl-V按我们现在的描述惯例以后就会写成 <Esc><CR>、v、V 和 <C-V>。这也是以后在 Vim 里对键进行重映射的写法——如果你还不了解重映射是什么也没关系,我们很快就会讨论到。
这里我要强调一下,对“<”的特殊解释仅在描述输入时生效。在描述命令行和代码时,我们写“<CR>”仍表示四个字符,而非回车键。特别是,如果我们描述的命令行首是“:”,表示这是一个输入 : 开始的 Vim 命令行模式命令(以回车键结束);如果行首是“/”或“?”,表示这是一个输入 / 或 ? 开始的搜索命令(以回车键结束);如果行首是“$”,表示这是一个在 shell 命令行上输入的命令(以回车键结束),“$”(和后面的空格)不是命令的一部分,通常后续行也不是命令的一部分,除非行尾有“\”或“^”字符,或行首有“$”字符。
也就是说,下面的命令是在 Vim 里输入“:set ft?<CR>”(用来显示当前编辑文件的文件类型):
:set ft?
下面的命令则是在 shell 里输入“which vim<CR>”(用来检查 vim 命令的位置):
$ which vim
/usr/bin/vim
此外,当我用“:help”描述帮助命令时你不仅可以在 Vim 里输入这个命令来得到帮助,也可以点击这个帮助的链接,直接在线查看相应的中文帮助页面。
这节内容不需要死记。建议使用“收藏”功能,这样,你可以在以后碰到不认识的符号标记的时候,返回来查看这一节的内容。
Vim 的选项和配置
了解了 Vim 模式和键描述,我们对 Vim 的认识又多了一些,第一步的学习成就达成。要想更好地使用 Vim下一个关键点就是配置了接下来我就带你看看 Vim 配置都有哪些需要注意的点。
作为一个可以越用越顺手的应用程序Vim 是需要配置的。我们才刚开始学习,所以目前我们的配置文件是相当简单的,但随着课程的进展和你使用 Vim 越来越多,你的 Vim 配置文件必然会越变越复杂。我们今天就先来做一些初步的讨论,看看能实际使用的一个最基本 Vim 配置文件是什么样子。
我们上节课已经讨论过,根据 Unix 下的惯例Vim 的配置文件放在用户的主目录下,文件名通常是 .vimrc而它在 Windows 下名字是 _vimrc。我们前面给出最基本的配置文件是这个样子的
set enc=utf-8
set nocompatible
source $VIMRUNTIME/vimrc_example.vim
如果你熟悉 shell 语法,你肯定能看到不少熟悉的影子在里面。这三行完成了下列功能:
设置编辑文件的内码是 UTF-8非所有平台缺省但为编辑多语言文件所必需
设置 Vim 不需要和 vi 兼容(仅为万一起见,目前大部分情况下这是缺省情况)
导入 Vim 的示例配置(这会打开一些有用的选项,如语法加亮、搜索加亮、命令历史、记住上次的文件位置,等等)
对于现代 Unix 系统上的 Vim 8实际上只需要最后一句就足够了。对于现代 Windows 系统上的 Vim 8中间的这句 set nocompatible 也可以删除。如果你在较老的 Vim 版本上进行配置,那么把这三行全放进去会比较安全。
接下来,我会讲一些基本的配置项,保证你的日常工作流顺畅。它们是:备份和跨会话撤销、鼠标支持、中文支持及图形界面的字体支持。除了字体支持主要牵涉到美观性,其他三项都是对编辑至关重要的基本功能。我们一一来看。
备份和撤销文件
上面的基本设置会产生一个有人喜欢、但也有很多人感到困惑的结果:你修改文件时会出现结尾为“~”的文件,有文件名后面直接加“~”的,还有前面加“.”后面加“.un~”的。这是因为在示例配置里Vim 自动设置了下面两个选项:
set backup
set undofile
前一个选项使得我们每次编辑会保留上一次的备份文件,后一个选项使得 Vim 在重新打开一个文件时仍然能够撤销之前的编辑undo这就会产生一个保留编辑历史的“撤销文件”undofile了。
我的通常做法是,不产生备份文件,但保留跨会话撤销编辑的能力;因为有了撤销文件,备份其实也就没有必要了。同时,把撤销文件放在用户个人的特定目录下,既保证了安全,又免去了其他目录下出现不必要文件的麻烦。
要达到这个目的,我在 Linux/macOS 下会这么写:
set nobackup
set undodir=~/.vim/undodir
在 Windows 下这么写:
set nobackup
set undodir=~\vimfiles\undodir
无论哪种环境,你都需要创建这个目录。我们可以用下面的命令来让 Vim 在启动时自动创建这个目录:
if !isdirectory(&undodir)
call mkdir(&undodir, 'p', 0700)
endif
如果我告诉你, &undodir 代表 undodir 这个选项的值,那么其他代码的基本作用,相信你也一定能看出来了吧?我们暂时就不做进一步分析了。如果你好奇的话,可以提前看一下下面各项的 Vim 帮助文档:
:help isdirectory()
:help mkdir()
:help :call
这个跨会话撤销的能力我还真不知道其他哪个编辑器也有。更妙的是Vim 还有撤销树的概念,可以帮助你回到任一历史状态。这个我们以后会和相关的插件一起讨论。
鼠标支持
我不知道你会不会像某些资深 Vim 用户一样只用键盘不用鼠标。我反正是做不到的也没有动力去那样做——毕竟浪费计算机界一项伟大的发明并不那么有必要😂。手一直在键盘上的本位排home row打字当然会更快但一个程序员看代码的时间比写代码的时间要多得多而在非线性的跳转任务上鼠标比键盘更加快也更加有效。
在 Vim 的终端使用场景下,鼠标的选择有一定的歧义:你希望是使用 Vim 的可视模式选择内容,并且只能在 Vim 里使用呢,还是产生 Vim 外的操作系统的文本选择,用于跟其他应用程序的交互呢?这是一个基本的使用问题,两种情况都可能发生,都需要照顾。
如果你使用 xterm 兼容终端的话,通常的建议是:
在不按下修饰键时,鼠标选择产生 Vim 内部的可视选择。
在按下 Shift 时,鼠标选择产生操作系统的文本选择。
对于不兼容 xterm、不支持对 Shift 键做这样特殊处理的终端,我们一般会采用一种“绕过”方式,让 Vim 在某种情况下暂时不接管鼠标事件。通常的选择是在命令行模式下不使用鼠标。下面,我们就分这两种情况来配置。
虽然最新的 Vim 缺省配置文件(示例配置文件会包含缺省配置),在大部分情况下已经可以自动设置合适的鼠标选项了,不过为照顾我们课程的三种不同平台,我们还是手工设置一下:
if has('mouse')
if has('gui_running') || (&term =~ 'xterm' && !has('mac'))
set mouse=a
else
set mouse=nvi
endif
endif
上面代码说的是,如果 Vim 有鼠标支持的话,那在以下任一条件满足时:
图形界面正在运行
终端是 xterm 兼容,并且不是 MacMac 上的终端声称自己是 xterm但行为并不完全相同
我们将启用完全的鼠标支持mouse=a。特别是此时鼠标拖拽就会在 Vim 里使用可视模式选择内容(只能在 Vim 里使用)。而当用户按下 Shift 键时,窗口系统接管鼠标事件,用户可以使用鼠标复制 Vim 窗口里的内容供其他应用程序使用。
否则(非图形界面的的终端,且终端类型不是 xterm就只在正常模式n、可视模式v、插入模式i中使用鼠标。这意味着当用户按下 : 键进入命令行模式时Vim 将不对鼠标进行响应,这时,用户就可以使用鼠标复制 Vim 窗口里的内容到其他应用程序里去了。
非 xterm 的鼠标支持在 macOS 和 Windows 下都有效。但在 Windows 下需要注意的一点是,如果使用非图形界面的 Vim 的话应当在命令提示符Command Prompt的属性里关闭“快速编辑模式”QuickEdit Mode否则 Vim 在运行时将无法对鼠标事件进行响应。
鉴于命令提示符的行为有很多怪异和不一致之处,强烈建议你在 Windows 下,要么使用图形界面的 Vim要么使用 Cygwin/MSYS2 里、运行在 mintty 下的 Vim。
中文支持
接下来我和你讲讲中文支持的问题。如果你一直在 UTF-8 下使用中文的话,那这一小节的内容可以跳过。对于大部分在 Unix 下工作的人员,应该是这样的情况。而如果你在 Windows 上工作,或者有需要跟别人交换 GB2312、GBK、GB18030 编码的文本文件,那这部分的内容还是需要看一下的。
完整的 Unicode 历史和原理可以讲上整整一讲,但从实用的角度,我们就简化成下面几条吧:
整个世界基本上在向 UTF-8 编码靠拢。
微软由于历史原因,内部使用 UTF-16UTF-16 可以跟 UTF-8 无损转换。
GB2312、GBK、GB18030 是一系列向后兼容的中文标准编码方式GB2312 编码的文件是合法的 GBK 文件GBK 编码的文件是合法的 GB18030 文件。但除了 GB18030都不能做到跟 UTF-8 无损转换;目前非 UTF-8 的简体中文文本基本上都用 GBK/GB18030 编码(繁体中文文本则以 Big5 居多)。鉴于 GB18030 是国家标准,其他两种编码也和 GB18030 兼容,我们就重点讲如何在 Vim 中支持 GB18030 了。
举一个具体的例子,“你好😄”这个字符串,在 UTF-8 编码下是下面 10 个字节(我按字符进行了分组):
e4bda0 e5a5bd f09f9884
如果使用 GB18030 编码GB2312/GBK 不能支持表情字符)的话,会编码成 8 个字节:
c4e3 bac3 9439fd30
这么看起来GB18030 处理中文在存储效率上是优势的。但它也有缺点:
GBK 外的 Unicode 字符一般需要四字节编码(非中文情况会劣化)
GBK 外的 Unicode 字符跟 Unicode 码点需要查表才能转换UTF-8 则可以用非常简单的条件判断、移位、与、或操作来转换)
一旦出现文件中有单字节发生损毁,后续的所有中文字符都可能发生紊乱(而 UTF-8 可以在一个字符之后恢复)
因此GB18030 在国际化的软件中不会作为内码来使用,只会是读取/写入文件时使用的转换编码。我们要让 Vim 支持 GB18030 也同样是如此。由于 UTF-8 编码是有明显规律的,并非任意文件都能成功地当成 UTF-8 来解码,我们一般使用的解码顺序是:
首先,检查文件是不是有 Unicode 的 BOM字节顺序标记字符有的话按照 BOM 字符来转换文件内容。
其次,检查文件能不能当作 UTF-8 来解码;如果可以,就当作 UTF-8 来解释。
否则,尝试用 GB18030 来解码;如果能成功,就当作 GB18030 来转换文件内容。
最后,如果上面的解码都不成功,就按 Latin1 字符集来解码;由于这是单字节的编码,转换必定成功。
事实上Vim 缺省差不多就是按这样的顺序,但第三步使用何种编码跟系统配置有关。如果你明确需要处理中文,那在配置文件里最好明确写下下面的选项设定:
set fileencodings=ucs-bom,utf-8,gb18030,latin1
图形界面的字体配置
图形界面的 Vim 可以自行配置使用的字体,但在大部分环境里,这只是起到美化作用,而非必需项。不过,对于高分辨率屏幕的 Windows这是一个必需项Vim 在 Windows 下缺省使用的不是 TrueType 字体,不进行配置的话,字体会小得没法看。
在 Windows 的缺省字体里一般而言Consolas 和 Courier New 还比较合适。以 Courier New 为例,在 _vimrc 里可以这样配置Windows 上的基本写法是字体名称加冒号、“h”加字号用“_”取代空格,否则空格需要用“\”转义):
if has('gui_running')
set guifont=Courier_New:h10
endif
字体名称如何写是件平台相关的事(可参见帮助文档“:help gui-font”。如果你不确定怎么写出你需要的字体配置或者你怎么写都写不对的话可以先使用图形界面的菜单来选择通常是“编辑 > 选择字体”;在 MacVim 里是“Edit > Font > Show Fonts”然后使用命令“:set guifont?”来查看。
注意Vim 在设置选项时,空格需要用“\”进行转义。比如,如果我们要在 Ubuntu 下把字体设成 10 磅的 DejaVu Sans Mono就需要写
" Linux 和 Windows 不同,不能用 '_' 取代空格
set guifont=DejaVu\ Sans\ Mono\ 10
此外,宽字符字体(对我们来讲,就是中文字体了)是可以单独配置的。这可能就更是一件仁者见仁、智者见智的事了。对于纯中文的操作系统,这一般反而是不需要配置的;但如果你的语言设定里,中文不是第一选择的话,就有可能在显示中文时出现操作系统误用日文字体的情况。这时你会想要手工选择一个中文字体,比如在 Ubuntu 下,可以用:
set guifontwide=Noto\ Sans\ Mono\ CJK\ SC\ 11
注意,在不同的中英文字体搭配时,并不需要字号相同。事实上,在 Windows 和 Linux 上我通常都是使用不同字号的中英文字体的。
在上面的动图中,你可以观察到设了中文字体之后,不仅中文字变大,更美观了,“将”、“适”、“关”、“复”、“启”等字的字形也同时发生了变化。
由于字体在各平台上差异较大,字体配置我就不写到 Vim 的参考配置中去了,只把如何选择和配置的方法写出来供你参考。
内容小结
好了,这一讲我就讲到这里,我们来做个内容小结。
今天我给出了一张键盘图,带你复习了 Vim 教程的内容,这里我要再强调一遍,这部分的内容如果你还有不熟悉的,一定要再去学习一下 Vim 教程,这段时间我们一定要多花点时间和精力来练习,把这一步跨过去。
掌握了 Vim 教程里的基础信息还远远不够,我们还得了解 Vim 的四种主要模式,你只要记住最重要的就是正常模式就可以了。
最后我带你学习了 Vim 的几个基本配置选项,包括对撤销、鼠标、中文和字体的支持,来满足最基本的编辑需要。最终的 Vim 配置文件可以在 GitHub 上找到:
https://github.com/adah1972/geek_time_vim
关于这个配置文件我这里做个备注说明master分支可以用在类 Unix 平台上windows 分支则用在 Windows 上。适用于今天这一讲的内容标签是 l2-unix 和 l2-windows你可以用 git checkout l2-unix 或 git checkout l2-windows 来得到相应平台对应本讲的配置文件。
好了,掌握了今天的内容,你就可以用 Vim 做最基本的编辑了。
课后练习
请使用本讲的配置文件,并尝试以下操作:
退出 Vim 然后重新打开文件,仍然可以撤销上次的编辑。
使用终端的 Vim在终端里用鼠标复制 Vim 里的文本到另外一个文本编辑器中(仅 Unix 下,可选)。
在 Vim 中使用“:help”命令大部分环境下也可以使用 <F1> 功能键),尝试查看命令说明,以及使用键盘和鼠标在帮助主题中跳转。
如果遇到任何问题,欢迎留言和我讨论。我们下一讲见。

View File

@ -0,0 +1,180 @@
因收到Google相关通知网站将会择期关闭。相关通知内容
03 更多常用命令:应对稍复杂的编辑任务
你好,我是吴咏炜。
上一讲我们通过 Vim 教程学习了 Vim 的基本命令,我还给你讲解了 Vim 的基本配置,现在你就已经可以上手基本的编辑工作了。
今天,我们将学习更多 Vim 的常用命令,以便更高效地进行编辑。我会先带你过一下光标移动命令和文本修改命令,然后重点讲解文本对象,随后快速讨论一下不能搭配文本修改的光标移动命令,最后讨论如何重复命令。
光标移动
我们先来讨论一下可以跟文本修改、复制搭配的光标移动命令。
通过前面的课程你已经知道Vim 里的基本光标移动是通过 h、j、k、l 四个键实现的。之所以使用这四个键,是有历史原因的。你看一下 Bill Joy 开发 vi 时使用的键盘就明白了:这个键盘上没有独立的光标键,而四个光标符号直接标注在 H、J、K、L 四个字母按键上。
当然除了历史原因外这四个键一直使用至今还是有其合理性的。它们都处于打字机的本位排home row这样打字的时候手指基本不用移动就可以敲击到。因此即使到了键盘上全都有了光标移动键的今天很多 Vim 的用户仍然会使用这四个键来移动光标。
不过,标准的光标移动键可以在任何模式下使用,而这四个键并不能在插入模式下使用,因此,它们并不构成完全的替代关系。
顺便提一句,你有没有注意到 ADM-3A 键盘上的 Esc 键在今天 Tab 的位置?在 Bill Joy 决定使用 Esc 来退出插入模式的时候Esc 在键盘上的位置还没像今天那样跑到遥远的左上角去……
Vim 跳转到行首的命令是 0跳转到行尾的命令是 $,这两个命令似乎没什么特别的原因,一般用 <Home><End> 也没什么不方便的,虽然技术上它们有一点点小区别。如果你感兴趣、想进一步了解的话,可以参考帮助 :help <Home>。此外,我们也有 ^,用来跳转到行首的第一个非空白字符。
对于一次移动超过一个字符的情况Vim 支持使用 b/w 和 B/W来进行以单词为单位的跳转。它们的意思分别是 words Backward 和 Words forward用来向后或向前跳转一个单词。小写和大写命令的区别在于小写的跟编程语言里的标识符的规则相似认为一个单词是由字母、数字、下划线组成的不严格的说法而大写的命令则认为非空格字符都是单词。
根据单个字符来进行选择也很常见。比如,现在光标在 if (frame->fr_child != NULL) 第五个字符上,如果我们想要修改括号里的所有内容,需要仔细考虑 w 的选词规则,然后输入 c5w 吗?这样显然不够方便。
这种情况下,我们就需要使用 ffind和 ttill了。它们的作用都是找到下一个如果在输入它们之前先输入数字 n 的话,那就是下面第 n 个紧接着输入的字符。两者的区别是f 会包含这个字符,而 t 不会包含这个字符。在上面的情况下,我们用 t 就可以了ct) 就可以达到目的。如果需要反方向搜索的话,使用大写的 F 和 T 就可以。
对于写文字的情况,比如给开源项目写英文的 README下面的光标移动键也会比较有用
( 和 ) 移到上一句和下一句
{ 和 } 移到上一段和下一段
在很多环境特别是图形界面Vim 支持使用 <C-Home><C-End> 跳转到文件的开头和结尾。如果遇到困难,则可以使用 vi 兼容的 gg 和 G 跳转到开头和结尾行小区别G 是跳转到最后一行的第一个字符,而不是最后一个字符)。
光标移动咱们就讲到这里。你需要重点掌握的就是 Vim 里除了简单的光标移动,还有“小词”、“大词”、句、段的移动,以及字符的搜索;每种方式都分向前和向后两种情况。
文本修改
接着,我们来看文本修改。
在 Vim 的教程里我们已经学到c 和 d 配合方向键,可以对文本进行更改。本质上,我们可以认为 c修改的功能就是执行 d删除然后 i插入。在 Vim 里,一般的原则就是,常用的功能,按键应尽可能少。因此很多相近的功能在 Vim 里会有不同的按键。不仅如此,大写键也一般会重载一个相近但稍稍不同的含义:
d 加动作来进行删除dd 删除整行D 则相当于 d$,删除到行尾。
c 加动作来进行修改cc 修改整行C 则相当于 c$,删除到行尾然后进入插入模式。
s 相当于 cl删除一个字符然后进入插入模式S 相当于 cc替换整行的内容。
i 在当前字符前面进入插入模式I 则相当于 ^i把光标移到行首非空白字符上然后进入插入模式。
a 在当前字符后面进入插入模式A 相当于 $a把光标移到行尾然后进入插入模式。
o 在当前行下方插入一个新行然后在这行进入插入模式O 在当前行上方插入一个新行,然后在这行进入插入模式。
r 替换光标下的字符R 则进入替换模式,每次按键(直到 <Esc>)替换一个字符。
u 撤销最近的一个修改动作U 撤销当前行上的所有修改。
熟练掌握这些按键需要一定的记忆和练习。但是,当你熟练掌握之后,大部分编辑操作只需要按一两个按键就能完成;而在你还没有做到熟练掌握之前,记住最简单、最有逻辑的按键也可以让你至少能够完成需要的编辑任务。
文本对象选择
好,接下来就是我们今天的重点内容,文本对象的选择了。我之所以把这部分内容作为这节课的重点,是因为这是一个很方便很强大的功能,并且特别适合程序中的逻辑块的编辑。
到现在,我们已经学习过,可以使用 c、d 加动作键对这个动作选定的文本块进行操作,也可以使用 v 加动作键来选定文本块(以便后续进行操作),我们也学习了好些移动光标的动作。不过,还有几个动作只能在 c、d、v、y 这样命令之后用,我们也需要学习一下。
这些选择动作的基本附加键是 a 和 i。其中a 可以简单理解为英文单词 a表示选定后续动作要求的完整内容而 i 可理解为英文单词 inner代表后续动作要求的内容的“内部”。这么说还是有点抽象我们来看一下具体的例子。
假设有下面的文本内容:
if (message == "sesame open")
我们进一步假设光标停在“sesame”的“a”上那么和一般的行文惯例不同下面在命令外面也加上了引号避免可能的歧义
dw理解为 delete word会删除“ame␣”结果是“if (message == "sesopen")”
diw理解为 delete inside word会删除“sesame”结果是“if (message == " open")”
daw理解为 delete a word会删除“sesame␣”结果是“if (message == "open")”
diW会删除“"sesame”结果是“if (message == open")”
daW会删除“"sesame␣”结果是“if (message == open")”
di"会删除“sesame open”结果是“if (message == "")”
da"’会删除“"sesame open"”结果是“if (message ==)”
di(di)会删除“message == "sesame open"”结果是“if ()”
da(da)’会删除“(message == "sesame open")”结果是“if␣”
上面演示了 a、i 和 w、双引号、圆括号搭配使用这些对于任何语言的代码编辑都是非常有用的。实际上可以搭配的还有更多
搭配 ssentence对句子进行操作——适合西文文本编辑
搭配 pparagraph)对段落进行操作——适合西文文本编辑,及带空行的代码编辑
搭配 ttag对 HTML/XML 标签进行操作——适合 HTML、XML 等语言的代码编辑
搭配 ` 和 ' 对这两种引号里的内容进行操作——适合使用这些引号的代码,如 shell 和 Python
搭配方括号(“[”和“]”)对方括号里的内容进行操作——适合各种语言(大部分都会用到方括号吧)
搭配花括号(“{”和“}”)对花括号里的内容进行操作——适合类 C 的语言
搭配角括号(“<”和“>”)对角括号里的内容进行操作——适合 C++ 的模板代码
再进一步,在 a 和 i 前可以加上数字,对多个(层)文本对象进行操作。下面图中是一个示例:
你看,无论你使用什么语言,这些快捷的文本对象选择方式是不是总会有一种可以适用?我个人觉得这些功能绝对是 Vim 的强项了,所以,我再敲一次黑板,这部分内容是重点,不要嫌内容多,挨个儿用一用、练一练,你会发现这个功能非常实用,在写代码的时候常常会用得上。
更快地移动
除了这讲开头提到的光标移动功能外,还有一些通常不和操作搭配的光标和屏幕移动功能。我们在这节里会快速描述一下。
我们仍然可以使用 <PageUp><PageDown> 来翻页,但 Vim 更传统的用法是 <C-B><C-F>,分别代表 Backward 和 Forward。
除了翻页Vim 里还能翻半页,有时也许这种方式更方便,需要的键是 <C-U><C-D>Up 和 Down。
如果你知道出错位置的行号,那你可以用数字加 G 来跳转到指定行。类似地,你可以用数字加 | 来跳转到指定列。这在调试代码的时候非常有用,尤其适合进行自动化。
下图中展示了 iTerm2 中捕获输出并执行 Vim 命令的过程(用 vim -c 'normal 5G36|' 来执行跳转到出错位置第 5 行第 36 列):
(如果你用 iTerm2 并对这个功能感兴趣,我设置的正则表达式是 ^([_a-zA-Z0-9+/.-]+):([0-9]+):([0-9]+): (?:fatal error|error|warning|note):,捕获输出后执行的命令是 echo "vim -c 'normal \2G\3|' \1"。)
你只关心当前屏幕的话,可以快速移动光标到屏幕的顶部、中间和底部:用 HHigh、MMiddle和 LLow就可以做到。
顺便提一句vimrc_example 有一个设定,我不太喜欢:它会设 set scrolloff=5导致只要屏幕能滚动光标就移不到最上面的 4 行和最下面的 4 行里,因为一移进去屏幕就会自动滚动。这同样也会导致 H 和 L 的功能发生变化:本来是移动光标到屏幕的最上面和最下面,现在则变成了移动到上数第 6 行和下数第 6 行,和没有这个设定时的 6H 与 6L 一样了。所以我一般会在 Vim 配置文件里设置 set scrolloff=1你也可以考虑设成 0减少这个设置的干扰。
只要光标还在屏幕上你也可以滚动屏幕而不移动光标不像某些其他编辑器Vim 不允许光标在当前屏幕以外)。需要的按键是 <C-E><C-Y>
另外一种可能更实用的滚动屏幕方式是把当前行“滚动”到屏幕的顶部、中部或底部。Vim 里的对应按键是 zt、zz 和 zb。和上面的几个滚动相关的按键一样它们同样受选项 scrolloff 的影响。
重复,重复,再重复
今天的最后,我来带你解决一个你肯定会遇到的问题,那就是如何更高效地解决重复的操作。
我们已经看到,在 Vim 里有非常多的命令而且很多命令都需要敲好几个键。如果你要重复这样的命令每次都要再手敲一遍这显然是件很费力的事。作为追求高效率的编辑器这当然是不可接受的。除了我们以后要学到的命令录制、键映射、自定义脚本等复杂操作外Vim 对很多简单操作已经定义了重复键:
; 重复最近的字符查找f、t 等)操作
, 重复最近的字符查找操作,反方向
n 重复最近的字符串查找操作(/ 和 ?
N 重复最近的字符串查找操作(/ 和 ?),反方向
. 重复执行最近的修改操作
有了这些,重复操作就非常简单了。要掌握它们的方法就是多练习,多用几次自然就会了。
内容小结
好了,今天的内容就讲完了,我们来做个小结。我们讨论了更多的一些常用 Vim 命令,包括:
基本光标移动命令(可配合 c、d 和 v
文本修改命令小汇总
文本对象命令c、d、v 后的a 和 i
更快的光标和屏幕移动功能
重复功能
今天讲的内容不难,重点是文本对象。你知道吗?我见到的 Vim 命令速查表里通常也没有它们,因而连很多 Vim 的老用户都不知道这些功能呢。所以,掌握了这部分内容,我们就已经走在很多 Vim 用户的前面了。请一定要多加练习,用好这个功能会大大提升你的代码编辑效率。
最后,提醒你去 GitHub 上看配置文件。配置文件我们有一处改动。类似地,适用于本讲的内容标签是 l3-unix 和 l3-windows。
课后练习
请把本讲里面描述的 Vim 功能自己练习一下,尤其需要重点掌握的是文本修改命令、文本对象命令和重复功能。其他某些功能可能只对部分人和某些场景有用,如果一个功能你觉得用不上,不用去强记。毕竟,不用的功能,即使一时死记硬背可以记住,也很快会遗忘的。
欢迎你在留言区分享自己的学习收获和心得,有问题也要及时反馈,我们一起交流讨论。我们下一讲见!

View File

@ -0,0 +1,302 @@
因收到Google相关通知网站将会择期关闭。相关通知内容
04 初步定制:让你的 Vim 更顺手
你好,我是吴咏炜。
在前几讲,我已经介绍了不少 Vim 的常用命令,我想你已经略有心得了吧。今天我们转换一下视角,来讲一下 Vim 这个软件本身。
作为一个 Vim 的使用者,光熟悉命令是不够的,你还需要定制 Vim。因为每个人的习惯和需求都是不一样的一个高度定制化的 Vim 环境能大大提高你的工作效率。
今天,我会先带你了解一下 Vim 的运行支持文件目录结构,然后我们再一起探索 Vim 8 带来的新功能,及如何对 Vim 进行初步配置来使得 Vim 更加好用。
Vim 的目录结构
Vim 的工作环境是由运行支持文件来设定的。如果你想要定制 Vim就要熟知 Vim 有哪些不同类型的运行支持文件分别存放在哪里怎样能快捷地找到它们。Vim 比较有意思的一点的是,虽然运行支持文件是在 Vim 的安装目录下,但用户自己是可以“克隆”这个目录结构的。也就是说,你自己目录下的用户配置,到你深度定制的时候,也有相似的目录结构。所以,我就先从这些文件的目录结构开始讲起。
安装目录下的运行支持文件
Vim 的运行支持文件在不同的平台上有着相似的目录结构。以 Vim 8.2 为例,它们的标准安装位置分别在:
大部分 Unix 下面:/usr/share/vim/vim82
macOS Homebrew 下:/usr/local/opt/macvim/MacVim.app/Contents/Resources/vim/runtime
Windows 下C:\Program Files (x86)\Vim\vim82
在这个目录下面,你可以看到很多子目录,如 autoload、colors、doc、pack、plugin、syntax 等等。这些子目录下面就是分类放置的 Vim 支持文件。最常用的子目录应该是下面这几个:
syntaxVim 的语法加亮文件
docVim 的帮助文件
colorsVim 的配色方案
pluginVim 的“插件”,即用来增强 Vim 功能的工具
以 syntax 目录为例,当前我在下面看到有 617 个文件也就是说Vim 对 617 种不同的文件类型提供了语法加亮支持!这里面的文件去掉“.vim”后缀后就是文件类型的名字你可以用类似 :setfiletype java 这样的命令来设置文件的类型,从而进行语法加亮。目录下我们可以看到大家都很熟悉的语言,也有很多我从来都没听说过的东西。
只要有正当的理由,你就可以向 Vim 的作者 Bram 提交改进版本,或是对全新的语言的支持。我就对若干种文件类型提交过补丁,新增了对《计算机编程艺术》中的 MIX 汇编语言的语法支持并维护着微软宏汇编MASM的语法文件。
在图形界面的 Vim 里,你可以通过“语法 > 在菜单中显示文件类型”“Syntax > Show File Types in Menu”来展示 Vim 的所有文件类型,然后可以选择某一类型来对当前文件进行设置。这儿的菜单项,跟 syntax 目录下的文件就基本是一一对应的了。
在菜单中显示文件类型这个额外的步骤,可能是因为很久很久以前,加载所有文件类型的菜单是一个耗时的操作吧。在 menu.vim 里,目前有这样的代码:
" Skip setting up the individual syntax selection menus unless
" do_syntax_sel_menu is defined (it takes quite a bit of time).
if exists("do_syntax_sel_menu")
runtime! synmenu.vim
else
endif
不知道这段注释是什么年代加上的……但显然,我们的电脑已经不会再在乎加载几百个菜单项所占的时间了。即使我不怎么用菜单,我也找不出不直接展示这个菜单的理由;我可不想在需要使用的时候再多点一次鼠标。
所以,我会在我的 vimrc 文件里写上:
let do_syntax_sel_menu = 1
同理,我会加载其他一些可能会被 Vim 延迟加载的菜单,减少需要在菜单上点击的次数:
let do_no_lazyload_menus = 1
上面两个设置在我的机器上会让我打开 Vim 的速度下降大概 20 毫秒。我想我不在乎这点时间差异……
我们用“:help”命令查看的帮助文件就放在 doc 目录下。我们可以用菜单“编辑 > 配色方案”“Edit > Color Scheme”浏览配色方案相应的文件就在 colors 目录下。
在 plugin 目录下的系统内置插件不多,我们下面就快速讲解一下:
getscriptPlugin获得最新的 Vim 脚本的插件(在目前广泛使用 Git 的年代,这个插件过时了,我们不讲)
gzip编辑 .gz 压缩文件(能在编辑后缀为 .gz 的文件时自动解压和压缩,你会感觉不到这个文件是压缩的)
logiPat模式匹配的逻辑运算符允许以逻辑运算、而非标准正则表达式的方式来写模式匹配表达式
manpager使用 Vim 来查看 man 帮助(强烈建议试一下,记得使用 Vim 的跳转键 C-] 和 C-T
matchparen对括号进行高亮匹配现代编辑器基本都有类似的功能
netrwPlugin从网络上编辑文件和浏览远程目录支持多种常见协议如 ftp 和 scp可直接打开目录来选择文件
rrhelper用于支持 --remote-wait 编辑Vim 的多服务器会用到这一功能)
spellfile在拼写文件缺失时自动下载Vim 一般只安装了英文的拼写文件)
tarPlugin编辑压缩的tar 文件(注意,和 gzip 情况不同,这儿不支持写入)
tohtml把语法加亮的结果转成 HTML自己打开个文件输入命令“:TOhtml”就知道效果了
vimballPlugin创建和解开 .vba 文件(这个目前也略过时了,我们不讲)
zipPlugin编辑 zip 文件(和 tar 文件不同zip 文件可支持写入)
除了 rrhelper 和 spellfile 属于功能支持插件,没有自己的帮助页面,其他功能都可以使用“:help”命令来查看帮助。查看帮助时插件名称中的“Plugin”后缀需要去掉查看 zip 文件编辑的帮助时,应当使用“:help zip”而不是“:help zipPlugin”。
从这些插件当中,我们已经可以看到 Vim 的一些特殊威力了吧。下面的动图里,我们可以看到部分插件功能的展示:
用户的 Vim 配置目录
Vim 的安装目录你是不应该去修改的。首先,你可能没有权限去修改这个目录;其次,即使你有修改权限,这个目录会在 Vim 升级时被覆盖,你做的修改也会丢失。用户自己的配置应当放在自己的目录下,这也就是用户自己的主目录下的 Vim 配置目录Unix 下的 .vimWindows 下的 vimfiles。这个目录应和 Vim 安装目录下的运行支持文件目录有相同的结构,但下面的子目录你在需要修改 Vim 的相关行为时才有必要创建。如果一个同名文件出现用户自己的 Vim 配置目录里和 Vim 的安装目录里,用户的文件优先。
换句话说,修改 Vim 行为最简单的一种方式,就是把一个系统的运行支持文件复制到自己的 Vim 配置目录下的相同位置,然后修改其内容。我自己常常用这种方式来精调 Vim 的语法加亮。
显然,这种方式的缺点(在适当的时候也是优点)是,如果 Vim 的运行支持文件后来被修改/更新了,你也会继续使用你自己目录下的老版本修改版。如果你的修改不只是你自己的临时方案、同时也适合他人的话,最佳做法还是给 Vim 项目提交补丁,让其他所有人都能用上你的修改,这样才是开源的最佳使用方式。
关于 Vim 的公用脚本这儿再多说几句。Vim 的网站过去是用来集中获取各种脚本——如插件和配色方案——的地方,而 getscriptPlugin 可以帮助简化这个过程。今天你仍然可以使用这个方法,但 Git 和 GitHub 的广泛使用已经改变了人们获取和更新脚本的方式。现在,最主流的分发 Vim 脚本的方式是利用 GitHub而用户则使用包管理器来调用 Git 从 GitHub或类似的 Git 库)获取和更新脚本,下面我们很快就会讲到。
理解了 Vim 的目录结构,我们接着来看 Vim 8 的新功能。
Vim 8 新功能
Vim 是一个持续改进中的应用程序。从 Vim 8.12018 年 5 月 17 日)到 Vim 8.22019 年 12 月 12日Vim 有 2424 个补丁,也就是说,平均每天超过 4 个补丁。很多 Vim 8 里的大功能并不是一次性引入而是在补丁中慢慢引入的。比如Vim 里现在有终端支持,这个功能从 Vim 8.0.0693 开始引入,到了 Vim 8.1,成为一个正式的大功能。
站在我个人的角度看,从 Vim 7.4 到 Vim 8.2,最大的新功能是:
Vim 软件包的支持(“:help packages”
异步任务支持(“:help channel”、“:help job”和“:help timers”
终端支持(“:help terminal”
今天我们就重点讲一下 Vim 软件包。这是 Vim 8 里带来的一个重要功能,也让我们在扩展 Vim 的时候变得更方便了。
Vim 软件包
Vim 的目录结构有点传统 Unix 式:一个功能用到的文件可能会分散在多个目录下。就像传统 Unix 上 Vim 的文件可能分散在 /usr/bin、/usr/share/man、/usr/share/vim 等目录下一样,一个 Vim 的插件(严格来讲,应该叫包)通常也会分散在多个目录下:
插件的主体通常在 plugin 目录下
插件的帮助文件在 doc 目录下
有些插件只对某些文件类型有效,会有文件放在 ftplugin 目录下
有些插件有自己的文件类型检测规则,会有文件放在 ftdetect 目录下
有些插件有特殊的语法加亮,会有文件放在 syntax 目录下
……
以前我们安装插件,一般是一次性安装后就不管了。安装过程基本上就是到 .vim 目录Windows 上是 vimfiles 目录)下,解出压缩包的内容,然后执行 vim -c 'helptags doc|q' 生成帮助文件的索引。到了“互联网式更新”的年代,这种方式就显得落伍了。尤其糟糕的地方在于,它是按文件类型来组织目录的,而不是按相关性,这就没法用 Git 来管理了。
Vim 上后来就出现了一些包管理器,它们的基本模式都是相通的:每个包有自己的目录,然后这些目录会被加到 Vim 的运行时路径runtimepath选项里。最早的 runtimepath 较为简单,在 Unix 上缺省为:
$HOME/.vim,-
$VIM/vimfiles,-
$VIMRUNTIME,-
$VIM/vimfiles/after,-
$HOME/.vim/after
而在有了包管理器之后runtimepath 就会非常复杂,每个包都会增加一个自己的目录进去。但是,好处也是非常明显的,包的管理变得非常方便。从 Vim 8 开始Vim 官方也采用了类似的体系。Vim 会在用户的配置目录Unix 下是 $HOME/.vim Windows 下是 $HOME/vimfiles )下识别名字叫 pack 的目录,并在这个目录的子目录的 start 和 opt 目录下寻找包的目录。
听着有点绕吧?我们看一个实际的 Vim 配置目录的结构就清楚了:
.
├── colors
├── doc
├── pack
│ ├── minpac
│ │ ├── opt
│ │ │ ├── minpac
│ │ │ ├── vim-airline
│ │ │ └── vimcdoc
│ │ └── start
│ │ ├── VimExplorer
│ │ ├── asyncrun.vim
│ │ ├── fzf.vim
│ │ ├── gruvbox
│ │ ├── killersheep
│ │ ├── nerdcommenter
│ │ ├── nerdtree
│ │ ├── tagbar
│ │ ├── undotree
│ │ ├── vim-fugitive
│ │ ├── vim-matrix-screensaver
│ │ ├── vim-rainbow
│ │ ├── vim-repeat
│ │ ├── vim-rhubarb
│ │ └── vim-surround
│ └── my
│ ├── opt
│ │ ├── YouCompleteMe
│ │ ├── ale
│ │ ├── clang_complete
│ │ ├── cvsmenu
│ │ └── syntastic
│ └── start
│ ├── vim-gitgutter
│ └── ycmconf
├── plugin
├── syntax
└── undodir
可以看到pack 目录下有 minpac 和 my 两个子目录(这些名字 Vim 不关心),每个目录下面又有 opt 和 start 两个子目录,再下面就是每个包自己的目录了,里面又可以有自己的一套 colors、doc、plugin 这样的子目录这样就方便管理了。Vim 8 在启动时会加载所有 pack/*/start 下面的包,而用户可以用 :packadd 命令来加载某个 opt 目录下的包,如 :packadd vimcdoc 命令可加载 vimcdoc 包,来显示中文帮助信息。
有了这样的目录结构,用户要自己安装、管理包就方便多了。不过,我们还是推荐使用一个包管理器。包管理器可以带来下面的好处:
根据文本的配置(一般写在 vimrc 配置文件里)决定要安装哪些包
自动化安装、升级和卸载,包括帮助文件的索引生成
在我们这门课程里,我会使用 minpac一个利用 Vim 8 功能的小巧的包管理器。如果你已经在使用其他包管理器,我接下来讲的两个小节你可以考虑跳过。
安装 minpac
根据 minpac 网页上的说明,我们在 Windows 下可以使用下面的命令:
cd /d %USERPROFILE%
git clone https://github.com/k-takata/minpac.git ^
vimfiles\pack\minpac\opt\minpac
在 Linux 和 macOS 下则可以使用下面的命令:
git clone https://github.com/k-takata/minpac.git \
~/.vim/pack/minpac/opt/minpac
然后,我们在 vimrc 配置文件中加入以下内容(先不用理解其含义):
if exists('*minpac#init')
" Minpac is loaded.
call minpac#init()
call minpac#add('k-takata/minpac', {'type': 'opt'})
" Other plugins
endif
if has('eval')
" Minpac commands
command! PackUpdate packadd minpac | source $MYVIMRC | call minpac#update('', {'do': 'call minpac#status()'})
command! PackClean packadd minpac | source $MYVIMRC | call minpac#clean()
command! PackStatus packadd minpac | source $MYVIMRC | call minpac#status()
endif
存盘、重启 Vim 之后,我们就有了三个新的命令,可以用来更新(安装)包、清理包和检查当前包的状态。
通过 minpac 安装扩展包
下面我们就来试验一下通过 minpac 来安装扩展包。我们在“Other plugins”那行下面加入以下内容
call minpac#add('tpope/vim-eunuch')
保存文件,然后我们使用 :PackUpdate 命令。略微等待之后,我们就能看到类似下面的界面:
这就说明安装成功了。我们可以按 q 来退出这个状态窗口。
我们也可以使用 :PackStatus 来重新打开这个状态窗口。要删除一个插件,在 vimrc 中删除对应的那行,保存,然后使用 :PackClean 命令就可以了。愿意的话,你现在就可以试一下。
最近使用的文件
安装好 Vim 软件包之后,我们进一步来实现一个小功能。
Vim 的缺省安装缺了一个很多编辑器都有的功能:最近使用的文件。
我们就把这个功能补上吧。你只需要按照上一节的步骤安装 yegappan/mru 包就可以了MRU 代表 most recently used。安装完之后重新打开 vimrc 文件,你就可以在图形界面里看到下面的菜单了:
估计你很可能会问:如果是远程连接,没有图形界面怎么办?
我们仍可以在文本界面上唤起菜单,虽然美观程度会差点。你需要在 vimrc 配置文件中加入以下内容(同样,我们暂时先不用去理解其意义):
if !has('gui_running')
" 设置文本菜单
if has('wildmenu')
set wildmenu
set cpoptions-=<
set wildcharm=<C-Z>
nnoremap <F10> :emenu <C-Z>
inoremap <F10> <C-O>:emenu <C-Z>
endif
endif
在增加上面的配置之后,你就可以使用键 <F10>(当然你也可以换用其他键)加 <Tab> 来唤起 Vim 的文本菜单了。如下图所示:
内容小结
本讲我们讨论了 Vim 8 下的基本目录结构和 Vim 8 的软件包。
你需要知道Vim 的运行支持目录和用户配置目录有相似的结构,用户配置目录下的文件优先于 Vim 安装目录下的文件。因此,在用户配置目录里进行修改,可以在 Vim 版本有变化时保留自己的定制行为。
而 Vim 8 的软件包使得维护 Vim 的扩展变得更为容易,每一个 Vim 软件包都是独立的目录,可以单独安装、修改和升级。包管理器,如 minpac则可以帮助我们更方便地安装和管理 Vim 软件包。
对于配置文件,适用于本讲的内容标签是 l4-unix 和 l4-windows。
课后练习
请仔细查看一下你的 Vim 安装目录下面的目录结构,对 Vim 的目录结构有一个直观的理解。你也可以打开一些 .vim 文件来看看,初步感受一下 Vim 脚本:如 filetype.vim 里有着 Vim 如何识别文件类型的逻辑值得大致了解一下——Vim 并不仅仅是用后缀来判断文件类型的!
另外,一定要根据文中的说明,实践一下安装 minpac及使用 minpac 来安装其他 Vim 软件包(除非你已经在使用另外一个包管理器了)。我们后面会经常性地安装新的软件包,这一部分操作一定需要牢牢掌握。
如有任何问题,请留言和我交流、讨论。我们下一讲见!

View File

@ -0,0 +1,240 @@
因收到Google相关通知网站将会择期关闭。相关通知内容
05 多文件打开与缓冲区:复制粘贴的正确姿势
你好,我是吴咏炜。
在前面的几讲里,我们介绍了 Vim 的基本命令和配置。有了这些基本功,单个文件的基本编辑对你来说应该已经不成问题了。不过,显然我们在工作和生活中不可能只用一个文件包打天下,你肯定还会遇到需要同时编辑多个文件的情况。今天,我们就来细细讨论一下这个话题,什么是编辑多个文件的正确姿势。
先来假设一个简单的使用场景,我们现在需要在某个目录下的所有 .cpp 和 .h 文件开头贴入一段版权声明,该如何操作?
单文件的打开方式
图形界面
使用图形界面的话,我们可以在操作系统的资源管理器里进入到合适的目录,然后逐个使用 Vim 来打开文件。我们可以使用右键菜单“Edit with Vim”、“Open with…”等也可以直接把文件拖拽到 Vim 里。使用“文件 > 打开”File > Open菜单当然也是一种选择但这需要你记住上次打开到第几个文件并不如使用资源管理器方便。
使用这几种编辑方式的话,你可以把需要粘贴的内容放到操作系统的剪贴板里,然后在图形界面的 Vim 里用以下方法之一粘贴进去(当然,如果光标不在开头的话,先用鼠标或用 gg 命令跳转到开头):
正常模式 Vim 命令 "+P意义我们后面再解释
快捷键 <D-V>(提醒:这是我们对 ⌘V 的标记方式;仅适用于 macOS<S-Insert>PC 键盘)
鼠标右键加“粘贴”Paste
菜单“编辑 > 粘贴”Edit > Paste
注意,如果你通常使用 Ctrl-V 键粘贴的话,这个快捷键在 Vim 里并不适用。即使你使用的是图形界面的 Vim 也是如此,因为这个键在 Vim 里有其他用途。顺便说一句,这个键在 Unix 终端上也一样是不能用作粘贴的。
显然,在远程连接到服务器上时,以上方法不可用,我们得考虑终端 Vim 的用法。
终端 Vim
如果直接把图形界面下的基本步骤,翻译成终端 Vim非图形界面的用法的话应该是这样子的
在终端里进入到目标目录下
使用 vim 文件名 来逐一打开需要编辑的文件
如果光标不在开头的话,用鼠标或 gg 命令跳转到开头
使用命令 i 进入插入模式
使用终端窗口的粘贴命令或快捷键(如 <S-Insert>)来粘贴内容
<Esc> 回到正常模式并用 ZZ 存盘退出
或者,我们还可以采用下面的不退出 Vim 的处理方法:
打开文件使用 :e 文件名;可以使用 <C-D> 来查看有哪些文件,及用 <Tab> 进行自动完成
存盘使用 :w
但是如果粘贴的内容含缩进、而 Vim 又不够新的话,我们还会有特殊的麻烦。请继续往下看。
Vim 老版本的特殊处理
上面的图片展示了 Vim 用户可能遇到的一种错误情况。这是因为对于终端 Vim 来说一般而言它是没法分辨用户输入和粘贴的。因此在粘贴内容时Vim 的很多功能,特别是和自动缩进相关的,就会和输入打架,导致最后的结果不对。
要解决这个问题,你就得让 Vim 知道你到底是在输入还是在粘贴。Vim 有一个 paste 选项,就是用来切换输入/粘贴状态的。如果这个选项打开的话(:set pasteVim 就认为你在粘贴,智能缩进、制表符转换等功能就不会修改粘贴的内容。
不过,手工设置该选项(及事后用 set nopaste 取消)是件烦人的事。所幸 xterm 里有一个“括号粘贴模式”bracketed paste mode可以帮 Vim 判断目前是输入还是粘贴。这个模式启用后,终端在发送剪贴板的内容之前和之后都会发送特殊的控制字符序列,来通知应用程序进行特殊的处理。
启用括号粘贴模式需要向 xterm 发送启用序列 <Esc>[?2004h关闭括号粘贴模式需要向 xterm 发送关闭序列 <Esc>[?2004l在启用了括号粘贴模式后xterm 在发送剪贴板内容时会在前后分别加上开始粘贴序列 <Esc>[200~ 和结束粘贴序列 <Esc>[201~。
Vim 8.0.0210 开始引入了对括号粘贴模式的支持。在兼容 xterm 的终端里进行粘贴时,你不再需要使用 paste 这个选项了。更棒的是,目前你甚至都不需要进入插入模式就可以粘贴了——这是不是就方便多了?
如果你使用的是 Vim 8.0.0210 之前的版本的话,那我们至少也可以通过代码来使得手工设置 paste 选项变得不必要。你可以在 vimrc 里加入下面的代码:
if !has('patch-8.0.210')
" 进入插入模式时启用括号粘贴模式
let &t_SI .= "\<Esc>[?2004h"
" 退出插入模式时停用括号粘贴模式
let &t_EI .= "\<Esc>[?2004l"
" 见到 <Esc>[200~ 就调用 XTermPasteBegin
inoremap <special> <expr> <Esc>[200~ XTermPasteBegin()
function! XTermPasteBegin()
" 设置使用 <Esc>[201~ 关闭粘贴模式
set pastetoggle=<Esc>[201~
" 开启粘贴模式
set paste
return ""
endfunction
endif
这个功能虽然小,但解决了在远程连接上使用 Vim 粘贴代码的一个常见烦恼。因此,我认为你需要了解一下。
“已经存在交换文件!”
对每个文件单独使用一个 Vim 会话来编辑很容易出现冲突的情况所以你迟早会遇到“已经存在交换文件Swap file “…” already exists!)的错误提示。出现这个提示,有两种可能的原因:
你上次编辑这个文件时,发生了意外崩溃。
你已经在使用另外一个 Vim 会话编辑这个文件了。
原因不同,我们处理的策略自然也不相同。当进程 IDprocess ID后面没有“STILL RUNNING”这样的字样时那就是情况 1否则就是情况 2 了。
上图中没有“STILL RUNNING”的字样说明是情况 1。这时你需要按 r 来恢复上次的编辑状态——Vim 支持即使在你没有存盘的情况下仍然保存你的编辑状态,因而这种方法可以恢复你上次没有存盘的内容。
需要注意的是在恢复之后Vim 仍然不会删除崩溃时保留下来的那个交换文件。因此,在确定内容无误、保存文件之后,你需要重新再打开文件,并按 d 键把交换文件删除。当然,如果你确定目前保存的文件版本就是你想要的,也可以直接按 d 把交换文件删除、重新编辑文件。
反过来,如果你已经在另一个 Vim 会话里编辑文件的话,我们就会在进程 ID 后面看到“STILL RUNNING”的字样同时Vim 界面上也没有了删除Delete交换文件这一选项。
这时,大部分情况下我们应当使用 q 或 a绝大部分情况下没有区别放弃编辑并找到目前已经打开的 Vim 窗口,从那里继续。少数情况下,我们只是要查看文件,那也可以选择 o 只读打开文件。需要使用 e 强行编辑的情况很少,需要非常谨慎——比如,你确认另外有 Vim 会话,但里面不会去做任何修改,这是我目前想得出来的唯一的合理需求。
如果我们使用图形界面 Vim 8 的话Vim 支持在文件已经打开时自动切换到已经打开的 Vim 窗口上。这个功能在文件处于一个不活跃的标签页(下一讲会讨论标签页支持)时特别有用,因为 Vim 能把这个标签页自动切到最前面。不过,这个功能不是默认激活的,我们需要在 vimrc 中加入以下内容:
if v:version >= 800
packadd! editexisting
endif
好了,目前我们已经讨论了最简单、无聊、低效的工作方式。可以明显看到,不管是使用图形界面 Vim还是终端 Vim上面的方法本质上把 Vim 当成了记事本来用,完全没有体现出任何高效性或方便性。
既然使用号称“高效”的 Vim我们当然就得有更加高效的做法。下面我们以多文件打开为例加以说明。
多文件的打开方式
首先我们需要知道Vim 支持一次性打开多个文件,你只需要在命令行上写出多个文件即可,或者使用通配符。比如,就我们刚才所说的编辑场景,我们可以使用 vim *.cpp *.h。
有可能让你吃惊的是输入这个命令之后Vim 只打开了一个文件,那就是所有文件中的第一个。
原来为了确保在配置较差的环境里仍然能够正常工作Vim 绝对不会不必要地消耗内存包括打开不必要立即打开的文件。所以在上面的命令后Vim 建立了一个文件列表,并且暂时只打开其中的第一个文件。接下来,用户可以决定,要编辑哪个文件,或者查看列表,或者提前退出,等等。
为此Vim 提供了以下命令:
:args可以显示“参数”即需要编辑的多个文件的列表
:args 文件名:使用新的文件名替换参数列表
:next可缩写为 :n打开下一个文件如当前文件修改未存盘则会报错中止但如果命令后面加 ! 则会放弃修改内容,其他命令也类似
:Next缩写 :N或 :previous缩写 :prev打开上一个文件
:first 或 :rewind回到列表中的第一个文件
:last打开列表中的最后一个文件
使用这些命令,我们的工作流当然就会发生变化了:
在终端里进入到目标目录下
使用 vim *.cpp *.h 或 gvim *.cpp *.h 来打开需要编辑的文件
对于第一个文件,使用之前的方法贴入所需的文本
使用 V 进入行选择的可视模式,移动光标选中所需的文本,然后使用 y 复制选中的各行
执行命令 :set autowrite告诉 Vim 在切换文件时自动存盘
执行命令 :n|normal ggP切换到下一个文件并执行正常模式命令 ggP跳转到文件开头并贴入文本
确认修改无误后,键入 :、上箭头和回车,重复执行上面的命令
待 Vim 报错说已经在最后一个文件里,使用 :w 存盘,或 :wq抑或更快的 ZZ存盘退出
注意,第 6 步可以拆成 :n 和 ggP 两步,但文件数量较多时,反复手工敲 ggP 也挺累的。因此,我这儿使用了 normal 命令,在命令行模式下执行正常模式命令,下面就可以直接重复切换命令加粘贴命令,我们的编辑效率也得以大大提升。
这种编辑方式,是不是就比之前的优越多了?
另外Vim 还能解决一个 shell 相关的不一致性问题。如果我们要编辑的文件除了当前目录下的,还有所有子目录下的,在大部分 shell 下,包括 Linux 上缺省的 Bash我们需要使用“*.cpp *.h */.cpp /*.h”来挑选这些文件重复、麻烦。Vim 在此处采用了类似于 Zsh 的简化语法,“”也包含了当前目录。这样,我们只需把上面第 2 步改成下面这样即可:
键入 vim 进入 Vim然后使用 :args **/*.cpp **/*.h 来打开需要编辑的文件
缓冲区的管理和切换
跟多文件相关又略微不同的一个概念是缓冲区buffer。它是 Vim 里的一个基本概念,和今天讲的很多其他内容有相关性和相似性,你也或迟或早终究会遇到它,我今天也一起概要描述一下。
Vim 里会对每一个已打开或要打开的文件创建一个缓冲区,这个缓冲区就是文件在 Vim 中的映射。在多文件编辑的时候你也会有同样数量的缓冲区。不过,缓冲区的数量常常会更高,因为你用 :e 等命令打开的文件不会改变“命令行参数”(只被命令行或 :args 命令修改),但同样会增加缓冲区的数量。
此外,:args 代表参数列表/文件列表,真的只是文件的列表而已。缓冲区中有更多信息的,最最基本的就是记忆了光标的位置。在 Vim 里,除了切换到下一个文件这样的批处理操作外,操作缓冲区的命令比简单操作文件的命令更为方便。
作为对比,我们来看一下文件列表和缓冲区列表的命令的结果。
可以看到,两者都展示了文件,都标示出了当前编辑的文件(分别使用方括号和“%a”。不过缓冲区列表中明显有更多的信息
文件名前面有编号;我们也马上就会说到利用编号的命令。
除了当前活跃文件的标记“%a”还有个文件被标成了“#”,这表示最近的缓冲区;缓冲区列表里还可能有其他标记,如“+”表示缓冲区已经被修改。
文件名后面有行号,表示光标在文件中的位置。
常用的缓冲区命令跟前面文件列表相关的命令有很大的相似性,因此我在这儿一起讲,可以帮助你记忆:
:buffers 或 :ls可以显示缓冲区的列表
:buffer 缓冲区列表里的编号(:buffer 可缩写为 :b跳转到编号对应的缓冲区如当前缓冲区已被修改未存盘则会报错中止但如果命令后面加 ! 则会放弃修改内容;其他命令也类似
:bdelete 缓冲区列表里的编号(:bdelete 可缩写为 :bd删除编号对应的缓冲区编号省略的话删除当前缓冲区
:bnext缩写 :bn跳转到下一个缓冲区
:bNext缩写 :bN或 :bprevious缩写 :bp跳转到上一个缓冲区
:bfirst 或 :brewind跳转到缓冲区列表中的第一个文件
:blast跳转到缓冲区列表中的最后一个文件
还有很常见的一种情况是我们需要在两个文件之间切换。Vim 对最近编辑的文件(上面提到的列表里标有“#”的文件)有特殊的支持,使用快捷键 <C-^> 可以在最近的两个缓冲区之间来回切换。这个快捷键还有一个用法是在前面输入缓冲区的编号:比如,用 1<C-^> 可以跳转到第一个缓冲区(跟命令行模式的命令 :bfirst 或 :b1 效果相同)。
从实际使用的角度,使用缓冲区列表有点像打开最近使用的文件菜单(但缓冲区列表不会存盘),可以当作一种快速切换到最近使用的文件的方式。
缓冲区是文件在某个 Vim 会话里的映射。这意味着,如果某个 Vim 会话里不同的窗口或标签页(下一讲里会讨论)编辑的是同一个文件,它们对应到的也会是同一个缓冲区。更重要的是,文件/缓冲区的修改在同一个 Vim 会话里是完全同步的——这就不会像在多会话编辑时那样发生冲突和产生错误了。
内容小结
本讲通过讨论使用 Vim 在多个文件里粘贴代码的多种方法,我们学习了以下知识:
在图形界面和终端里,粘贴系统剪贴板的内容需要使用不同的方法:前者使用 Vim 命令,后者则需进入插入模式,使用终端的粘贴命令进行粘贴
Vim 能在崩溃后恢复未存盘的内容,也能在多会话编辑同一个文件时检测到这种冲突
在 Vim 里我们可以使用通配符“*.后缀”和“*/.后缀”来打开多个文件
使用 :args 命令我们可以展示或替换参数列表,使用 :next 等命令我们可以在这些参数指定的文件中切换
使用 :buffers 或 :ls 命令我们可以展示缓冲区列表,即所有已编辑和将编辑的文件,使用 :b 和 :bnext 等命令我们可以在这些缓冲区中进行切换
今天讲到了一些命令行模式的命令你应该可以看到它们都是非常有规律的最基本的操作就是“first”、“last”、“next”、“Next” 或 “previous”等英文单词以及它们与前缀的组合。把命令行模式的命令记住就能完成基本的编辑任务至于像 <C-^> 这样的正常模式命令,万一记不住,也可以用命令行模式的命令来替代。但是,正常模式的命令更加高效,有助于提高你的编辑效率,所以最好通过多加练习来形成“肌肉记忆”。
对于配置文件,本讲只有很小的更改,对应的标签是 l5-unix 和 l5-windows。
课后练习
请在课后进行以下练习,熟悉今天所讲的内容:
用 Vim 打开一个文件,进行编辑(不存盘),然后将这个 Vim 进程 kill 掉;重新打开文件,恢复其中内容并存盘;再次打开文件,删除交换文件。
用 Vim 打开一个文件,然后在另外一个终端窗口里再次打开这个文件,阅读冲突信息,然后退出编辑。
使用 Vim 打开多个文件,逐个查看,然后退出。
我是吴咏炜,我们下一讲再见!

View File

@ -0,0 +1,184 @@
因收到Google相关通知网站将会择期关闭。相关通知内容
06 窗口和标签页:修改、对比多个文件的正确姿势
你好,我是吴咏炜。
上一讲我们讨论了多文件的编辑。不过,迄今为止,我们即使编辑多个文件,也是在单个窗口里进行的。这样做的局限在于,我们既不能同时修改两个文件,也不能在单个 Vim 会话里对比显示两个文件。当然了,在两个 Vim 会话里倒是可以做到,但有很多不足之处,其中之一就是容易出现“已经存在交换文件”这样的冲突。
所以,这一讲我们就来讨论一下如何利用多窗口、多标签页编辑来实现这些功能。
多窗口编辑
Vim 有窗口的概念。事实上,如果你使用过 Vim 的帮助功能的话,那你就已经见过 Vim 的多窗口界面了。在那种情况下Vim 自动打开了一个水平分割的帮助窗口。
那如果我们想要自己同时查看、编辑多个文件呢?最基本的命令就是 :split缩写 :sp了。这个命令后面如果有文件名表示分割窗口并打开指定的文件如果没有文件名那就表示仅仅把当前窗口分割开当前编辑的文件在两个窗口里都显示。跟显示帮助文件一样:split 默认使用水平分割的方式。
既然我说了“水平分割”,聪明如你,一定想到了还有竖直分割。
确实如此。由于 Vim 经常是在终端窗口里打开,而终端宽度常常不能允许在竖直分割时显示两个文件,所以 Vim 默认分割是水平方式。竖直分割要求屏幕比较宽,但如果你想对比两个文件时,竖直分割就会更方便了。
我们可以在会产生分割的命令(如 help 和 split之前加上 vertical缩写 vert来进行竖直分割。对于最常见的竖直分割操作我们则可以直接写成 :vsplit缩写 :vs
下面的动画展示了我们进行一次竖直分割后,再进行水平分割的过程:
多窗口编辑是一个比较适宜使用鼠标的情况。你可以使用鼠标来激活想要使用的窗口,也可以使用鼠标来拖拉窗口的大小——只要启用了鼠标支持,终端窗口(包括远程连接的 mintty、PuTTY 等)里的 Vim 的窗口分割线也是可以拖动的(上面动画里的分割线拖动就是在一个终端窗口里)。
当然,作为 Vim 用户,基本的键盘使用肯定是少不了的:
<C-W> 加方向键h、j、k、l、<Left> 等等)可以在窗口之间跳转
<C-W>w 跳转到下一个(往右和往下)窗口,如果已经是右下角的窗口,则跳转到左上角的窗口
<C-W>W 跳转到上一个(往左和往上)窗口,如果已经是左上角的窗口,则跳转到右下角的窗口
<C-W>n 或 :new 打开一个新窗口
<C-W>c 或 :close 关闭当前窗口;当前窗口如果已经是最后一个则无效
<C-W>q 或 :quit 退出当前窗口,当最后一个窗口退出时则退出 Vim
<C-W>o 或 :only 只保留当前窗口,关闭其他所有窗口
<C-W>s 和 :split 作用相同,把当前窗口横向一分为二
<C-W>v 和 :vsplit 作用相同,把当前窗口纵向一分为二
<C-W>= 使得所有窗口大小相同(当调整过终端或图形界面 Vim 的窗口大小后特别有用)
<C-W>_ 设置窗口高度,命令前的数字表示高度行数,默认为纵向占满(想专心编辑某个文件时很有用)
<C-W>| 设置窗口宽度,命令前的数字表示宽度列数,默认为横向占满
<C-W>+ 增加窗口的高度,命令前的数字表示需要增加的行数,默认为 1
<C-W>- 减少窗口的高度,命令前的数字表示需要减少的行数,默认为 1
<C-W>> 增加窗口的宽度,命令前的数字表示需要增加的列数,默认为 1
<C-W><lt>(提醒,我们用 <lt> 表示“<”键)减少窗口的宽度,命令前的数字表示需要增加的列数,默认为 1
由于切换窗口是一个非常常见的操作,我通常会映射一下快捷键。为了跟一般的图形界面程序一致,我使用了 Ctrl-Tab 和 Ctrl-Shift-Tab
nnoremap <C-Tab> <C-W>w
inoremap <C-Tab> <C-O><C-W>w
nnoremap <C-S-Tab> <C-W>W
inoremap <C-S-Tab> <C-O><C-W>W
简单解释一下nnoremap 命令映射正常模式下的键盘inoremap 命令映射插入模式下的键盘;正常模式的映射简单直白,应该不需要解释,插入模式的映射使用了临时模式切换键 <C-O>:help i_CTRL-O在正常模式下执行相应的窗口命令然后返回插入模式。使用这样的键盘映射之后这两个快捷键在正常模式和插入模式下就都可以使用了。
双窗口比较
多窗口编辑中有一个非常有用的使用方式那就是比较两个文件Vim 对此也有特殊的支持。使用 vimdiff 或 gvimdiff 命令后面跟两个文件名我们就可以对这两个文件进行比较。在比较时Vim 会自动折叠相同的代码行,并加亮两边文本的不同部分。窗口的滚动也是联动的。一个实际的截图如下所示:
顺便说一句,因为使用双窗口比较功能要求 Vim 的宽度是平时的两倍左右,所以我通常都会对 Vim 窗口使用最大化、拖拉之类的操作。这些操作一般只影响右边的窗口的大小,因此,在放大窗口的操作后,我通常紧跟着就会执行 <C-W>= 来使两个窗口的宽度相同——事实上,我使用 <C-W>= 主要就在这种场合。你也可以试试。
当然了,在 Vim 内部也可以发起这样的比较。你需要做的是打开第一个文件,然后使用命令 :vert diffsplit 第二个文件。这一点只要了解一下就好,毕竟大部分情况下你不需要这样去做。
多标签页编辑
接下来我们继续讨论和多窗口编辑构成互补的另外一种方式,也就是多标签页。
这里我先给你一个结论:单窗口多文件编辑最适合的场景是批量修改具有相似性质的文件,多窗口编辑最适合的场景是需要对多个文件进行对比编辑,而其他的一些同时编辑多个文件的场景,就可以考虑多标签页的编辑方式。
我把多标签页编辑归为“其他”,但仍然还是有其特殊性质的。如果你熟悉现代多标签页的其他编辑器的话,你应该已经熟悉它的基本特性了。我们这儿再温习一下:
多标签页编辑允许在编辑器里同时修改多个(未存盘的)文件
多标签页编辑一次只展示一个文件
通过选择标签页(或使用键盘)可以方便地在多个标签页中进行切换
Vim 中的标签页在图形界面或终端模式下都能支持上面描述的这些特性。
鉴于你在实际使用中对标签页这种方式应当已经相当熟悉如何使用标签页图形界面我就不讲了。有一点需要注意一下和某些图形界面应用程序不同Vim 里标签页可包含多个窗口(一个标签页里默认有一个窗口),而不是窗口可包含多个标签页——这也意味着,在标签页里关闭最后一个窗口就关闭了整个标签页。
此外Vim 的标签页在纯文本的终端模式里也是可用的。在存在多个标签页的情况下即使在终端里你也可以用鼠标点击标签页来进行切换双击标签栏的空白处添加新标签页以及点击“X”标记来关闭标签页。
当然Vim 用户更经常会使用键盘:
在已有命令行模式命令前加 tab␣ 可以在新标签页中展示命令的结果,如 :tab help 可以在新标签页中打开帮助,:tab split 可以在新标签页中打开当前缓冲区
:tabs 展示所有标签页的列表
:tabnew 或 :tabedit 可以打开一个空白的新标签页,后面有文件名的话则打开该文件
:tabclose 可以关闭当前标签页(如果标签页里只有一个窗口,使用窗口关闭命令 <C-W>c 应该更快)
:tabnext、gt 或 <C-PageDown> 可以切换到下一个标签页
:tabNext、:tabprevious 、gT 或 <C-PageUp> 可以切换到上一个标签页
:tabfirst 或 :tabrewind 切换到第一个标签页
:tablast 切换到最后一个标签页
这些命令跟多文件、多缓冲区的命令有诸多相似之处,我就不需要再多加描述了。
如果一开始用多窗口编辑后来发现不需要一直参照这个文件了或者屏幕空间不足了该怎么办呢Vim 提供了一个命令,可以把当前窗口转变成一个新标签页:按下 <C-W>T 即可(仅当当前屏幕上有多个窗口时有效)。
这讲我们对多窗口和多标签页编辑的基本讨论到这儿就暂告一个段落。不知道你记不记得,上一讲我们说过,如果某个 Vim 会话里不同的窗口(或标签页;以下略)编辑的是同一个文件,它们对应到的也会是同一个缓冲区。这意味着多个窗口编辑同一个文件不会有冲突,同时,如果缓冲区被修改了,但只要当前关闭的窗口不是包含这个缓冲区的唯一窗口,那关闭窗口不会有任何问题,也不会影响文件的状态。在任何一个瞬间,任何一个窗口都指向一个缓冲区,而任何一个缓冲区都属于一个或多个窗口。(例外情况是你使用了一个不那么常用的功能,隐藏缓冲区;这个功能在本课程中不会讨论。)
NERDTree 插件
讨论了多窗口和多标签页之后,我们来看几个利用这些特性的插件。我们讨论的第一个插件就是 NERDTree。
我们上一讲开始提出的问题就是对多个文件进行编辑。对于找文件这件事NERDTree 就是你知道文件大概在哪里、但不知道文件具体名字时的一个好选择。跟很多 Vim 插件一样NERDTree 会利用多窗口(少数情况下利用标签页)的特性。
拿我们上一讲提到的在文件开头插入版权声明的例子来说至少在文件在一个目录下的情况下使用一个文件浏览插件也能解决问题。NERDTree 就是最为著名的一个文件浏览/管理插件。下面是一个功能展示截图,先给你一个直观的印象:
安装
如果使用 minpac 的话,我们需要在 vimrc 中“Other plugins”那行下面加入下面的语句并运行 :PackUpdate 来安装一下:
call minpac#add('preservim/nerdtree')
NERDTree 缺省就会抢占 netrw 使用的路径形式,所以我们可以用 :e . 来打开 NERDTree。不过更常用的方式仍然是使用 :NERDTreeToggleNERDTree 窗口的切换命令。我们使用这个命令可以打开上面左侧的那个 NERDTree 窗口,也可以关闭。这样,我们如果频繁需要浏览文件系统的话,就可以把这个命令映射到一个快捷键,免得每次都要打这么长的命令。鉴于功能键只有 12 个,映射其他键容易忘记,我暂时就不帮你在 vimrc 中映射了。对我来说,这个插件是需要安装的,但使用并不那么高频。
使用
在打开 NERDTree 窗口之后,使用还是相当直观的,并且按下 ? 就可以查看帮助信息,所以我也不必一一列举所有功能了。在这里,我就概要提一下最重要的几个功能点:
顾名思义,这个插件以树形方式展示文件系统,在目录上敲回车或双击即可打开或关闭光标下的目录树。
在文件上敲回车或双击立即打开该文件,并且光标跳转到文件窗口中,这样你就可以立即开始编辑了。
在文件上使用 go 会预览该文件,也就是光标不会跳转到文件所在的窗口中,方便快速查看多个文件的内容。
按 i 会打开文件到一个新的水平分割的窗口中,按 s 会打开文件到一个新的竖直分割的窗口中,按 t 会打开文件到一个新的标签页中。
NERDTree 会自动过滤隐藏文件和目录,但如果你需要看到它们的话,也可以用 I 来开启和关闭隐藏文件的显示。
按 m 会出现一个菜单,允许添加、删除、更名等操作。
这些命令不需要死记硬背。从使用的角度,知道回车、双击就可以使用这个插件了,其他命令可以根据需要,在使用中慢慢掌握。
类似插件
Vim 里预置了 netrw 插件,其功能包含多个网络文件协议,同时也包含了对本地文件系统的支持,使用 :e . 或 :vs . 这样的命令就可以直接启用。不过,它在本地目录浏览相关功能上比较简单,没有 NERDTree 好用。如果有条件安装 NERDTree 的话,你一定会更喜欢 NERDTree 的。
我的朋友明白mbbill写了个叫 VimExplorer 的插件,我也挺喜欢的,一直在用。它可以把 Vim 转变成一个双面板的资源管理器,设计上也更侧重于管理,而不是纯粹的文本编辑。不过,这个插件没有像 NERDTree 一样处于积极开发状态,我就不详细介绍了。有兴趣的可以自己试一下。
内容小结
好了,这一讲到这里我就全部讲完了。我来简单做一下小结:
今天我们讲了 Vim 里的窗口概念和标签页概念,并讨论了相关命令。窗口的命令主要是以 <C-W> 开始的双键命令而标签页的命令则和上一讲的文件操作命令和缓冲区操作命令非常相似也用了“first”、“last”、“next”、“Next”、“previous”等英文单词但前缀得使用“tab”。
多窗口适合你同时参照多个文件的内容,或者同一个文件的不同部分。多标签页适合其他你希望同时编辑多个文件的情况。在同一个 Vim 会话的多窗口和多标签页里编辑同一个文件不会发生任何冲突。
NERDTree 是一个利用多窗口和多标签页的流行插件,可以方便地在 Vim 里浏览文件系统并打开文件进行编辑。
本讲的配置文件更改也不多,对应的标签是 l6-unix 和 l6-windows。
课后练习
请在今天的课后进行以下的练习,来熟悉今天讲解的内容:
尝试在打开文件后使用 :split 和 :vsplit 分割窗口,改变窗口大小,在窗口里跳转,及关闭窗口。
使用 vimdiff 比较两个相似的文件。
运行 NERDTree在多个标签页里打开当前目录下的文件。
尝试在标签页里切换,及关闭标签页。
我是吴咏炜,我们下一讲再见。

View File

@ -0,0 +1,201 @@
因收到Google相关通知网站将会择期关闭。相关通知内容
07 正则表达式:实现文件内容的搜索和替换
你好,我是吴咏炜。
上面两讲里我们讨论了如何找到你想要查看/编辑的文件,及如何处理多个文件。今天我们来看一下如何在一个文件中搜索和替换内容,其核心主题就是正则表达式。
正则表达式搜索
通过 Vim 教程,你已经学到了搜索命令 / 和替换命令 :s 的基本用法。教程里没有提到的是,你输入的待查找的内容是被 Vim 当成正则表达式来看待的。正则表达式的学习资料很多(极客时间上就有专门的课程),完整学习也相当复杂,我们就不从头学习了。下面我们会简单讨论的,是 Vim 里的正则表达式,重点是它和其他常用正则表达式(正则表达式还是有很多种不同的风格的)的区别之处。如果你之前对正则表达式完全没有了解,建议你这儿暂停一下,先在网上搜索一下关于正则表达式的资料,了解它的基本概念和用法,然后继续阅读。
在一个搜索表达式里或者称为模式pattern注意不要和 Vim 的 mode 混淆)里,.、*、^、$、~、[]、\ 是有特殊含义的字符:
. 可以匹配除换行符外的任何字符:如 a. 可以匹配“aa”、“ab”、“ac”等但不能匹配“a”、“b”或“ba”。如果需要匹配换行符跨行匹配的话则需要使用 _.。
* 表示之前的匹配原(最普通的情况为单个字符)重复零次或多次:如 aa* 可以匹配“a”、“aa”或“aaa”a.* 可以匹配“a”、“aa”、“abc”等等但两者均不能匹配“b”。
^ 匹配一行的开头,如果出现在模式的开头的话;在其他位置代表字符本身。
$ 匹配一行的结尾,如果出现在模式的结尾的话;在其他位置代表字符本身。
~ 匹配上一次替换的字符串即如果上一次你把“foo”替换成了“bar”那 ~ 就匹配“bar”。
[…] 匹配方括号内的任一字符;方括号内如果第一个字符是 ^,表示对结果取反;除开头之外的 - 表示范围:如 [A-Za-z] 表示任意一个拉丁字母,[^-+*/] 表示除了“+”、“-”、“*”、“/”外的任意字符。
\ 的含义取决于下一个字符,在大部分的情况下,包括上面的这几个(.、*、\、^、$、~、[ 和 ]),代表后面这个字符本身;在跟某些字符时则有特殊含义(后面我们会讨论最重要的那些)。
除此之外的字符都是普通字符,没有特殊含义。不过,需要注意的是,如果使用 / 开始一个搜索命令,或者在替换命令(:s中使用 / 作为模式的分隔符,那模式中的 / 必须写作 \/ 才行,否则 Vim 看到 / 就会以为模式结束了,导致错误发生。
为了避免写模式的困扰,如果模式中使用“/”作为路径的分隔符,在替换命令中可以使用其他模式中没有的符号作为分隔符。比如,想把“/image/”全部替换成“/images/”的话,不要用 :%s/\/image\//\/images\//g而应该用类似于 :%s!/image/!/images/!g 的写法。这只能适用于替换命令,而在使用 / 命令搜索时我们就没什么好办法了,只能把模式里的 / 写作 \/。不过我们也可以取巧一下,用 ? 向上、也就是反向搜索,只要记得 n、N 反过来用找下一个就行。
通过 \ 开始的特殊表达式有不少,如果你需要完整了解的话,可以去看看参考文档(:help pattern-overview。我们下面先学习一下最基本的 6 个特殊模式项:
\? 表示之前的匹配原重复零次或一次:如 aa\? 可以匹配“a”、“aa”但不能完整匹配“aaa”可以匹配其前两个字符、后两个或最后一个字符
\+ 表示之前的匹配原重复一次或多次:如 aa\+ 可以匹配“aa”、“aaa”但不能匹配“a”或“b”。
\{n,m} 表示之前的匹配原重复 n 到 m 遍之间,两个数字可以省略部分或全部:如 a\{3}可读作3 个“a”可以匹配“aaa” a\{,3}(可读作:最多 3 个“a”可以匹配“”、“a”、“aa”和“aaa”两个数字都省略时等价于 *,也就是之前的匹配原可以重复零次或多次。
\( 和 \) 括起一个模式,将其组成为单个匹配原:如 \(foo\)\? 可以表示单词“foo”出现零次或一次。\( 和 \) 还有一个附加作用,是捕获匹配的内容,按 \( 出现的先后顺序,可以用 \1、\2 到 \9 来引用。如果你不需要捕获匹配内容的话,用 \%( 和 \) 的性能更高。
\& 是分支内多个邻接concat的分隔符概念上可以和与操作相比表示每一项都需要匹配成功然后取最后一项的结果返回如 .*foo.*\&.*bar.* 匹配同时出现了“foo”和“bar”的完整行。相对来讲\& 没那么常用。
\| 是多个分支的分隔符,概念上可以和或操作相比,表示任意一项匹配成功即可:如 foo\|bar 可匹配“foo”或“bar”两单词之一。
接下来,我再和你分享 13 个特殊模式项。虽然它们相对来说不那么必需,但掌握它们可以大大地提高程序员的编辑效率。
\< 匹配单词的开头
\> 匹配单词的结尾
\s 匹配空白字符 <Space><Tab>
\S 匹配非空白字符
\d 匹配数字,相当于 [0-9]
\D 匹配非数字,相当于 [^0-9]
\x 匹配十六进制数字,相当于 [0-9A-Fa-f]
\X 匹配非十六进制数字,相当于 [^0-9A-Fa-f]
\w 匹配单词字符,相当于 [0-9A-Za-z_]
\W 匹配非单词字符,相当于 [^0-9A-Za-z_]
\h 匹配单词首字符,相当于 [A-Za-z_]
\H 匹配非单词首字符,相当于 [^A-Za-z_]
\c 忽略大小写进行匹配
以上我们讨论的实际上是 Vim 缺省设置下的正则表达式。通过选项(:help /magic我们可以对哪些字符有特殊意义进行一定程度的调整。不过一般情况下我认为修改这个选项只会造成混乱、增加心智负担因此我也就不在这儿展开了。
搜索实例
抽象地讨论正则表达式恐怕你也不容易记住,我们还是拿一些具体的例子来看一下吧。
首先,如果我们要查找某个函数,该怎么做呢?简单,按下 /,然后输入函数名,回车,不就行了?
错。这种方式对函数名是部分匹配,你搜 begin 还会得到 begin1、_begin 之类的结果。正确的方法是,要在前后加上匹配单词头尾的标记,如,\<begin\>。
顺便说一句,被誉为最有用的 Vim 提示,是把光标移到希望搜索的关键字上,然后按下 * 键。Vim 会提取光标下的关键字,并自动添加 \< 和 \> 进行搜索。
如果我要搜索 begin 或 end 呢?我想,你应该已经知道了,是:/\<\(begin\|end\)\>。注意,写成 /\<begin\|end\> 可是不对的。(为什么?你想明白了吗?)
对于 HTML你应该多多少少有些了解。如果我们想匹配一下 HTML 标签的话,该怎么做呢?
一个标签以 < 开始 > 结束。所以,最简单的模式应该是 <.\+>,对吗?
不对这个写法忽略了一行里可能有多个标签的事实对于“title”这样一个字符串上面这个简单的模式会匹配整个字符串而不是“”和“”……
有一种解决方案是,排除不应该匹配的字符,把模式写成 <[^>]\+>:一对尖括号里有一个或多个不是“>”的字符。不过,这样的写法会让像 > 这样的结尾字符在模式中重复出现,因此这并不是最理想的写法。更好的方式是,使用最短匹配。
最长匹配和最短匹配
我们上面学到的 *、\?、\+ 和 \{} 都属于最长匹配(也叫贪婪匹配),也就是说,当模式既可以匹配一个较长的字符串,也可以匹配一个较短的字符串时,结果会是那个较长的字符串。
相应地,还有一种匹配叫做最短匹配,也就是在同时可以匹配较长的字符串和较短的字符串时,产生较短的匹配。在 Vim 里,最短匹配只有一种形式,{-n,m},其意义和之前说的 {n,m} 基本相同,但结果是较短而非较长的字符串。
以上面的 HTML 标签匹配为例,使用最短匹配的话,我们可以把模式写成 <.\{-1,}>,要求在一对尖括号里至少有一个字符,但越短越好。
搜索加亮和取消
如果你一边学一边在试验的话就会发现Vim 缺省在你输入搜索模式时就会高亮跟你输入的模式匹配的文本。这对验证你输入的模式是否正确,以及进行进一步的编辑,都是非常方便和重要的。用惯了 Vim就会把它当成是一件理所当然的事——直到你被迫使用其他编辑器时才发现一边输入正则表达式一边就能看到匹配的结果原来不是谁都这样做的啊……
但也有些时候我们已经做完了搜索或替换和模式匹配的文本内容仍然还高亮着非常碍眼。有些人就会随便搜索一个不存在的字符串来取消加亮但这显然不是一种高效的处理方式。事实上Vim 有一个专门命令来取消搜索加亮,这个命令就是 :nohlsearch不要高亮搜索。
鉴于这个命令使用的频度实在是太高了,我们需要给它专门分配一个快捷键。请在 vimrc 中加入:
" 停止搜索高亮的键映射
nnoremap <silent> <F2> :nohlsearch<CR>
inoremap <silent> <F2> <C-O>:nohlsearch<CR>
这样一来,在搜索或替换工作完成之后,只要按下 <F2> 就可以取消搜索加亮了。
好,关于正则表达式的搜索部分,我们暂时就先学到这里。下面我们来看一下替换。
正则表达式替换
你可能要说了:替换不就是找到跟模式匹配的字符串,然后把它换成另外一个字符串么,有什么复杂的?
事实上,还真是有些复杂情况的。你在看下面这些复杂的替换情况时,也可以同时考虑下自己有没有解决方案:
你可能要保留匹配中的某些字符,而替换另外一些字符
你可能要对匹配出的内容做大小写转换
你可能需要“计算”出替换结果
你可能需要决定一行里要替换单次还是多次,是自动替换还是要一一确认,等等
接下来,我们就分别看看这些复杂情况。
在这些情况里,最常用的显然就是在替换结果中保留匹配出的字符串了。前面说到 \(\) 除了将一个模式转变成匹配原外,还有一个作用是捕捉匹配的内容,按 \( 的出现顺序依次编号为 1 到 9并可以在模式和替换字符串中用 \1 到 \9 来访问。如果要在替换字符串中完整使用匹配内容的话,则可以使用 \0 或 &(字符“&”也因此要在替换字符串中写成 \&)。
从搜索的角度,我们一般只关心匹配与否,而不关心匹配的大小。举个例子,如果我想找出作为函数调用的 begin那我可以写成 \<begin(,虽然 ( 不是我想匹配的内容(函数名称)的一部分。但从替换的角度,我需要在替换时再处理一下多匹配的内容,也是件麻烦事;在非匹配的内容比较复杂或者会变化的时候,尤其会是这样。所以 Vim 里还有专门标识匹配开始和结束的匹配原,分别是 \zs 和 \ze。对于这个例子搜索模式就应该是 \<begin\ze(。为了巩固前面学到的知识,你应该知道,这个模式也可以啰嗦地写成 \<begin(\&begin 或 \<begin(\&.....。
Vim 里还有一些大小写转换的特殊替换字符串。它们是:
\U 把下面的字符变成大写,直到 \E 出现
\u 把下一个字符变成大写
\L 把下面的字符变成小写,直到 \E 出现
\l 把下一个字符变成小写
\E 结束大小写转换
Vim 还能用 \= 开始一个返回字符串的表达式,用来计算出一个替换结果。鉴于我们目前还没有讨论 Vim 脚本,这个我们就留到后面第 14 讲再说了。
跟常用的编程语言一样Vim 的正则表达式中支持 \t、\r、\n 等特殊转义字符,但在替换表达式中,由于一些技术原因(:help NL-used-for-Nul\n 插入的是空字符NUL 或“\0”而非在模式中出现时代表的 LF。如果要插入正常的行尾符 LF 的话,我们得使用 \r。这意味着如果想把一个回车变成两个的话我们得别扭地写 :s/\n/\r\r/,略遗憾。如果有特殊需要得插入 CR 的话,就要更别扭地输入 \<C-V><CR> 才行。还好,我们基本不会在替换时遇到要插入 CR 的情况……
Vim 有很多用来控制替换的标志,你可以通过 :help s_flags 查看详细的介绍我就不一一列举了。今天这一讲中我们只会用到最常用的一个标志g代表可以在一行内进行多次替换没有这个标志的话Vim 在一行里只会对第一个成功的匹配进行替换。
替换实例
同样,我们还是通过例子来巩固一下对正则表达式替换的理解。
先来看一个简单的,删除行尾的“//”注释。我们可以用这个命令
:%s!\s*//.*$!!
把零到多个空白字符后面出现的“//”直到行尾全部删除。
如果要删除“/* */”注释,那就复杂多了。首先,匹配内容可以跨行;其次,有跟 HTML 标签类似的问题,需要使用最短匹配。我们需要使用的命令是:
:%s!/*_.\{-}*/!!g
由于一行里可以有多个“/* */”注释,我们在替换命令的尾部还加上了 g 标志,允许一行里进行多次替换。
假设我们目前的编码规范规定,所有的函数名应该首字母大写(简单起见,我们假设所有的类名已经是首字母大写了,因而构造函数自动符合该要求,不会发生冲突;但其他很多函数名称仍然是小写字母开头),我们能不能用 Vim 的替换命令做到呢?答案也是肯定的。所有需要的知识点我们都已经讲过了,我就直接公布答案了:
:%s/\<\(_*\)\([a-z]\w*\)\ze(/\1\u\2/g
这个命令比较长,请你慢慢体会一下,尝试去理解每一部分的意图。如果你有哪个点卡住了,可以留言给我,我再帮你详细分析一下。
内容小结
好了,今天的内容就讲到这里了。内容有点密集,我把要点再总结一下:
Vim 支持用 / 进行搜索和用 :s 进行替换,它们都用到了正则表达式。
在搜索的模式里,.、*、^、$、~、[]、\ 是有特殊含义的字符,你一定要记住它们的含义。在 \ 开始的特殊表达式中,最重要的是 \?、\+、\(\)、\| 和 \{n,m}。对于程序员来说,\<、\> 等匹配原对于提高编辑效率也非常重要。Vim 中的常用搜索命令 * 则会自动在搜索的关键字前后加上 \< 和 \>。
在替换时,我们需要特别记住 \1、\2 到 \9 可以用来引用前面用 \( 和 \) 括起来的内容,字符“&”出现在替换内容中需要使用反斜杠转义成 \&,否则代表完整的被匹配字符串。
正则表达式就可以算是一种独立的语言了,靠死记硬背是不行的。最后我还要建议你再把这一讲中的例子仔细看一下、尝试一下,多多练习是掌握正则表达式搜索和替换的必经之路。如果你日后遇到了这一讲没有覆盖的问题,可以再去查阅 Vim 的帮助文档 :help regexp。
本讲我们在配置文件中只更改了一处,对应的标签是 l7-unix 和 l7-windows。
课后练习
有两道练习题,请你在课后尝试一下。练习对于掌握正则表达式是非常重要的。
如果我要搜索“/* */”注释的话,搜索命令应该是什么样的?
例子里只说了首字母大写,但实际的编码规范是要求把 begin_search_nocase 这样的函数名称转变成 BeginSearchNocase。请用 Vim 的替换命令完成这一任务。提示:可能需要一条以上的替换命令。
我是吴咏炜,我们下一讲再见。

View File

@ -0,0 +1,419 @@
因收到Google相关通知网站将会择期关闭。相关通知内容
08 基本编程支持:规避、解决编程时的常见问题
你好,我是吴咏炜。
在前面的几讲里,我们已经学了很多使用 Vim 的基本知识。今天是编程专题,我来专门介绍一下 Vim 对编程的特别支持。学了这一讲之后,你会进一步了解 Vim 编辑程序时的重要特性,并能够规避、解决编程时的常见问题。
文件类型和关联设定
程序源代码通常由文件组成,每个文件都有一个关联的文件类型。这个文件类型决定了 Vim 对其进行处理的一些基本设定,可能包括:
如何对文件进行高亮
制表符tab的宽度空格数
是否在键入 <Tab> 时扩展为空格字符
每次缩进的空格数(是的,可以和制表符宽度不同)
采用何种自动缩进方法
其他可适用的选项
文件高亮通常需要一套相当复杂的规则,我们今天就只把它当成一个既成事实了,不讨论这些规则的细节。其他各项在 Vim 里一般以选项的形式出现。这些选项都是文件本地local选项即可以在一个文件里修改其数值而不影响其他文件。对于这样的选项可以用 :setlocal 和 :setglobal 命令分别访问本地值和全局值。一般的 :set 命令在读取数值时(如 :set tabstop?)返回本地值,在写入数值时(如 :set tabstop=4同时设置本地值和全局值。
制表符宽度对应的选项是 tabstop。这在不同的语言里可能有不同的惯例自然不必多说。它的缺省值是 8但在不同的文件里可以不一样。不同的文件类型也可能会自动设定不同的数值。
是否扩展 <Tab> 为空格由 expandtab 选项控制。我们前面看到过但没有讲过Vim 选项有些是用等号赋值的,也有些不用等号,而只用选项名称或选项名称前面加 no表示否定。这些就是布尔类型选项expandtab 也是其中之一。如果打开了 expandtab 选项,那输入中的 tab 会被转变成空格;如果关闭的话,则 tab 字符会被保留。
让事情变得更复杂的是Vim 还有个 softtabstop 选项,软制表符宽度。一旦设置了这个选项为非零值,再键入 <Tab><BS>(退格键),你就感觉像设置了这个宽度的 tabstop 一样,有相应数量的缩进或取消缩进,但实际插入的字符仍然受 expandtab 和 tabstop 两个选项控制。在设置软制表符宽度时,一种最常用的用法是同时设置 expandtab这样编辑时你感觉像使用了这个宽度的制表符一样但你输入的内容里实际被保存的仍然是空格字符。
这些还不是 Vim 真正使用的“缩进”值。以 C 语言为例,当 Vim 看到你输入“{”和回车键时,会自动产生一个缩进,而这个缩进值跟 tabstop 和 softtabstop 都无关,是一个独立的选项 shiftwidth。
最后Vim 还有很多精细的选项来控制如何进行缩进。默认情况下Vim 没有特殊缩进,回车键回到行首。一般而言,使用选项 autoindent 可以使 Vim 至少记住上一行的缩进位置而对于特定语言Vim 可以设置更合适的选项,达到更佳的缩进效果——如对类 C 语言 Vim 会设置 cindent 选项达到最优的缩进效果。我们下面还会提到Vim 支持对类 C 语言的缩进有一些精调选项,你也可以自己进一步进行调整。
我之前提到过Vim 会根据文件类型来设置选项。所以相关问题就是Vim 如何判断文件类型,如何根据文件类型来设置选项,以及我们该如何定制这些行为。我们下面就来一一作答。
文件类型判断
Vim 的文件类型判断是在 filetype.vim 中执行的。我们可以用下面的命令来打开这个文件:
:e $VIMRUNTIME/filetype.vim
这个文件相当复杂,但有编程功底的你,应该可以看出大概的意思吧?其中最主要的逻辑仍然是通过后缀来进行判断,如:
" C++
au BufNewFile,BufRead *.cxx,*.c++,*.hh,*.hxx,*.hpp,*.ipp,*.moc,*.tcc,*.inl setf cpp
其中 au 是 autocmd 的缩写,代表 Vim 在发生某事件时触发某一动作。上面说的就是在创建BufNewFile或读入BufRead跟指定文件名模式匹配的文件时把文件类型设为 C++setf cpp setf 是 setfiletype 的缩写)。
但在后缀不足以唯一判断时Vim 可以进一步执行代码,如:
au BufNewFile,BufRead *.h call dist#ft#FTheader()
上面函数的定义在文件 $VIMRUNTIME/autoload/dist/ft.vim 里:
func dist#ft#FTheader()
if match(getline(1, min([line("$"), 200])), '^@\(interface\|end\|class\)') > -1
if exists("g:c_syntax_for_h")
setf objc
else
setf objcpp
endif
elseif exists("g:c_syntax_for_h")
setf c
elseif exists("g:ch_syntax_for_h")
setf ch
else
setf cpp
endif
endfunc
它的大概意思是,如果在头 200 行里找到某行以 @interface 等内容开始,那就认为这是 Objective-C/C++,否则认为是 C/C++。具体是 C 还是 C++,则由全局变量 g:c_syntax_for_h 控制(我们忽略 Ch 这种小众情况)。详细语法我们就不展开讲述了,留待讨论 Vim 脚本的时候再看。
上面讲的是 Vim 的缺省行为。我们当然也可以定制 Vim的行为。按照惯例一般把定制放在用户 Vim 配置目录里的 filetype.vim 里。我的定制如下所示:
if exists("did_load_filetypes")
finish
endif
function! s:CheckCPP()
if expand('%:t') !~ '.'
setfiletype cpp
endif
endfunction
augroup filetypedetect
au! BufRead,BufNewFile *.asm setfiletype masm
au! BufRead proxy.pac setfiletype javascript
au! BufRead */c++/* call s:CheckCPP()
au! BufRead */include/* call s:CheckCPP()
augroup END
我们可以跳过一些语法方面的细节,只讨论代码里的意图。上面这段代码主要做了以下事情:
当读入或创建后缀为“.asm”的文件时设置文件类型为微软宏汇编默认为 GNU 的汇编格式)。
当读入名字为“proxy.pac”的文件时把内容当成 JavaScript 解释。
当读入路径含“c++”或“include”的文件时调用脚本内部函数 CheckCPP检查文件名% 代表文件名,:t 代表尾部,即去掉路径部分)是否不含“.”,是的话当成 C++ 文件类型。这是为了处理像“memory”这样的无后缀 C++ 头文件。
随后 Vim 会继续载入自带的 filetype.vim如果文件类型还未确定的话则继续使用 Vim 自带的规则进行判断。
文件类型选项
一旦确定了文件类型Vim 会从运行支持文件目录下载入同名的文件。以 Python 为例:
syntax/python.vim 包含了如何对 Python 进行语法加亮的设置
indent/python.vim 包含了如何对 Python 代码进行缩进的设置(如在用户输入 if 时进行缩进等)
ftplugin/python.vim 是文件类型插件,包含了其他跟文件类型相关的设置
文件类型插件中包含我们上面提到的制表符宽度方面的设定,具体来说,是下面这几行:
if !exists("g:python_recommended_style") || g:python_recommended_style != 0
" As suggested by PEP8.
setlocal expandtab shiftwidth=4 softtabstop=4 tabstop=8
endif
默认情况下,该文件使用 PEP 8 推荐的设置:
把用户输入的制表符扩展成空格
缩进和软制表符宽度设为 4
如果文件中包含制表符的话,仍按宽度为 8 来解释
缩进和软制表符宽度设成 4 估计不需要解释,这应该是最常用的缩进值了。使用空格而不是制表符的最大好处是,在无论何种环境下,展示效果都可以完全一致,不会在 diff 时或制表符宽度不符合预期时代码就乱了。至于“硬”制表符宽度仍然是 8则是为了确保显示文件的兼容性尤其是在终端里 cat 文件时和在浏览器中浏览源代码时;这两种情况下,制表符宽度一般都是 8。
跟 Python 不同,很多其他文件类型没有推荐的风格设定,这时就应该用户自己进行设定了。我推荐在 vimrc 配置文件里进行设置,因为比较集中、容易管理。如:
au FileType c,cpp,objc setlocal expandtab shiftwidth=4 softtabstop=4 tabstop=4 cinoptions=:0,g0,(0,w1
au FileType json setlocal expandtab shiftwidth=2 softtabstop=2
au FileType vim setlocal expandtab shiftwidth=2 softtabstop=2
上面设置了几种不同文件类型的编辑选项。大部分我们都已经知道了,下面这个则是新的:
cinoptions 可以精调 C 风格缩进的方式;上面 :0 表示 switch 下面的 case 语句不进行额外缩进g0 代表作用域声明public:、private: 等)不额外缩进,(0 和 w1 配合代表没结束的圆括号里的内容折行时不额外缩进。
我们也可以根据文件类型以外的条件来进行设定,如下面设定是要把 /usr/include 目录下的文件按 GNU 编码风格来解释:
function! GnuIndent()
setlocal cinoptions=>4,n-2,{2,^-2,:2,=2,g0,h2,p5,t0,+2,(0,u0,w1,m1
setlocal shiftwidth=2
setlocal tabstop=8
endfunction
au BufRead /usr/include/* call GnuIndent()
当然,除了设定选项,我们也可以做其他事情,比如下面的代码是在 Vim 帮助文件中,将 q 设定为关闭窗口的按键,映射中的 <buffer> 表示该映射只对这个缓冲区有效。
au FileType help nnoremap <buffer> q <C-W>c
Tags 支持
Vim 对一种叫 tags 的文本索引格式有特殊支持。事实上Vim 自己的帮助文件都是用 tags 来索引的。我们用过了 Vim 帮助,也就用过了 tags 文件。下面展示了 $VIMRUNTIME/doc/tags 文件中的一部分:
? pattern.txt /*?*
?<CR> pattern.txt /*?<CR>*
@ repeat.txt /*@*
@/ change.txt /*@\/*
@: repeat.txt /*@:*
@= change.txt /*@=*
@@ repeat.txt /*@@*
@r eval.txt /*@r*
A insert.txt /*A*
ACL editing.txt /*ACL*
ANSI-C develop.txt /*ANSI-C*
我们可以清楚地看到,其中内容分为三列:第一列是关键字,第二列是文件名,第三列是在目标文件中的匹配文本。当你在 Vim 的帮助文件中使用双击或 <C-]> 等命令跳转时Vim 就会在 tags 文件中搜索,寻找到匹配项的时候就跳转到指定的文件,并利用匹配文本跳转到指定的位置。
注意我们有不止一个 tags 文件。单单从 Vim 帮助的角度,个人 Vim 配置目录下的 doc 目录里有一个 tags 文件;每当你装了一个新的带帮助文件的 Vim 插件时,你都需要到这个 doc 目录下运行 helptags . 来重新生成索引。每个 Vim 软件包的 doc 目录下也同样需要有 tags 文件,不过包管理器能够在安装、更新时自动帮我们在 doc 目录下生成 tags 文件。Vim 在你使用 :help 命令查帮助时,会自动在你的所有运行时目录(可以使用 :set runtimepath? 查看)下的 doc/tags 里查找第一个匹配项。
生成 tags 文件的工具
如果 tags 文件只支持 Vim 帮助文件的话,那我就没必要对其进行详细讨论了。之所以在这里讨论 tags是因为它可以用在编程语言上。要生成 Vim 可以使用的支持常用编程语言的 tags 文件,我们需要使用下列两个工具之一:
Exubertant Ctags
Universal Ctags
Exuberant Ctags 是已经存在了好多年的老牌工具。Windows 下可直接下载可执行程序,而 Linux 和 macOS 上的包管理器一般也直接支持。如:
Ubuntu 下可使用 sudo apt install exuberant-ctags
CentOS 下可使用 sudo yum install ctags
macOS Homebrew 可使用 brew install ctags但需要注意 macOS 本身提供了一个功能较简单的 ctags 命令,你可能需要将 /usr/local/bin 在路径里移到 /usr/bin 前面,或自己设置 alias确保优先使用 /usr/local/bin/ctags
Universal Ctags 还比较新,目前各操作系统的包管理器里多半还没有它,所以安装会麻烦一点。你需要自己查看文档,找到在你的操作系统上的安装方式。(我是直接从源代码编译了一个版本。)
我之所以要推荐 Universal Ctags是因为虽然 Exuberant Ctags 和 Universal Ctags 都支持超过 40 种的常见编程语言,但 Exuberant Ctags 的最后一个版本 5.8,发布于 2009 年之后就一直没有更新了。Universal Ctags 是基于 Exuberant Ctags 代码的改进版本,并把开发移到了 GitHub 上,项目一直处于活跃状态。想偷懒的话,可以直接使用 Exuberant Ctags如果愿意折腾一下或者明确遇到 Exuberant Ctags 的问题,则可以试试 Universal Ctags。
对于现代 C++ 代码,使用 Universal Ctags 还是挺重要的。老的 Exuberant Ctags 不能处理 C++11 以来的新语法——这当然也是件显而易见的事。
生成 tags 文件的命令
要生成 tags 文件时,你可以简单地进入到一个目录下,然后执行下面的语句对该目录及子目录下的程序源文件生成一个 tags 文件:
ctags -R .
但根据场景和语言不同,你可能需要使用更多的选项。比如,对于 C++,我一般使用:
ctags --fields=+iaS --extra=+q -R .
如果是对系统的头文件生成 tags 文件——可以用来查找函数的原型信息——那我们一般还需要加上 --c-kinds=+p 选项。为了一次性地对系统头文件简单地生成 tags 文件,我还专门写了个脚本 gen_systags 来自动化这项工作。你如果感兴趣的话,也可以点进去看一下。
鉴于我们主要讲 Vim 而不是 Ctags这个话题我暂时就点到为止、不展开了。你可以通过我给出的链接以及 man ctags 或 ctags --help 的输出,自己进一步学习一下。讲到 C 的工作环境时,我们会再回到 Ctags。
使用 tags 文件
如果当前目录下或当前文件所在目录下存在 tags 文件Vim 会自动使用这个文件,不需要你做额外的设定。你所需要做的就是在待搜索的关键字上(也可以在可视模式下选中需要的关键字)使用正常模式命令 <C-]>,或者按 gg 可理解成 go键加鼠标单击。你愿意的话也可以手工输入命令 :tag 后面跟空格和待搜索的符号加回车键。这样 Vim 即会跳转到该符号的定义或声明位置。
如果待搜索的符号找不到Vim 会报错“E426: tag not found”。如果存在一个或多个匹配项Vim 会跳转到第一个匹配的位置。下面我列举一下其他相关的常用命令:
:tnext缩写 :tn跳转到下一个标签匹配位置
:tNext缩写 :tN或 :tprevious缩写 :tp跳转到上一个标签匹配位置
:tfirst 或 :trewind 跳转到第一个标签匹配位置
:tlast 跳转到最后一个标签匹配位置
:tselect 名称(:tselect 可缩写为 :ts跟 :tag 类似,但会列举可能的匹配项,让你自己选择(而非跳转到第一个匹配位置)
g] 跟 <C-]> 类似,但跟 :tselect 一样会给出一个列表而非直接跳转
:tjump 名称(:tjump 可缩写为 :tj跟 :tselect 类似,但在只有一个匹配项的时候会直接跳转到匹配位置
g<C-]> 跟 g] 类似,但跟 :tjump 一样在只有一个匹配项时会直接跳转到匹配位置
:stselect 名称(:stselect 可缩写为 :sts跟 :tselect 类似,但结果会打开到一个新分割的窗口中
:stjump 名称(:stjump 可缩写为 :stj跟 :tjump 类似,但结果会打开到一个新分割的窗口中
我们的标签跳转分为 :tag、:tselect 和 :tjump 三种不同方法,正常模式和可视模式的命令 <C-] 也同样有后两种方法的变体对应的命令分别是 g] g<C-]>。这三个命令前面也都可以额外加上 <C-W>,表示结果打开到新窗口中而非当前窗口。
Vim 默认只在当前目录下和文件所在目录下寻找 tags 文件。对于含多层目录的项目,这个设定就不合适了。解决方法是使用 Vim 的选项 tags。一个小技巧是根据项目的可能深度检查上层存在的 tags 文件:
" 加入记录系统头文件的标签文件和上层的 tags 文件
set tags=./tags,../tags,../../tags,tags,/usr/local/etc/systags
tags 选项的默认值是 ./tags,tags即检查文件所在目录下的 tags 文件和当前目录下的 tags 文件。上面这样的写法还会额外检查父目录下的 tags 文件,祖父目录下的 tags 文件,以及我们上面用 gen_systags 生成的 systags 文件。这对一个有不超过三层目录结构的项目来讲就足够了。如果你的项目目录层次更深,也只需要在 tags 选项里添加 ../../../tags 这样的内容即可。
Tagbar 插件
根据上面的描述,我们可以看到 Ctags 是一个可以从源代码中提取符号的工具。事实上,这个工具在我们不生成 tags 文件也都是有用的。Vim 的插件 tagbar 就可以利用 Ctags 来提取符号,生成源代码的结构图。只要 Ctags 能支持这种语言,插件就能“识别” 这种语言,来生成结构图;识别的好坏程度也视 Ctags 对其的支持程度而定。下面是一个示例:
跟之前类似,假设使用 minpac 的话,我们需要在 vimrc 中“Other plugins”那行下面加入下面的语句并运行 :PackUpdate 来安装一下:
call minpac#add('majutsushi/tagbar')
我给它映射了快捷键 <F9>,可以快速打开和关闭 Tagbar 的窗口:
" 开关 Tagbar 插件的键映射
nnoremap <F9> :TagbarToggle<CR>
inoremap <F9> <C-O>:TagbarToggle<CR>
Quickfix 窗口
Vim 里有一种特殊类型的窗口,被称作 quickfix快速修复。这个窗口中会展示外部命令的结果并可以通过这个窗口中的内容直接跳转到特定文件的特定位置。这个设计最初是用来加速“编辑-编译-编辑”这个循环的,但它的实际用处并不只是用来编译程序。
我们先来看一下 Vim 的 :make 命令。如果你的代码可以简单执行 make 来编译的话(也就是说,你已经写了或者生成了合适的 Makefile你可以尝试直接在 Vim 里执行 :make。你会看到正常的执行过程。唯一不一样的地方是如果编译失败了Vim 会自动跳转到第一个出错的位置!
如果使用 :copen 命令,我们就可以打开 quickfix 窗口。在里面我们可以看到完整的出错信息,并能通过颜色看出 Vim 解析了文件名和行号。我们在带文件名的行上双击即可跳转到对应位置。另外,我们在 quickfix 窗口中也有跟之前类似的“next”类命令
:cnext缩写 :cn跳转到下一个出错位置
:cNext缩写 :cN或 :cprevious缩写 :cp跳转到上一个出错位置
:cfirst 或 :crewind 跳转到第一个出错位置
:clast 跳转到最后一个出错位置
事实上,在这些下一个、上一个的命令中,我用得最多的就是这个快速修复里的跳转了。为了方便记忆,我对它们都映射了相似的快捷键。
" 用于 quickfix、标签和文件跳转的键映射
nmap <F11> :cn<CR>
nmap <F12> :cp<CR>
nmap <M-F11> :copen<CR>
nmap <M-F12> :cclose<CR>
nmap <C-F11> :tn<CR>
nmap <C-F12> :tp<CR>
nmap <S-F11> :n<CR>
nmap <S-F12> :prev<CR>
这是我的映射,你可以根据自己的需要进行调整。另外要留意的一点是,取决于环境,不是所有的快捷键都能被 Vim 接收到,尤其在使用终端和远程连接的时候。比如,在 Mac 上有些快捷键已经被系统占用,并且终端基本不接受修饰键;在 Windows 的远程连接客户端里PuTTY 不支持使用 Alt 的快捷键,但 mintty 就可以。
:make 命令的其他细节
Vim 里的 :make 命令缺省会执行 make 命令,并且这是可以通过选项 makeprg 来进行配置的。比如,如果你希望启用四路并发编译,你就可以设置 :set makeprg=make\ -j4。你也可以使用 GNU Make 之外的构建工具,但需要注意的是,如果发现 Vim 不能识别你使用的构建工具产生的错误信息,你可能需要利用 errorformat:help errorformat选项来告诉 Vim 如何处理错误信息。
:grep 命令
对我而言,跟构建使用频度至少一样高的命令是搜索,也就是根据关键字找到相关的源代码。这就可以使用 Vim 的 :grep 命令。跟 :make 命令相似Vim 会调用一个合适的外部程序(可通过 grepprg 选项来进行配置)来进行搜索,并从结果中找到文件名、行号等信息。注意:在 Windows 上如果 Vim 没找到 grep 的话,它会调用 Windows 自带的 findstr 命令行工具;为了获得跟其他平台相同的体验和跟 Vim 本身相似的正则表达式,我强烈推荐你在 Windows 上也安装 grep 工具。我们上一讲讲到的搜索模式,大部分在 grep 里可以原封不动地使用,尤其是对 \?、\+、\< 和 \> 的解释。考虑到 vi 源自 Bill Joygrep 源自 Ken Thompson两者的老祖宗都是 ed这自然也不是件令人意外的事。
如果使用 grep 命令的话,我们的命令大致如下所示:
:grep '要查找的符号' 文件名列表
当然grep 支持的复杂参数我们都可以用上。比如,下面的命令可以在所有的子目录里查找用到了 printf 的 .c 和 .h 文件:
:grep -R --include='*.c' --include='*.h' '\<printf\>' .
小提示:在查看搜索结果时,适时使用 zz或 zt、zb重定位当前行在屏幕上的位置可能可以更清晰地查看前后的相关代码。
异步支持
上面这些命令,都有一个缺点:在执行过程中你干不了其他事情。对于执行过程可能较慢的 make这个问题尤其严重。幸好在 Vim 8 支持异步任务之后,这个问题也得到了解决。我们利用一个插件,就可以获得类似在一些集成开发环境中的体验,在构建过程中仍然可以继续做其他事情。
我们首先需要安装一个插件 asyncrun.vim。跟前面类似假设我们使用 minpac 的话,我们需要在 vimrc 中的合适位置加入下面这行:
call minpac#add('skywind3000/asyncrun.vim')
我们还需要一个跟 :make 相似的命令。我使用下面的命令定义(今天我们重点看使用,定义的细节就不讨论了):
" 和 asyncrun 一起用的异步 make 命令
command! -bang -nargs=* -complete=file Make AsyncRun -program=make @ <args>
这个命令同样会使用 makeprg 选项。不过,还有个问题是默认情况下屏幕上看不到执行过程的信息。我们可以让 asyncrun 在执行命令时立即打开 quickfix 窗口:
" 异步运行命令时打开 quickfix 窗口,高度为 10 行
let g:asyncrun_open = 10
对于 C/C++ 程序员来讲,启动和停止构建应该是一个很频繁的操作吧。所以,我也给它分配了一个快捷键:
" 映射按键来快速启停构建
nnoremap <F5> :if g:asyncrun_status != 'running'<bar>
\if &modifiable<bar>
\update<bar>
\endif<bar>
\exec 'Make'<bar>
\else<bar>
\AsyncStop<bar>
\endif<CR>
上面的代码通过判断异步任务状态和窗口是否可修改,还会自动执行保存文件和终止构建等操作。建议你自己尝试一下。鉴于我们本讲内容已经很多了,我们暂时就不讲解了。
查看文档
Vim 里快捷键 K 可以用来查看光标下关键字的相关文档。它的行为是由选项 keywordprg:help 'keywordprg')控制的。这个选项的缺省值是 man表示查看 Unix 的 man 手册,很多文件类型插件会对当前缓冲区设置一个更合适的值,如 Vim 脚本就会直接把行为改成调用 :help 命令。
查看 man 手册的默认行为通常只在终端工作良好,而在图形界面 Vim 里会出现显示问题。我推荐使用 Vim 内置的 man 插件,并把全局的 keywordprg 设成 :Man
" 启用 man 插件
source $VIMRUNTIME/ftplugin/man.vim
set keywordprg=:Man
这样,我们在使用 K 命令时,将在 Vim 里直接打开 man 手册,效果如下所示:
你看,是不是就方便多了?
内容小结
今天我们讨论了 Vim 中对编程的基本支持,包括:
Vim 使用编程规则来判断文件类型,逻辑放在文件 filetype.vim 里。
Vim 里有很多设置文件格式的选项,自动设置一般在 ftplugin 和 indent 目录下;我们可以简单地在 vimrc 配置文件中进行定制。Vim 通过 cindent 和 cinoptions 选项,对类 C 的语言提供了相当细颗粒的缩进风格支持。
Vim 对 tags 文件提供了完整的支持Ctags 工具可以对超过 40 种主流编程语言生成 tags 文件,供 Vim 和 Tagbar 使用。
Vim 里通过 quickfix 窗口,对构建和搜索提供了内置支持;从 Vim 8 开始,我们可以使用异步支持,在构建时继续进行编辑。
Vim 里通过 K 命令,可以快速地查阅文档;通过 man 插件,我们可以直接在 Vim 里查阅 man 手册。
本讲我们的配置文件更改较多,请仔细检查一下其中内容。对应的标签是 l8-unix 和 l8-windows。
课后练习
今天的内容不难,但较多较杂。请你务必自己试验一下我上面总结的这些功能,来加深对这些功能的理解。要提高编辑的效率,熟悉基本功能一定是必要的。
不管你用什么语言编程,找出 Vim 是如何判断你的源代码文件类型的。
看看你能不能找出 Vim 对你使用的编程语言有没有附加的设置(可选)。
尝试一下 Ctags并用 <C-]> 来跳转到某个符号的定义。
如果你的语言有构建过程,尝试异步的构建过程。
尝试使用 :grep 命令来在你的源代码中搜索某一符号。
对于 C 系语言,尝试使用 K 命令来查阅文档。
我是吴咏炜,我们下一讲再见。

View File

@ -0,0 +1,216 @@
因收到Google相关通知网站将会择期关闭。相关通知内容
09 七大常用技巧:让编辑效率再上一个台阶
你好,我是吴咏炜。
学到这里,你应该已经初步掌握 Vim 了。我们这一讲来重点看一下 Vim 里的七大常用编辑技巧。有些技巧你直接就可以用,有些则需要安装第三方插件。但无论是哪种情况,它们都可以大大提高你的编辑效率。
从这一讲开始,我们将不再讨论插件的安装过程,而只是给出像 skywind3000/asyncrun.vim 这样的名称。相信你学到现在应该已经不需要我再详细讲述这样的基础知识了。如果你对安装插件还不太熟练的话,请复习第 4 讲。接下来,我们正式开始今天的内容。
自动完成
自动完成是一个编辑器中很主流的功能了。通常,我们希望编辑器能在我们输入一部分内容时就能猜到我们希望输入的是什么,并能够予以提示。自动完成可以节约我们输入的工作量,是一件编辑中非常必要的利器。
Vim 内置有自动完成功能。最基本的自动完成功能有两种:
基于当前文件文本的自动完成
基于文件系统的自动完成
我们先说基于当前文件文本的自动完成。在当前文件里,或当前文件用 #includeC 类语言的情况)包含的文件里包含某个关键字时,你可以输入头若干个字母并按下 <C-P>(表示 previous<C-N>(表示 next来进行自动完成。这两者的区别是<C-P> 是从当前位置往前找,而 <C-N> 是从当前位置往后找。当只有一个匹配项时Vim 直接给出完成结果,再次按下 <C-P><C-N> 则取消自动完成。当存在多个匹配项时Vim 会根据搜索顺序给出匹配项列表并使用第一个匹配项;再次按下 <C-P><C-N> 则可以在列表里进行选择。
Vim 的缺省选项能帮你在 Unix 系统上找到系统的头文件,利用里面出现的关键字来完成。想要在其他语言或平台里找到当前文件“包含”的文件里的关键字,请参考下列选项帮助:
:help include
:help includeexpr
:help isfname
:help path
我们再看一下基于文件系统的自动完成。当你在插入模式下输入一个绝对路径或者当前目录下的文件/目录名称的一部分时,你可以使用 <C-X><C-F> 来启动文件自动完成。在此之后,操作就和前面一样了,你可以使用 <C-P><C-N> 在匹配项中跳转和取消。
Vim 里还有其他一些以 <C-X> 开始的自动完成功能。比如,你可以用 <C-X><C-K> 从配置的词典中选择合适的单词,可以用 <C-X><C-O> 进行“代码自动完成”。但这些功能要么不常用,要么在缺省配置下工作得并不好。所以,今天我就暂时不讨论其他自动完成功能了。等到了提高篇和拓展篇,我们再来看英文文本编辑和代码自动完成这两个话题。
最后,要注意任何自动完成功能都可能会重复你的错误。如果你一开始拼错了,后面又拼对了,很可能会发现前面的错误。而一旦使用自动完成,你要是一开始就拼错了,后面可能就会不断重复之前的错误。这当然不是编辑器的错,但作为我曾经见到发生过的问题,我觉得值得提醒你一下。
文本目标跳转
如果光标下面是一个计算机可以找到的文件,你一定希望我们有办法可以一下子打开这个文件吧。这正是我们这一节要讨论的技巧。
当光标下的文件名可以在 path 选项标识的目录下找到时,我们可以很方便地跳转过去。你需要的是正常模式命令 gf 和 <C-W>f。估计你很容易猜到前者是直接跳转到文件理解为“goto file”后者则会打开一个新窗口window在新窗口里打开该文件。
如果光标下面是一个链接,或者非文本文件,那我们该怎么办呢?显然,即使 Vim 可以打开这个文件,看到的内容也多半不是你想要的(你想看图片,还是把图片当成文本的乱码?)。这时候,最简单的解决方式是使用 netrw 插件提供的 gx 命令。它的缺省行为是使用操作系统提供的机制来打开光标下的文件或链接。
比较让人伤心的是,最新版本的 netrw 插件在打开链接时的行为不正常。这个问题已经报告有一年了,还没有解决。作为临时方案,我在 Vim 配置的目录放了一个可以工作的老版本,你可以把这个文件复制到你的 Vim 配置目录下的 plugin 子目录下来绕过这个问题。此外gx 只适合本机,不适合在远程连接上使用。
Vim 寄存器/剪贴板
我们已经学到Vim 的删除和复制命令(如 d 和 y会把内容存起来以供粘贴命令如 p 和 P使用。我们还没有讨论这种内容存储有什么特别的地方。
首先估计你已经知道的是Vim 把要粘贴的内容存在 Vim 内部的“寄存器”register而非系统的剪贴板。你不一定知道的是Vim 里的寄存器有好多个。事实上Vim 有超过 40 个不同的寄存器!我们挨个来看一下:
首先是无名寄存器。当操作没有用 " 加寄存器名称指定寄存器时,我们默认使用无名寄存器。不过,我们仍可以使用 "" 来指定使用无名寄存器,也就是说,""p 和 p 效果相同。
其次是数字寄存器 0 到 9。0 号寄存器中放的永远是最近一次复制yank的内容。这和无名寄存器很不一样它里面放的是最近操作的结果也包括了 d、x、c 等命令特别是包括了粘贴命令所替换的内容。1 到 9 号寄存器中放的则是上一次、倒数第二次、直到倒数第九次被删除或修改命令删除的文本。在做少量的用一个名字替换另一个名字、而又懒得使用替换命令时,"0p 是一个接近图形界面里的粘贴命令的常用选择。
然后有小删除寄存器 -。上面我说得不全,删除内容进入 1 到 9 号寄存器的前提条件是被删除的内容至少有一行,或者使用了移动命令 %、(、)、`、/、?、n、N、{ 和 } 进行删除。否则,删除的内容只会进入 - 而不是 1 到 9 号寄存器。
常用的有名寄存器 a 到 z。这些寄存器仅在用户手工指定时才会使用内容在下一次打开 Vim 时仍然存在。比如,我们可以用 "ayy 代替 yy 把当前行复制到 a 寄存器中,以后就一直可以用 "ap 来进行粘贴了,直到 a 寄存器的内容被替换为止。
不常用的特殊寄存器 . 、:、# 和 %。这些相对来说不那么常用,请自行查看帮助文件 :help ". 等。
黑洞寄存器 _。专门用来删除目的就是不要影响无名寄存器的内容。
搜索寄存器 /。存放是上一次搜索使用的模式。
表达式寄存器 =。可以把 Vim 表达式估值的结果作为寄存器的内容。这个我们以后讲 Vim 脚本编程的时候再探讨。
最后是图形界面剪贴板寄存器 +、* 和 ~。一般而言,+ 寄存器代表操作系统的剪贴板,和图形界面应用程序交互用这个就好;你用图形界面 Vim 菜单里的拷贝和粘贴访问的也是系统剪贴板。* 和 ~ 在 X11 和 GTK 环境下有一些特殊用途,我们目前就不展开了。想深入钻研的话,可以查看帮助文档 :help "+、:help "* 和 :help "~。
寄存器在正常模式下可以用 d、y、p 等命令来访问,你现在应当已经很清楚了。它们在插入模式和命令行模式下也可以用 C-R 加寄存器名来访问,这经常也会省去你很多打字的麻烦。
这些寄存器当然不是每个都常用。具体你是否会用到它们,取决你的工作方式。下面我说几个我自己编辑时的常用场景。
常用的寄存器使用场景
如果要交换两行内容,可以直接利用删除命令会把删除的内容放到无名寄存器这个特性。我们在第一行上面按下 dd然后直接按 p 粘贴即可。
如果要交换两处文本内容,可以类似地使用删除和粘贴替换都会把内容放到无名寄存器这个特性。我们选中第一处文本,按下 d 进行删除;然后选中第二处文本,按下 p 进行粘贴;最后回到第一处文本的原来位置,使用 P 把文本粘贴回去即可。
如果要少量修改某一变量名称(多的话使用 :s 替换命令更合适),可以把光标移到变量名称上,用 * 进行开启自动搜索,然后编辑变量名称到合适;随后复制新的变量名称,反复使用 n 命令搜索,并用 ve"0p 进行替换即可。
当然,反复打 ve"0p 真的会感觉这个命令有点长。鉴于这个组合键使用的频度还挺高,我觉得映射一个更短的按键比较好,我的选择是 \v同时我做了点更通用的处理
" 替换光标下单词的键映射
nnoremap <Leader>v viw"0p
vnoremap <Leader>v "0p
关于 <Leader> 的含义,可查看帮助文档 :help <Leader>,里面说得很清楚,我就不重复了。如果你忘了 viw 的意义,请复习一下第 3 讲里的文本对象。
宏的录制和播放
Vim 里可以用 q 把动作记录到寄存器里,然后使用 @ 来播放这些动作。上面这个变量更名,如果用宏来做也可以:
用 * 开启搜索
键入 qa 开始录制宏到 a 寄存器;当然我们可以使用其他寄存器,只要被录制的命令不会修改这个寄存器即可,所以一般使用 a 到 z 这 26 个有名寄存器
键入 n 进行搜索;先行搜索的目的是,如果搜索不到内容,命令出错,宏的剩余部分就不会被执行
键入 eabar<Esc> 把 foo 修改为 foobar
键入 q 结束宏录制
键入 @a 播放录制的宏
重复上一步直到 Vim 报告找不到 foo 为止
关于宏的进一步细节可以查看帮助文件(:help q我就不展开了。
从上一节的 \v 到宏再到 :s 命令,对我们当前的任务而言,自动化程度逐步上升,但交互性逐步下降,“僵硬”性也逐步上升。对于重复遍数较多、信心较高的修改,我们应当偏向使用更自动化的方式,对于重复遍数较少或信心较低的修改,我认为使用不那么自动化的方式更有助于实时检查修改的效果。
今天关于寄存器和复制粘贴我们就讲到这里。我们以后还会有讨论到寄存器的时候。
文本对象增强
Vim 对文本对象的支持我已经在第 3 讲里讨论过了。那些当然是很不错的功能,不过,能不能在那些功能的基础上再进一步,做出更有用的功能呢?对于写了多个 Vim 插件的 Tim Pope 来说,答案是肯定的。
具体来说,如果你安装了他的 tpope/vim-surround 插件,你可以实现下面这些功能:
在一个单词的外面加上引号,如把 word 变成 "word",可以使用命令 ysiw"
把一个单词的外面的双引号变成单引号(有强迫症的 Python 程序员很可能有这样的需求),如把 "word" 变成 'word',可以使用命令 cs"'
把外面的引号或括号变成 HTML 标签也没有问题,如把 [my choice] 变成 <em>my choice</em>,可以使用命令 cs[<em>
可视模式也有类似的命令,如可以在选中 my choice 后,输入 S<em> 把文本变成 <em>my choice</em>
当然,你也可以把加上的包围符号移除,命令是 ds 后面跟包围符号,如 ds" 可以移除外围的双引号;要移除 HTML 标签则使用 t 来表达,即使用 dst 来移除文本外面的第一个 HTML 标签
注意 Vim 命令 . 只能用来重复 Vim 的内置命令,而不能用来重复上面这样的用户自定义命令。为了解决这个问题,我也会安装 tpope/vim-repeat 插件,使得重复命令对上面这样的情况依然能够生效。
撤销树
Vim 不仅支持多级撤销而且有撤销树的概念。利用撤销树你可以转回到编辑中的任何一个历史状态。不过问题是Vim 用来管理撤销树的命令不那么直观。在使用撤销树的图形化插件之前,我自己也没有把相关的命令真正用好。
著名的撤销树插件我知道两个,一个是 mbbill/undotree一个是 sjl/gundo.vim。两者功能相似界面风格和快捷键有所不同。鉴于 undotree 功能更加丰富,我就以它为例来介绍一下。
从下图中可以看到undotree 可以展示完整修改历史。你可以用 J 和 K 在历史中跳转,左下角的预览窗口中就会显示修改的内容,右侧文件直接会回到相应的历史状态,并加亮最近的那次修改。一旦用上这个插件,就真的回不到没有这个插件的环境了。
另外需要稍加注意的一点是一旦这个文件在其他编辑器里修改了Vim 发现内容对不上,就无法保留编辑的历史。有一个绕过方法是,当你需要使用其他编辑器修改前,确保你在 Vim 里打开了该文件并且所有修改已保存;这样,在修改完成之后,只要在 Vim 里用 :e 命令重新载入该文件Vim 就可以把外部的修改也保存在撤销历史记录里,保留完整的编辑历史。此外要注意的是,最后得在 Vim 里使用 :w 存盘一次,才能把编辑历史真正保存下来——即使你在 Vim 里没有进行任何修改,也需要这样做一下才能保存修改的历史。
对当前缓冲区的更名和移动
你肯定遇见过文件需要更名或者移动吧。这当然很简单,你可以通过图形界面或命令行进行操作。但这样操作之后,有一个问题是 Vim 的撤销历史跟文件就再也对不上了,你也没法再继续撤销更名或移动前的编辑操作了。有一个 Vim 插件,也是 Tim Pope 写的 tpope/vim-eunuch可以解决这个问题。
事实上,这个插件的功能远不止更名和移动。它实际上是把 Unix 的很多命令行工具搬到了 Vim 里(比较一下 Unix 和 eunuch 的发音你就知道这个插件的名字是什么意思了)。对我来说,最重要的就是它提供的 :Rename 和 :Move 命令,后面跟的参数就是新的名字或路径。这样操作之后,以后再打开这个更名或移动后的文件,仍然能够访问它的一切编辑历史。
模糊文件查找
使用 NERDTree 的话你可以通过浏览目录来打开文件。这种方式对于你知道文件在哪个目录下、但不知道文件名的时候特别有用。另外一种可能的情况是你知道文件名或其中的关键部分但你不知道或不关心文件在哪里。这种情况下Fzf 的模糊匹配就非常有用了。我们先来看一下动画演示,有一个初步的印象:
从动画可以看到,插件使用的是模糊匹配的方式,可以动态展示搜索的结果,并能直接预览当前选中的文件内容(在窗口足够宽的情况下)。因而这种方式不仅快,而且非常直观。
跟其他插件不同的是fzf.vim 插件依赖于 fzf 命令行工具。在 fzf 的页面上列出了具体安装方式,支持各个平台。但由于这个软件比较新,老一点的 Linux 发布版(如 Ubuntu 18.04 和 CentOS 7在包管理器里还没有 fzf。所以我的安装建议是
对于 macOS使用 Homebrew 命令 brew install fzf 安装。
对于 Ubuntu 19.10 或更新版本,使用 sudo apt-get install fzf 安装。
对于较老的 Ubuntu、CentOS 7 和其他页面中没有列出的 Linux 发布版直接使用二进制发布版本一般使用后缀为“linux_amd64.tgz”的文件。
对于 Windows也使用二进制发布版本使用后缀为“windows386.zip”32 位或“windows_amd64.zip”64 位)的文件。
在安装了 fzf 后(可以执行 fzf 来验证一下,它会枚举当前目录下的所有文件,并在你输入字符时缩小匹配;按 <CR> 选择文件,按 <Esc> 取消选择),就可以安装插件了。使用 minpac 的话,我们需要在 vimrc 中加入下面两行:
call minpac#add('junegunn/fzf', {'do': {-> fzf#install()}})
call minpac#add('junegunn/fzf.vim')
在安装完成之后,你就可以像我前面展示的那样使用 :Files 命令了。更多高级用法可以查看 fzf.vim 的页面。
顺便说一句,如果你对安装一个可执行文件有点发怵的话,插件也可以自动帮你下载 fzf 命令。但这样做的缺点是fzf 就只能在 Vim 里面使用了。如果你使用包管理器安装或手工安装fzf 可以在 Vim 里使用,也可以在 Bash 等其他地方使用——fzf 的 Bash 集成是可以大大提升 shell 的使用体验的,不过这不属于我们 Vim 课程要讨论的话题,就请你自行参阅文档了。
这个插件可以跟其他工具进一步配合。如果你安装了 ripgrep 和 bat 的话,可以获得更好的效果。动图中右下角文件预览的语法加亮的效果就依赖于系统里有 bat。如果你装了 ripgrep 的话,可以考虑设置下面的环境变量:
export FZF_DEFAULT_COMMAND='rg --files --sortr modified'
这样的话fzf 可以利用 ripgrep 来自动过滤掉被 Git 忽略的文件、隐藏文件、二进制文件等程序员通常不关心的内容,并将结果以修改时间倒排,确保最新修改的文件在最下面,大大提高了迅速找到你需要的文件的概率。
内容小结
今天我们讲述了不少编辑中的技巧。鉴于这些内容比较散、单项内容又比较小,我在这儿只对它们适用的场合作一下快速总结:
Vim 里有自动完成功能,可以让你只输入文本或文件名/路径的一部分,让 Vim 来帮你完成剩余部分。
反过来对于文件中出现的文件名和超链接Vim 也支持打开它们。
Vim 里的寄存器相当于几十个不同用途的自动剪贴板,用好它们,能更加高效地完成常见的编辑动作。
Vim 里的文本对象是个特色功能vim-surround 和 vim-repeat 插件又对其进行了进一步增强。我觉得这对前端程序员和 Python 程序员会特别有用。
跨会话撤销已经很强大了,而撤销树则让你能够充分发挥这个强大功能的潜力。
Vim-eunuch 插件可以让你在对文件进行更名和移动时仍然保留其编辑/撤销历史。
Fzf.vim 插件提供若干快速查找文件的工具,它的最基本命令 :Files 可以让你使用部分匹配的文件名快速地在当前目录或指定的目录下面的任一目录里找到你需要的文件。
本讲我们的配置文件修改也不少,包含了我们今天讲到的这些插件。对应的标签是 l9-unix 和 l9-windows。
课后练习
今天讲的主要是技巧,而非理论知识,所以最主要的课后练习,就是需要自己实践一下,把需要安装的插件也全部都装起来。如果有任何问题,可以留言和我讨论。
希望你根据你的实际使用场景,可以举一反三、融会贯通。比如,我讲到了交换两行,你能不能也能做到交换两列的内容呢?学以致用是掌握 Vim 这样的工具的唯一方法。
我是吴咏炜,我们下一讲再见。

View File

@ -0,0 +1,222 @@
因收到Google相关通知网站将会择期关闭。相关通知内容
10 代码重构实验:在实战中提高编辑熟练度
你好,我是吴咏炜。
在前几讲中,我们已经学了很多关于 Vim 的知识,现在需要好好消化一下。今天是基础篇的最后一讲,我们就基本上不学新的内容了,而是通过一个假想的代码重构实验,来复习、巩固已经学到的编辑技能。
开始前的准备工作
这是一堂实验课,你需要跟着我一步步地操作。跟只学习文字内容相比,实践操作能让你收获更多。所以,就请你现在把电脑准备好,跟我来吧。
今天我们将要做的是,签出我为极客时间写的 C++ 示例程序,并对其中的代码进行重构。别紧张,你不需要精通 C++,因为我会在必要的时候对代码进行解释。你学习的重点在于,我是如何进行编辑的,而不是我写的代码是什么意思。
首先你需要先为工作代码找一个合适的父目录然后用下面的命令签出代码Windows 下面去掉“\”全部写一行,或者把“\”换成“^”)):
git clone --recurse-submodules \
--shallow-submodules \
https://github.com/adah1972/geek_time_cpp.git
万一我以后更改代码的话,就有可能造成内容或路径发生变化。所以,请把我们今天编辑的 commit id 记下来632b067。如果你用 git log 看到 HEAD 的 commit id 不是它,可使用 git checkout 632b067 这个命令来签出跟今天完全相同的版本。
下面,我们就开始了!
类模板 smart_ptr 更名
我们第一步要做的,是把示例的 smart_ptr 类模板更名为 shared_ptr。同时为了避免跟标准的 shared_ptr 发生冲突,我们要把它放到名空间 gt里面去当然你可以用其他名字这只是我们的示例
大体思路是,先需要找到 shared_ptr 定义所在的文件,对其进行修改;然后找到使用该文件的地方,也进行相应的修改。下面我们就来做一下。
修改类定义
首先,我们需要进入 geek_time_cpp 所在的目录。如果你前面的命令就是 git clone 的话,那现在使用 cd geek_time_cpp 就可以了。
然后,我们当然是启动 Vim 了。假设我们知道 smart_ptr 被定义在 smart_ptr.h 头文件里,那我们最快的打开方式就是使用 :Files 命令然后输入“sm”即可看到“common/smart_ptr.h”成了第一选择。我们此时按下回车键即可打开文件。
进入文件后,我们先来看一下文件的结构。根据目前的 Vim 配置,我们可以使用 <F9> 打开 tagbar 插件。注意,这个文件使用了 C++11Exuberant Ctags 会有错误的识别。下面的截图是安装了 Universal Ctags 之后的结果:
我们可以看到这个文件比较简单里面主要就是两个类的定义和一些全局函数。不过我们还是要确认一下文件中没有任何会被错误匹配替换的内容。我们可以在右侧窗口里双击“smart_ptr”这样左侧窗口就会跳转到 smart_ptr 的定义上,并且光标停留在类名上面。这样,我们只需使用 * 启动搜索和加亮即可。使用 n 继续搜索,我们很快就能确认文件中确实没有冲突的内容。
下面,我们进行替换操作,需要键入的是 :%s/<C-R>//shared_ptr/g<CR><C-R><CR> 都是按键,而非小于符号后面跟其他字符)。我们不需要手工输入 \<smart_ptr\>,因为搜索寄存器 / 中已经有我们要的内容了。
最后,我们在第一个类定义的前面加上 namespace gt {、在最后的 #endif 前面加上 } /* namespace gt */,就完成了定义的修改。
不过,现在文件名还没有更改,文件里的包含保护(即宏 SMART_PTR_H也没有更改。包含保护需要简单的重命名就请你用我们目前介绍的任一方法自己完成了。随后我们用命令 :Rename shared_ptr.h 即可完成更名和存盘操作。
修改使用 smart_ptr 的地方
我们先试着用下面的命令搜索一下:
:grep -R --include="*.cpp" --include="*.h" "\<smart_ptr\>" .
(小提示:在查看搜索结果的时候,适时使用 zz、zt 和 zb 命令,可以把周边的代码看得更清楚。)
使用 :cn (或我们定义的快捷键)仔细检查搜索出来的结果,我们会发现有一些误匹配:有 smart_ptr 是 unique_ptr 的情况,也有 smart_ptr 是策略类的情况。
我们稍微改换一下方法,搜索对 smart_ptr.h 的使用:
:grep -R --include="*.cpp" --include="*.h" "\<smart_ptr.h\>" .
这样的话,我们会发现结果只有一个匹配,那就简单了。
在上一讲里,我们已经讨论了在这种情况下进行修改的三种不同方法(忘了?请回过去复习一下)。今天,我们用第四种方法。这种方法的每一步我们实际上都讲过,但串起来用,你可能就没有试过了。我们使用的基本命令是 cw、n 和 .。
由于之前搜索过 smart_ptr我们现在仍然可以继续使用 n 找到需要修改的地方。我们随即需要键入的,是 cwgt::shared_ptr<Esc>。这样输入虽然有点长、有点啰嗦,但它的好处是整个修改会被 Vim 看作是一步,因而可以用 . 命令来重复。这样,下面我们只需要反复利用 n 和 . 命令,把除了 #include 那行之外的所有 smart_ptr 都改成 gt::shared_ptr 即可。
很显然,这并不是唯一的方法,也不一定是最好的方法。所以,我建议你在这里暂停一下,用 :e! 重新载入这个文件,试试使用上一讲提到的其他方法。我这里就仅仅再给你展示一下如何使用替换命令,同时又不会误匹配文件名:
:%s/\<smart_ptr\>\ze\%([^.]\|$\)/gt::shared_ptr/g
这个匹配模式说的是我要查找完整的单词“smart_ptr”这就是要替换的内容了但是在匹配结束\ze我还有两个额外的匹配要求用 \%( 和 \) 括起来),要么不是句点([^.]),要么(\|)是行尾($)。
我们最后把唯一残留的 smart_ptr.h 修改成 shared_ptr.h就完成了 smart_ptr 的更名任务。
编译执行(可选)
如果你懂 C++,并且有 geek_time_cpp 的 README 文件里要求的执行环境的话,可以选择体验一下编译执行。
我们需要先在 02 目录下创建并进入 build 子目录,然后运行 cmake ..。随后,在 Unix 环境下,一般可立即使用快捷键 <F5> 进行编译;想要在 Windows 下也能正常进行编译,我们则应当设置 set makeprg=cmake\ --build\ .\ -j老版本的 cmake 可能不支持 -j 命令行参数的话,这样的话,我们会没法用 cmake 进行并发编译;不过对于我们的小例子没啥关系)。
另外一个要注意的地方是Vim 在缺省配置下不能识别 Visual C++ 的错误输出格式 。为了能进行识别,并在发生错误时跳转到文件的指定位置,我们需要设置下面的选项:
set errorformat=\ %#%f(%l\\\,%c):\ %m
目前来讲,环境没问题的话,我们就会……遇到编译错误。
原因是 dynamic_pointer_cast 前面也需要加上 gt::。做了这个修改之后,我们就应该可以顺利编译出可执行文件了。在 Windows 下使用命令 :!.\Debug\sp_test02_shared_ptr或在 Unix 平台下使用命令 :!./sp_test02_shared_ptr我们即可在终端看到下面的输出
circle()-
use count of ptr1 is 1-
use count of ptr2 was 0-
use count of ptr2 is now 2-
ptr1 is not empty-
use count of ptr3 is 3-
~circle()
同时,如果愿意的话,我们也可以使用 AsyncRun 提供的机制,在 Windows 下使用命令 :AsyncRun .\Debug\sp_test02_shared_ptr或在 Unix 平台下使用命令 :AsyncRun ./sp_test02_shared_ptr异步运行程序并把输出重定向到 quickfix 窗口里。
添加跟踪语句
假设我们对这个代码执行过程有些疑问,想添加些跟踪语句,该怎么做呢?
我们首先需要在一个新窗口中打开 common/smart_ptr.h。由于我们第一个打开的文件就是它所以它的缓冲区编号为 1我们可在用 <C-W>n 打开一个新窗口后,使用 1<C-^> 飞速地重新打开文件。
我们希望对引用计数的增、减、删除等操作进行跟踪。最简单的方式,当然就是执行对应操作的时候,把执行的语句也输出一下。像这样简单的机械化操作,显然就是宏的天下了。我们来试一下。
我们先来改造一下 smart_ptr 析构函数里面的第一个 delete ptr_。一个可能的操作步骤是
复制当前行
粘贴当前行
选中行首缩进后、结尾分号前的内容,套上双引号
在这个新对象前后插入输出所必须的命令
我们需要录制的宏的内容是 yyPv$hS"gvS)iputs<Esc>l%a;<Esc>,而你把这一串东西用 nmap 命令映射给某个按键上也完全可行(注意,此处不能用 nnoremap因为我们需要使用 vim-surround 插件带来的新的 S 按键的定义。当然在交互的环境中录制按键会比眼睛看这个字符串容易理解多了。Vim 的宏,就其本质而言,可算是一种只写不读的简单过程式语言。
我们用到的命令里,只有 gv 是之前没有学过的。我们当然也有其他方法来选中行中的内容,但 gv 的作用是重新选中刚才选中的内容,最快,也最方便。
利用这个宏,我们可以把添加调试语句变成按两个键。哦,对了,宏一旦执行过后,第二次执行同一个宏只需要键入 @@ 即可,这样还能更快些。
在我们把所有的 delete 语句和 add_count 函数调用行上执行了这个宏之后,我们运行程序可以得到下面的结果:
circle()-
use count of ptr1 is 1-
use count of ptr2 was 0-
other.shared_count_->add_count();-
use count of ptr2 is now 2-
ptr1 is not empty-
other.shared_count_->add_count();-
use count of ptr3 is 3-
delete ptr_;-
~circle()-
delete shared_count_;
如果想对这个代码作进一步调整,类似操作即可,相当容易吧?
调整测试用例
我们现在使用鼠标点击或者 <C-W>j 等命令跳转到测试代码 test02_shared_ptr.cpp 中。我们随即使用 <C-W>_ 命令来最大化窗口,因为似乎暂时用不着编辑 smart_ptr.h但还不那么确定否则就可以直接关闭那个窗口了
我们打算在 ptr1 不为空的那个条件判断下面再加点内容。那行输出看着也挺无聊的,我们就直接把它干掉了。我们可以在那组大括号内的任意地方点击后,使用 ci{ 开始编辑,然后输入以下内容:
printf("ptr1 %s ptr2\n",
ptr1 == ptr2 ? '==' : '!=');
代码编译居然有奇怪的告警出现……我是 Python 写多了,脑子没转回来吗?没关系,在第一处单引号内部键入 cs'",然后在第二处单引号内部键入 . 重复一下就好,现在代码应该是正确的了:
printf("ptr1 %s ptr2\n",
ptr1 == ptr2 ? "==" : "!=");
再次编译,完美,没有问题了!运行程序,我们得到:
circle()-
use count of ptr1 is 1-
use count of ptr2 was 0-
other.shared_count_->add_count();-
use count of ptr2 is now 2-
ptr1 == ptr2-
other.shared_count_->add_count();-
use count of ptr3 is 3-
delete ptr_;-
~circle()-
delete shared_count_;
内容小结
今天我们尝试对一小段 C++ 代码进行了简单的重构。在这个过程中,我们使用和复习了下面这些编辑技巧:
使用 fzf.vim 来根据部分文件名迅速打开文件
使用 tagbar 来浏览文件的结构
使用 vim-eunuch 来进行文件更名
使用替换命令来进行批量代码更名
使用 . 命令技巧来进行批量代码更名
使用 <C-R> 在插入模式和命令行模式中使用寄存器的内容
使用 :grep 命令在文件中进行文本搜索
使用异步的构建命令,并设置选项使得错误信息解析在 Visual Studio 工具里也能工作
使用文本对象命令对用括号、引号等符号包起来的文本进行统一的修改
使用宏,在一次操作之后,在遇到类似场景时可以快速修改
虽然今天的代码是 C++ 的但这些编辑方式适用于任何语言。请你一定要牢牢掌握。我们也应该慢慢看到了编辑的一个要点在于把需要重复的工作自动化和简单化。Vim 作为一个程序员的编辑器,提供了灵活而强大的编辑机制——最终用户,或扩展包的开发者,都可以利用这些底层机制,使编辑变得更加高效。
本讲我们对 Windows 下的 vimrc 配置文件有一处小修改对应的标签是“l10-windows”。
课后练习
实验课中的内容你已经一一尝试了吧请你再向前一步想一想我们的每次编辑是否可以有不同的执行方式及哪种方式对你最顺手。Vim 的命令一定是在使用中才能熟练应用的。你不一定要记住所有可能的编辑方式,但每一种最好都至少尝试一次,然后找出最适合自己的、最能牢牢掌握的编辑方式。
我是吴咏炜,我们下一讲再见!

View File

@ -0,0 +1,199 @@
因收到Google相关通知网站将会择期关闭。相关通知内容
11 文本的细节:关于字符、编码、行你所需要知道的一切
你好,我是吴咏炜。
从今天开始,我们进入提高篇和拓展篇的学习。
在提高篇,我会带你对 Vim 的高级用法和技巧进行专项突破,让你可以对 Vim 做深度定制,应对复杂的工作也不在话下。
在拓展篇,我会介绍一些针对性较强的内容,适合特定场景下的 Vim 使用。为了平衡一般性和特殊性让你拥有更舒适的学习体验拓展篇将会与提高篇交叉发布。如果一时用不到相关的知识拓展的内容可以暂时延后学习。但一旦你需要这些知识时你会发现原来如此Vim 是可以这么使用的!
Vim 是一个文本编辑器,很多人甚至把它称为“编辑器之神”。在基础篇中,你已经了解了很多用 Vim 编辑文本的常用技巧。可是你有没有想过,到底什么才算是文本?在提高篇的第一讲,我们就先来细细分析一下,关于文本你需要知道的一切知识。这会让你更好地理解编辑时出现的一些奇怪问题(如“乱码”),并予以恰当解决。
什么是文本
从二元论的角度看计算机文件可以分为文本文件text file和二进制文件binary file但这个分法并没有对文本做出清晰的界定。从实用的角度我们大致可以这么区分
文本文件里存放的是用行结束符EOL即 End of Line隔开的文本行二进制文件里则没有这样的明确分隔符
文本文件可以通过简单、直接的算法转换为人眼能够识别的文字,而二进制文件里含有不能简单转化为文字的信息
我这个描述当然还是有点含糊。事实上,计算机判定一个文件是不是文本文件,并不是件容易的事情,特别是在这个文件含有非 ASCII 字符的时候。曾有一些操作系统(如古老的 Apple DOS会明确区分文件的类型但现代的操作系统基本上在文件系统层面完全不关心文件的类型和里面的内容了。因为操作系统不对文件类型进行限定会更加灵活。
但我们还是需要关心的,因为 Vim 最适合编辑的,就是文本文件了。从实用的角度,我对文本文件的判定通常是:
一个文本文件可以直接输出到终端上,或在简单的编码转换后输出到终端上,显示为一行或多行可识别的字符,并且不包含乱码。
想要理解这句话,你得先知道什么是字符?什么是编码?什么是行和行结束符?下面我就来为你一一解说。
字符和编码
从文件系统的角度看文件的内容就是一堆比特bit而已。把比特对应到字符的方法就是编码encoding。在目前的主流操作系统里通常八比特是一个基本单位也就是字节byte。最基本的编码方式就是把一个字节对应到一个字符。
目前的大部分编码方式,在 0127 的范围里,字节值和字符的对应关系是基本相同的。除了个别字符外,编码的基本方式都和 ASCIIAmerican Standard Code for Information Interchange美国信息交换标准代码兼容如下图所示
注意,头 32 个字符和最后一个字符是控制字符,其中大部分现在已经很少有人使用了,但还有一些我们今天仍然会在不同的场合遇到,如马上就会讨论的 LF 和 CR。
ASCII 是美国标准里面只有基本的拉丁字母对其他国家来讲可能就不合适。比如对欧洲国家来说ASCII 既没有带变音符的拉丁字母(如 é 和 ä ),也不支持像希腊字母(如 α、β、γ)、西里尔字母(如 Пушкин)这样的其他欧洲文字,使用起来很不方便。很多其他编码方式使用了 128255 的字节值范围作为扩展,总共最多是 256 个字符一次允许一套方式生效称之为一个代码页code page。这种做法只能适用于文字相近、且字符数不多的国家。比如下图表示的 ISO-8859-1也称作 Latin-1和后面的 Windows 扩展代码页 1252下图中绿框部分为 Windows 的扩展),就只能适用于西欧国家。
最早的中文字符集标准是 1980 年的国标 GB2312其中收录了 6763 个常用汉字和 682 个其他符号。至于我们平时用到的编码 GB2312它更准确的名字其实是 EUC-CN是一种与 ASCII 兼容的编码方式。它用单字节表示 ASCII 字符而用双字节表示 GB2312 中的字符;由于 GB2312 中本身也含有 ASCII 中包含的字符,在使用中逐渐就形成了“半角”和“全角”的区别。
国标字符集后面又有扩展,这个扩展后的字符集就是 GBK是中文版 Windows 使用的标准编码方式。GB2312 和 GBK 所占用的编码位置可以参看下面的图(由 John M. Długosz 为 Wikipedia 绘制):
图中 GBK/1 和 GBK/2 为 GB2312 中已经定义的区域,其他的则是 GBK 后面添加的字符,总共定义了两万多个编码点,支持了绝大部分现代汉语中还在使用的字。
显然,多个不同的编码方式是不利于信息交换的。我们在打开文本文件时看到的“乱码”,最常见的情况就是文件的编码和打开文件的工具以为的编码不同。毕竟,只要出现了非 ASCII 字符,解释方式就多了。对我们来说,常见的情况是 Latin-1/Windows-1252西欧文字、GBK简体中文、Big5繁体中文今天还增加了 UTF-8。
我们终于说到了 UTF-8它的全称是 8-bit Unicode Transformation Format8 比特的 Unicode 转换格式。Unicode 自发明伊始就是为了统一编码问题但它的最早编码方式UCS-2存在两个重大问题
和 ASCII 不兼容,不能在现有软件和文件系统中直接使用
在储存 ASCII 为主的字符时,存在一字节变两字节的空间浪费
Ken Thompson 在 1992 年和 Rob Pike罗勃 · 派克)一起发明了 UTF-8解决了这两个问题牛人就是牛人啊。到了今天UTF-8 已经成了互联网和 Unix 世界里文本文件(含 HTML 和 XHTML的主流编码方式。但是Windows 下的文本文件,由于历史原因,可能还大量使用着传统的编码方式(很错误地被叫做 ANSI对于中文 Windows这个传统编码就是 GBK 了。
抛开编码方式的细节(从网上你可以找到足够多的关于 Unicode 和 UTF-8 的资料我们需要牢牢记住的是UTF-8 是 Unicode 里最重要的编码方式,可以把一到四字节长度的字节序列映射成为一个 Unicode 字符。目前我们使用的任何字符都可以用 UTF-8 表示,因而 UTF-8 是我们在 Vim 中使用的内部编码(选项 encoding。我们在第 2 讲中给出 fileencodings 选项设置,就是为了在读写文件时把文件内容进行适当的转换。这个选项表示的是自动检测使用的编码;而在文件被 Vim 载入后,文件的编码会出现在选项 fileencoding 里。如果 fileencoding 选项为空,则表示文件保存时不做任何转换。
关于编码我们暂时讨论到这里。下面我们讨论一下字符character和字形glyph
字符和字形
Unicode 设计时的一个决定,目前看起来有点短视,那就是对中日韩文字中使用到的汉字进行了“统一”。如果字源相同,它们在 Unicode 中就只占据一个编码点。于是,一个字符可能就有多个字形。这个问题,我在第 2 讲中已经展示过了,它也是我们可能需要在图形界面 Vim 中单独设置宽字符字体guifontwide的原因。
跟中文字符集中“半角”和“全角”的概念有点像Unicode 中也有字宽的概念。和简单的半角与全角的区别不同Unicode 里除了窄字符和宽字符还有模糊宽度ambiguous width字符。这些字符的宽度根据上下文而定在东亚文字里一般是宽字符而在西方文字里一般是窄字符。最常用的模糊宽度字符有“U+”后面跟十六进制数值是用来表示 Unicode 字符所占编码点数值的通常方法):
U+00B0「°」
U+00B7「·」
U+00D7×
U+00F7「÷」
U+2014「—」
U+2018
U+2019
U+201C「“」
U+201D「”」
U+2103「℃」
对于某一特定字体它们的宽度当然就是确定的尤其使用变宽字体大部分英文字体不同字符宽度不同如在极客时间的正文里这个模糊宽度没有什么意义。对于使用等宽字体程序员一般使用的字体Vim 只能用等宽字体)的文本编辑器,到底是把这些字符显示成跟 ASCII 字符一样的“单”宽度,还是显示成跟汉字一样的“双”宽度,就是一个需要考虑的问题了。
稍微展开一点点,这个模糊宽度,在我们日常生活中还是造成了一点麻烦的。非常常见的一个排版错误,就是由于使用的软件(在中文 Windows 下的)的字体选择规则,西文中的 「’」误用了中文字体展示,导致这个符号展示出来的字间距过宽。一个相反的麻烦,是中文中写「·」希望两侧留空很足,但在另外一些环境下,永远优先选择西文的字体(如大部分的手机操作系统),导致需要手工两侧加空格才能有比较理想的排版效果……
扯远了,这些毕竟不是 Vim 的问题。Vim 里的解决方式是提供选项 ambiwidth可以设为 single默认值或 double表示 Vim 到底把这些字符的宽度当成是占一个字符还是两个字符,你想怎么样都可以。对于终端 Vim由于 Vim 不能决定显示的字体,这个选项只能决定光标在这些字符上应当移动的列数,用户必须自己保证在终端里的设定和 Vim 的设定是一致的;否则,可能导致眼睛看到的编辑位置和实际编辑位置不一致。虽然 macOS 的终端应用、Linux 的 GNOME Terminal 和 Windows 下的 PuTTY 都提供了如何处理模糊宽度字体的设定关键字是“模糊”或“ambiguous”但鉴于这些软件的字体选择策略选择“宽”容易导致显示问题所以我的建议是保留缺省的“窄”设定。
对于图形界面的 Vimambiwidth 选项同时也决定了显示这些模糊宽度字符是使用 guifont 选项还是 guifontwide 的设定。在这种情况下,把 ambiwidth 设成 double 才比较有意义:
修改 ambiwidth 主要影响的是一行的长度,而 Vim 具有根据行长来进行断行的功能。下面,我们先来看一下什么是行。
从 Vim 和 Unix 的角度看一个文本文件由多行构成每一行都以一个行结束符EOL结束。根据传统习惯这个 EOL 在存盘时使用的字符是 LF编码值是 10U+000A
这只是 Unix 格式。常用的还有 DOS 格式(也包括了 Windows以及老的 Mac 格式。
在 DOS 格式里,行尾就不只使用 LF 这一个字符了,在 LF 前面会多一个 CR编码值为 13U+000D。这个用法的来源是以前的打字机CR 表示机架归位carriage returnLF 表示换行line feed。在使用 CR LF 作为行结束符的系统里CR 只负责光标回到第一列,而 LF 负责光标向下一行。
老的 Mac 则使用单个 CR 字符作为行结束符,但苹果从 Mac OS X2001 年)开始就使用了 Unix 风格的行结束符。所以,目前我们遇到的文本文件,应当都使用 LF 或 CR LF 作为行结束符了。这也是 Vim 的 fileformats 选项的意义:它的默认值通常是 unix,dosUnix 环境下)或 dos,unixWindows 环境下),即会自动检测 Unix 和 DOS 行尾;如果检测不到,则以第一个风格设置作为默认值。
fileencodings 有一个对应的文件相关的 fileencoding 选项跟它一样fileformats 也对应有一个文件相关的 fileformat 选项,表示当前文件的行尾风格。需要注意的是,如果一个文件里既有 LF 行尾、又有 CR LF 行尾的话Vim 会把文件当成 Unix 格式,于是文件里会出现最后一个字符显示成“^M”通常为蓝色表示是控制字符跟正常文本不同的情况。如果你想保留这种行尾那不需要做任何事情。但绝大多数情况下你会希望把行尾统一成 Unix 风格或 DOS 风格。此时,你可以使用下面两种方法之一:
使用 :e ++ff=dos 命令强制以 DOS 行尾加载文件;此时文件的行尾格式是 dos。
使用 :%s/\r$// 命令删除行尾多余的 CR 字符;此时文件的行尾格式保持 unix 不变。
此外再说明一下Unix/Vim 的传统是任何一行都以行结束符终结,包括最后一行。使用 Vim 编辑的文本文件,最后一个字符通常是 LF除非使用 Mac 行尾风格,则结尾是 CR。Windows 上大部分文本编辑器则允许最后一行不以行结束符结束;这样的文件在 Vim 打开时Vim 默认会给出一个“[noeol]”的提示。在存盘时Vim 则会自动在最后添加一个行结束符。
除了 Vim很多 Unix 工具都会有类似的要求。比如,用于文件比对的命令行工具 diff它在文件比对时如果输出下面的信息就是表示文件之一没有用行尾结束符来结束
\ No newline at end of file
断行
中文文本文件的行文习惯通常是在一段之中不空行一段结束了再换行。文本编辑器需要做的是在行长超过屏幕宽度时自动折行。Vim 虽然也能在这种情况下自动折行,但 Vim 的更惯常用法是欧洲字母文字和源代码的做法,行长有一定的限制(根据惯例,常用值是 72、80、120到了指定的行长则应当进行断行用一个空行来明确表示分段。这也是 Markdown 格式里的标准做法:单个换行符仅相当于空格而已。(这个额外插入的空格就是中文一段之中不换行的原因。)
Vim 有一个文本宽度的选项 textwidth表示插入文字时的最大行宽度。这个选项的全局默认值为 0表示不进行限制但 Vim 脚本可能会设置它,你也可以自己在 vimrc 等地方对其进行设置。我自己的设置是文件相关的,如:
au FileType changelog setlocal textwidth=76
这个设置,加上对行进行格式化的命令 gq可以让你方便地对英文文本进行整理。gq 命令跟 c、d 等命令一样,可以先在可视模式下选定文本,也可以在命令之后跟动作键。对于源代码,它的妙处在于它知道什么是注释,什么是列表:
如果对这些功能有兴趣的话,请查看相关的帮助::help gq 和 :help fo-table。我这儿特别要指出的是
要能够在无空格的中文之中断行,我们需要有 :set formatoptions+=m
选项 ambiwidth 会影响行宽的判断,如左右弯引号的宽度算 1 还算 2
在 Vim 8.2.0901 之前Vim 断行时不考虑中文标点符号的规则;要使用 gq 对中文文本断行,最好升级到这个版本或更高版本
编辑二进制文件
到这里你已经知道什么是文本和关于文本的基本知识了。Vim 当然是一个文本编辑器,但在某些情况下,它也是可以用来编辑二进制文件的。有几个工具在你必须用 Vim 编辑二进制文件时会有帮助。
首先Vim 有个 binary 选项和一个 -b 命令行参数。当你通过 -b 命令行参数,或 :e ++binary … 命令来打开文件时binary 选项会自动被设置用户不应该手动设置该选项。这个选项保证了Vim 在读取和存储文件时,不会做会影响文件内容的转换和修改。
不过,即使有这个选项,二进制文件打开后仍然是一堆乱码,这当然是正常的。你除了可以在里面搜索文本之外,还可以利用 Vim 的 Tools工具菜单下的“Convert to HEX”转换成十六进制和“Convert Back”转换回两项来对二进制文件进行编辑。下面的两张图显示了打开二进制文件后的样子和使用了“Convert to HEX”后的样子
不管你是要检查文件中的具体字节内容还是要修改某个字节HEX 格式都更方便一些。当然如果你要把修改写回硬盘的话一定要先使用“Tools > Convert Back”。
如果你有专门的二进制编辑工具的话Vim 的这个功能可能不那么有用。如果正好你没有安装其他的二进制文件编辑工具,那这个功能还是可以救救急的。
要是你使用的不是图形界面,菜单里的这两个命令可以用 :%!xxd 和 :%!xxd -r 来手工替代。
内容小结
这一讲我们讨论了什么是文本,包括:
文本是用行结尾符隔开的、使用某种特定编码的字符序列
UTF-8 是目前最主流的编码方式,但我们仍然可以对个别文件使用不同的编码
字符和字形在 Unicode 下并不是简单的一一对应,我们需要语言相关信息才能保证正确的显示
目前主流的行尾格式是 Unix 和 DOSVim 都支持,并且可以自动判断
Vim 支持设置特定的行宽,然后据此来进行断行,且对代码注释、数字列表等文本形式有特殊支持
虽然 Vim 是文本编辑器,但在需要的时候,我们也可以使用 Vim 来查看和编辑二进制文件
本讲我们对 vimrc 配置文件有一处小修改对应的标签是“l11-unix”和“l11-windows”。
课后练习
文中提到的内容,你都应该手工尝试一下。除此之外,如果你平时接触到 GBK/GB18030 和 UTF-8 之外的其他文本编码的话,你可以考虑查看一下插件 FencView注意在 Windows 下你现在也不需要下载 iconv.dll 了Vim 8 的 Windows 安装包中现在已经包含了 libiconv-2.dll
如果你使用的是 Linux 或 macOS 的话,可以键入 iconv -l 来看一下 libiconv 支持的编码方式Vim 内部就是使用 libiconv 来实现编码转换的。
过去我们会有一些文件用后缀表示文件编码,比如 .gb 是 gbk 编码,.big5 是 big5 编码,.nfo 是 cp437 编码,你能想出如何正确载入这些文件的方法吗?
今天就到这里了。你的回答或问题,都可以留言告诉我。我们下一讲再见!

View File

@ -0,0 +1,219 @@
因收到Google相关通知网站将会择期关闭。相关通知内容
12 语法加亮和配色方案:颜即正义
你好,我是吴咏炜。
语法加亮这个功能,我们都非常熟悉。和 vi 刚出现的时代不同,它现在已经成为编程的基本功能了。在我们使用的各种代码编辑器中,都有语法加亮的功能。我们甚至可以拿一句俗语反过来说:没见过猪跑,还能没吃过猪肉么?
但是,你有没有想过,语法加亮到底是怎么实现的呢?今天,我们就不仅要尝尝不同“风味”的猪肉,还要进一步看看猪到底是怎么跑的——这样,我们才能选择,然后调整出,最符合自己口味的大菜。
语法加亮
在[第 8 讲]里我们已经提到Vim 的语法加亮依靠的是在 syntax 目录下的运行支持文件。今天我就通过例子给你解说一下Vim 里如何实现语法加亮,然后语法加亮又如何映射到屏幕上的颜色和字体。
我们先来看一个比较简单的例子xxd。
xxd 这个名字看起来,是不是有点陌生又有点熟悉?其实,我们在第 11 讲还刚讲过 xxd它是一个把二进制文件转换成地址加十六进制数值再加可读 ASCII 文本的工具,它的输出格式在 Vim 里也被称作 xxd。不过在用菜单项或 :%!xxd 命令转换之后Vim 并不会自动使用 xxd 格式。要应用 xxd 格式的语法加亮,我们需要使用自动命令(可以参考 :help using-xxd或者手工使用命令 :setf xxd。下图是对上次的二进制文件使用了 xxd 语法加亮的效果:
这个格式的语法加亮足够简单,我们就拿它来分析一下。不过,我有个小建议,你在看具体的语法加亮代码前,先花几秒钟的时间看一下图,自己分析一下里面有几种不同的语法加亮效果。
下面我们就来逐步看一下 syntax/xxd.vim 的内容。首先是开头和结尾部分:
" quit when a syntax file was already loaded
if exists("b:current_syntax")
finish
endif
let b:current_syntax = "xxd"
" vim: ts=4
最后一行的模式行,设定了这个文件使用的 tab 宽度。剩余部分基本上算是语法文件的固定格式了,有一个检查缓冲区变量(使用前缀 b:)、防止语法文件重复载入的条件判断,并在结尾设定这个缓冲区变量为语法的名称。
剩余部分可以分为两段。第一段是语法匹配:
syn match xxdAddress "^[0-9a-f]\+:" contains=xxdSep
syn match xxdSep contained ":"
syn match xxdAscii " .\{,16\}\r\=$"hs=s+2 contains=xxdDot
syn match xxdDot contained "[.\r]"
这儿定义了 4 种不同的“语法项目”,其中 1、2 和 3、4 还互相有包含“contains”和被包含“contained”的关系。
xxdAddress。它是地址匹配所以匹配条件是从行首开始的一个或更多的十六进制字符后面跟一个冒号。
xxdSep。它是分隔符仅匹配 xxdAddress 中的冒号部分,也算是地址的一部分。
xxdAscii。它是右边的 ASCII 字符部分,条件是两个空格后面跟最多 16 个字符,然后是可选的 CR 字符(\= 和 \? 效果相同),然后必须是一行结束。
xxdDot。它是对“.”和 CR 字符的特殊匹配,可以留意一下上面图里“.”和其他字符的加亮效果的不同之处。同样,这个句点也属于 ASCII 字符部分。
上面的正则表达式都比较简单,唯一之前没出现过的是第 3 个正则表达式后面的 hs=s+2它的含义是语法加亮的起始位置是模式匹配部分的开始位置再加 2可查看 :help :syn-pattern-offset这是在语法加亮文件里的常用特殊语法。
上面的代码可以从 xxd 格式的内容中找出 4 种不同的语法格式。如何展示这些语法,就要看下面的第二段代码了:
" Define the default highlighting.
if !exists("skip_xxd_syntax_inits")
hi def link xxdAddress Constant
hi def link xxdSep Identifier
hi def link xxdAscii Statement
endif
外面的条件语句不是惯用法,我们可以忽略。里面重要的是三个 hi def link 语句,拼写完整的话是 highlight default link可参见帮助 :help :highlight-link。这三个语句建立了默认的语法加亮链接组也就是在用户没有自己在 vimrc 配置文件中使用 highlight link 来修改语法加亮时,默认的语法项目和加亮组之间的关系。目前,地址 xxdAddress 使用常数 Constant 的加亮方式,冒号分隔符 xxdSep 使用标识符 Identifier 的加亮方式ASCII 文本 xxdAscii 使用语句 Statement 的加亮方式。
那 xxdDot 到哪儿去了呢?答案是,它没有加亮组,因为我们不需要对其进行特殊加亮。虽然 Vim 会认出它使用了特殊的语法格式,在显示上它和中间的十六进制数值一样,没有任何语法加亮效果。
Constant、Identifier、Statement 这些加亮组,又应该以何种方式展示呢?这就是配色方案要做的事情了。如果说语法加亮是逻辑问题的话,那配色方案就是个审美问题。你要个性化的话,就靠配色方案了。
配色方案
类似地,配色方案里包含的也是一些模板语句加上色彩的定义。比如,在配色方案 koehler 里,跟 xxd 相关的核心色彩定义是:
set background=dark
hi Normal guifg=white guibg=black
hi Constant term=underline cterm=bold ctermfg=magenta guifg=#ffa0a0
hi Identifier term=underline ctermfg=brown guifg=#40ffff
hi Statement term=bold cterm=bold ctermfg=yellow gui=bold guifg=#ffff60
首先,这个配色方案设定背景为 dark深色允许的另外一个值是 light浅色背景。这会调整缺省的颜色组使得文字色彩在深色背景上显示比较友好。但这不会在终端里真正改变背景仍要靠下面的背景色设定因此如果你在浅色背景的终端里使用这个配色方案会显得不太友好。有些比较好的配色方案会采用相反的做法根据目前是深色还是浅色背景采用不同的配色。
对于“正常”Normal的加亮组这个配色方案采用了最直截了当的前景白、背景黑。可以预见这个配色会比较醒yǎn
对于 Constant 加亮组,这个配色方案就稍微复杂点了,分了单色终端、色彩终端和图形界面的不同配色。古老的单色终端里使用下划线(应该已经没人用吧,所以以后我就忽略这种设定了);色彩终端下使用粗体和紫色前景;图形界面指定了前景色为 RGB 色彩 #ffa0a0,亮棕色。
Identifier 加亮组也类似,色彩终端下使用棕色前景,图形界面下前景色则是 RGB 色彩 #40ffff,亮青色。
Statement 加亮组在色彩终端和图形界面下都使用粗体,色彩终端使用黄色前景色,图形界面使用前景色是 RGB 色彩 #ffff60,亮黄色。
使用这个配色方案在图形界面和色彩终端下的效果,如下面的截图所示:
配色方案在终端下的优化
说到这里,我们有必要来讨论一下 Vim 里允许使用的色彩数量。在图形界面 Vim 里,色彩是 Vim 本身调用系统的编程接口来控制的,可以使用 RGB 的所有 16,777,216 种不同颜色。但在终端里Vim 会受到终端能力的限制,只能根据终端的能力来显示色彩。根据终端的类型,我们可以分为 4 种情况:
第 1 种是最古老的是单色终端,没有颜色,只能使用下划线、粗体等效果。效果定义使用 term=… 的形式。今天,我们应该基本碰不到这样的环境了。
第 2 种是 816 色终端,允许使用最基本的八种颜色(黑、红、绿、黄、蓝、紫、青、白),以及这些颜色的较亮变体(即使 8 色终端一般也能在前景色上使用加亮的变体)。我们可以使用 cterm=… 定义粗体等效果(由于兼容性问题,不常用),用 ctermfg=… 和 ctermbg=… 定义前景和背景色,其中可以使用英文色彩名称或序号(见 :help cterm-colors。鉴于序号在不同的环境里可能是不同的我们一般使用色彩名称。如果你使用非图形界面终端可能会遇到这种情况但这应当也很不常见了吧。
这些颜色虽然是标准的,但很多终端允许用户调整这些颜色,以达到最好的色彩组合效果。比如,下图是 macOS 里终端应用的一个设置界面其中的“ANSI 颜色”就是用户可以调整的 16 种“标准色”:
第 3 种是 256 色终端,用户可以选择预先定义的 256 种颜色之一,这在目前的终端里是非常主流的方式了。你可以在网上很方便地找到脚本来输出这些颜色,效果如下图所示:
要选择这 256 种颜色中的一种,方式不太直观:你需要使用 ctermfg=… 和 ctermbg=…,并直接写出这 256 种颜色之一的编号。
这 256 种颜色都可以算是标准的,它们的标准 RGB 值有明确的定义。头 16 种颜色就是上面的“ANSI 颜色”,在终端里常常可以直接调整,图中也可以看到和前面图里的颜色已经有明显的不同。虽然界面只提供了头 16 种颜色的调整,但为了达到最佳的显示效果,你也可以编程修改这 256 种颜色的调色板。
第 4 种是支持真彩truecolor的终端跟编程修改 256 色的调色板相比,这是更简单的做法。下面是部分比较常见的支持 RGB 真彩的终端(此处是一个更完整的列表):
GNOME-TerminalLinux
iTerm2macOS
minttyWindows
命令提示符Windows 10 版本 1703 及以后;在命令提示符里使用 Vim如果不启用真彩支持颜色可能完全错误
在这些终端里,终端 Vim 就能显示跟图形界面 Vim 同样多的颜色数因而能达到最佳色彩效果。你仍需手工打开默认关闭的Vim 选项 termguicolors。此后Vim 就会使用你在 guifg 和 guibg 中写的 RGB 色彩,也就是说,把终端当图形界面一样看待(在色彩方面)。
鉴于真彩终端的一个惯例是设置环境变量 COLORTERM 为 truecolor 或 24bit我们可以在 vimrc 配置文件中进行检查:
if has('termguicolors') &&
\($COLORTERM == 'truecolor' || $COLORTERM == '24bit')
set termguicolors
endif
不过,这个检查方式仅限于类 Unix 平台。对于 WindowsVim 提供了另外一个专门的特性检查项:
if has('vcon')
set termguicolors
endif
推荐配色方案
上面我们分析的 koehler算是 Vim 内置的配色方案中比较中规中矩的一个,可用,但不那么好看。如果你想要一个漂亮的配色方案,还是不应该在内置配色方案里寻找。
一个广受好评的 Vim 配色方案是 gruvbox包管理器中的安装名称是“morhetz/gruvbox”。它不仅是一个支持深色背景和浅色背景的配色方案而且还特意确保自己能和 Vim 的一些最流行插件兼容。下面是这个配色方案的示意截图:
我觉得 gruvbox 在深色背景下还是挺漂亮的。它的颜色总体偏暖,而你如果喜欢深色背景下较为清冷的色彩,我觉得 jellybeans包管理器中的安装名称是“nanotech/jellybeans.vim”还不错
不过这两种方案我都是偶尔使用我使用更多的还是明白mbbill设计的 desertEx包管理器中的安装名称是“mbbill/desertEx”的一个版本。它的特点是无加亮的普通文字对比度设得比较低读起来比较轻松但加亮部分效果仍然比较强烈。如果你想试试这个方案的话可以自己安装尝试一下
检查/调试配色方案
如果你想自己对配色方案进行调整的话,有一个小工具肯定会非常有用,那就是 vim-scripts/SyntaxAttr.vim。不过这个插件不会自己添加键映射需要你在用包管理器安装之后自己在 vimrc 配置文件中加入类似下面的语句:
nnoremap <Leader>a :call SyntaxAttr()<CR>
这样,我们就能用 \a 来检查光标下面的语法高亮详情了。下面是一个示例:
从上面可以看到constexpr 属于 cppStorageClass 语法加亮组(这是在 syntax/cpp.vim 中定义的),并且被链接到了 Type 加亮组。后面的 guifg 和 gui 设定就是 Type 加亮组的内容:使用色彩 tan1RGB 值为 #ffa54f特殊效果为粗体bold
在你自己设计、调试语法文件或配色方案时,你会发现这个工具非常有用。
输出加亮效果
作为一个文本编辑器Vim 只接受文本的复制和粘贴。如果你想要在一个(非 Markdown文档中展示有语法加亮的代码Vim 也是可以用来产生这样的代码的——通过 HTML 输出。
Vim 默认就提供了 :TOhtml 命令(参见[第 4 讲]中讨论的系统内置插件),可以把当前展示的语法加亮效果输出为一个 HTML 文件。你可以根据最终文档的要求,选择合适的深色或浅色配色方案,然后使用该命令来输出 HTML。这个命令默认输出整个文件你也可以自己在可视模式下选定范围或者用逗号隔开的行号选定范围这样 :TOhtml 命令就只会输出选定范围的 HTML 代码。
这个方式灵活是挺灵活,但不能直接把带色彩加亮的文本贴到文档里去,终究还是不太方便。令人欣慰的是,早就有人找到了在各个平台上把 HTML 代码转成剪贴板富文本的方法。我最近对一个 Vim 插件 vim-copy-as-rtf 作了点改造使其可以在我们现在讲的三大主流平台macOS、Linux 和 Windows上都可以直接复制出带语法加亮的代码。在 macOS 和 Windows 上,没有特别的配置要求;在 Linux 桌面环境下,我们要求系统必须装有 xclip 工具。这样,我们只需要在使用 TOhtml 的地方,把命令改成 CopyRTF 就能把加亮的代码复制到系统的剪贴板中。
内容小结
好了,我们现在来总结一下今天学到的内容:
Vim 使用正则表达式来匹配代码,把代码分到不同的“语法项目”里。
语法项目可以跟加亮组进行链接,加亮组可以针对终端和图形界面定义不同的显示方式。
主流的终端可以显示 256 色,某些终端已经可以跟图形界面一样显示 RGB 真彩。
我推荐了 gruvbox、jellybeans 和 desertEx 三个配色方案;当然,你也可以自己在网上找其他好的配色方案。
SyntaxAttr.vim 可以显示语法加亮组的细节,可以用来帮助调试语法加亮和配色方案。
Vim 的加亮效果可以输出成 HTML 文件,也可以复制到剪贴板中成为带语法加亮的富文本,方便在办公文档中使用。
本讲我们的配置文件有一些改动,对应的标签是 l12-unix 和 l12-windows。
课后练习
想一想,如果希望颜色在 816 色终端下和 256 色终端下都有效,并且在 256 色终端下使用 256 色的配置,该怎么做?
在自己尝试解决这个问题之后,可以看一下 jellybeans 里面的颜色定义,了解它是如何解决在不同终端下面的颜色一致性问题的。
如果有任何问题,欢迎留言和我交流。
我是吴咏炜,我们下一讲再见!

View File

@ -0,0 +1,319 @@
因收到Google相关通知网站将会择期关闭。相关通知内容
13 YouCompleteMeVim 里的自动完成
你好,我是吴咏炜。
在集成开发环境里,自动完成是一个非常重要的功能。可是 Vim 并不能真正理解你输入的代码因此它自身无法提供自动完成的功能。不过Vim 仍然提供了一些接口,允许第三方的软件实现这样的功能,并和 Vim 自身进行集成。YouCompleteMe简称YCM就是这样的一个第三方软件今天我就为你详细介绍一下它。
YCM 对 C++ 程序员最为适合,它可以提供其他工具实现不了的功能。而且,它也适用于很多其他语言,包括 C 家族的各种语言和其他常用的语言,如 Python、Java 和 Go 等。即使在 YCM 不直接支持你使用的语言的时候,它仍然能通过标识符完成功能提供比没有 YCM和其他语言支持插件时更好的编辑体验。因此我推荐你使用这个插件。
YouCompleteMe
功能简介
首先我来介绍一下 YCM 的基本功能吧。根据它的主页(我的翻译):
YouCompleteMe 是一个快速、即输即查、模糊搜索的 Vim 代码完成引擎。它实际上有好几个完成引擎:-
一个基于标识符的引擎,可以在任何编程语言中工作
一个强大的基于 clangd 的引擎,可以为 C/C++/Objective-C/Objective-C++/CUDAC 家族语言)提供原生的语义代码完成
一个基于 Jedi 的完成引擎,可以支持 Python 2 和 3
一个基于 OmniSharp-Roslyn 的完成引擎,用来支持 C#
一个基于 Gopls 的完成引擎,支持 Go
一个基于 TSServer 的完成引擎,支持 JavaScript 和 TypeScript
一个基于 rls 的完成引擎,支持 Rust
一个基于 jdt.ls 的完成引擎,支持 Java
一个通用的语言服务器协议LSP实现用来支持任何其他有 LSP 服务器的语言
还有一个基于 omnifunc 的完成器,使用 Vim 的全能补全omnicomplete系统提供的数据来为很多其他语言提供语义完成
其实Vim 里的自动完成插件并不止这一个,但 YCM 是比较成熟也比较全面的。虽说它的安装配置有一定的复杂性,但比起另外一些要求你独立安装、配置语言服务器的方案,它至少能一次性搞定插件和你需要的语言支持,所以反而算是简单的了。我最近的主要开发语言是 C、C++、Python 和 Vim 脚本,因此这也算是个很完美的匹配了。
下面是一个简单的示例,展示了 YCM 的效果。
总体上,你只要在 YCM 给你提示的时候,敲 <Tab> 来选择合适的选项,然后继续往下输入就行。由于 YCM 使用模糊匹配,你只要输入你希望的标识符中的每一段中的若干字符,就可以快速把候选项减到你要的内容敲一两下 <Tab> 就能出来。事实上,我后来发现,在 std:: 后只要输入 mu 就足以让 make_unique 成为第一选择了。
不过这里面最让我吃惊的还是clangd 引擎居然能在我只提供部分头文件的情况下(完全不提供是不行的),自动帮我插入正确的头文件并保持其字母序排列。这个功能我以前还真还没有见过!
安装
Ubuntu 下的 apt 安装
如果你使用一个较新的 Linux 发布版,有可能系统本身已经自带了 YCM。虽然这个版本多半会有点老但对于有些人来说可能也够用了。毕竟Linux 下的包安装确实方便。我们就先以 Ubuntu 为例,来介绍 Linux 包管理器下的安装过程。
首先,我们需要使用 apt 命令来安装 YCM命令是
sudo apt install vim-youcompleteme
这步成功之后YCM 就已经被安装到了你的系统上。不过,在你个人的 Vim 配置里,仍然还没有启用 YCM。要启用的话可以输入下面的命令
vim-addon-manager install youcompleteme
这个命令之后,你会在你的 ~/.vim/plugin 目录下看到 youcompleteme.vim 的符号链接。这样,安装就算完成了。
手动安装
如果你的系统不直接提供 YCM或者你想要使用最新版本的 YCM那你就需要手工编译安装了。安装之前你需要确保你的系统上有 CMake、Python 3 和平台主流的 C++ 编译器,即 Linux 上的 GCCmacOS 上的 Clang及 Windows 上的 MSVC。如果要安装其他语言如 Java 和 Go的支持也同样要准备好相应语言的环境这些在 YCM 的主页上有介绍,我就先不多说了。
因为 YCM 是一个需要编译组件的插件,所以我不建议你用 Vim 的包管理器来安装,那样会出什么错都搞不清楚。大致安装过程是:
选择安装目录
签出 YCM
根据你需要使用的语言使用合适的选项,来进行编译安装
下面,我们就快速地过一下。
首先,我们需要给 YCM 一个独立的安装目录。这个目录应该在 pack 下面但不要放在包管理器使用的目录下以免发生冲突。我的选择是“我的”my。因为希望 YCM 直接启动,所以最后需要放到这个目录的 start 子目录下。换句话说Unix 上的 ~/.vim/pack/my/startWindows 上的 ~\vimfiles\pack\my\start。
然后,我们就应当在这个目录下签出 YCM。可以在进到这个子目录里面后使用下面的命令Windows 下面去掉“\”全部写一行,或者把“\”换成“^”):
git clone --recurse-submodules \
--shallow-submodules \
https://github.com/ycm-core/YouCompleteMe.git
最后就是编译安装了。主要工作由 install.py 来完成但如果我们不提供额外的选项YCM 不会安装上面说的那些特定语言的语义完成引擎。我们需要显式地提供相应语言的选项:
--clang-completer基于 libclang 的老 C 族语言引擎
--clangd-completer基于 clangd 的新实验 C 族语言引擎
--cs-completerC# 引擎
--go-completerGo 引擎
--rust-completerRust 引擎
--java-completerJava 引擎
--ts-completerJavaScript 和 TypeScript 引擎
--all除 clangd 外的上述索引引擎
关于 clangd我多说一句。虽然这个引擎被标为实验状态但它的易用性和功能确实比老的引擎有了巨大的提升。同样是上面的代码如果用老的 libclang 引擎的话,效果是这样的:
我们可以看到:
老版本不会添加 #include 结束的 > 或 "
老版本不会自动添加头文件
老版本不会提供函数原型提示
老版本不会在输入中时刻提醒当前有错误(这倒不算是件坏事)
所以,如果你编译和使用 clangd 支持没有问题,那就用它吧。对我来说,使用 libclang 引擎可能有两个理由:
clangd 支持编译不过(我遇到过)
机器配置低clangd 太慢了(我的机器上能感到性能差异,但 clangd 的响应速度完全可以接受)
你可以同时安装这两个引擎,然后通过你的 vimrc 配置文件来选择使用哪一个,使用 let g:ycm_use_clangd = 0 就是使用老引擎,这个值设为 1 或者干脆不设,则是使用新引擎。
另外一个要提醒你的地方是,编译环境应尽可能干净,不要暴露出自己用的第三方库的路径。我就碰到过因为环境变量里设了 Boost 库的包含路径,从而导致 YCM 编译出错的情况。YCM 自己已经包含了所需的依赖库,系统的和用户自己安装的类似库如果版本不合适的话,反而会对 YCM 造成干扰。
(此外,也告诉你一下我编译 clangd 时遇到的失败情况,也供你参考一下。我的原因是自己编译了 Python 3由于系统上缺了一些开发包导致 Python 功能不完整,到运行 YCM 的安装脚本时才暴露出来。我很高兴我后来花点时间解决了问题,因为 clangd 的功能真的强大很多。)
配置
项目配置
像我上面的简单例子YCM 是可以不需要配置就能直接工作的。但如果环境稍微复杂一点C/C++ 程序就可能会出现识别错误。原因通常是以下三种:
头文件没找到,可能是因为项目内部路径比较复杂,也可能是因为编译器的头文件不是在 Clang 查找的默认位置下面
项目需要特别的宏定义
项目需要特定的 C 或 C++ 标准,或特定的编译器选项
这些情况如果出现的话,你需要让 YCM 了解项目的相应信息。
YCM 在 clangd 下的推荐做法是在源代码或其某个父目录下放一个 CMake 输出的 compile_commands.json 文件(可在 cmake 命令行上加上 -DCMAKE_EXPORT_COMPILE_COMMANDS=1 来产生此文件)。这种方式最为通用和严格,因为 CMake 输出的这个编译命令文件里包含每一个源文件的编译命令,因此只要你的 CMake 配置是正确的YCM 通常就能正确识别,哪怕你每个文件的编译选项不同都没有关系。它可以给 YCM 提供完整的项目编译信息,使得查找一个符号的引用成为可能。如果你的工程里,这个文件产生在一个 build 目录下的话,别忘了你需要在项目的根目录下执行类似下面的命令:
ln -s build/compile_commands.json .
使用 compile_commands.json 也有缺点。CMake 在这个编译命令文件里放置的是绝对路径,因此,把源代码放在一个共享位置供不同的系统使用就会有麻烦。此外,只有少数 CMake 产生器支持 CMAKE_EXPORT_COMPILE_COMMANDS特别是Windows 上默认的 Visual Studio 产生器不支持输出这个文件。抛开这些问题如果项目比较大的话clangd 的“编译”会消耗 CPU 和内存,也是一个可能的负面因素。但我们同时要记住,这种开销是让 YCM 能看到整个项目而不只是单个文件的代价。
如果因为某种问题你决定不使用编译命令文件这一方法,那 YCM 的经典做法是在文件所在目录或其父目录下寻找一个名字叫 .ycm_extra_conf.py 的文件。找到之后,它就会弹出一个提醒,提示用户是不是要载入这个文件,运行其中的代码来得到需要的配置信息。
虽然这个恼人的提示可以关掉,但这实际上会带来潜在的安全问题,毕竟 YCM 是会运行其中的 Python 代码的。此外,写这个文件也算是件麻烦事吧,尤其对不熟悉 Python 的人而言。
在 2014—2015 年期间,有人维护了一个叫 ycmconf 的插件,以一种我喜欢的方式解决了这个问题。不过,今天再直接用这个插件,就有点问题了。所以我在 GitHub 上复刻了这个插件,并进行了更新。如果你使用 YCM 进行 C 系语言的自动完成,那我推荐你安装 adah1972/ycmconf 这个插件。
这个插件支持两种简单的方式来配置 C/C++ 的自动识别:
使用 CMake 输出的 compile_commands.json 文件(由于 YCM 目前已经直接支持该文件,这只对较老版本的 YCM 有意义)
使用上一讲(拓展 2里用的 .clang_complete 配置文件
目前我当然是以第 2 种方式来使用这个插件了:手写一个 .clang_complete 很简单,非常适合临时写的小程序。但它的问题是,这个文件对整个目录有效。所以如果你在其中写了像 -std=c++14 这样的选项,选择某一特定 C++ 标准,那这个选项对于 C 文件就是错的了YCM 就会抱怨。不过解决起来也很容易,像上面说的情况,让 C 文件单独占一个子目录或者平行目录都可以消除此问题。
不管是哪种方法你都需要确保配置文件在源代码的目录下或其父目录下。YCM 和 ycmconf 的搜索规则都是从源代码的所在位置往上找有没有满足文件名约定的文件,并在找到第一个时终止。
全局配置
YCM 有很多命令,但它默认只对少量的功能进行了键映射,其中最重要的就是 <Tab> 了。在使用中,我觉得自动修正和跳转功能值得单独进行一下键映射:
nnoremap <Leader>fi :YcmCompleter FixIt<CR>
nnoremap <Leader>gt :YcmCompleter GoTo<CR>
nnoremap <Leader>gd :YcmCompleter GoToDefinition<CR>
nnoremap <Leader>gh :YcmCompleter GoToDeclaration<CR>
nnoremap <Leader>gr :YcmCompleter GoToReferences<CR>
自动修正功能可以参考下图的演示。
自动修正的范围很广,小到修正一个拼写错误或者漏写名空间这样的问题,大到提供安全方面或代码风格现代化的调整(它可以利用你的 Clang-Tidy 配置来向你报告错误和提供修正建议)。有了它,编码工作真的轻松了许多。
我们这儿有四种跳转:
GoTo无脑跳转最常用的就是这个功能如果能跳转到定义就跳转到定义否则就跳转到声明。
GoToDefinition顾名思义就是跳转到定义。
GoToDeclaration专门跳转到声明。
GoToReferences可以用来查出一个符号被引用的地方libclang 引擎不支持该命令)。
注意,对 C 族语言来说,只有让 clangd 看到你的项目的 compile_commands.json 文件,才能使用 GoToReferences 这个命令查找整个项目里符号被引用的地方;否则,你只能查出当前 Vim 里可见的引用,而非整个项目。
YCM 有很多配置参数,有些在默认状态下工作得不是很好。我通常会配置下面这些:
let g:ycm_auto_hover = ''
let g:ycm_complete_in_comments = 1
let g:ycm_filetype_whitelist = {
\ 'c': 1,
\ 'cpp': 1,
\ 'python': 1,
\ 'vim': 1,
\ 'sh': 1,
\ 'zsh': 1,
\ }
let g:ycm_goto_buffer_command = 'split-or-existing-window'
let g:ycm_key_invoke_completion = '<C-Z>'
第一项 ycm_auto_hover 用来禁用光标在一个符号上长期停留出现的自动文档提示。未禁用时的效果如下图:
这个自动提示不能说一点用都没有,但它很容易成为写代码时的干扰,所以我还是把它禁用了。
第二项 ycm_complete_in_comments 表示我希望在写注释的时候也能启用自动完成——毕竟注释里通常也要写代码里的变量、函数名什么的。
第三项 ycm_filetype_whitelist 用来仅对白名单列表里的文件类型才启用 YCM。没有这一项YCM 在打开一些特殊类型的文件时可能会报错,有时候也会导致打开的延迟。我就明确一下,让它只在我常用的源代码类型里才启用。
第四项 ycm_goto_buffer_command 用来告诉 YCM当跳转的目的文件尚未打开时用分割窗口的方式打开新文件如果已经打开则跳转到相应的窗口。其他的可能值是 'same-buffer',在同一个缓冲区的位置打开(除非这个位置因为文件修改的原因不能被替换),及 'split',除非跳转目的在同一个文件,永远在新分割的窗口打开。
第五项 ycm_key_invoke_completion 用来定义手工启用语义完成的按键。在你输入时YCM 会自动尝试标识符匹配,而当你输入 .、->、:: 或这个按键时YCM 则会启用语义完成,来给出当前上下文中允许出现的符号。这个按键默认是 <C-Space>,在某些操作系统上是不能用的(如 Mac 和老的 Windows所以我改成了 <C-Z>。你也可以选择你自己喜欢的按键但要注意映射冲突问题Vim 里在插入模式下的可用键不多,事实上只有在终端下容易出问题的 <C-S><C-Z> 在 Vim 里没有默认功能)。
使用
说了这么多,实际上 YCM 大部分使用方法我也已经提到了。它基本上只要你使用 <Tab> 就能使用,你如果不理睬它的提示,它也不会对你造成什么干扰(我遇到过一些 Vim 的插件,虽然能提供些有用的提示,但是会侵入式地影响正常输入,那就只能删除/禁用了)。其他最重要的功能,我们也已经进行了按键映射,上面也都有了初步的描述。
还有一个可能有用的命令,不能通过按键映射,那就是重命名的重构。这个命令需要你把光标移到要修改的符号上,然后输入的命令里要有新的名称。比如,如果你要把一个 foo 符号重命名成 bar需要把光标移动 foo 上面,然后输入:
:YcmCompleter RefactorRename bar
命令虽然略长,但你一样可以用 <Tab> 来自动完成,所以你多打不了几个字符的。
YCM 默认只在屏幕底部显示当前行的问题,并且显示很可能被截断。要看到所有的代码问题,可以使用命令 :YcmDiags。
此外 YCM 还有一些调试命令,一般不需要使用,我这边就不介绍了。你可以自己在帮助文档里查看。
RTags选学
因为有段时间我只能用 libclang 引擎不能查找符号的引用因此我安装了另外一个开源工具RTags来弥补这一缺憾。在使用 clangd 的情况下RTags 已经不那么必要了。它仍然提供了一些特别的功能,并且 Linux 发布版里可能仍只提供了 libclang 引擎的 YCM因此我就把这部分作为选学提供了相当于本讲内部的一个小加餐。爱折腾并且使用非 Windows 环境RTags 尚不支持 Windows的小伙伴们可以把这部分读完其他人就跳到内容小结吧。
安装
这次我就只讲自己构建安装的大概过程了。如果你的平台支持二进制安装,相信你应该可以自己搞定了。
首先我们要安装 RTags 本身。安装前,我们需要确认所在的平台有 CMake、C++ 编译器和 libclang 的开发包。这些都有了之后,我们选一个目录,执行下面的命令就可以编译安装了:
git clone --recursive https://github.com/Andersbakken/rtags.git
cd rtags
mkdir build
cd build
cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=1 -DCMAKE_BUILD_TYPE=Release ..
make -j4
sudo make install
在这些命令都正常执行之后,你就已经把 RTags 的命令安装到了 /usr/local 下面。
随后我们安装 RTags 和 Vim 集成的插件。这个比较简单,用你的包管理器安装 lyuts/vim-rtags 即可。在配置文件里,我通常只做一处调整:
let g:rtagsUseLocationList = 0
这是因为 vim-rtags 默认使用位置列表location list而不是快速修复窗口。我们之前没有介绍过位置列表我就快速引用一下文档
位置列表是一个窗口局部的快速修复列表。由 :lvimgrep、:lgrep、:lhelpgrep、:lmake 等命令产生,它们生成位置列表而不是对应的 :vimgrep、:grep、:helpgrep、:make 生成的快速修复列表。
位置列表和窗口相关联,而每个窗口都要单独的位置列表。一个位置列表只能和一个窗口相关联。位置列表和快速修复列表相互独立。
我通常不怎么使用位置列表,主要是为了简单,可以使用固定的快捷键。
运行和项目配置
RTags 是一个客户端/服务器端架构的程序。但它不是用 TCP/IP而是 Unix 域套接字,一个用户只能运行一个服务器,可以支持多个客户端。启动服务器端的命令非常简单,就是:
rdm &
我这儿用了 &,让 rdm 在后台运行,但输出仍能直接从终端上看到。在我们刚开始学习使用 RTags 时,我们仍需多多监控 rdm 的输出。
在某个项目中启用 RTags最简单的方式就是使用 CMake 输出的 compile_commands.json 文件。我们只需要在这个目录下执行以下命令:
rc -J .
随后我们就能看到运行 rdm 的那个窗口屏幕哗哗地翻滚,忙着“编译”代码。等到 rdm 忙完了项目索引就算完成了。而当你修改代码时rdm 会看到你修改,然后就会自动编译相关的文件,保持索引为最新。
有没有注意到我上面编译 RTags 的命令已经生成了 compile_commands.json 文件?所以,如果你没有现成的其他 CMake 项目,你可以用这个项目本身来进行搜索。
使用
如果我们使用默认的 vim-rtags 键映射的话,我们只需要把光标移到一个符号上面,然后输入 \rf理解为 find references即可。下面是一个示例
这个结果出来的速度比用 :grep 可快多了!
它还有很多其他命令,可以用来查找定义、查找父类、查找子类、显示调用树,等等。有些功能在 YCM 里并没有对应物,这也是 RTags 的价值了。你可以在 vim-rtags 的主页上查看学习所有这些命令及其快捷键。
内容小结
在本讲中,我们主要介绍了 YouCompleteMe 这个重量级插件,包括其安装和配置。我们可以看到,在插件的帮助下,我们可以获得不输于集成开发环境的自动完成体验,同时,仍然享受 Vim 的快速启动和强大编辑功能。最后我们花了一点点时间介绍了 RTags 工具和 vim-rtags 插件,它在你写 C 族语言而不能使用 clangd 引擎时会特别有帮助。
YCM 的参数和键映射我写到了示例配置文件里,对应的标签是 l13-unix 和 l13-windows。
课后练习
今天学完之后的主要任务,当然就是把 YouCompleteMe 装起来、配置好了。它的使用反而是相当简单的,大部分情况下使用 <Tab> 就行。
至于 RTagsUnix 下的 C++ 程序员们可以根据自己的兴趣,决定是否捣腾一下。这个工具还是有点可玩性的。
如果遇到什么问题,欢迎留言和我讨论。我们下一讲再见!

View File

@ -0,0 +1,333 @@
因收到Google相关通知网站将会择期关闭。相关通知内容
14 Vim 脚本简介:开始你的深度定制
你好,我是吴咏炜。
学到今天,我们已经看到了很多的 Vim 脚本,只是还没有正式地把它作为一门语言来介绍。今天,我就正式向你介绍把 Vim 的功能粘合到一起的语言——Vim 脚本Vim script。掌握 Vim 脚本的基本语法之后,你就可以得心应手地定制你的 Vim 环境啦。
语法概要
首先,我们需要知道,通过命令行模式执行的命令就是 Vim 脚本。它是一种图灵完全的脚本语言:图灵完全,说明它的功能够强大,理论上可以完成任何计算任务;脚本语言,说明它不需要编译,可以直接通过解释方式来执行。
当然,这并没有说出 Vim 脚本的真正特点。下面,我们就通过各个不同的角度,进行了解,把 Vim 脚本这头“大象”的基本形状完整地摸出来。
在这一讲里,我们改变一下惯例,除非明确说“正常模式命令”,否则用代码方式显示的都是脚本文件里的代码或者命令行模式命令,也就是说,它们前面都不会加 :。毕竟我们这一讲介绍的全是 Vim 脚本,而不是正常模式的快捷操作。
打印输出和字符串
学习任何一门语言我们常常以“Hello world!”开始。对于 Vim 脚本,我们不妨也这样——毕竟,打印是一种重要的调试方式,尤其对于没有专门调试器的脚本语言来说。
Vim 脚本的“Hello world!”是下面这样的:
echo 'Hello world!'
echo 是 Vim 用来显示信息的内置命令,而 'Hello world!' 是一个字符串字面量。Vim 里也可以使用 " 来引起一个字符串。' 和 " 的区别和在 shell 里比较相似,前者里面不允许有任何转义字符,而后者则可以使用常见的转义字符序列,如 \n 和 \u.... 等。和 shell 不同的是,我们可以在 ' 括起的字符里把 ' 重复一次来得到这个字符本身,即 'It''s' 相当于 "It's"。不过,在这个例子里,显然还是后者更清晰了。
因为 " 还有开始注释的作用,一般情况下我推荐在 Vim 脚本里使用 ',除非你需要转义字符序列或者需要把 ' 本身放到字符串里。
字符串可以用 . 运算符来拼接。由于字典访问也可以用 . 为了避免歧义Bram 推荐开发者在新的 Vim 脚本中使用 .. 来拼接。但要注意,这个写法在 Vim 7 及之前的版本里不支持。我目前仍暂时使用 . 进行字符串拼接,并和其他大部分运算符一样,前后空一格。这样跟不空格的字典用法比起来,差异就相当明显了。
除了 echoVim 还可以用 echomsg缩写 echom命令来显示一条消息。跟 echo 不同的是,这条消息不仅会显示在屏幕上,还会保留在消息历史里,可以在之后用 message 命令查看。
变量
跟大部分语言一样Vim 脚本里有变量。变量可以用 let 命令来赋值,如下所示:
let answer = 42
然后你当然就可以使用 answer 这个变量了,如:
echo 'The meaning of life, the universe and everything is ' . answer
Vim 的变量可以手工取消,需要的命令是 unlet。在你写了 unlet answer 之后,你就不能再读取 answer 这个变量了。
数字
上面的赋值语句用到了整数。Vim 脚本里的数字支持整数和浮点数,在大部分平台上,两者都是 64 位的有符号数字类型,基本对应于大部分 C 语言环境里的 int64_t 和 double。表示方式也和 C 里面差不多:整数可以使用 0八进制、0b二进制和 0x十六进制前缀浮点数含小数点不可省略可选使用科学计数法。
复杂数据结构
Vim 脚本内置支持的复杂数据结构是列表list和字典dictionary。这两者都和 Python 里的对应数据结构一样。对于 C++ 的程序员来说,列表基本上就是数组/array/vector但大小可变而且可以直接使用方括号表达式来初始化
let primes = [2, 3, 5, 7, 11, 13, 17, 19]
然后你可以用下标访问,比如用 primes[0] 就可以得到 2。
字典基本上就是 map可以使用花括号来初始化
let int_squares = {
\0: 0,
\1: 1,
\2: 4,
\3: 9,
\4: 16,
\}
键会自动转换成字符串,而值会保留其类型。上面也用到了 Vim 脚本的续行——下一行的第一个非空白字符如果是 \,则表示这一行跟上一行在逻辑上是同一行,这一点和大部分其他语言是不同的。
访问字典里的某一个元素可以用方括号(跟大部分语言一样),如 int_squares['2'];或使用 .,如 int_squares.2。
表达式
跟大部分编程语言类似Vim 脚本的表达式里可以使用括号,可以调用函数(形如 func(…)),支持加(+)、减(-)、乘(*)、除(/)和取模(%),支持逻辑操作(&&、|| 和 !还支持三元条件表达式a ? b : c。前面我们已经学过可以使用 [] 访问列表成员,可以使用 [] 或 . 访问字典的成员,也可以使用 . 或 .. 进行字符串拼接。== 和 != 运算符对所有类型都有效,而 <、>= 等运算符对整数、浮点数和字符串都有效。
对于文本处理,常见的情况是我们使用 =~ 和 !~ 进行正则表达式匹配,前者表示匹配的判断,后者表示不匹配的判断。比较操作符可以后面添加 # 或 ? 来强制进行大小写敏感或不敏感的匹配(缺省受 Vim 选项 ignorecase 影响)。表达式的左侧是待匹配的字符串,右侧则是用来匹配的正则表达式。
注意表达式不是一个合法的 Vim 命令或脚本语句。在表达式的左侧,需要有 echo 这样的命令。如果你只想调用一个函数,而不需要使用其返回的结果,则应使用 call func(…) 这样的写法。
此外,我们在插入模式和命令行模式下都可以使用按键 <C-R>=(两个键)后面跟一个表达式来使用表达式的结果。在替换命令中,我们在 \= 后面也同样可以跟一个表达式,来表示使用该表达式的结果。比如,下面的命令可以在当前编辑文件的每一行前面插入行号和空格:
:%s/^/\=line('.') . ' '/
line 是 Vim 的一个内置函数line('.') 表示“当前”行的行号,剩下部分你应该直接就明白了吧?
控制结构
作为一门完整的编程语言标准的控制结构当然也少不了。Vim 支持标准的 if、while 和 for 语句。语法上Vim 的写法有点老派,跟当前的主流语言不太一样,每种结构都要用一个对应的 endif、endwhile 和 endfor 来结束,如下面所示:
" 简单条件语句
if 表达式
语句
endif
" 有 else 分支的条件语句
if 表达式
语句
else
语句
endif
" 更复杂的条件语句
if 表达式
语句
elseif 表达式
语句
else
语句
endif
" 循环语句
while 表达式
语句
endwhile
在 while 和 for 循环语句里,你可以使用 break 来退出循环,也可以使用 continue 来跳过循环体内的其他语句。作为一个程序员,理解它们肯定没有任何困难。
Vim 脚本的 for 语句跟 Python 非常相似,形式是:
for var in object
这儿可以使用 var
endfor
表示遍历 object通常是个列表对象里面的所有元素。
哦,跟 Python 一样Vim 脚本也没有 switch/case 语句。
函数和匿名函数
为了方便开发函数肯定也是少不了的。Vim 脚本里定义函数使用下面的语法:
function 函数名(参数1, 参数2, ...)
函数内容
endfunction
Vim 里用户自定义函数必须首字母大写(和内置函数相区别),或者使用 s: 表示该函数只在当前脚本文件有效。... 可以出现在参数列表的结尾,表示可以传递额外的无名参数。使用有名字的参数时,你需要加上 a: 前缀。要访问额外参数,则需要使用 a:1、a:2 这样的形式。特殊名字 a:0 表示额外参数的数量a:000 表示把额外参数当成列表来使用,因而 a:000[0] 就相当于 a:1。
在函数里面,跟大部分语言一样,你可以使用 return 命令返回一个结果,或提前结束函数的执行。
Vim 脚本里允许匿名函数,形式是 {逗号分隔开的参数 -> 表达式}。如果你对函数式编程完全没有概念,你可以跳过匿名函数。如果你喜欢函数式编程,那你应该会很欣喜地看到,在 Vim 脚本里可以使用类似下面的语句:
echo map(range(1, 5), {idx, val -> val * val})
结果是 [1, 4, 9, 16, 25]。跟常见的 map 函数不同Vim 会传过去两个参数,分别是列表索引和值;同时,它会修改列表的内容。不想修改的话,要把列表复制一份,如 copy(mylist)。
Vim 特性
上面描述的只是一般性的编程语言语法,但 Vim 脚本如果只当作通用编程语言来用的话,就没啥意义了。我们使用 Vim 脚本,肯定是为了和 Vim 进行交互。下面我们就来仔细检查一下 Vim 脚本里的 Vim 特性。
变量的前缀
我们上面已经提到了变量的 a: 前缀。变量的前缀实际上有更多,通用编程概念上很容易理解的是下面四个:
a: 表示这个变量是函数参数,只能在函数内使用。
g: 表示这个变量是全局变量,可以在任何地方访问。
l: 表示这个变量是本地变量,但一般这个前缀不需要使用,除非你跟系统的某个名字发生了冲突。
s: 表示这个变量(或函数,它也能用在函数上)只能用于当前脚本,有点像 C 里面的 static 变量和函数,只在当前脚本文件有效,因而不会影响其他脚本文件里定义的有冲突的名字。
一般编程语言里没有的,是下面这些前缀:
b: 表示这个变量是当前缓冲区的,不同的缓冲区可以有同名的 b: 变量。比如,在 Vim 里b:current_syntax 这个变量表示当前缓冲区使用的语法名字。
w: 表示这个变量是当前窗口的,不同的窗口可以有同名的 w: 变量。
t: 表示这个变量是当前标签页的,不同的标签页可以有同名的 t: 变量。
v: 表示这个变量是特殊的 Vim 内置变量,如 v:version 是 Vim 的版本号,等等(详见 :help v:var
还有下面这些前缀,可以让我们像使用变量一样使用环境变量和 Vim 选项:
$ 表示紧接着的名字是一个环境变量。注意,一些环境变量是由 Vim 自己设置的,如 $VIMRUNTIME。
& 表示紧接着的名字是一个选项,比如, echo &filetype 和 set filetype? 效果相似,都能用来显示当前缓冲区的文件类型。
&g: 表示访问一个选项的全局global值。对于有本地值的选项如 tabstop我们用 &tabstop 直接读到的是本地值了,要访问全局值就必须使用 &g:tabstop。
&l: 表示访问一个选项的本地local值。对于有本地值的选项如 tabstop我们用 &tabstop 直接读到的已经是本地值了,但修改则和 set 一样,同时修改本地值和全局值。使用 &l: 前缀可以允许我们仅修改本地值,像 setlocal 命令一样。
你可能要问,什么时候我们会需要用变量形式来访问选项,而不是使用 set、setlocal 这样的命令呢答案是当我们需要计算出选项值的时候。set filetype=cpp 基本上和 let &filetype = 'cpp' 等效,我们需要注意到后者里面 cpp 是个字符串,可以是通过某种方式算出来的。光使用 set就不方便做到这样的灵活性了。
重要命令
Vim 里有很多命令,很多我们已经介绍过,或者直接在 vimrc 配置文件里使用了。这节里我们会介绍跟 Vim 脚本相关性比较大的一些命令。
首先是 execute缩写 exe它能用来把后面跟的字符串当成命令来解释。跟上一节使用选项还是 & 变量一样,这样做可以增加脚本的灵活性。除此之外,它还有两种常见特殊用法:
在使用键盘映射等场合、需要在一行里放多个命令时,一般可以使用 | 来分隔,但某些命令会把 | 当成命令的一部分(如 !、command、nmap 和用户自定义命令),这种时候就可以使用 execute 把这样的命令包起来exe '!ls' | echo 'See file list above'。
normal 命令把后面跟的字符直接当成正常模式命令解释,但如果其中包含有特殊字符时就不方便了。这时可以用 execute 命令,然后在 " 里可以使用转义字符。我们上面讲字符串时没说的是,按键也可以这样转义,比如,"\<C-W>" 就代表 Ctrl-W 这个按键。所以如果你想在脚本中控制切换到下一个窗口可以写成exe "normal \<C-W>w"。
然后,我要介绍一下 source缩写 so命令。它用来载入一个 Vim 脚本文件,并执行其中的内容。我们已经多次在 vimrc 配置文件中使用它来载入系统提供的 Vim 脚本了,如:
source $VIMRUNTIME/vimrc_example.vim
command! PackUpdate packadd minpac | source $MYVIMRC | call minpac#update('', {'do': 'call minpac#status()'})
这里要注意的地方是,要允许一个文件被 source 多次,是需要一些特殊处理的。我目前给出的 vimrc 配置文件由于需要被载入多次,进行了下面的特殊处理:
清除缺省自动命令组里当前的所有命令,以免定义的自动命令被执行超过一次
使用 command! 来定义命令,避免重复命令定义的错误
使用 function! 来定义函数,避免重复函数定义的错误
没有手工设置 set nocompatible因为该设置可能会有较多的副作用在 defaults.vim 里会确保只设置该选项一次)
上面我已经展示了一个 command 命令的例子。这个命令允许我们自定义 Vim 的命令,并允许用户来定制自动完成之类的效果(详见 :help user-commands。注意这个命令的定义要写在一行里所以如果命令很长或者中间出现会吞掉 | 的命令的话,我们就会需要用上 execute 命令了。
最后,我再说明一下我们用过的 map 系列键映射命令(详见 :help key-mapping。这些命令的主干是 map然后前面可以插入 nore 表示键映射的结果不再重新映射,最前面用 n、v、i 等字母表示适用的 Vim 模式。在绝大部分情况下,我们都会使用带 nore 这种方式,表示结果不再进行映射(排除偶尔偷懒的情况)。但是,如果我们的 map 命令的右侧用到了已有的(如某个插件带来的)键映射,我们就必须使用没有 nore 的版本了。
事件
和用户主动发起的命令相对应Vim 里的自动处理依赖于 Vim 里的事件。迄今为止,我们已经遇到了下面这些事件:
BufNewFile 事件在创建一个新文件时触发
BufRead跟 BufReadPost 相同)事件在读入一个文件后触发
BufWritePost 事件在把整个缓冲区写回到文件之后触发
FileType 事件在设置文件类型filetype 选项)时被触发
Vim 里的事件还有很多(详见 :help autocmd-events-abc我们就不一一介绍了。上面这些是我们最常用的你应该了解它们的意义。
内置函数
Vim 里内置了很多函数(列表见 :help function-list可以实现编程语言所需要的基本功能。我们目前用得比较多的是下面这两个
exists 用来检测某一符号(变量、函数等)是否已经存在。在 Vim 脚本里最常见的用途是检测某一变量是否已经被定义。
has 用来检测某一 Vim 特性(列表见 :help feature-list是否存在。帮助文档里已经描述得很清楚我就不详细介绍了。你可以对照看一下我们的 vimrc 配置文件里的用法,应该就明白了。
Vim 的内置函数真的很多,我也没法一一介绍。你可以稍作浏览,了解其大概,然后在使用中根据需要查询。别忘了,在看 Vim 脚本时,在关键字上按下 K 就可以查看这个关键字的帮助,如下图所示:
风格指南
结束 Vim 脚本的介绍之前,我向你推荐一下 Google 出品的 Vim 脚本风格指南Google Vimscript Style Guide。写一种语言有一个风格指南肯定是会有帮助的尤其对于初学者而言。
Python 集成(选学)
Vim 脚本功能再强大也还是一种小众的编程语言。所以Vim 里内置了跟多种脚本语言的集成,包括:
Python
Perl
Tcl
Ruby
Lua
MzScheme
由于 Python 的高流行度,目前 Vim 插件里常常见到对 Python 的要求——至少我还没有用过哪个插件要求有其他语言的支持。所以,在这儿我就以 Python 为例,简单介绍一下 Vim 对其他脚本语言的支持。各个语言当然有不同的特性,但支持的方式非常相似,可以说是大同小异。
这部分作为选学提供相当于本讲内部的一个小加餐。Python 程序员一定要把这部分读完,其他同学则可以选择跳到内容小结。
Vim 很早就支持了 Python 2Vim 的命令 python缩写 py就是用来执行 Python 2 的代码的。后来Vim 也支持了 Python 3使用 python3缩写 py3来执行 Python 3 的代码。鉴于 Python 的代码还是有不少是 2、3 兼容的Vim 还有命令 pythonx缩写 pyx可以自动选择一个可用的 Python 版本来执行。
我在[拓展 3]里给出了一段代码,用 Python 来检测当前目录是不是在一个 Git 库里。我们先用 pythonx 命令定义了一个 Python 函数,然后用 pyxeval 函数来调用该函数。这就是一种典型的使用方式:在 Python 里定义某个功能,然后在 Vim 脚本里调用该功能。这种情况下Python 部分的代码一般不需要对 Vim 有任何特殊处理,只是简单实现某个特定功能。
下面是另一个小例子,通过 Python 来获得当前时区和协调世界时的时间差值(对于中国,应当返回 ␣+0800
function! Timezone()
if has('pythonx')
pythonx << EOF
import time
def my_timezone():
is_dst = time.daylight and time.localtime().tm_isdst
offset = time.altzone if is_dst else time.timezone
(hours, seconds) = divmod(abs(offset), 3600)
if offset > 0: hours = -hours
minutes = seconds // 60
return '{:+03d}{:02d}'.format(hours, minutes)
EOF
return ' ' . pyxeval('my_timezone()')
else
return ''
endif
endfunction
从 pythonx << EOF EOF中间是 Python 代码定义了一个叫 my_timezone 的函数我们然后调用该函数来获得结果对于不支持 Python 的情况我们就直接返回一个空字符串了
另一种更复杂的情况是我们的主干处理逻辑就放在 Python 这种情况下我们就需要在 Python 里调用 Vim 的功能了 Vim 调用 Python 代码时Python 可以访问 vim 模块其中提供多个 Vim 的专门方法和对象
vim.command 可以执行 Vim 的命令
vim.eval 可以对表达式进行估值
vim.buffers 代表 Vim 里的缓冲区
vim.windows 代表当前标签页里的 Vim 窗口
vim.tabpages 代表 Vim 里的标签页
vim.current 代表各种 Vim 当前对象详见 :help python-current包括行缓冲区窗口等
此外在拓展 2 里我们给出的使用 pyxf 来执行一个 Python 脚本文件也是一种在 Vim 里调用 Python 的方式详见 :help pyxfile)。那段 clang-format 的代码总体上也就是访问 vim.current.buffer 对象调用外部命令格式化指定行然后把修改的内容写回到 Vim 缓冲区里
内容小结
好了我们的 Vim 脚本介绍就到这里了这一讲和大部分其他讲不同只是给了你一个 Vim 脚本的概览目的是让你全面了解一下 Vim 脚本能够读懂一般的 Vim 脚本而不是真正教会你如何去写脚本这讲的主要知识点是
Vim 脚本的基本语法包括变量数字字符串复杂数据结构表达式控制结构和函数
Vim 的专门特性包括变量的前缀脚本相关命令Vim 里的事件和内置函数
Vim 脚本风格指南
Vim Python 等其他脚本语言的支持
作为一门编程语言只有在实践中不断操练才能真正学会它的使用如果你对 Vim 脚本有兴趣的话我们下一讲会剖析几个 Vim 脚本来分析一下让你有更深入的体会
课后练习
请查看几个现有的 Vim 脚本来仔细分析一下理解各行的意义建议可以从我们在 vimrc 配置文件中包含的 vimrc_example.vim 开始然后查看其中使用的 defaults.vim别忘了我们可以使用普通模式快捷键 gf <C-W>f 直接跳转到光标下的文件里。
如果遇到什么问题,欢迎留言和我讨论。我们下一讲再见!

View File

@ -0,0 +1,275 @@
因收到Google相关通知网站将会择期关闭。相关通知内容
15 插件荟萃:不可或缺的插件
你好,我是吴咏炜。
关于 Vim 的基本知识,我们已经讨论得差不多了。下面,我们需要的是练习和积累,在实践中成长。在今天的这一讲里,我们就来看看一些我们之前还没来得及介绍、但真的“必需”的插件,让你的开发效率再进行一次大幅提升。
跟之前各讲比起来,这讲会比较轻松。不过,在你已经学过了 Vim 的基本原理之后(特别是如果你学习了[拓展 4]的样例,进一步了解了插件代码是如何编写的话),我希望你看到这些插件时,不仅可以看到它们的外观和用法,而且还能大概知道它们的工作原理。这样,你就不再仅仅是一个初级用户,而是已经晋升为真正的 Vim 高手,能够根据自己的需要进行定制,甚至是“魔改”了。
Fugitive
对于大部分开发者来说,使用 Git 应该已经和呼吸空气一样自然了吧。我跟很多工作经历丰富的开发者一样,从 CVS 的年代开始(那时我还一直维护着 CVSMenu经历了 SVN然后看着 Git 慢慢一统天下,号令江湖。在 Vim 中如何高效地使用 Git当然也就成了个不得不讨论的话题。
今天,我会介绍两个 Git 插件。一个是重量级的、功能很多的 Fugitive一个是集中在几个特色小功能上的 GitGutter。下面我们就从 Fugitive 开始。
安装和配置
Fugitive 是纯 Vim 脚本,我们使用包管理器安装 tpope/vim-fugitive 即可(对了,他也是 Time Pope 大牛写的)。
这个插件基本上不需要配置,即装即用。
使用
Fugitive 是一个全功能的 Git 支持插件,支持在 Vim 里直接使用 Git 命令你只需要把“G”变成大写即可。比如原本你要在 shell 使用 git log现在在 Vim 里使用 :Git log 命令即可。结果会在一个 Vim 窗口里打开,并且有着语法加亮的效果:
Fugitive 有很多命令,你可以在它的帮助文件里找到较为完整的信息。下面我从使用的角度说明一下最常用的命令,重点是对当前文件操作的命令。像 :Git pull 这样的命令,我就略过不讲啦。
首先,要把当前文件的修改存盘并加到 Git 的暂存区,可以使用 :Gwrite。
反过来,要放弃当前的修改,回到暂存区的状态,可以使用 :Gread。
如果你需要对当前文件进行移动、更名或删除,可以分别使用 :GMove、:GRename、:GDelete。跟命令行上的 git 命令不同,你不需要给出当前文件的名字。
要在 quickfix 窗口里查看当前文件的修改历史,可以使用 :0GclogGclog 是查看整个项目的历史,对大项目使用 quickfix 窗口可能会有性能问题)。同时,你可以通过历史窗口,回到这个文件的任一版本。
要查看文件的某一行是谁最后修改的,可以使用 :Git blame 命令。你不需要给出当前文件的名字,而且 Fugitive 会把输出放到一个跟当前文件编辑窗口联动的新窗口里,并进行了色彩加亮。
要比较当前文件和暂存区版本的区别,可以使用命令 :Gvdiff。
要查看当前 Git 库的状态,可以直接输入 :Git 命令。在结果缓冲区里,有很多正常模式命令可用(见 :help fugitive-staging-maps。其中最实用的可能是
使用 s 来把文件(整个文件)或光标下的修改(部分修改)加到暂存区中
使用 u 来重置加入暂存区的修改(撤销 s
使用 = 来切换开关这个文件的内联 diff 显示
使用 o 来在新分割的窗口中打开文件
使用 dv 来比较文件和暂存区版本的区别
使用 cc 来签入commit当前暂存区中的文件相当于 :Git commit 命令
最后,:Git push 本来是不需要解释的,但你可能会高兴知道,在安装了 AsyncRun见[第 8 讲])之后,这个操作自动就是异步的,不会在操作完成之前妨碍你继续进行编辑。
此外,所有的 :Git … 命令都可以打字成 :G …,事实上,之前给出的命令清单里,连这个空格都不要,所以你可以输入 :Gpush 来代替 :G push。不过作者宣称 :Gblame 和 :Gpush 这样的命令在将来的版本里可能会被删除,所以你如果在用 :Gpush 这样的命令的话,可能需要知道一下这个事情。
你已经可以看到Fugitive 的命令是相当复杂的,毕竟 Git 也很复杂。我上面讨论的,只是它的功能的一部分。你可以在它的帮助文件中了解进一步的信息(:help fugitive
GitGutter
如果说 Fugitive 是一个全功能 Git 插件的话GitGutter 就只是 Git 使用中的一个非常小的点。它的功能列出来也可以有不少,但跟 Fugitive 不同,它专注在单个文件内的修改管理上。
安装和配置
你需要使用包管理器安装 airblade/vim-gitgutter。如果你使用了一个比较知名的配色方案的话多半你不需要额外配置了。GitGutter 会试图从配色方案里找出合适的颜色,但这个方法对配色方案会比较挑,尤其是对默认配色方案不适用。如果你觉得边栏或边栏里符号的颜色不让人满意的话,可以在 vimrc 配置文件中作类似于下面的配置:
highlight! link SignColumn LineNr
highlight GitGutterAdd guifg=#009900 ctermfg=2
highlight GitGutterChange guifg=#bbbb00 ctermfg=3
highlight GitGutterDelete guifg=#ff2222 ctermfg=1
let g:gitgutter_set_sign_backgrounds = 1
没有上面那样的配置的话,在简单配色方案(如 Vim 的默认配色方案GitGutter 的默认设置会出现突兀的边栏和无色的符号,丑得让人无法接受。
使用
下面的动图展示了 GitGutter 的核心功能(使用 gruvbox 配色方案):
简单说明一下:
在 Vim 的边栏里用 +、- 等符号和合适的配色来标注哪些行有了修改
[c 和 ]c 可以用来跳转到上一个和下一个修改的位置
<Leader>hp 可以将光标下的修改块和缓存区中的内容进行对比
<Leader>hs 可以将光标下的修改块加入到暂存区中
<Leader>hu 可以恢复暂存区中的内容
换句话说GitGutter 的重点是一个文件内修改的管理——这个功能 Fugitive 可以说也有(在 :Git 命令下管理),但不及 GitGutter 直观。使用 GitGutter你可以在编辑文件时立即选择把部分修改加入暂存区方便后续的签入。因此虽然看起来 GitGutter 的功能不多,我使用它的频度跟 Fugitive 差不多。
Airline
如果你认为界面的美观很重要的话,你多半也会喜欢 Airline。Airline 应该可以算是 Vim 中最流行的插件之一了。它会和很多其他知名 Vim 插件自动进行集成,并在界面上展示非常丰富的信息。事实上,我在开篇词中给出的 Vim 界面截图中就使用了 Airline。我们这儿再重新展示一下。
靠近底部、开头是“NORMAL”的那行状态栏就是由 Airline 控制的。我们可以看到其中有非常丰富的信息:
当前的模式直接显示在状态栏上,并且每种模式都有自己的特别颜色,可以一目了然(在 gruvbox 提供的配色里,插入模式和可视模式的背景色都比目前的要突出得多)。
后面紧跟着的分支符号和“master”是 Airline 检测到了 Fugitive通过 Fugitive 获得了分支名称。
后面跟着的是文件名和修改标记。
再后面,我们可以看到光标所在的位置的函数名,这是 Airline 通过 Tagbar 获得的。
随后,我们看到文件类型 cpp。
最后,我们看到光标位置在文件中的行数百分比、行号、总行数和列号,通过一些花哨的 Unicode 符号来进行分隔。
这些只是 Airline 功能的一部分。事实上,它内部有对超过 50 个 Vim 插件的集成扩展,会自动检测它们的存在,并显示相关信息。要看到这些扩展的完整列表和当前状态,可以使用 :AirlineExtensions 命令。
安装和配置
Airline 本身的安装非常简单,通过包管理器安装 vim-airline/vim-airline 即可。不过要显示出比较好的效果你需要有一个支持它用到的特殊字符的字体。这些字体常常被叫做 Powerline 字体,原因是一个叫 Powerline 的插件最早开始使用这些特殊符号。Airline 也算是 Powerline 的一个替代品了不过两者还是有点区别的其中之一就是Powerline 要求 Python 支持,而 Airline 是纯 Vim 脚本。)
下面这几个是我比较推荐的:
Source Code Pro一款设计非常优秀的字体不花哨但耐看
Fira Code这是一款非常花哨的好字体如果你疑惑上一张图里居然有“→”就是这个字体对 -> 的特殊渲染),也支持 Airline 需要的特殊符号
DejaVu Sans Mono for Powerline著名的开源字体 DejaVu Sans Mono 加入了特殊符号支持的版本
在我目前的 vimrc 配置文件中,我对 Airline 进行了下面这样的配置:
let g:airline_powerline_fonts = 1
let g:airline#extensions#tabline#enabled = 1
let g:airline#extensions#tabline#buffer_nr_show = 1
let g:airline#extensions#tabline#overflow_marker = '…'
let g:airline#extensions#tabline#show_tab_nr = 0
首先,我告诉 Airline可以使用 Powerline 字体来显示特殊符号。
其次,我启用 tabline 扩展,在顶部显示缓冲区列表。
再次,我让 tabline 显示缓冲区的编号,这样方便我使用数字加 <C-^> 来切换缓冲区。
然后,我使用 … 来表示省略(单个字符,而非占据三列的三个点),这样可以节约一点屏幕空间。
最后,我一般不在一个 Vim 会话中打开很多个标签页,一般使用上一个、下一个标签页的命令来进行切换,所以也就不需要显示标签页的编号了,免得和缓冲区编号两个显示在一起会比较乱。
这样配置下的效果,可以参见下面的截图(图中的字体是 Source Code Pro
哦,对了,下面的那个闪电符号也是 Airline 搞出来了,提醒你当前的 Git 库里有没有提交的更改。
使用
Airline 的使用基本上是自动的安装、配置完了差不多就不用去管它了。留意看它的提示就行了。比如它会在右下角进行可能错误的显示提示包括行尾的空格trailing spaceYCM 发现的代码错误,等等。
Airline 还提供了一些命令,如 :AirlineExtensions 和 AirlineToggle。这些你直接看帮助文档:help airline就应该可以清楚了。
NERDCommenter
我们前面已经介绍过了 NERDTree而同一个作者写的另外一个插件 NERDCommenter也在我的不可或缺的 Vim 插件的列表上。它提供的是又一个开发常用的功能,对某个代码块或代码行加上注释,及反过来把注释去掉。
安装和配置
我们需要在包管理器中安装 preservim/nerdcommenter。然后我一般在 vimrc 配置文件中加入下面的代码,让 NERDCommenter 不要在终端 Vim 中加入菜单,干扰我使用 <F10> 查看最近的文件:
if !has('gui_running')
let g:NERDMenuMode = 0
endif
使用
NERDTree 提供的命令,有些可以工作于当前行或选定的行内部分字符,有些则只能工作于整行,即使用可视模式的话选行内部分字符相当于选了整行。能够适用于行内的有下面这些命令(也能适用于整行):
<Leader>cc 把代码变为注释
<Leader>cu 把注释起止符剥掉,恢复原先的代码
下面这些则只能工作于完整的一行或多行上:
<Leader>c<Space> 用来切换注释和非注释
<Leader>cb 用来加上“美观”的注释
<Leader>cs 用来加上“性感”的注释
取决于具体的语言,行内注释、“美观”注释和“性感”注释不一定全部都适用(比如,在 Python 里就没有在一行内部开始加结束的注释)。我们拿支持所有这些情况的 C++ 代码来演示一下这些不同的注释类型:
目前 NERDCommenter 支持的文件类型也有一百种左右了,应该说还是非常全面的。对于调试代码,它也是一个非常重要的工具。
Visual-Multi
我们今天要介绍的最后一个重要插件是 Visual-Multi它允许你把“光标”同时放在多个不同的位置上然后下面的命令对所有的位置都有效。利用这个插件我们要做变量重命名之类的操作就会变得非常简单。
安装和配置
我们利用包管理器安装 mg979/vim-visual-multi 即可。这个插件的默认配置挺合理,我目前没有发现要修改的内容。
使用
这个插件的核心功能,是帮你选中多个不同的位置,然后我们就可以使用正常的编辑命令,包括 c、d、i 等,来进行编辑。
所以,下一个问题自然是,我们该如何选中多个不同的位置呢?
Visual-Multi 里还是提供了好几种不同的选择方式的(:help vm-quickref我们就只讨论最常见的情况根据搜索条件来进行选择。
在 Visual-Multi 里,特别的搜索命令是 \\/。跟普通的搜索命令一样在输入的过程中我们即可看到屏幕上的匹配。但在搜索完成之后n 命令不再仅仅意味着跳转到下一个匹配位置,而是选中当前的位置,并跳转到下一个匹配位置。如果想要跳过一个位置,则可以使用 q 命令。想要直接选中所有匹配的位置的话,可以使用 \\A 命令。
跟我们可以用 * 命令直接搜索光标下的单词一样,在 Visual-Multi 里我们也有一个快捷搜索命令 <C-N>。之后,我们的使用方式就一样了。下面,我就演示一下怎么用这个快捷搜索命令加其他的 Visual-Multi 命令来完成[第 10 讲]综合实验里的那个批量代码更名操作。
看明白了吗?保险起见,我稍微解释一下吧。
我首先使用 <C-N> 命令搜索 \<smart_ptr\>,开始了特殊的 Visual-Multi 模式,扩展模式(相当于可视模式)。
然后我使用了 \\A 命令选中所有的匹配位置。
由于文件名不应该更改,我使用 q 键跳过第一个匹配。
随后,我移动光标位置,缩小选中的位置。
之后,我就可以使用 c 命令直接输入需修改部分的新名字了。
最后,我按一次 <Esc> 退出插入模式,再按一次 <Esc> 退出 Visual-Multi 模式(从屏幕底部也可以看到文字提示)。
Visual-Multi 也支持用 \\\ 命令来手工增加一个光标位置,也有其他的一些命令和功能。这些,就请你自己慢慢摸索练习了。毕竟,它的帮助文件的篇幅就比我这整个一讲还多呢。
内容小结
今天我们介绍了五个非常重要的插件。其中Fugitive 和 GitGutter 是用来支持 Git 版本管理的Airline 是提供界面上的丰富信息提示的NERDCommenter 和 Visual-Multi 则可以让一些代码的编辑任务变得更加高效。
本讲我们的配置文件中加入了这些不可或缺的插件,对应的标签是 l15-unix 和 l15-windows。
课后练习
高效地用好这些插件,可以让你的 Vim 使用效率再上一层楼。我们今天的课后练习,就主要是温习一下学到的知识了:
安装好这些插件(应该每个都很有用)
打开 Git 管理下的我们的配置文件,体验一下 Airline 的显示效果
随便做点修改,并存盘,观察 GitGutter 和 Airline 对边栏和状态栏的提示变化
使用 :Git 命令打开 Fugitive 面板,在修改的文件上用 = 观察其中的修改
使用 s 把修改加到 Git 的暂存区,然后用 cc 签入修改
关闭 Fugitive 面板,回到 .vimrc 或 _vimrc 文件,用 :0Gclog 浏览文件的历史,并查看这个文件的历史状态
用 1<C-^> 命令或 :e .vimrc 之类的命令回到原始文件,然后用 :Git blame 命令查看文件每行的修改历史
如果光标在左侧窗口,使用 gq 命令,如果光标在右侧,使用 <C-W>o 命令,回到只有配置文件的状态
使用 NERDCommenter 的命令,如 <Leader>cb来修改代码行的注释状态
使用 Visual-Multi把代码中的 source 全部改成 so
最后,如果你不知道怎么回退刚才对 Git 库的修改的话,可以手工输入下面的 Git 命令:
git reset HEAD~1
git reset --hard
在基本用法掌握之后,有学习余力的同学可以再仔细阅读一下帮助文件,了解我没有描述的用法。
如果有任何问题或疑问,欢迎留言和我讨论!

View File

@ -0,0 +1,174 @@
因收到Google相关通知网站将会择期关闭。相关通知内容
16 终端和 GDB 支持:不离开 Vim 完成开发任务
你好,我是吴咏炜。
早在 Vim 和 Emacs 的“圣战”时期Emacs 有个功能可是 Vim 用户一直暗暗垂涎的,那就是可以集成 GDB 来调试程序。Emacs 之所以能够实现这个功能,是因为它可以模拟一个终端环境,像终端一样跟一个程序进行输入输出的交互。这样一来,我们不离开编辑器,也能调试程序,既可以方便地看到目前执行在源代码的第几行,也可以直接在编辑器里跟执行中的程序进行交互。
很多主流的开发环境都支持类似的功能。但 Vim 一直不支持这样的功能,直到 Vim 8。虽然到得有点晚但 Vim 也算是厚积薄发,利用 libvterm 给出了完整的终端支持。今天,我们就拿终端窗口支持和 GDB 支持,作为我们最后的技术话题来介绍了。
终端窗口支持
基本用法
使用 :terminal缩写 :term命令我们可以在 Vim 的窗口中运行终端模拟器。基本的用法就是下面两种:
使用 :terminal后面不跟其他命令分割一个新窗口并使用默认的 shell 程序进行终端模拟shell 退出后窗口自动关闭(可用使用命令参数 ++noclose 改变这一行为)。
使用 :terminal 命令 的方式,分割一个新窗口,在其中运行指定的命令并进行终端模拟;命令执行完成退出后窗口不自动关闭,保留执行中显示的信息(可用使用命令参数 ++close 改变这一行为)。
跟其他的多窗口命令一样,:terminal 默认会进行横向分割,但你也可以在 terminal 前面加上 vert 来进行纵向分割,或加上 tab 来把终端窗口打开到一个新的标签页里。
跟 quickfix 窗口里只能看到程序的输出不同,在终端模拟器里我们既可以看到程序的输出,也可以向程序提供输入。同时,这个终端模拟器像一个真正的终端一样,能够支持色彩和其他的文本控制。你甚至可以在里面运行 Vim就像 Matrix 电影里层层嵌套的世界一样。
当然,从实用的角度,我并不建议你这么做——那样可能会让人头昏,并且容易在使用 <C-W> 和 :q 这样的命令时,出现结果跟自己预想不一致的情况。
终端模拟器的行为应当跟普通的终端一致;因此在 Vim 的终端模拟器里,你可以直接使用的命令跟一般的 Vim 窗口很不一样。毕竟,你在终端模拟器里输入 : 时,肯定不是想进入 Vim 的命令行模式吧?这时候,你需要知道下面这些在“终端作业模式”下的特殊命令(完整列表见 :help t_CTRL-W
<C-W>N注意大写<C-\><C-N> 退出终端作业模式,进入终端普通模式。这时终端窗口变成一个普通的文本窗口(终端缓冲区),不再显示色彩,但可以像普通的只读窗口一样自由使用,只是不能修改其中的内容而已。按下 a 或 i 可重新激活终端模拟器,进入终端作业模式。
<C-W>" 后面跟寄存器号,表示粘贴该寄存器中的内容到终端里。
<C-W>: 相当于普通窗口中的 :,执行命令行模式的命令。
<C-W>. 可以给终端窗口发送一个普通的 Ctrl-W。
<C-W><C-\> 可以给终端窗口发送一个普通的 Ctrl-\。
大部分的 <C-W> 开始的命令仍然可以使用,如窗口跳转命令(后面跟 j、k 等)、窗口大小调整命令(后面跟 +、_ 等),等等。
需要注意,终端模拟器里的光标只能用正常终端里的光标移动键来移动,比如在 Bash 默认配置下,可以用 <C-A><Home> 移到行首,用 <C-E><End> 移到行尾等。在退出终端作业模式后,光标就只是普通文本窗口的光标,不会影响终端模式里的光标位置——在你按下 a 或 i 时,光标还是在原来的位置,而不是退出终端作业模式后你移动到的新位置。你也不能修改终端缓冲区中的内容。只要稍微仔细想一想,你就知道这些是完全符合逻辑的。
当你从终端窗口切到另外一个窗口时,终端窗口里面的程序仍然在继续运行;如果你不退出终端作业模式的话,终端窗口里面的内容也会持续更新,跟正常的终端行为一致。要结束终端运行的话(而不只是临时退出终端模式),也跟普通的终端情况一下,可使用 exit 命令或 <C-D>。如果由于某种原因无法正常退出终端的话,则可以使用 <C-W><C-C> 来强行退出。
使用提示
如果你觉得自己不会在终端里另外启动 Vim似乎也就很少有机会用到 <Esc> 了,那我们干吗不把这个键用作退出终端作业模式呢?说干就干:
tnoremap <Esc> <C-\><C-N>
tnoremap <C-V><Esc> <Esc>
前缀 t 表示在终端作业模式下的键映射。我们把 <Esc> 映射到我们上面说的退出终端作业模式的快捷键;同时,我们又把 <C-V><Esc> 这一在终端里等价于 <Esc> 的按键组合映射为 <Esc>,这样万一我们需要 <Esc>,仍然可以用一种较为自然的方式获得这个按键。
遗憾的是,在 Unix 终端的情况下,很多功能键本身包含 <Esc>,因而会误触发这个键映射。对于这种情况,我们使用下面的键映射,用连按两下 <Esc> 退出终端作业模式效果更好:
tnoremap <Esc><Esc> <C-\><C-N>
此外,对于大部分人而言(像 Bram 这样,用 Vim 调试 Vim不属于大众需求吧在 Vim 的终端模式里启动 Vim恐怕是失误的可能性最大。为了防止这样的失误发生我们可以在 Vim 启动时检查一下,检测这种嵌套的 Vim 使用。你只需要把下面的代码加到 vimrc 配置文件的开头即可:
if exists('$VIM_TERMINAL')
echoerr 'Do not run Vim inside a Vim terminal'
quit
endif
你可以试验一下在 Vim 的终端窗口里再运行 Vim看一下上面的代码产生的出错效果。
终端的用途
说了这么多,你可能有点疑惑,单独起一个终端有什么问题吗?我为什么要在 Vim 里运行终端呢?
我是这么理解的:
方便。特别在远程连接的时候,有可能新开一个连接在某些环境里需要特别的认证,比较麻烦。即使连接没有任何障碍,你总还需要重新 cd 到工作目录里吧?而如果在一个现有的 Vim 会话里开一个新的终端,可以一个命令搞定,然后用你已经很熟悉的 Vim 命令在不同的窗口或标签页里切换。
文本。我们可以从终端作业模式切换到终端普通模式,然后用我们熟悉的 Vim 命令来对缓冲区中的文本进行搜索、复制等处理工作。
控制。你可以发送命令给终端,也可以读取终端屏幕上的信息。这样,事实上就打开了一片新天地,可以在 Vim 里做很多之前做不到的事情,比如,用 Vim 来比较两个屏幕输出的区别(:help terminal-diff
终端窗口相关的函数名称都以 term_ 打头(可以查看帮助文件 :help terminal-function-details。比如如果我们想要用程序向缓冲区编号为 2可以用 :ls 和 :echo term_list() 等命令来检查)的终端发送 ls 命令来显示当前目录下的文件列表的话,我们可以使用(注意转义字符序列要求使用双引号):
call term_sendkeys(2, "ls\n")
下面这个比较无聊的例子,可以用来获取 ~/.vim 目录下的文件清单:
let term_nbr = term_start('bash')
call term_wait(term_nbr, 100)
let line_pos1 = term_getcursor(term_nbr)[0]
call term_sendkeys(term_nbr, "ls ~/.vim|cat\n")
call term_wait(term_nbr, 500)
let line_pos2 = term_getcursor(term_nbr)[0]
let result = []
let line_pos1 += 1
while line_pos1 < line_pos2
call add(result, term_getline(term_nbr, line_pos1))
let line_pos1 += 1
endwhile
call term_sendkeys(term_nbr, "\<C-D>")
while term_getstatus(term_nbr) != 'finished'
call term_wait(term_nbr, 100)
endwhile
exe term_nbr . 'bd'
echo join(result, "\n")
这当然不是完成这件任务的最好方法,但上面的代码展示了终端相关函数的一些基本用法:
我们用 term_start 命令创建一个新的终端,得到终端缓冲区的编号
我们用 term_wait 等待 100 毫秒,待其就绪
我们用 term_getcursor 获取光标的当前行号
我们用 term_sendkeys 发送一个命令到终端上ls 之后用 cat 是为了防止 ls 看到输出是终端而产生多列的输出
然后我们等待命令执行完成并更新终端
我们获取光标的当前位置,然后用 term_getline 获得上一次的行号和这一次的行号之间的行的内容,放到变量 result 里
我们然后发送一个 <C-D> 到终端,结束作业
然后我们等待到 term_getstaus 返回的状态成为 'finished',即终端作业已经执行结束
最后我们用缓冲区编号加 bd 命令删除缓冲区(所以屏幕上我们看不到这个终端窗口),并用换行符作为分隔符打印 ls 返回的内容
你可以实际测试一下这个脚本,体会一下这些基本功能。比如,可以把脚本存盘为 test.vim然后用 :so % 来运行。
GDB 支持
为什么 Vim 直到最近才支持 GDB 呢?因为这真不是件容易的事情啊。为了能在 Vim 里顺畅地使用 GDBBram 需要在 Vim 里实现下面这些不同的功能:
终端支持
作业job和通道channel
窗口工具条、弹出窗口和弹出式菜单
有了这些功能之后Vim 通过一个内置的插件,就可以提供 GDB 的调试支持了。我们可以通过 :packadd termdebug 命令来加载这个插件,然后通过 :Termdebug 可执行程序名称 来调试一个可执行程序。
下面这个动图可以说明最主要的流程:
我简要说明一下需要注意的几点:
:Termdebug 命令会把屏幕分成三个区域,从上到下分别是 gdb 命令行,程序输出,以及含调试控制按钮的源代码窗口。
在最上面的 gdb 窗口中,我们可以输入 gdb 的命令,但程序的输出和纯终端使用 gdb 的情况不同,是在中间的窗口输出的。
最下面的的源代码窗口里,我们有五个按钮可以用,允许习惯图形界面的用户使用鼠标进行操作。我们也可以使用鼠标右键直接在源代码行上设置断点。(当然,我们仍然可以在最上面的 gdb 窗口用命令来完成这些任务。)
鼠标在变量上悬停时,可以显示变量的值。只要 gdb 能打印的信息,它就能用浮动提示显示出来。这比手工使用 gdb 的 p 命令还是要方便多了。
还有一个需要稍微注意的地方是如果你在不同的作用域有两个同名变量那浮动提示只能显示当前作用域的变量的信息即使你把光标放到不在当前作用域的变量上也是如此。这点上Vim 还是比较笨的——毕竟它不理解代码。
内容小结
这讲我们介绍了 Vim 8 带来的新功能:终端支持。这个功能给 Vim 打开了一片新的天空。使用终端支持,我们可以不离开 Vim 打开一个或多个新的终端窗口,里面可以模拟真正的终端功能,包括色彩控制。我们可以使用 Vim 命令来处理新的终端缓冲区中的文本。我们还可以利用代码来控制这个终端和读取其中的内容。有了这些支持Vim 也就顺理成章地支持使用 GDB 像集成开发环境一样地调试程序了。
根据我个人的经验,在使用了这个功能之后,我开启新远程连接比之前少了,而经常在一个服务器上只开一个连接,里面开一个 Vim 来完成所需的任务。编译和执行,可以全部在这个 Vim 会话里完成。
本讲我们的配置文件中加入了针对终端窗口的键映射和防 Vim 重入,对应的标签是 l16-unix 和 l16-windows。
课后练习
请尝试使用 :terminal 命令,打开一个新窗口,并在其中进行操作,然后退出终端作业模式,把终端缓冲区中的内容复制到新的缓冲区中。
如果你使用一种可以用 GDB 调试的编译语言的话,也请你尝试一下使用 :Termdebug 命令进行调试。如果你之前用的是纯命令行的 gdb 的话,这个功能还是有很大的易用性提升的。
最后,同样地,如果有任何问题或疑问,欢迎留言和我讨论!
我是吴咏炜,让我们在告别这个课程之前,再道一次再见。

View File

@ -0,0 +1,265 @@
因收到Google相关通知网站将会择期关闭。相关通知内容
拓展1 纯文本编辑:使用 Vim 书写中英文文档
你好,我是吴咏炜。
今天是拓展篇的第 1 讲,我想带你对 Vim 的纯文本编辑技巧做一个专项突破。由于 Vim 是在欧美世界诞生的工具,贡献者中也是说英语的人居多,因而它对英文的支持要远远超出其他语言。所以今天,我们就深入讨论一下,如何使用 Vim 来进行纯文本编辑,特别是英文的文本编辑。
熟练掌握这一讲的内容,可以让你使用 Vim 书写中英文文档时都感到游刃有余。如果你有这个需求,一定要亲自动手尝试我提到的这些功能,加深自己的记忆。如果你觉得还需要多花一点时间,消化吸收前几讲的基础知识,也可以先阅读全文,把握要点,之后再回过头来深入学习。
为什么不使用字处理器?
你可能已经开始怀疑了,我为什么要使用 Vim 来进行文字编辑?用 Word 不香么?如果嫌 Word 贵,还有免费的 WPS 啊……
首先Word 和 WPS 这些字处理器不是用来生成纯文本文件的。在处理纯文本文件上,它们反而会有诸多劣势,如:
只能本地使用,既不能在远程 Linux 服务器上运行,也不能用 SSH/SCP 的方式打开远程的文件(除非在服务器上启用 Samba 服务,但体验真的不好)
分段和分行一般没有很好的区分
如果存成纯文本的话,格式会全部丢失
最后一句话似乎是废话?还真不是,纯文本文件里面是可以存储格式的,但 Word 和其他字处理软件对于文本类型一般只能支持纯文本或富文本Rich Text而富文本虽然包含了格式信息但却对直接阅读不友好。我想没有人会去手写富文本文件吧。仍有一些带格式的文本文件比较适合手写下面这些是其中较为流行的格式
Tex 和 LaTeX著名的特别适合写公式的文档系统在数学和物理学界尤其流行。
DocBook基于 SGML/XML 的文档系统,可以生成多种不同的输出格式;大量开源软件的文档是用 DocBook 写的。
AsciiDoc功能和 DocBook 等价、但使用非 XML 的简化语法的文档系统;有的国外技术书出版社接受作者用这种格式提交的稿件。
HTMLHTML 的阅读友好性一般,但胜在熟悉的人多。
MarkdownMarkdown 的阅读体验非常友好,因而它虽然最“年轻”却最流行。接下来,我们就介绍一下这种文件类型。
Markdown 简介
Markdown 是由 John Gruber约翰 · 格鲁伯)在 2004 年发明的,它不是一种标准化的格式,存在着多个实现,功能也并不完备。尽管如此,由于它轻量、易写、易读,很快就在互联网上流行开了。在 GitHub 上,现在 README 文件一般都使用 Markdown。
通过工具(很多是开源的,如 pandocMarkdown 可以很容易地转成网页、PDF 等其他格式,同时也很适合以纯文本的形式阅读。而 HTML、DocBook 等格式实际上是不太适合人直接看源代码来阅读的。此外,极客时间,以及很多写作平台,用的也是 Markdown。
事实上在处理代码相关的文档时Word 还真没有 Markdown 方便。Word 的某些“自动”功能,如把直引号替换为弯引号,会对键入代码造成干扰。而对嵌入的代码进行语法加亮,则是 Word 里没有、而 Markdown 里非常成熟的功能了……
不过,我们今天的重点不是 Markdown而是怎么使用 Vim 来编辑上面提到的所有文件格式。没有了图形界面的简单化处理你可以把控文本的一切细节但同时Vim 的语法加亮和文字编辑功能本身,也会让你编辑起来非常得心应手。鉴于 Markdown 的优势,我们今天的例子还是会用 Markdown。这样你学了这一讲之后至少会知道怎么高效地给开源项目写个 README 文件。
英文文本编辑
考虑到 Markdown 等标准在中文处理的标准化上面有先天不足,我们先学习文档的主语言为英文的情况。我们可以先看一眼下面的截图:
然后对比一下它的 Markdown 源代码在 Vim 中的展示效果:
我们明显可以看到,用 Vim 编辑 Markdown 文件时,虽然没有浏览器里显示得那么美观,但在使用等宽字体的前提下仍有着合适的语法加亮。
有两个细节值得关注一下:
跨行的那个链接加亮正常(我在至少两种其他环境下看到在方括号跨行时链接就无法得到正确的处理)。
单词“LICENCE”在 Vim 展示时也使用了斜体(一对星号中间的内容在 Markdown 里就是使用斜体强调),并且如果光标移出该行,星号会被自动隐藏,更方便阅读。
需要注意,在网页中的换行位置和源代码中的换行位置是不一样的。源代码中存在真正的换行(上一讲提到的 LF 或 CR LF 构成的行尾结束符);而转换到网页显示之后,单个换行只相当于空格字符,浏览器里一行应当显示多少字符仍然由浏览器的宽度和样式表来决定。这就是标准的 Markdown 的行为了。
行宽设置
英文文本文件的惯例仍然是一行放不超过 80 个字符,所以在源代码中仍然是有手工断行的。这个习惯是为阅读“源代码”优化的:可以看到,上面这个 Markdown 文件虽然在浏览器里查看效果更好,以纯文本的形式查看也是非常干干净净、毫无问题——只要你的编辑器列宽大于等于 80 就行了。
我们上一讲已经提到了文本宽度选项 textwidth。在对英文文本编辑时这个选项的推荐数值通常是 72比标准列宽 80 稍窄。这个设置有历史原因,但更重要的是,这也是经过历史验证对人阅读比较舒适的设定:既不会产生频繁的换行而打乱阅读节奏,也不会因为行太长而发生寻找下一行起始位置的困难。
被誉为“排版圣经”的 The Elements of Typographic Style 对行宽有这样的描述:
Anything between 45 to 75 characters is widely regarded as a satisfactory length of line for a single-column page… . The 66-character line … is widely regarded as ideal.
我们说列宽 72是指最大值而使用 72 产生的实际文本宽度,差不多就是落在 66 这个理想值附近了。一些编码规范,如 Python 的 PEP 8也约定对文档内容的列宽数值应当是 72。大部分编码规范对代码宽度的约定稍宽松些一般是 79 或 80。
格式化选项
我们上一讲已经提到格式化选项 formatoptions缩写 fo今天我们来稍微展开一下看看这些格式化选项对我们写文档有什么样的影响。
在 Vim 里fo 选项的默认值是 tcq。根据 Vim 的帮助文档,它们的含义是:
t使用 textwidth 自动回绕文本。
c使用 textwidth 自动回绕注释,自动插入当前注释前导符。
q允许 gq 排版时排版注释。
不过,根据你编辑的内容的语法,这个选项内容可能会发生变化。如运行支持文件里的 ftplugin/c.vim 里有下面的语句:
" Set 'formatoptions' to break comment lines but not other lines,
" and insert the comment leader when hitting <CR> or using "o".
setlocal fo-=t fo+=croql
即,正常输入非注释内容时,不进行回绕。但对注释还是要使用回绕的。另外几个我们还没检查过的选项是:
r在插入模式按回车时自动插入当前注释前导符。
o在普通模式按 o 或者 O 时,自动插入当前注释前导符。
l插入模式不分行: 当一行已经超过 textwidth 时,插入命令不会自动排版。
上一讲我们用到的 n 则是:
n在对文本排版时识别编号的列表。实际上这里使用了 formatlistpat 选项,所以可以使用任何类型的列表。出现在数字之后的文本缩进距离被应用到后面的行。数字之后可以有可选的 .、:、)、] 或者 }。注意 autoindent 也必须同时置位。
formatlistpat 的缺省值是 ^\s*\d\+[]:.)}\t ]\s*。也就是说,起始处有可选的空格,然后是至少一个数字,之后必须跟 .、:、)、]、}、制表符或空格中的一个,随后是可选的若干空格。
运行支持文件里的 ftplugin/markdown.vim 会对 formatlistpat 做额外的设定,使得 Vim 不仅可以识别数字列表,也能识别用 - 等字符开始的无序列表。有兴趣的同学可以自己分析一下。
如果你希望有比较接近字处理器的体验,不用自己手工断行,下面两个选项对英文文本编辑比较重要:
w拖尾的空格指示下一行继续同一个段落。而以非空白字符结束的行结束一个段落。
a自动排版段落。每当文本被插入或者删除时段落都会自动进行排版。
这两个选项结合的效果,会让在 Vim 里编辑的效果在某种程度上接近字处理器:你会看到在某行增删内容会自动导致下面的行跟着卷动,你可以通过下面的动图看下效果(我使用了 Vim 的 listchars 选项来加亮行尾空格和行尾结束符):
段中不换行的文本
到现在为止,我们讨论的英文文本编辑,基本都是行尾结束符不代表真正分行的情况。这种方式最常见,但也存在例外,如有些网站不使用标准的 Markdown 规则,把行尾结束符直接就当成换行了。在这样的情况下,我们就不应该在一段中间手工插入换行符。不过,由于这种方式不是 Vim 的“自然”处理方式,我们需要修改一些选项和处理习惯来应对这种情况。
首先Vim 在默认配置下会在窗口宽度不足时自动折行显示,但不会对折行的位置进行特殊挑选,很可能会在单词的中间折行。我们可以设置选项 linebreak告诉 Vim 要在 breakat 的字符上才可以折行。默认的 breakat 设置包含了空白字符和英文标点符号,因此直接可以在英文环境中进行使用。下图展示了 linebreak 的效果:
在这种模式下进行编辑时,另外一个要注意的问题是 j 和 k 移动的是一个物理行,而非屏幕行。这和很多编辑器的行为是不同的。要让光标一次移动一个屏幕行,需要的按键是 gj 和 gk。如果你希望 <Up><Down> 的行为跟主流的编辑器一致,可以考虑以下的设定:
" 修改光标上下键一次移动一个屏幕行
nnoremap <Up> gk
inoremap <Up> <C-O>gk
nnoremap <Down> gj
inoremap <Down> <C-O>gj
最后,如果之前换行符不代表分段,现在你希望换行即分段,你可以用 J代表 join连接命令把多行重新连接成一行。这个命令在某种程度上可以看作是 gq 的逆命令。由于这个命令根据字符的类型来决定是否插入空格和插入几个空格(参考 :help 'joinspaces'),它并不能简单地用替换命令来代替。如果考虑最简单的情况(:set nojoinspaces那至少在 formatoptions 中含有 w 时,我们可以用下面的替换命令来连接同一段的所有行:
:%s/\([^\n]\)\s\+\n\s*\([^\n]\)/\1 \2/
事实上,使用 joinspaces 和 Markdown 的双行尾空格代表换行是潜在会冲突的。在 Markdown 中使用 w 时,应当使用 nojoinspaces。
模式行
到现在为止我们讨论的好些选项不仅不适合作为全局选项也不适合作为某一文件类型的选项而更适合用作单个文件的选项。Vim 也确实提供了这样的功能,叫做模式行(:help modeline能自己使用 :setlocal 仅对当前缓冲区设置本地选项。这个功能本身在帮助文件里说得挺清楚,我就不重复了。
在上面图里的那个文本文件中,我使用了下面的模式行:
vim:set et sts=2 tw=68 com-=mb\:* com+=fb\:* fo=tcqaw:
上面这个模式行,除了设定了我们讨论过的 et 扩展 tab 为空格选项、sts 软 tabstop 选项、tw 行宽选项和 fo 格式化选项外,对 comments缩写 com选项进行了调整不用星号作为注释中间部分的开始mb而用星号作为一个列表的开始fb。关于 comments 选项的详细解释可以参看帮助文档 :help format-comments我这儿就不展开了。
拼写检查
写英文时启用自动拼写检查这是一个写作的好习惯。哪怕你英语很好也可能因为疏忽而拼错。Vim 从版本 7 开始,就内置了拼写检查的功能,可以通过选项 spell 来打开。
上面我存心写错了一个地方“userz”被 Vim 用红波浪线标了出来。标蓝波浪线的是 Vim 提醒我,句首一般需要首字母大写(此处没有错误)。另外,可以注意到单个换行是不会被 Vim 当作一段结束的因此行首的“right”和“promise”等单词不会被 Vim 当成有拼写问题。
在 macOS 和 Windows 的图形界面 Vim 里,右键默认就可以弹出拼写纠正菜单,跟大部分其他有拼写检查功能的软件差不多。在 Linux 上,右键默认是 xterm 标准的扩展选择区域的行为。要让右键在 Linux 下也能弹出菜单,你需要手工在 vimrc 配置文件里加入:
set mousemodel=popup_setpos
这可以算是图形界面的 Vim 跟终端 Vim 比起来的明显优势了:终端 Vim 显示不了波浪线,对右键的响应也通常有问题。在终端 Vim 里,你可能就需要去记拼写检查的命令了(见帮助 :help spell
Vim 拼写检查的默认语言是英语。对不同语言的支持,我们可以使用 Vim 选项 spelllang 来设定。比如,如果你希望按照英式英语的拼写,那你可以设置:
set spelllang=en_gb
这样一来Vim 就会把非英式英语的拼写方式用绿色波浪线(当然,这和色彩方案有关)标出来。在图形界面 Vim 里,你同样可以很方便地用右键点击来更改:
你如果希望使用美式英语,当然使用 :set spelllang=en_us 就可以了。同时使用英式和美式,可以用 :set spelllang=en_gb,en_us。如果任何英语拼写都能接受那使用默认值或 en 就行。
作为东亚文字的特殊情况,如果你希望所有的东亚字符不被标成拼写错误的话,可以在 spelllang 选项里使用特殊值 cjk。作为中国人我们可能会需要这么用
set spelllang+=cjk
拼写完成
我们之前提到了 Vim 支持用一个字典文件来进行拼写完成,你可以在帮助 :help 'dictionary' 里找到相关的信息。不过,这种方式需要你手工去寻找一个字典文件,并在 vimrc 里进行配置,不那么方便。更简单的方式,是先启用拼写检查,然后正常使用拼写完成的快捷键 <C-X><C-K> 即可。如果你完整拼出了单词,但 Vim 提示拼错了,你也可以使用快捷键 <C-X>s 来使用和查看拼写建议。
注意:虽然 <C-S> 在图形界面 Vim 里可以使用,但终端(不是 Vim可能会解释 <C-S> 为特殊控制字符,因而我们一般不使用 <C-X><C-S>。如果你发现一不小心键入 <C-S> 导致终端表现得像失去响应一样,一般可以用 <C-Q> 来恢复。
中文文本编辑
跟英文文本编辑类似,中文处理同样有段中有断行和段中无断行两种方式。如果段中有断行,中文的主要处理麻烦是在转换成 HTML 或其他格式时通常不应该把行尾结束符转换成空格。我目前测下来GitHub 的 Markdown 能有这样的合理行为,但很多其他工具,如 pandoc则没有对中文作这样的特殊处理。因此中文文档还是不在段中进行断行更保险一些。
这两种文本组织方式 Vim 都是能处理的,其方式和英文文本编辑差不多,差别主要表现在下面两点。
首先,英文段中不分行时我们推荐使用 linebreak 选项,但中文段中不分行时我们则不推荐启用这个选项。因为让 Vim 挑空格位置来折行,反而会让中文文本显得乱、不好看。下面的图展示了区别:
其次Vim 在处理中日韩CJK文字时在格式化选项 formatoptions 里是有些特殊设置的。我们重点关注下面 4 个:
m可以在任何值高于 255 的多字节字符上分行。这对 CJK 文本尤其有用,因为每个字符都是单独的单位。
M在连接行时不要在多字节字符之前或之后插入空格。优先于 B 标志位。
B在连接行时不要在两个多字节字符之间插入空格。有 M 标志位时无效。
]:严格遵循 textwidth 选项。当设定这个标志时,除非断行禁则使得行长不可能保留在限定的文本宽度以内,行长不允许超出限定的文本宽度。这个选项主要用于 CJK 文字,并且仅在 encoding 是 utf-8 时才生效。
如果文档的主语言是中文(或日文、韩文),那 m 肯定是需要设置的这样才能在中文之中断行。M 和 B 一般我们也会设置其中一个,取决于行文规则,在中文和西文字符之间是否手工插一个空格。如果不插空格,那就用 M如果用空格就像本文一样那就用 B。由于 Vim 不区别汉字和汉字标点,使用 B 时会导致全角标点前后出现空格,我一般仍然使用 M 而不是 B。
标志 ] 是和 CJK 断行规则一起在 Vim 8.2.0901 这个版本引入的。你可以通过下面的截图看下这个标志的效果:
换句话说,默认未使用 ] 标志时Vim 的行为是允许 CJK 标点符号突出到 textwidth 限定的宽度以外;使用 ] 标志则不允许这样的特殊处理。你可以按照个人喜好和文本类型来酌情使用这个选项。
内容小结
在这一讲,我们讨论了使用 Vim 进行文本编辑的一些要点,重点是对 Markdown 文件进行编辑。学过这讲之后,我们应当记住:
中英文文本编辑通常有段中换行和段中不换行两种惯例,英文文本使用段中换行居多,而中文文本使用不换行的比例要高得多。
Vim 的 textwidth 选项用来设置一行文本的最大半角字符数对于英文72 是一个常见的数值。
Vim 的 formatoptions 选项可以设置很多如何对文本进行格式化的标志很多文件格式插件ftplugin会对其进行设置。
模式行可以用来记录只对单个文件生效的 Vim 选项;如果文件有特殊的格式化要求,可以写在模式行里。
Vim 提供了拼写检查和拼写完成功能,可以用于英文等字母文字。
如果使用段中不换行的惯例,则英文文本应当使用 linebreak 选项,但中文文本不应当使用该选项。
在处理中文文本时m、M、B 和 ] 是可能有用的特殊 formatoptions应当根据实际需要使用。
本讲我们的配置文件有几处改动,对应的标签是 x1-unix 和 x1-windows。
课后练习
请尝试写一个小小的英文 README.md内容不限可以从其他地方复制。要求是
内容除标题外至少三段,每段至少两行
使用模式行控制选项
段中分行,启用自动格式化但不使用行尾空格的方式
尝试在该文件中嵌入代码
想一想,为什么 a 选项一般和 w 选项一起使用
你在写 README 文件,或者其他文本文档时,还遇到过其他问题吗?请及时和我交流。你也可以把今天这一讲的内容,分享给身边其他需要编辑纯文本文件的朋友。
我是吴咏炜,我们下一讲再见!

View File

@ -0,0 +1,268 @@
因收到Google相关通知网站将会择期关闭。相关通知内容
拓展2 C 程序员的 Vim 工作环境C 代码的搜索、提示和自动完成
你好,我是吴咏炜。
从今天开始,我们会用连续 3 讲,深入讨论怎么为编程定制环境。如果你是 C 程序员,那么今天这一讲对你来说毫无疑问是必修课。如果你用的是类 C 语言也能从这一讲中学到很多有用的内容尤其是在语法加亮精调、tags 和 Clang-Format 部分。
语法加亮精调
在[第 4 讲]中我们已经学到了Vim 能根据文件类型对代码进行加亮。在[第 12 讲]里,我们还进一步讨论了 Vim 实现语法加亮的细节,知道这些是如何通过代码来进行控制的。对于 C含其他基于 C 的语言如 C++),语法加亮文件有一些精调选项,还挺有意思,能应对一些特殊场景的需求。我一般会设置以下几项:
let g:c_space_errors = 1
let g:c_gnu = 1
let g:c_no_cformat = 1
let g:c_no_curly_error = 1
if exists('g:c_comment_strings')
unlet g:c_comment_strings
endif
第一项 c_space_errors 用来标记空格错误,包括了 tab 字符前的空格和行尾空格,这样设置之后 Vim 会把这样的空格加亮出来。
第二项 c_gnu 激活 GNU 扩展,这在 Unix 下一般是必要的。
第三项 c_no_cformat 不对 printf 或类似函数里的的格式化字串进行加亮。这条可能看个人需要了。我是对错误的加亮超级反感,所以关闭这种不分场合的加亮。
第四项 c_no_curly_error 也是为了让一些 GNU 的扩展能够正确显示,不会被标志成错误。
最后Vim 默认会在注释中加亮字符串和数字c_comment_strings。虽然这种加亮有时候也能派上用场但这种配置下我常常在注释中见到错误的加亮所以我还是关闭这个功能。
关于这些选项的说明,以及其他的 C 语法加亮选项,你可以查看帮助 :help ft-c-syntax。
Tags
我们在[第 8 讲]里已经讨论过了 tags 文件。回想一下tags 是 ctags 工具分析你的代码之后生成的可读文本符号数据库,帮助你找到一个符号的定义所在位置。这个 tags 格式有点简单、有点平却也正好像极了 C 语言的特点:简单,平淡,然而强大。
C 是一种比较简单的语言,近些年来没有什么大变化。因而如果你能安装 Universal Ctags 固然好,但很多包管理器里自带的 Exuberant Ctags 也基本够用了。本讲中的内容只要求 Exuberant Ctags。
推而广之,本讲我们用到的工具都比较简单,且容易获得。这实际上就是我把 C 和 C++ 分开的原因——要处理好 C++,需要的工具就会复杂不少。
回到 tags。Exuberant/Universal Ctags 有大量的命令行参数,如果我们要对某个目录下的所有 C 代码生成 tags 文件,我们可以使用:
ctags --languages=c --langmap=c:.c.h --fields=+S -R .
这儿我用了 --languages 选项来指定只检查 C 语言的文件;同时,因为 .h 文件默认被认为是 C++ 文件,所以我使用 --langmap 选项来告诉 ctags 这也是 C 文件。而 --fields=+S 的作用,是在 tags 文件里加入函数签名信息。我们后面会看到这类信息的作用。
这样生成的 tags 文件只考虑符号的定义,而不考虑符号的声明。对于大部分项目,这应该是合适的。如果你希望 Vim 能跳转到函数的声明处,则需要加上 --c-kinds=+p让 tags 文件包含函数的原型声明。但是这样一来,一个函数就可能有原型声明和实际定义这两个不同的跳转位置,所以通常你不应该这样做。我只对系统的头文件生成 tags 文件时使用 --c-kinds=+p。
此外,一个可能的麻烦就是 tags 文件需要进行更新。对于一个更新不频繁的项目,最简单的方式就是在开始时运行一下上面的命令,然后就一直不管了。而对于活跃开发中的项目,我们需要更好的办法。
一个最简单的办法,显然就是在文件存盘后自动运行上面这样的命令。比如:
function! RunCtagsForC(root_path)
" 保存当前目录
let saved_path = getcwd()
" 进入到项目根目录
exe 'lcd ' . a:root_path
" 执行 ctagssilent 会抑制执行完的确认提示
silent !ctags --languages=c --langmap=c:.c.h --fields=+S -R .
" 恢复原先目录
exe 'lcd ' . saved_path
endfunction
" 当 /project/path/ 下文件改动时,更新 tags
au BufWritePost /project/path/* call
\ RunCtagsForC('/project/path')
但这种方式对于大项目是不可行的因为会在文件存盘时引入不可接受的时延。还好Ctags 支持用 -a 选项对 tags 文件做渐进式更新。把上面的后两段代码换成下面这样就可以了:
function! AppendCtagsForC(file_path)
let saved_path = getcwd()
exe 'lcd ' . a:root_path
exe 'silent !ctags --languages=c --langmap=c:.c.h --fields=+S -a '
. a:file_path
exe 'lcd ' . saved_path
endfunction
au BufWritePost /project/path/* call
\ AppendCtagsForC('/project/path/', expand('%'))
在执行很多 Vim 命令时,可以用 % 指代当前文件;而在调用函数时,就得用 expand 函数了(可查看帮助文档 :help expand())。代码中的其他内容你应当可以理解了。
如果你觉得上面这样做太麻烦的话,有个好消息是已经有人把类似于上面、但更加完善的功能做成了插件。插件 ludovicchabant/vim-gutentags 可以完成 tags 文件的管理工作。如果你希望 tags 文件能自动更新的话,这个插件很可能可以满足你的所有需求:基本上你只需要配置它的选项,而不需要自己写 Vim 脚本代码。
不过,如果你在一个大项目上工作,代码很少发生结构性更动的话,也许在 shell/cron 里定期执行 ctags 命令,是个最简单、最不对开发者造成干扰的选项。毕竟,靠编辑器触发 ctags 命令也不那么可靠——因为很多其他操作(比如 git 切换分支)之后,你也同样需要更新 tags。
EchoFunc
记得我们刚刚讲过在 tags 文件里加入函数签名信息吧下面我们就要用上了。EchoFunc 插件可以用来回显函数的原型。
首先我们需要安装 EchoFunc使用包管理器安装 mbbill/echofunc 即可。然后,如果你有正确的 tags 文件,现在当你输入函数名加 ( 时Vim 就会在屏幕底部自动提示函数的原型了:
上图中实际上表现了 EchoFunc 的两个效果:一个是我们说的屏幕底部的原型回显,还有一个是鼠标移到符号上的气泡显示。后一个功能默认也是开启的(需要图形界面),但可以通过在 vimrc 配置文件中加入 let g:EchoFuncAutoStartBalloonDeclaration = 0 来禁用。
此外,当一个函数有多个原型声明时,可以用 Alt-\= 和 Alt-- 键来进行切换。但在 Mac 上,我和明白当年选择了把 Alt-- 改成了 Alt-Shift-\=。原因我现在想不起来了,估计是因为在 Mac 键盘上 Alt-- 会产生短破折号(“–”),我们写文档时仍然可能会用到;而 Alt-\= 和 Alt-Shift-\= 产生的分别是不等号(“≠”)和正负号(“±”),基本上不会在 Vim 里使用。
Cscope
虽然 tags 是一个很有用的工具,但在我们常见的代码跳转操作里它只做到了一半:可以通过符号名称(从使用的地方)跳转到定义的地方,但不能通过符号名称查找所有使用的地方。这时候,我们有两种基本的应对策略:
使用搜索工具(第 8 讲讨论过的 :grep
使用专门的检查使用位置的工具
诚然,第一种方式很常用、也很通用,但这毕竟是一种较“土”的办法,其主要缺点是容易有误匹配。第二种方法如果能支持的话,在完成大部分任务时会比第一种方法优越。
对于 C 代码,我们有这样的现成开源工具。由于 Vim 直接内置了对 Cscope 的支持,因此我们今天就讨论一下 Cscope。
根据 Cscope 的文档,它的定位是一个代码浏览工具,最主要的功能是代码搜索,包括查找符号的定义和符号的引用,查找函数调用的函数和调用该函数的函数,等等。查找引用某符号的地方、调用某函数的地方、包含某文件的地方,就是 Cscope 的独特之处了。
安装和配置
用 Cscope 之前,我们首先得把它装起来。我们先要安装 Cscope 本身:
Ubuntu 下可使用 sudo apt install cscope
CentOS 下可使用 sudo yum install cscope
macOS 下可使用 brew install cscope
Windows 下可在 cscope-win32 项目里下载 cscope.exe 的可执行文件,然后放到 PATH 设定里的某一个目录下即可
然后我们需要映射使用 Cscope 的按键。Cscope 的网站上提供了一个映射的脚本,不过,它里面用到了 <C-Space>,这个快捷键在很多系统上是会有问题的。因此我替换了一下,你可以安装 adah1972/cscope_maps.vim 来使用它。现在:
使用 <C-\> Cscope 命令是在当前窗口里执行 Cscope 命令
使用 |Shift-\ Cscope 命令是横向分割一个窗口来执行 Cscope 命令
使用 || Cscope 命令是纵向分割一个窗口来执行 Cscope 命令
Cscope 也可以配合 quickfix 窗口使用这样相应命令的结果就会放到 quickfix 窗口里而不是直接在界面上提供一个可能有几千项的列表让你选择一般推荐在 vimrc 配置文件里加入
set cscopequickfix=s-,c-,d-,i-,t-,e-,a-
我们就采用这样的设定
创建 Cscope 数据库
要创建 Cscope 的数据库只要在项目的根目录下运行 cscope -b 即可 Vim 的约 50 万行代码为例在我的笔记本上首次运行该命令需要四秒产生了一个 13M 大小的 cscope.out后面再运行该命令Cscope 只会更新修改的部分那就快得多了不过 Ctags 不管是首次创建还是后面更新Cscope 都要慢一点这当然也很正常毕竟 Cscope 要干的事情更多
使用
上面我们已经提到了 Cscope 命令但我们还没有说过 Cscope 到底有哪些命令我们可以简要列表如下
g查找一个符号的全局定义global definition
s查找一个符号symbol的引用
d查找被这个函数调用called的函数
c查找调用call这个函数的函数
t查找这个文本text字符串的所有出现位置
e使用 egrep 搜索模式进行查找
f按照文件file名查找 Vim gf、<C-W>f 命令相似)
i查找包含include这个文件的文件
a查找一个符号被赋值assigned的地方
比如,我们在 Vim 的源代码里要查找 vim_free 函数的定义,只需要键入命令 :cscope find g vim_free或者把光标移到符号 vim_free 上,然后按下 <C-\>g在目前的配置下使用 <C-]> 也可以,因为 cscope_maps.vim 脚本里设置了 :cscopetag 选项(参见帮助 :help cscopetag
如果我们想要使用分割窗口,那对应的命令是 :scscope find g vim_free光标已经在符号上时的按键是 |g 或 <C-W>]。如果我们想要垂直分割窗口,那命令是 :vert scscope find g vim_free光标已经在符号上时的按键是 ||g。
其他的 Cscope 命令也类似,把 g 替换成相应的命令即可。值得提一下,虽然 t 和 e 命令在 Vim 里可以用 :grep 命令替代,但这两个命令执行起来要比 :grep 快。当然Cscope 的真正优势还是 s、c、i、a 这样的命令。下面展示的是在一个 Vim 源文件中执行符号引用查找s的结果可以看到比 :grep 还是方便快捷了很多的:
ClangComplete
下面我们来讨论一下一个新话题C 代码的自动完成。
众所周知Clang 是一个目前很流行的、模块化的 C/C++ 编译器。它跟其他 C/C++ 编译器最不一样的地方,是它让其他程序能够很容易利用 Clang 对源代码的处理结果。目前很多对 C/C++ 源代码进行处理的工具,都是基于 Clang 来开发的。ClangComplete 也是其中的一个,它在 Vim 中添加了对 C/C++ 代码的自动完成功能。
说到这里我需要强调一下ClangComplete 目前已经不是我最推荐的自动完成插件了——我更喜欢下一讲要讨论的 YouCompleteMe它的功能更为强大使用也更为方便。不过呢ClangComplete 在某些环境里安装起来更加简单,如果出于某种原因,你的系统上安装 YouCompleteMe 不成功,那么 ClangComplete 也不失为一个后备方案。
此外在我的配置方案里ClangComplete 和 YouCompleteMe 是可以共用配置文件的,所以讲 ClangComplete 的功夫也不会完全白费。下面我就简单地介绍一下。
安装
ClangComplete 对系统的基本要求就是你已经安装了 LLVM/Clang。你需要告诉 ClangComplete在哪里可以找到 libclang。
Windows 下默认是没有 Clang 的,如果你不是已经安装了 Clang你可以直接跳过这节直奔下一讲讨论 YouCompleteMe 的安装过程。
在 macOS 上,如果你安装了开发工具,那其中就应该有 libclang通常你可以在 /Library/Developer/CommandLineTools/usr/lib 目录下找到 libclang.dylib或者如果你用 Homebrew 安装了 llvm 的话,应该可以在 /usr/local/opt/llvm/lib 目录下找到 libclang.dylib。
在 Linux 上这又是个跟发布版相关的问题了。我们一般可以通过关键字“libclang”、“clang”和“llvm”从最特别到最通用来查找。在 Ubuntu 18.04 上,我们可以在使用命令 sudo apt install libclang1-10 之后找到文件 /usr/lib/x86_64-linux-gnu/libclang-10.so.1。在 CentOS 7 上,我们可以在安装 LLVM Toolset 7.0 之后找到文件 /opt/rh/llvm-toolset-7.0/root/usr/lib64/libclang.so。这些路径我们等会儿就要用到。注意如果 libclang 的文件名不是“libclang”加平台的动态库后缀的话我们需要使用 libclang 的完整名字。
有了 libclang 之后ClangComplete 本身的安装很简单,就是在包管理器里安装 xavierd/clang_complete然后在 vimrc 配置文件里加一个全局变量,告诉 ClangComplete 在哪儿可以找到 libclang。比如在上面说的 Ubuntu 18.04 里,我们就应该在 vimrc 配置里加上:
let g:clang_library_path = '/usr/lib/x86_64-linux-gnu/libclang-10.so.1'
下面就是我们在这样配置过后,在 Ubuntu 里得到的结果:
我们可以看到,现在 Vim 知道了 tm 是个结构的指针,并且知道指针的成员有哪些。
这个例子比较简单,如果我们在命令行上进行编译的话,不需要任何特殊参数。如果我们命令行上需要参数,那很可能 ClangComplete 也需要知道这些参数,才能正确工作。这些参数信息应该放在文件所在目录或其父目录下的名为 .clang_complete 的文件里。比如,我的极客时间 C++ 课程的示例代码里就有这个文件,内容也很简单:
-std=c++17
-D ELPP_FEATURE_CRASH_LOG
-D ELPP_FEATURE_PERFORMANCE_TRACKING
-D ELPP_NO_DEFAULT_LOG_FILE
-D ELPP_PERFORMANCE_MICROSECONDS
-D ELPP_UNICODE
-I common
-I 3rd-party/nvwa
-I 3rd-party/cmcstl2/include
-I 3rd-party/cppcoro/include
-I 3rd-party/expected/include
由于只要能编译就可以工作,一般这个配置文件里只需要定义语言标准、预定义宏和头文件路径就可以了,优化选项、库路径和链接库名字则不需要。
Clang-Format
Clang-Format 是又一个 Clang 项目提供的工具,能够很“聪明”地格式化你的代码。作为 Clang 家族的一部分,它的代码格式化是在基于能够真正理解语言语法的基础上做的,因此比其他的格式化工具要智能、强大得多。在你安装了这个工具之后,它很容易和 Vim 集成,能大大提升代码格式化体验,因此我在这里也介绍一下。
在 Windows 上和 macOS 上Clang-Format 一般作为 LLVM 安装的一部分提供。Windows 用户建议直接安装官方提供的下载版本。macOS 用户一般建议使用 Homebrew 安装brew install llvm。
Linux 上就复杂点了取决于不同的发布版Clang-Format 可能作为 LLVM 大包的一部分提供,也可能是一个单独的工具。比如,在 Ubuntu 里Clang-Format 是单独安装的sudo apt install clang-format。而在 CentOS 7 里Clang-Format 是 llvm-toolset-7.0 的一部分。所以你需要自己检查一下。
有了 clang-format 可执行程序之后,我们还需要一个 clang-format.py 脚本来和 Vim 集成。这个脚本文件的安装位置在不同环境是不同的,而且路径可能跟 LLVM 版本相关。比如,我在 Ubuntu 下从 /usr/share/clang/clang-format-10 下面找到了这个文件,在 macOS 上则是一个固定位置 /usr/local/opt/llvm/share/clang。但如果你在 LLVM 所在的目录下找不到的话,也没关系。你可以直接从网上下载,放到你自己知道的一个位置。
我们如果把这个脚本的位置记作 /path/to/clang-format.py那我们现在在 vimrc 配置里加上这行以后就能工作了:
noremap <silent> <Tab> :pyxf /path/to/clang-format.py<CR>
我是映射了 <Tab> 在正常模式和可视模式下对代码进行格式化。如果正常模式的话,我们是对当前语句执行格式化。如果可视模式,那就是对选定的行进行格式化了。这些应该都非常自然了。
Clang-Format 使用规则配置文件来确定如何进行格式化。首次配置觉得复杂的话,你可以参考我的配置文件。具体的效果你可以根据你的项目要求来调整。这个配置文件 .clang-format 同样是放在你的源代码文件目录下或其某一父目录下。
格式化的过程可以参考下面的动画:
内容小结
这一讲我们讨论了 C 程序员应当如何在软件项目配置 Vim使得开发和浏览更为便利。我们主要讨论了下面各项
Vim 的 C 语法加亮有一些选项,可以精调来满足一些特殊需求。
Vim 的 tags 支持使我们可以快速地跳转到符号的定义处。我们也可以配置 Vim 来自动更新 tags 文件。
EchoFunc 插件可以让我们更容易查看函数的原型。
Cscope更近一步让我们可以飞快地找到符号的使用位置、函数在哪儿被调用等等这样的信息。
ClangComplete 依托 libclang 对 C/C++ 代码的解析能力,可以让我们在 Vim 里得到对 C/C++ 代码的自动完成功能。
Clang-Format 依托 libclang 对 C/C++ 代码的解析能力,可以通过规则非常智能地对 C/C++ 代码进行重新格式化。
课后练习
卖油翁说“无他唯手熟尔。”这句话对开发来说是绝对适用的对使用编辑器也同样如此。今天讨论的功能和插件没什么难的唯用而已。对于语法加亮精调、tags、EchoFunc、ClangComplete 和 Clang-Format只要配置好了后续使用是不需要费任何力气的可以先搞好。对于 Cscope它的按键需要记忆建议对照我给出的英文多试几次能形成按键直觉之后使用也会很轻松。
请把这些插件都装好,使用一下。对于 Cscope尤其需要在你的 C 项目中实际操练几下,做到真正会用和真正去用。如果遇到任何问题,欢迎留言和我交流。
我是吴咏炜,我们下一讲再见!

View File

@ -0,0 +1,216 @@
因收到Google相关通知网站将会择期关闭。相关通知内容
拓展3 Python 程序员的 Vim 工作环境:完整的 Python 开发环境
你好,我是吴咏炜。
今天这一讲,我会介绍 Python 程序员定制 Vim 工作环境的完整方法。
Python 的流行程度越来越高Python 程序员们对此一定是很高兴的。在 Stack Overflow 的 2020 年开发者调查里Python 在最受爱戴most loved的语言里排名第三而在最想要most wanted的语言里则已经连续四年排名第一因此它在 Vim 的生态系统里受到了良好的支持,也不会是件令人吃惊的事。有开发者已经把 Python 开发所需要的若干插件揉到了一起组成了一套开箱即用的工具python-mode。
今天我们就以它为基础,讨论一下 Vim 对开发 Python 提供的支持。
功能简介
Python-mode 实际上是以 Vim 插件形式出现的一套工具,它包含了多个用于 Python 开发的工具。根据官网的介绍,它的主要功能点是:
支持 Python 3.6+
语法加亮
虚拟环境支持
运行 Python 代码(<leader>r
添加/删除断点(<leader>b
改善了的 Python 缩进
Python 的移动命令和操作符(]], 3[[, ]]M, vaC, viM, daC, ciM, …)
改善了的 Python 折叠
同时运行多个代码检查器(:PymodeLint
自动修正 PEP 8 错误(:PymodeLintAuto
自动在 Python 文档里搜索K
代码重构
智能感知的代码完成
跳转到定义(<C-c>g
……
不过,还是要提醒一句,它的功能虽然挺多,但作为非商业软件,全靠志愿者来贡献代码,并不是所有功能的完成度都很高。有些功能做得尚不完善,有些功能则略显鸡肋,所以,我也不会全部都讲解。我们就择善而从之,在利用它不需要用户介入就能提供的功能外(如语法加亮和缩进),重点讲解它做得好的地方,以及可能有陷阱需要规避的地方。
安装
Python-mode 没有编译组件,全部由脚本代码组成,因而使用你的包管理器安装 python-mode/python-mode 即可,非常简单。
以 minpac 为例,你只需要在 vimrc 配置文件中“Other plugins”那行下面加入
call minpac#add('python-mode/python-mode')
然后执行 :PackUpdate 命令即可。
配置
在没有任何配置的情况下python-mode 也是完全可用的。但如果你再做一些基本设置的话,就能够解决一些常见问题和规避一些常见陷阱。
我个人的设置是下面这个样子的:
function! IsGitRepo()
" This function requires GitPython
if has('pythonx')
pythonx << EOF
try:
import git
except ImportError:
pass
import vim
def is_git_repo():
try:
_ = git.Repo('.', search_parent_directories=True).git_dir
return 1
except:
return 0
EOF
return pyxeval('is_git_repo()')
else
return 0
endif
endfunction
let g:pymode_rope = IsGitRepo()
let g:pymode_rope_completion = 1
let g:pymode_rope_complete_on_dot = 0
let g:pymode_syntax_print_as_function = 1
let g:pymode_syntax_string_format = 0
let g:pymode_syntax_string_templates = 0
稍微解释一下
IsGitRepo 是利用 Python 代码检测当前是不是在 Git 库目录下的一个函数它要求你在 Python 环境里安装了 GitPythonpip3 install GitPython)。
我们仅仅在当前目录是一个 Git 库下面才启用 rope 支持pymode_rope)。Rope python-mode 里提供语义识别和自动完成的主要工具它会扫描所有子目录并创建 rope 工程目录如果你一不小心在你的主目录或子目录非常多的地方执行 python-mode 的命令可能会导致 Vim 卡顿python-mode 并不是一个异步的插件)。所以我们在这儿特别限制一下防止误操作
我们启用 rope 的完成功能pymode_rope_completion)。
我们禁用在输入 . 号时自动完成的功能pymode_rope_complete_on_dot)。这是因为 rope 提供的自动完成会侵入式地影响正常输入流即如果我想不理睬自动完成是不行的这一点就不如 YCM 因此我们的自动完成仍然使用 YCM不过需要的话我们仍可以通过 <C-X><C-O> 来使用 rope 的自动完成。
Python-mode 对 Python 语法的加亮改善还不错,但它的默认行为是把 print 作为保留字显示,而不是普通函数。在写 Python 3 时还是需要修改一下它的行为pymode_syntax_print_as_function
Python-mode 会试图对字符串中出现的格式化字串和模板替换字串做特殊的加亮pymode_syntax_string_format 和 pymode_syntax_string_templates。这儿主要的问题是它会误匹配字符串中出现的 {} 和 $ 序列。我个人不习惯错误的加亮,不过你可以根据自己的喜好,来决定是不是要启用这个功能。
使用
语法加亮
Python-mode 提供了自己的语法加亮文件。除了上面提到的可以选择对 print 如何加亮,以及在字符串内部进行特殊加亮的选项外,它还提供了很多改进,并且可以由用户通过选项来微调(:help pymode-syntax如对赋值号=)的特殊高亮和对 self 的特殊高亮,等等。这些改进我觉得还挺有用。
代码折叠
我个人一直不怎么喜欢代码折叠(主要是觉得额外展开这个步骤非常有干扰,而更愿意一目十行式地快速浏览),所以 Vim 的这个功能我基本不用。如果你喜欢折叠的话,你应该会很高兴 python-mode 能帮你自动折叠 Python 代码。你只需要在 vimrc 配置文件中加入下面这行即可:
let g:pymode_folding = 1
效果见下图:
这个功能会导致打开 Python 文件变慢。你可以试试,斟酌一下自己是否希望使用这个功能。
快速文档查阅
Python-mode 默认映射了 K 对光标下的单词进行文档查阅。跟其他查阅文档的方式比起来,这还是非常快捷方便的。
缩进支持
在 Vim 的运行支持文件中,本来就包含了对 Python 缩进的支持,但默认的支持并没有把像 PEP 8 这样的 Python 编程规范考虑进去,缩进风格并不十分正确。安装了 python-mode 后,缩进就能更好地自动遵循 PEP 8 规范了。
代码检查
不管 Vim 的缩进对不对,如果你在其他编辑器里编辑了 Python 代码Vim 是不会修正其中的缩进或其他问题的——除非你启用代码检查器。
Python-mode 里带了好几个代码检查器,默认启用的是下面三个:
pyflakes一个很轻量的代码检查器检查常见的 Python 编码问题,如未使用的变量和导入
pep8一个专门检查代码是否符合 PEP 8 的检查器
mccabe一个专门检查圈复杂度的代码检查器
默认启用哪些检查器,是通过下面的全局变量来控制的:
let g:pymode_lint_checkers = ['pyflakes', 'pep8', 'mccabe']
你可以自己在 vimrc 配置文件里定义这个变量,调节希望使用的代码检查器。我觉得默认的代码检查器还比较合适,因为执行真的很快,基本上可以在执行检查的瞬间帮你检查完代码并标记出问题。你可以手工执行 :PymodeLint 来检查代码python-mode 也会自动在你保存文件时进行检查。
可以看到检查的结果会在屏幕的左侧标记出来表示不同的问题类型并且光标移到这样的行上Vim 底部还会显示问题的描述信息。同时python-mode 检查出问题时会自动打开一个位置列表,我们在第 13 讲提过,这是跟窗口关联的类似于快速修复窗口的信息窗口。由于我们可能在多个窗口/标签页编辑多个文件,位置列表确实比较合适。当 python-mode 认为你修复了所有问题时,这个位置列表也会自动关闭。
顺便提醒你注意一下屏幕右侧的红线(在某些配色方案里可能是其他颜色)。这条线在第 80 列上,也是提醒你写代码不能到那个位置,因为 PEP 8 规定 Python 代码行最长是 79 个字符。如果到达红线位置的话,那 pep8 检查的时候,一定跑不了,会报错的。
上面图中的错误都是 PEP 8 问题,绝大部分可以简单地执行 :PymodeLintAuto 命令来自动解决,用不着我们自己去动手修改代码。
Python-mode 还有两个没有默认启用的检查器:
pylint一个功能很强的代码检查器它可以嗅出你的代码中的坏味道除了性能可以说是全面强于 pyflakes使用它你得擦亮眼睛做好被它虐的准备
pep257一个检查文档串docstring是否符合 PEP 257 的工具(这个工具我个人感觉不成熟,给出的建议有点混乱)
由于 pylint 执行比较慢,我觉得还是先写完代码再专门来扫描并解决其报告的问题比较合适。上面的这个示例代码,跑 pylint 需要超过一秒才能执行完成,在存盘时自动执行检查基本属于不可忍受。这当然也是因为 python-mode 没有异步执行外部命令造成的。我们最后还会再看一下执行慢和异步的问题。
Rope 支持
Rope 是一个 Python 库,提供对 Python 代码的分析、重构和自动完成功能。由于我们使用 YCM 来进行自动完成也能完成像跳转到定义这样的任务rope 就略显鸡肋了。不过,它有重命名重构功能,而 YCM 并不支持对 Python 的重命名重构,所以两者功能还不算完全重叠。
你如果决定要用一下 rope 的话,需要了解以下几点:
rope 会使用一个叫做 .ropeproject默认名字的目录在里面缓存需要的信息这个目录在当前目录下或当前目录的一个父目录下如果找不到默认会在当前目录下创建这个目录
使用命令 :PymodeRopeNewProject 路径 可以在指定路径下创建这个 .ropeproject 目录
使用命令 :PymodeRopeRegenerate 可以重新产生项目数据的缓存
默认情况下g:pymode_rope_regenerate_on_write 等于 1在文件存盘时 python-mode 即会自动执行 :PymodeRopeRegenerate 命令
在启用 rope 之后,你就可以使用下面的命令了:
使用 <C-X><C-O> 来启用自动完成(我们把 . 还是交给 YCM 了)
使用 <C-C>g 来跳转到定义(跟 YCM 的 \gt 比大部分情况下没区别rope 跳转更好和 YCM 跳转更好的情况都有,但都不多见)
使用 <C-C>d 来查看光标下符号的文档;和 K 键不同,这个命令可以查看当前项目代码里的文档字串
重构refactor功能以 <C-C>r 开始,如 <C-C>rr 是重命名rename光标下的符号这些功能还是比较强大的可以使用 :help pymode-rope-refactoring 来查看完整的帮助信息)
下面的动图展示了 rope 的若干功能:
替换方案
如果你对 python-mode 的某些功能不满意,可以禁用其部分功能,用其他插件来代替。
首先,如果你如果觉得 rope 提供的额外功能对你用处不大的话,我们可以完全禁用 ropelet g:pymode_rope = 0专心使用 YCM。这样硬盘上也就不会出现 .ropeproject 那样的目录了。
其次,如果你真的希望能在写代码的时候自动进行 pylint 检查,那你也可以禁用 python-mode 里的代码检查器功能let g:pymode_lint = 0转而使用 ALE 来进行异步检查。你需要安装它(包管理器需要的名字是 dense-analysis/ale并在 vimrc 配置文件中加入:
let g:ale_linters = {
\'python': ['pylint'],
\}
别忘了这种情况下,你需要自己用 pip 安装 pylint。这不像 python-mode 的情况,所有工具都已经打包在那一个套件里面了。
内容小结
在这一讲,我们通过介绍 python-mode介绍了一个比较适用于 Python 程序员的 Vim 开发环境。这个工具集成了对 Python 的语法加亮、代码折叠、文档查阅、代码检查、自动完成等多方面的功能,对 Python 开发者非常适用。我们同时也讨论了 Vim 之外的一些代码检查工具,以及当你对 python-mode 不满意时,如何部分替换其功能。
课后练习
同样地,学完今天这一讲之后,你的主要任务就是把 python-mode 装起来、配置好、用一下。如果遇到什么问题,欢迎留言和我讨论。
我是吴咏炜,我们下一讲再见!

View File

@ -0,0 +1,311 @@
因收到Google相关通知网站将会择期关闭。相关通知内容
拓展4 插件样例分析:自己动手改进插件
你好,我是吴咏炜。
上一讲,我们对 Vim 脚本作了初步的介绍。Vim 脚本可以应用在很多不同的场景下,比如我们的 vimrc 配置文件和配色方案也都是 Vim 脚本。但我们今天更关心的,是我们经常使用的、一般称为“插件”的特殊 Vim 脚本。虽说插件和其他 Vim 脚本之间的界限也并非泾渭分明,但我们一般把满足以下条件的 Vim 脚本称为插件:
功能独立,不依赖特殊的个性化配置
存在加载时即会执行的代码,一般放在 plugin 目录下(也有放在 ftplugin、ftdetect 等其他目录下的情况)
今天,我们就利用目前学到的知识,来由浅入深地分析四个样例,了解插件代码是如何编写的,从而可以自己动手定制、改进,进而编写自己的插件,真真正正地定制自己的 Vim 环境,并为整个 Vim 社区作出贡献。
ycmconf
我们要看的第一个脚本,是在讲 YCM 时引入的 ycmconf。这是一个非常简单的插件我们就拿它开始我们今天的课程。
如果你之前按我说的步骤安装的话,现在应该可以在 Vim 配置目录下的 pack/minpac/start/ycmconf 里找到它。你也可以自己用 Git 签出:
git clone https://github.com/adah1972/ycmconf.git
除去一些文本说明文件,这个插件里只有两个真正的脚本文件:
plugin/ycmconf.vim
ycm_extra_conf.py
plugin 目录是 Vim 里主要放置无条件执行的脚本的地方,即“插件”的主体。打开 plugin/ycmconf.vim我们看到里面只有一行注释加一行代码
" Set the global extra configuration
let g:ycm_global_ycm_extra_conf=expand('<sfile>:p:h:h') . '/ycm_extra_conf.py'
这个差不多是个最简单的插件了吧。Vim 脚本里只做了一件事,设置全局变量 g:ycm_global_ycm_extra_conf 给 YCM 用。关于脚本中的 expand 函数,我们稍微展开一下:
expand 是用来展开文件名的。参数字符串里如果有 %,就代表当前编辑的文件名;如果有 <sfile>,代表当前执行的源代码文件(其他可展开的名字请参见 :help expand())。
:p 用来告诉 expand我们需要得到完整的路径。比如在我的机器上这样展开的结果是 /Users/yongwei/.vim/pack/minpac/start/ycmconf/plugin/ycmconf.vim。
:h 用来告诉 expand我们需要头部即去掉路径的最后部分。我会得到 /Users/yongwei/.vim/pack/minpac/start/ycmconf/plugin。
第二次使用 :h我们再次去掉路径的最后部分即 plugin。我会得到 /Users/yongwei/.vim/pack/minpac/start/ycmconf。
随后,我们拿这个路径跟 '/ycm_extra_conf.py' 进行拼接,就得到了 YCM 可以看到的 ycm_extra_conf.py 文件的路径。
这个插件的主体功能在 ycm_extra_conf.py 里。鉴于这是 Python 的代码,而不是 Vim 脚本,我就不再讲解了。你如果有兴趣的话,可以自己看一下。文件虽然总共要好几百行,但注释比较多,逻辑其实不复杂;如果你懂 Python一定可以理解里面的内容。
cscope_maps.vim
你一定觉得,上面这个脚本实在也太太太简单了吧……
下面我们就来看一个稍复杂点的。[拓展 2]里我给出了一个自己改过的 cscope_maps.vim我们现在就来看看它的原始版本然后看一下怎么修改它的行为。
原始版本在 Cscope 的网站上。可以看到,这也是一个比较简单的 Vim 脚本,应当直接放到 plugin 目录下。虽然文件总共有一百多行,倒有一大半是注释;实际代码行只有三十几行。我们可以细细地分析一下:
最外围,是一个条件语句,确保这个插件的内容仅在 Vim 支持 Cscope 时得到执行:
if has("cscope")
endif
在条件语句里,有三行是设置 Vim 选项的:
set cscopetag
set csto=0
set cscopeverbose
我们在 Vim 帮助里可以查到它们的详细说明。简单来说:
设置 cscopetag 使得我们在使用原先 tags 相关的命令时会同时查找 Cscope 数据库
设置 csto 为 0 是让 Vim 先查找 Cscope 数据库,找不到才查找 tags
设置 cscopeverbose 是让 Vim 在之后添加 Cscope 数据库时,告诉你结果成功与否
设置最后这个选项是在下面的语句之后:
if filereadable("cscope.out")
cs add cscope.out
elseif $CSCOPE_DB != ""
cs add $CSCOPE_DB
endif
也就是说Vim 会在启动时悄无声息地试图加载当前目录下的 cscope.out 数据库或环境变量 CSCOPE_DB 指定的数据库,并且不会报告结果。
剩下的代码就全部是……键映射了。我们就看其中的一个,其余的都大同小异:
nmap <C-\>s :cs find s <C-R>=expand("<cword>")<CR><CR>
这个键映射把 <C-\>s 映射成了一个 :cs find s 命令值得注意的是命令的后半截
脚本里使用 <C-R>=…<CR> 来执行一个 Vim 表达式,并把结果填到命令里。
我们又一次见到了 expand 函数。这一次,要展开的是 <cword>,即当前光标下的单词。
注意结尾两个 <CR> 里第一个是给 <C-R>= 的,第二个才是执行命令的回车键。
让我有意见的是下面这样的键映射:
nmap <C-@>s :scs find s <C-R>=expand("<cword>")<CR><CR>
这儿用 <C-@> 代表 Ctrl-空格,而这个组合键在很多系统上不可用。既然已经使用了 Ctrl-\ 作为 Cscope 的专用起始键,我觉得继续用 Shift-\ 就好。由于 | 在 Vim 里用来分隔多个语句,这儿我们要换个写法,改成:
nmap <bar>s :scs find s <C-R>=expand("<cword>")<CR><CR>
我的完整修改过程,可以查看:
https://github.com/adah1972/cscope_maps.vim/commits/master
总的来说,这也是个非常小、非常轻松的修改。
EchoFunc
事实上大部分行为良好的插件会允许用户通过一些全局变量来定制键映射之类的设定。不过对于没有提供这种定制性的插件我们自己找到代码里的键映射语句手工修改一下也是一种可能发生的常见情况。比如EchoFunc 里查看下一个和上一个函数的按键分别可以用全局变量 g:EchoFuncKeyNext 和 g:EchoFuncKeyPrev 来修改。一般来说,插件的文档里会进行说明,你也可以在插件里通过搜索 exists 函数来找到插件提供出来的定制点。
以 EchoFunc 为例它虽然简单到没有提供帮助文档但插件的主文件after/plugin/echofunc.vim开头有大段的注释。同时它有大量的 exists 的函数调用,来检查用户是否已经定义了某一全局变量来定制行为:
if !exists("g:EchoFuncMaxBalloonDeclarations")
let g:EchoFuncMaxBalloonDeclarations=20
endif
if !exists("g:EchoFuncKeyNext")
if has ("mac")
let g:EchoFuncKeyNext='≠'
else
let g:EchoFuncKeyNext='<M-=>'
endif
endif
if !exists("g:EchoFuncKeyPrev")
if has ("mac")
let g:EchoFuncKeyPrev='±'
else
let g:EchoFuncKeyPrev='<M-->'
endif
endif
在我这儿给出的三个全局变量的相关定义里第一个是对起泡提示的数量限制第二个是下一个函数的键定义第三个是上一个函数的键定义。在后两个键定义里还分平台Mac 或非 Mac进行了不同的设置。这些都是非常直接了当的。
如果我们在 EchoFuncKeyNext 上面按下 * 来搜索这个变量的使用,我们就会发现它们是在函数 EchoFuncStart 里被真正使用的:
if maparg(g:EchoFuncKeyNext, "i") == '' && maparg(g:EchoFuncKeyPrev, "i") == ''
exec 'inoremap <silent> <buffer> ' . g:EchoFuncKeyNext . ' <c-r>=EchoFuncN()<cr>'
exec 'inoremap <silent> <buffer> ' . g:EchoFuncKeyPrev . ' <c-r>=EchoFuncP()<cr>'
endif
这儿的代码说的是:
如果 g:EchoFuncKeyNext 和 g:EchoFuncKeyPrev 描述的键映射(:help maparg())在插入模式("i")没有被占用(== ''的话那我们就执行exec针对当前缓冲区<buffer>的插入模式键映射inoremap让其安静地<silent>)执行(<c-r>=)函数中的语句。
注意,在键映射中使用 <C-R>= 来执行语句是一种常用技巧。这种情况下,我们常常不是要获得函数返回的结果(所以这些函数通常返回 ''),而只是需要执行一些指定的代码,产生需要的“副作用”。在这儿,我们需要的副作用就是选择函数列表里的下一项和上一项了。
EchoFunc 算是一个中等规模的 Vim 插件,也有好几百行代码了,我们没有必要全部讲一遍。它的初始化过程比较有特点,我们看一下:
augroup EchoFunc
autocmd BufRead,BufNewFile * call s:EchoFuncInitialize()
augroup END
也就是说,在读入文件后,或创建新文件后,才调用 s:EchoFuncInitialize() 进行初始化。
那 s:EchoFuncInitialize() 究竟做了些什么呢?看下面:
function! s:EchoFuncInitialize()
augroup EchoFunc
autocmd!
autocmd InsertLeave * call EchoFuncRestoreSettings()
autocmd BufRead,BufNewFile * call CheckedEchoFuncStart()
if has('gui_running')
menu &Tools.Echo\ F&unction.Echo\ F&unction\ Start :call EchoFuncStart()<CR>
menu &Tools.Echo\ F&unction.Echo\ Function\ Sto&p :call EchoFuncStop()<CR>
endif
if has("balloon_eval")
autocmd BufRead,BufNewFile * call CheckedBalloonDeclarationStart()
if has('gui_running')
menu &Tools.Echo\ Function.&Balloon\ Declaration\ Start :call BalloonDeclarationStart()<CR>
menu &Tools.Echo\ Function.Balloon\ Declaration\ &Stop :call BalloonDeclarationStop()<CR>
endif
endif
augroup END
call CheckedEchoFuncStart()
if has("balloon_eval")
call CheckedBalloonDeclarationStart()
endif
endfunction
我下面概要解说一下:
在 EchoFunc 自动命令组里,执行 autocmd!,清空已有的自动命令,即刚才的 call s:EchoFuncInitialize() 语句。
在 InsertLeave离开插入模式事件里调用 EchoFuncRestoreSettings 函数,停止函数回显。
在读入文件或创建新文件时,检查是否需要启用函数回显。
在图形界面下创建启停函数回显的菜单项。
如果 Vim 支持气泡显示,在读入文件或创建新文件时,检查是否需要启用气泡函数声明提示,并在图形界面下创建启停气泡函数声明提示的菜单项。
对当前文件,检查是否需要启用函数回显和起泡函数声明提示。
最后,如果你好奇为什么 EchoFunc 选择使用 after/plugin 目录而不是 plugin 目录,在它的 Git 日志里是有说明的:
fix key “(” “)” mapping conflict with other plugins:-
-
first, move plugin folder into after/ folder, so that echofunc will be load after most plugins have been loaded-
-
Second, if during initialization time, if it find “(” or “)” key have been mapped, it will try to append EchofuncXX function to it.
因为它用到 ( 和 ) 作为键映射,容易和其他插件冲突,因此它会最后加载,并尽量把自己键映射补充进去。
对于插件功能本身的特殊逻辑,我就不解释啦。
arm-syntax-vim
今天最后一个插件样例,是我最近的一个实际需求。由于我写的代码需要最终跑在 ARM 平台上,我偶尔需要检查一下产生的 ARM 汇编代码。在 Vim 的默认配置下,产生的汇编代码效果不太理想,如下图所示:
这里最糟糕的地方是stmfd 那行里的 {r4, lr} 居然显示成了注释?是可忍,孰不可忍!
还好,我用不着从头开始自己搞。网上略加搜索,我就找到了 ARM9/arm-syntax-vim 这个 Vim 脚本,可以获得好得多的效果,如下所示:
不过,这个脚本还是缺了点东西,它只包含了语法文件,不能把 GCC 产生的 .s 文件识别为它支持的 arm、armv4 和 armv5 格式。我要做的就是添加文件类型识别,让 Vim 把 ARM 的汇编文件识别成合适的 ARM 格式。
在[第 8 讲]讨论文件类型判断时,我已经说过,在 Vim 里后缀不是判断文件类型的唯一依据。既然我懒到不愿意在汇编文件里加帮助识别的文本我当然也懒得去改汇编文件的后缀了。GCC 产生的汇编代码里的一些特定标识,也使得我利用文本判断变得相当容易:取决于不同的环境,汇编中一般会出现 .arch arm 和 .cpu arm 这样的明确行。
要让 Vim 进行文件类型判断,标准做法是在 ftdetect 目录下加入判断脚本。既然我们知道后缀是 .s在这个文件中我会写入
au BufRead *.[sS] call arm#ft#FTarm()
为了加快 Vim 的启动速度,真正检测需要的代码一般推荐放到 autoload 目录下。这是 Vim 的专门机制,允许脚本“按需”加载,仅在用到函数的时候,才载入函数的定义(:help autoload。在上面的写法下面当 Vim 读入后缀为 .s 或 .S 的文件时Vim 会自动在 autoload/arm 目录下载入 ft.vim然后调用其中的 FTarm 函数。
下面我们来看一下 ft.vim 文件。这个文件不大,完整内容展示如下:
let s:cpo_save = &cpo
set cpo&vim
function! arm#ft#FTarm()
let head = ' '.getline(1).' '.getline(2).' '.getline(3).' '.getline(4).
\' '.getline(5).' '
" Can't use setf, as we need to overrule the default filetype setting
if matchstr(head, '\s.arch\s\+armv4') != ''
set filetype=armv4
elseif matchstr(head, '\s.arch\s\+armv5') != ''
set filetype=armv5
elseif matchstr(head, '\s.arch\s\+arm') != ''
set filetype=arm
elseif matchstr(head, '\s.cpu\s\+arm') != ''
set filetype=arm
endif
endfunction
let &cpo = s:cpo_save
unlet s:cpo_save
开头和结尾的四行属于 Vim 脚本的标准模板写法:进入脚本时保存兼容性选项(:help 'cpoptions')的当前值,然后恢复其为默认值,免得其他地方的设置影响对脚本的解释;退出时则恢复原来保存的兼容性选项值。
中间主体部分就一个函数,做的事情也很简单,就是把文件的头五行内容拼到一起,然后看能不能找到满足条件的“.arch”和“.cpu”语句。找到的话就设置合适的文件类型找不到就不做处理留给其他的 Vim 脚本来继续判断。
这儿唯一比较特别点的地方是,一般设置文件类型推荐使用 :setfiletype 命令,它会避免重复设置,在一次 Vim 的自动事件触发过程中只执行一次。对于我们当前的目的,这是不够的:因为在我们的代码执行之前,当前缓冲区一般已经被系统的自动命令设置过类型了。具体来说,是运行支持文件里的 autoload/dist/ft.vim 里的 dist#ft#FTasm 函数。
所以,我们这儿需要强行覆盖已经设置的文件类型,用 set filetype=… 就可以做到。要注意,仅在你很有信心你设置的类型正确时才可以这么做,否则,你可能会干扰其他插件的结果。
这样,我就做到了在用 Vim 打开 GCC 产生的 ARM 汇编文件时,能自动检测并应用合适的 arm 语法。完整的代码可从 adah1972/arm-syntax-vim 下载。
内容小结
今天我们分析了四个大小不同的 Vim 脚本,并展示了常见的 Vim 脚本用法。我们可以总结一下相关的知识点:
Vim 里主要放置无条件执行脚本的目录是 plugin。
exists 、expand 和 has 恐怕是 Vim 里最重要的函数,常用法需要牢牢掌握。
好的 Vim 脚本一般会通过全局变量允许用户定制部分行为如键映射Vim 脚本里面通过 exists 函数来检测用户定义的全局变量。
一般来说,插件会使用自己的名字开始自己的自动命令组,这样比较便于管理,包括统一清除。
after 目录下的内容会晚于其他目录加载。
ftdetect 目录下一般放置用来检测文件类型的脚本。
autoload 目录专门放置延迟加载的脚本。
课后练习
请选择一个你常用的插件(如 nerdtree 和 undotree分析它的主体结构看一下它使用了哪些不同的目录分成几个主要的模块提供了哪些命令又给用户留出了哪些定制点。
如果有任何的问题和想法,请留言和我交流。我们下一讲再见。

View File

@ -0,0 +1,217 @@
因收到Google相关通知网站将会择期关闭。相关通知内容
拓展5 其他插件和技巧:吴咏炜的箱底私藏
assets/拓assets/展assets/5assets/ assets/其assets/他assets/插assets/件assets/和assets/技assets/巧assets/assets/吴assets/咏assets/炜assets/的assets/箱assets/底assets/私assets/藏assets/
assets/
assets/你assets/好assets/assets/我assets/是assets/吴assets/咏assets/炜assets/。assets/
assets/
assets/上assets/一assets/讲assets/我assets/介assets/绍assets/了assets/最assets/重assets/要assets/的assets/一assets/些assets/插assets/件assets/。assets/今assets/天assets/这assets/讲assets/拓assets/展assets/assets/我assets/们assets/就assets/算assets/是assets/查assets/漏assets/补assets/缺assets/assets/再assets/分assets/享assets/一assets/些assets/我assets/个assets/人assets/这assets/些assets/年assets/压assets/箱assets/底assets/的assets/收assets/藏assets/。assets/这assets/些assets/插assets/件assets/和assets/技assets/巧assets/有assets/新assets/有assets/旧assets/assets/都assets/非assets/常assets/好assets/用assets/assets/欢assets/迎assets/你assets/挑assets/选assets/感assets/兴assets/趣assets/的assets/内assets/容assets/assets/纳assets/入assets/自assets/己assets/的assets/个assets/人assets/收assets/藏assets/箱assets/。assets/
assets/
assets/插assets/件assets/
assets/-assets/-assets/
assets/
assets/#assets/#assets/#assets/ assets/Sassets/yassets/nassets/tassets/aassets/sassets/tassets/iassets/cassets/ assets/和assets/ assets/Aassets/Lassets/Eassets/
assets/
assets/说assets/到assets/代assets/码assets/检assets/查assets/插assets/件assets/assets/我assets/最assets/早assets/是assets/从assets/ assets/[assets/Sassets/yassets/nassets/tassets/aassets/sassets/tassets/iassets/cassets/]assets/(assets/hassets/tassets/tassets/passets/sassets/:assets//assets//assets/gassets/iassets/tassets/hassets/uassets/bassets/.assets/cassets/oassets/massets//assets/vassets/iassets/massets/-assets/sassets/yassets/nassets/tassets/aassets/sassets/tassets/iassets/cassets//assets/sassets/yassets/nassets/tassets/aassets/sassets/tassets/iassets/cassets/)assets/ assets/开assets/始assets/用assets/的assets/assets/然assets/后assets/慢assets/慢assets/转assets/向assets/了assets/ assets/[assets/Aassets/Lassets/Eassets/]assets/(assets/hassets/tassets/tassets/passets/sassets/:assets//assets//assets/gassets/iassets/tassets/hassets/uassets/bassets/.assets/cassets/oassets/massets//assets/dassets/eassets/nassets/sassets/eassets/-assets/aassets/nassets/aassets/lassets/yassets/sassets/iassets/sassets//assets/aassets/lassets/eassets/)assets/。assets/不assets/过assets/assets/我assets/因assets/为assets/主assets/要assets/写assets/ assets/Cassets/+assets/+assets/ assets/和assets/ assets/Passets/yassets/tassets/hassets/oassets/nassets/assets/所assets/以assets/慢assets/慢assets/放assets/弃assets/了assets/使assets/用assets/这assets/两assets/个assets/插assets/件assets/assets/转assets/而assets/使assets/用assets/对assets/这assets/两assets/种assets/语assets/言assets/支assets/持assets/较assets/好assets/的assets/ assets/Yassets/Cassets/Massets/assets/第assets/ assets/1assets/3assets/ assets/讲assets/assets/和assets/ assets/Passets/yassets/tassets/hassets/oassets/nassets/-assets/massets/oassets/dassets/eassets/assets/拓assets/展assets/ assets/3assets/assets/。assets/
assets/
assets/虽assets/然assets/ assets/Yassets/Cassets/Massets/ assets/和assets/ assets/Passets/yassets/tassets/hassets/oassets/nassets/-assets/massets/oassets/dassets/eassets/ assets/集assets/成assets/的assets/工assets/具assets/比assets/较assets/有assets/限assets/assets/比assets/如assets/ assets/Yassets/Cassets/Massets/ assets/对assets/ assets/Cassets/+assets/+assets/ assets/的assets/代assets/码assets/检assets/查assets/仅assets/限assets/于assets/ assets/Cassets/lassets/aassets/nassets/gassets/ assets/系assets/列assets/工assets/具assets/提assets/供assets/的assets/支assets/持assets/assets/而assets/不assets/像assets/ assets/Sassets/yassets/nassets/tassets/aassets/sassets/tassets/iassets/cassets//assets/Aassets/Lassets/Eassets/ assets/还assets/可assets/以assets/选assets/择assets/很assets/多assets/其assets/他assets/的assets/工assets/具assets/assets/但assets/是assets/assets/它assets/们assets/对assets/我assets/来assets/讲assets/还assets/是assets/够assets/用assets/了assets/—assets/—assets/何assets/况assets/对assets/于assets/ assets/Cassets/ assets/和assets/ assets/Cassets/+assets/+assets/assets/要assets/让assets/ assets/Sassets/yassets/nassets/tassets/aassets/sassets/tassets/iassets/cassets/ assets/或assets/ assets/Aassets/Lassets/Eassets/ assets/干assets/活assets/的assets/话assets/assets/大assets/部assets/分assets/情assets/况assets/下assets/需assets/要assets/配assets/置assets/头assets/文assets/件assets/包assets/含assets/路assets/径assets/和assets/编assets/译assets/选assets/项assets/assets/也assets/是assets/件assets/麻assets/烦assets/事assets/。assets/
assets/
assets/不assets/过assets/assets/对assets/于assets/其assets/他assets/语assets/言assets/的assets/开assets/发assets/者assets/assets/Sassets/yassets/nassets/tassets/aassets/sassets/tassets/iassets/cassets//assets/Aassets/Lassets/Eassets/ assets/可assets/能assets/还assets/是assets/非assets/常assets/有assets/用assets/的assets/。assets/
assets/
assets/先assets/说assets/ assets/Sassets/yassets/nassets/tassets/aassets/sassets/tassets/iassets/cassets/。assets/这assets/是assets/一assets/个assets/老assets/牌assets/的assets/代assets/码assets/检assets/查assets/插assets/件assets/assets/其assets/ assets/1assets/.assets/0assets/ assets/版assets/本assets/发assets/布assets/在assets/ assets/2assets/0assets/0assets/9assets/ assets/年assets/。assets/这assets/些assets/年assets/下assets/来assets/assets/这assets/个assets/插assets/件assets/里assets/积assets/累assets/了assets/好assets/几assets/十assets/种assets/语assets/言assets/的assets/代assets/码assets/检assets/查assets/支assets/持assets/assets/既assets/有assets/常assets/见assets/的assets/ assets/Cassets/、assets/Cassets/+assets/+assets/、assets/Passets/yassets/tassets/hassets/oassets/nassets/、assets/Jassets/aassets/vassets/aassets/、assets/Jassets/aassets/vassets/aassets/Sassets/cassets/rassets/iassets/passets/tassets/ assets/等assets/语assets/言assets/assets/也assets/有assets/冷assets/门assets/一assets/点assets/的assets/ assets/Aassets/Cassets/Passets/Iassets/、assets/Aassets/passets/passets/lassets/eassets/Sassets/cassets/rassets/iassets/passets/tassets/、assets/Jassets/uassets/lassets/iassets/aassets/、assets/Vassets/Hassets/Dassets/Lassets/、assets/zassets/8assets/0assets/ assets/汇assets/编assets/等assets/语assets/言assets/。assets/对assets/于assets/每assets/种assets/语assets/言assets/assets/它assets/能assets/自assets/动assets/识assets/别assets/已assets/经assets/安assets/装assets/的assets/代assets/码assets/检assets/查assets/器assets/assets/并assets/在assets/你assets/文assets/件assets/存assets/盘assets/时assets/自assets/动assets/检assets/查assets/代assets/码assets/assets/也assets/可assets/以assets/手assets/工assets/使assets/用assets/ assets/assets/:assets/Sassets/yassets/nassets/tassets/aassets/sassets/tassets/iassets/cassets/Cassets/hassets/eassets/cassets/kassets/assets/ assets/命assets/令assets/来assets/检assets/查assets/assets/。assets/要assets/检assets/查assets/当assets/前assets/文assets/件assets/ assets/Sassets/yassets/nassets/tassets/aassets/sassets/tassets/iassets/cassets/ assets/识assets/别assets/到assets/了assets/哪assets/些assets/代assets/码assets/检assets/查assets/器assets/assets/可assets/以assets/使assets/用assets/ assets/assets/:assets/Sassets/yassets/nassets/tassets/aassets/sassets/tassets/iassets/cassets/Iassets/nassets/fassets/oassets/assets/ assets/命assets/令assets/assets/而assets/在assets/ assets/Aassets/Lassets/Eassets/ assets/中assets/没assets/有assets/等assets/价assets/的assets/好assets/用assets/命assets/令assets/。assets/
assets/
assets/Sassets/yassets/nassets/tassets/aassets/sassets/tassets/iassets/cassets/ assets/的assets/主assets/要assets/问assets/题assets/是assets/它assets/的assets/检assets/查assets/是assets/同assets/步assets/的assets/assets/代assets/码assets/检assets/查assets/时assets/你assets/不assets/能assets/同assets/时assets/进assets/行assets/编assets/辑assets/工assets/作assets/assets/不assets/但assets/不assets/能assets/即assets/输assets/即assets/查assets/assets/而assets/且assets/耗assets/时assets/长assets/的assets/检assets/查assets/还assets/会assets/中assets/断assets/正assets/常assets/的assets/编assets/辑assets/流assets/程assets/。assets/后assets/起assets/之assets/秀assets/ assets/Aassets/Lassets/Eassets/ assets/恰assets/恰assets/解assets/决assets/了assets/这assets/个assets/问assets/题assets/assets/它assets/会assets/异assets/步assets/地assets/检assets/查assets/你assets/的assets/代assets/码assets/assets/在assets/编assets/辑assets/时assets/即assets/输assets/即assets/查assets/assets/完assets/全assets/不assets/会assets/影assets/响assets/正assets/常assets/的assets/编assets/辑assets/流assets/程assets/。assets/从assets/支assets/持assets/的assets/语assets/言assets/上assets/来assets/说assets/assets/Aassets/Lassets/Eassets/ assets/虽assets/然assets/不assets/及assets/ assets/Sassets/yassets/nassets/tassets/aassets/sassets/tassets/iassets/cassets/assets/但assets/也assets/已assets/经assets/覆assets/盖assets/到assets/了assets/大assets/量assets/的assets/冷assets/门assets/语assets/言assets/。assets/并assets/且assets/assets/它assets/仍assets/然assets/处assets/于assets/积assets/极assets/开assets/发assets/之assets/中assets/assets/2assets/0assets/2assets/0assets/ assets/年assets/ assets/7assets/ assets/月assets/assets/Sassets/yassets/nassets/tassets/aassets/sassets/tassets/iassets/cassets/ assets/一assets/共assets/有assets/ assets/3assets/ assets/次assets/提assets/交assets/assets/而assets/ assets/Aassets/Lassets/Eassets/ assets/有assets/ assets/4assets/8assets/ assets/次assets/非assets/合assets/并assets/提assets/交assets/和assets/ assets/1assets/3assets/ assets/次assets/合assets/并assets/提assets/交assets/assets/
assets/
assets/这assets/两assets/个assets/插assets/件assets/的assets/配assets/置assets/都assets/略assets/显assets/复assets/杂assets/assets/通assets/常assets/需assets/要assets/你assets/针assets/对assets/每assets/种assets/代assets/码assets/检assets/查assets/器assets/进assets/行assets/配assets/置assets/。assets/因assets/此assets/assets/总assets/体assets/来assets/说assets/assets/我assets/对assets/代assets/码assets/检assets/查assets/的assets/推assets/荐assets/顺assets/序assets/是assets/assets/
assets/
assets/1assets/.assets/ assets/ assets/使assets/用assets/ assets/Yassets/Cassets/Massets/、assets/Passets/yassets/tassets/hassets/oassets/nassets/-assets/massets/oassets/dassets/eassets/、assets/Vassets/iassets/massets/-assets/gassets/oassets/ assets/等assets/有assets/语assets/言assets/针assets/对assets/性assets/的assets/插assets/件assets/assets/如assets/果assets/你assets/用assets/的assets/语assets/言assets/被assets/支assets/持assets/assets/并assets/且assets/插assets/件assets/集assets/成assets/的assets/代assets/码assets/检assets/查assets/功assets/能assets/够assets/用assets/的assets/话assets/
assets/2assets/.assets/ assets/ assets/使assets/用assets/ assets/Aassets/Lassets/Eassets/assets/如assets/果assets/你assets/的assets/语assets/言assets/和assets/代assets/码assets/检assets/查assets/插assets/件assets/它assets/能assets/够assets/支assets/持assets/的assets/话assets/
assets/3assets/.assets/ assets/ assets/使assets/用assets/ assets/Sassets/yassets/nassets/tassets/aassets/sassets/tassets/iassets/cassets/assets/如assets/果assets/其assets/他assets/选assets/项assets/不assets/适assets/用assets/assets/或assets/者assets/你assets/需assets/要assets/的assets/检assets/查assets/执assets/行assets/够assets/快assets/的assets/话assets/
assets/
assets/#assets/#assets/#assets/ assets/Rassets/eassets/nassets/aassets/massets/eassets/rassets/
assets/
assets/在assets/需assets/要assets/对assets/文assets/件assets/进assets/行assets/批assets/量assets/更assets/名assets/时assets/assets/我assets/会assets/使assets/用assets/ assets/[assets/qassets/passets/kassets/oassets/rassets/rassets//assets/vassets/iassets/massets/-assets/rassets/eassets/nassets/aassets/massets/eassets/rassets/]assets/(assets/hassets/tassets/tassets/passets/:assets//assets//assets/gassets/iassets/tassets/hassets/uassets/bassets/.assets/cassets/oassets/massets//assets/qassets/passets/kassets/oassets/rassets/rassets//assets/vassets/iassets/massets/-assets/rassets/eassets/nassets/aassets/massets/eassets/rassets/)assets/ assets/插assets/件assets/。assets/它assets/提assets/供assets/ assets/assets/:assets/Rassets/eassets/nassets/aassets/massets/eassets/rassets/assets/ assets/命assets/令assets/assets/会assets/打assets/开assets/当assets/前assets/目assets/录assets/下assets/所assets/有assets/文assets/件assets/的assets/列assets/表assets/。assets/你assets/随assets/后assets/就assets/可assets/以assets/利assets/用assets/ assets/Vassets/iassets/massets/ assets/强assets/大assets/的assets/正assets/则assets/表assets/达assets/式assets/和assets/编assets/辑assets/功assets/能assets/来assets/调assets/整assets/这assets/些assets/名assets/字assets/了assets/。assets/在assets/调assets/整assets/完assets/成assets/后assets/assets/执assets/行assets/ assets/assets/:assets/Rassets/eassets/nassets/assets/ assets/命assets/令assets/即assets/可assets/。assets/
assets/
assets/!assets/[assets/Fassets/iassets/gassets/Xassets/5assets/.assets/1assets/]assets/(assets/aassets/sassets/sassets/eassets/tassets/sassets//assets/5assets/2assets/2assets/0assets/9assets/0assets/6assets/bassets/cassets/8assets/fassets/cassets/3assets/aassets/fassets/bassets/0assets/9assets/aassets/aassets/bassets/0assets/5assets/fassets/2assets/7assets/bassets/cassets/dassets/fassets/dassets/4assets/.assets/passets/nassets/gassets/ assets/“assets/Rassets/eassets/nassets/aassets/massets/eassets/rassets/ assets/的assets/界assets/面assets/assets/额assets/外assets/展assets/开assets/了assets/一assets/层assets/目assets/录assets/assets/“assets/)assets/
assets/
assets/注assets/意assets/assets/图assets/中assets/的assets/第assets/二assets/、assets/三assets/行assets/提assets/供assets/了assets/命assets/令assets/的assets/说assets/明assets/assets/比assets/如assets/用assets/ assets/assets/>assets/assets/ assets/来assets/多assets/展assets/开assets/一assets/层assets/目assets/录assets/assets/等assets/等assets/。assets/你assets/应assets/该assets/只assets/做assets/行assets/内assets/的assets/修assets/改assets/assets/而assets/不assets/去assets/删assets/除assets/行assets/或assets/调assets/整assets/行assets/的assets/顺assets/序assets/assets/否assets/则assets/可assets/能assets/引assets/致assets/意assets/外assets/的assets/后assets/果assets/。assets/使assets/用assets/ assets/assets/<assets/Cassets/-assets/Dassets/eassets/lassets/>assets/assets/ assets/可assets/以assets/删assets/除assets/当assets/前assets/行assets/的assets/文assets/件assets/assets/这assets/算assets/是assets/一assets/种assets/例assets/外assets/情assets/况assets/。assets/
assets/
assets/在assets/图assets/中assets/assets/我assets/只assets/是assets/手assets/工assets/修assets/改assets/了assets/“assets/lassets/oassets/gassets/.assets/cassets/oassets/nassets/fassets/”assets/那assets/行assets/的assets/文assets/件assets/名assets/。assets/更assets/常assets/见assets/的assets/情assets/况assets/是assets/利assets/用assets/ assets/Vassets/iassets/massets/ assets/的assets/编assets/辑assets/功assets/能assets/做assets/批assets/量assets/操assets/作assets/。assets/比assets/如assets/assets/你assets/需assets/要assets/把assets/文assets/件assets/名assets/变assets/成assets/小assets/写assets/assets/可assets/以assets/选assets/中assets/要assets/修assets/改assets/的assets/部assets/分assets/然assets/后assets/使assets/用assets/ assets/assets/gassets/uassets/assets/ assets/命assets/令assets/。assets/又assets/比assets/如assets/assets/如assets/果assets/你assets/的assets/文assets/件assets/里assets/有assets/大assets/量assets/的assets/编assets/号assets/assets/你assets/希assets/望assets/对assets/编assets/号assets/进assets/行assets/增assets/减assets/的assets/操assets/作assets/assets/也assets/可assets/以assets/利assets/用assets/ assets/Vassets/iassets/massets/ assets/的assets/ assets/assets/<assets/Cassets/-assets/Aassets/>assets/assets/ assets/和assets/ assets/assets/<assets/Cassets/-assets/Xassets/>assets/assets/ assets/来assets/对assets/编assets/号assets/进assets/行assets/加assets/减assets/操assets/作assets/。assets/当assets/然assets/assets/这assets/时assets/你assets/可assets/能assets/需assets/要assets/使assets/用assets/可assets/视assets/模assets/式assets/的assets/列assets/选assets/择assets/assets/assets/<assets/Cassets/-assets/Vassets/>assets/assets/assets/。assets/
assets/
assets/你assets/还assets/有assets/可assets/能assets/需assets/要assets/注assets/意assets/一assets/下assets/选assets/项assets/ assets/assets/nassets/rassets/fassets/oassets/rassets/massets/aassets/tassets/sassets/assets/assets/因assets/为assets/如assets/果assets/其assets/中assets/含assets/有assets/ assets/assets/oassets/cassets/tassets/aassets/lassets/assets/ assets/的assets/话assets/assets/Vassets/iassets/massets/ assets/会assets/把assets/ assets/assets/0assets/assets/ assets/打assets/头assets/的assets/数assets/字assets/序assets/列assets/当assets/成assets/八assets/进assets/制assets/来assets/处assets/理assets/。assets/还assets/好assets/assets/如assets/果assets/你assets/按assets/照assets/我assets/目assets/前assets/给assets/出assets/的assets/方assets/式assets/来assets/设assets/置assets/ assets/vassets/iassets/massets/rassets/cassets/ assets/配assets/置assets/文assets/件assets/的assets/话assets/assets/缺assets/省assets/里assets/面assets/不assets/含assets/ assets/assets/oassets/cassets/tassets/aassets/lassets/assets/assets/只assets/会assets/对assets/ assets/assets/0assets/bassets/assets/ assets/和assets/ assets/assets/0assets/xassets/assets/ assets/打assets/头assets/的assets/数assets/字assets/做assets/特assets/别assets/处assets/理assets/assets/这assets/就assets/不assets/会assets/跟assets/普assets/通assets/的assets/十assets/进assets/制assets/数assets/字assets/编assets/号assets/有assets/任assets/何assets/冲assets/突assets/了assets/。assets/
assets/
assets/#assets/#assets/#assets/ assets/Uassets/nassets/dassets/oassets/wassets/aassets/rassets/nassets/iassets/nassets/gassets/
assets/
assets/Vassets/iassets/massets/ assets/里assets/有assets/跨assets/会assets/话assets/撤assets/销assets/修assets/改assets/的assets/功assets/能assets/assets/这assets/当assets/然assets/是assets/它assets/的assets/强assets/大assets/的assets/特assets/色assets/功assets/能assets/。assets/不assets/过assets/assets/有assets/时assets/候assets/也assets/许assets/你assets/会assets/发assets/现assets/assets/不assets/小assets/心assets/多assets/按assets/了assets/几assets/下assets/ assets/assets/uassets/assets/assets/你assets/就assets/退assets/回assets/到assets/打assets/开assets/文assets/件assets/之assets/前assets/的assets/版assets/本assets/去assets/了assets/。assets/我assets/想assets/assets/这assets/很assets/有assets/可assets/能assets/不assets/是assets/你assets/想assets/要assets/的assets/行assets/为assets/吧assets/assets/如assets/果assets/你assets/assets/像assets/我assets/一assets/样assets/assets/希assets/望assets/能assets/够assets/无assets/限assets/制assets/地assets/进assets/行assets/编assets/辑assets/撤assets/销assets/assets/同assets/时assets/还assets/想assets/在assets/退assets/回assets/打assets/开assets/文assets/件assets/的assets/状assets/态assets/之assets/前assets/能assets/有assets/一assets/个assets/提assets/醒assets/assets/那assets/ assets/uassets/nassets/dassets/oassets/wassets/aassets/rassets/nassets/iassets/nassets/gassets/.assets/vassets/iassets/massets/ assets/可assets/能assets/就assets/是assets/你assets/想assets/要assets/的assets/。assets/
assets/
assets/这assets/个assets/插assets/件assets/不assets/支assets/持assets/用assets/包assets/管assets/理assets/器assets/自assets/动assets/安assets/装assets/。assets/你assets/需assets/要assets/自assets/行assets/下assets/载assets/ assets/[assets/uassets/nassets/dassets/oassets/wassets/aassets/rassets/nassets/iassets/nassets/gassets/.assets/vassets/iassets/massets/]assets/(assets/hassets/tassets/tassets/passets/sassets/:assets//assets//assets/gassets/iassets/tassets/hassets/uassets/bassets/.assets/cassets/oassets/massets//assets/tassets/hassets/oassets/uassets/gassets/hassets/tassets/sassets/tassets/rassets/eassets/aassets/massets//assets/Dassets/aassets/massets/iassets/aassets/nassets/-assets/Cassets/oassets/nassets/wassets/aassets/yassets/-assets/sassets/-assets/Vassets/iassets/massets/-assets/Sassets/eassets/tassets/uassets/passets//assets/bassets/lassets/oassets/bassets//assets/massets/aassets/sassets/tassets/eassets/rassets//assets/passets/lassets/uassets/gassets/iassets/nassets//assets/uassets/nassets/dassets/oassets/wassets/aassets/rassets/nassets/iassets/nassets/gassets/sassets/.assets/vassets/iassets/massets/)assets/assets/并assets/把assets/它assets/放assets/到assets/你assets/的assets/ assets/Vassets/iassets/massets/ assets/配assets/置assets/目assets/录assets/的assets/ assets/passets/lassets/uassets/gassets/iassets/nassets/ assets/子assets/目assets/录assets/下assets/。assets/下assets/图assets/是assets/一assets/个assets/运assets/行assets/中assets/的assets/示assets/例assets/assets/
assets/
assets/!assets/[assets/Fassets/iassets/gassets/Xassets/5assets/.assets/2assets/]assets/(assets/aassets/sassets/sassets/eassets/tassets/sassets//assets/1assets/dassets/bassets/7assets/fassets/1assets/cassets/cassets/cassets/8assets/9assets/2assets/aassets/8assets/2assets/1assets/7assets/aassets/5assets/4assets/8assets/1assets/9assets/3assets/4assets/6assets/4assets/3assets/5assets/2assets/4assets/9assets/.assets/gassets/iassets/fassets/ assets/“assets/Uassets/nassets/dassets/oassets/wassets/aassets/rassets/nassets/iassets/nassets/gassets/ assets/的assets/效assets/果assets/“assets/)assets/
assets/
assets/#assets/#assets/#assets/ assets/Rassets/aassets/iassets/nassets/bassets/oassets/wassets/
assets/
assets/代assets/码assets/中assets/括assets/号assets/多assets/了assets/assets/有assets/时assets/候assets/眼assets/睛assets/就assets/有assets/点assets/看assets/不assets/过assets/来assets/assets/需assets/要assets/有assets/个assets/更assets/好assets/的assets/颜assets/色assets/提assets/示assets/。assets/因assets/此assets/assets/就assets/有assets/了assets/很assets/多assets/彩assets/虹assets/效assets/果assets/的assets/ assets/Vassets/iassets/massets/ assets/插assets/件assets/。assets/在assets/这assets/些assets/插assets/件assets/中assets/assets/我assets/最assets/喜assets/欢assets/的assets/是assets/ assets/[assets/fassets/rassets/aassets/zassets/rassets/eassets/passets/oassets//assets/vassets/iassets/massets/-assets/rassets/aassets/iassets/nassets/bassets/oassets/wassets/]assets/(assets/hassets/tassets/tassets/passets/sassets/:assets//assets//assets/gassets/iassets/tassets/hassets/uassets/bassets/.assets/cassets/oassets/massets//assets/fassets/rassets/aassets/zassets/rassets/eassets/passets/oassets//assets/vassets/iassets/massets/-assets/rassets/aassets/iassets/nassets/bassets/oassets/wassets/)assets/assets/它assets/最assets/妙assets/的assets/地assets/方assets/是assets/assets/居assets/然assets/能assets/把assets/ assets/Cassets/+assets/+assets/ assets/代assets/码assets/中assets/的assets/尖assets/括assets/号assets/也assets/进assets/行assets/加assets/亮assets/assets/还assets/能assets/基assets/本assets/不assets/会assets/在assets/出assets/现assets/小assets/于assets/、assets/大assets/于assets/、assets/流assets/输assets/入assets/输assets/出assets/时assets/进assets/行assets/错assets/误assets/的assets/加assets/亮assets/。assets/效assets/果assets/见assets/下assets/图assets/assets/
assets/
assets/!assets/[assets/Fassets/iassets/gassets/Xassets/5assets/.assets/3assets/]assets/(assets/aassets/sassets/sassets/eassets/tassets/sassets//assets/3assets/eassets/1assets/4assets/1assets/fassets/1assets/cassets/2assets/dassets/3assets/8assets/8assets/3assets/dassets/dassets/8assets/3assets/8assets/4assets/1assets/8assets/2assets/dassets/4assets/bassets/cassets/7assets/2assets/bassets/dassets/0assets/.assets/passets/nassets/gassets/ assets/“assets/彩assets/虹assets/括assets/号assets/的assets/效assets/果assets/“assets/)assets/
assets/
assets/效assets/果assets/默assets/认assets/不assets/自assets/动assets/启assets/用assets/assets/可assets/以assets/用assets/ assets/assets/:assets/Rassets/aassets/iassets/nassets/bassets/oassets/wassets/Tassets/oassets/gassets/gassets/lassets/eassets/assets/ assets/命assets/令assets/来assets/切assets/换assets/assets/或assets/用assets/ assets/assets/:assets/Rassets/aassets/iassets/nassets/bassets/oassets/wassets/Lassets/oassets/aassets/dassets/assets/ assets/命assets/令assets/来assets/加assets/载assets/。assets/我assets/觉assets/得assets/在assets/括assets/号assets/多assets/的assets/时assets/候assets/按assets/需assets/启assets/用assets/挺assets/好assets/assets/推assets/荐assets/assets/
assets/
assets/#assets/#assets/#assets/ assets/Aassets/uassets/tassets/oassets/-assets/passets/aassets/iassets/rassets/sassets/
assets/
assets/代assets/码assets/中assets/永assets/远assets/有assets/着assets/大assets/量assets/成assets/双assets/成assets/对assets/的assets/符assets/号assets/assets/输assets/入assets/一assets/个assets/assets/就assets/自assets/动assets/出assets/来assets/另assets/一assets/个assets/assets/会assets/是assets/一assets/个assets/非assets/常assets/有assets/用assets/的assets/功assets/能assets/。assets/但assets/这assets/样assets/的assets/功assets/能assets/assets/也assets/需assets/要assets/处assets/理assets/一assets/些assets/特assets/殊assets/情assets/况assets/assets/比assets/如assets/assets/如assets/果assets/程assets/序assets/员assets/输assets/入assets/了assets/一assets/对assets/符assets/号assets/ assets/assets/(assets/)assets/assets/assets/结assets/果assets/千assets/万assets/不assets/能assets/是assets/ assets/assets/(assets/)assets/)assets/assets/。assets/在assets/很assets/多assets/现assets/代assets/的assets/编assets/辑assets/器assets/上assets/assets/这assets/已assets/经assets/是assets/个assets/标assets/准assets/功assets/能assets/了assets/assets/但assets/ assets/Vassets/iassets/massets/ assets/一assets/直assets/没assets/有assets/类assets/似assets/的assets/功assets/能assets/。assets/
assets/
assets/实assets/际assets/上assets/assets/Vassets/iassets/massets/ assets/里assets/已assets/经assets/有assets/插assets/件assets/ assets/[assets/jassets/iassets/aassets/nassets/gassets/massets/iassets/aassets/oassets//assets/aassets/uassets/tassets/oassets/-assets/passets/aassets/iassets/rassets/sassets/]assets/(assets/hassets/tassets/tassets/passets/sassets/:assets//assets//assets/gassets/iassets/tassets/hassets/uassets/bassets/.assets/cassets/oassets/massets//assets/jassets/iassets/aassets/nassets/gassets/massets/iassets/aassets/oassets//assets/aassets/uassets/tassets/oassets/-assets/passets/aassets/iassets/rassets/sassets/)assets/ assets/支assets/持assets/了assets/这assets/个assets/功assets/能assets/assets/并assets/解assets/决assets/了assets/大assets/部assets/分assets/边assets/角assets/情assets/况assets/。assets/我assets/觉assets/得assets/可assets/以assets/推assets/荐assets/给assets/大assets/家assets/。assets/
assets/
assets/这assets/个assets/插assets/件assets/assets/要assets/不assets/要assets/推assets/荐assets/我assets/还assets/是assets/犹assets/豫assets/了assets/一assets/下assets/的assets/。assets/我assets/一assets/开assets/始assets/对assets/它assets/相assets/当assets/满assets/意assets/assets/但assets/后assets/来assets/我assets/发assets/现assets/仍assets/然assets/有assets/一assets/些assets/边assets/角assets/情assets/况assets/处assets/理assets/不assets/好assets/assets/使assets/用assets/它assets/就assets/会assets/导assets/致assets/无assets/法assets/编assets/辑assets/出assets/我assets/需assets/要assets/的assets/效assets/果assets/。assets/所assets/以assets/我assets/又assets/把assets/它assets/卸assets/载assets/了assets/。assets/但assets/再assets/后assets/来assets/assets/我assets/又assets/觉assets/得assets/assets/毕assets/竟assets/瑕assets/不assets/掩assets/瑜assets/assets/而assets/且assets/有assets/问assets/题assets/时assets/把assets/它assets/禁assets/用assets/不assets/就assets/得assets/了assets/assets/
assets/
assets/所assets/以assets/assets/它assets/的assets/配assets/置assets/项assets/我assets/也assets/只assets/需assets/要assets/提assets/一assets/个assets/assets/就assets/是assets/禁assets/用assets/的assets/键assets/映assets/射assets/。assets/这assets/个assets/键assets/映assets/射assets/由assets/全assets/局assets/变assets/量assets/ assets/assets/gassets/:assets/Aassets/uassets/tassets/oassets/Passets/aassets/iassets/rassets/sassets/Sassets/hassets/oassets/rassets/tassets/cassets/uassets/tassets/Tassets/oassets/gassets/gassets/lassets/eassets/assets/ assets/控assets/制assets/assets/默assets/认assets/值assets/是assets/ assets/assets/<assets/Massets/-assets/passets/>assets/assets/。assets/如assets/果assets/你assets/在assets/ assets/Massets/aassets/cassets/ assets/上assets/assets/这assets/个assets/键assets/多assets/半assets/就assets/不assets/工assets/作assets/了assets/assets/除assets/非assets/你assets/只assets/在assets/ assets/Massets/aassets/cassets/ assets/终assets/端assets/里assets/使assets/用assets/ assets/Vassets/iassets/massets/assets/并assets/且assets/在assets/终assets/端assets/应assets/用assets/里assets/配assets/置assets/了assets/“assets/将assets/ assets/Oassets/passets/tassets/iassets/oassets/nassets/ assets/键assets/用assets/作assets/ assets/Massets/eassets/tassets/aassets/ assets/键assets/”assets/。assets/对assets/于assets/大assets/部assets/分assets/ assets/Massets/aassets/cassets/ assets/用assets/户assets/assets/你assets/需assets/要assets/进assets/行assets/类assets/似assets/下assets/面assets/的assets/配assets/置assets/assets/因assets/为assets/ assets/Massets/aassets/cassets/ assets/上assets/按assets/ assets/Oassets/passets/tassets/iassets/oassets/nassets/-assets/Passets/ assets/会assets/产assets/生assets/“assets/πassets/”assets/assets/assets/
assets/
assets/ assets/ assets/ assets/ assets/lassets/eassets/tassets/ assets/gassets/:assets/Aassets/uassets/tassets/oassets/Passets/aassets/iassets/rassets/sassets/Sassets/hassets/oassets/rassets/tassets/cassets/uassets/tassets/Tassets/oassets/gassets/gassets/lassets/eassets/ assets/=assets/ assets/assets/πassets/assets/
assets/ assets/ assets/ assets/ assets/
assets/
assets/其assets/他assets/内assets/容assets/就assets/请assets/自assets/行assets/查assets/看assets/它assets/的assets/帮assets/助assets/文assets/件assets/了assets/。assets/
assets/
assets/#assets/#assets/#assets/ assets/Lassets/aassets/rassets/gassets/eassets/fassets/iassets/lassets/eassets/
assets/
assets/如assets/果assets/你assets/经assets/常assets/打assets/开assets/很assets/大assets/的assets/日assets/志assets/文assets/件assets/assets/那assets/ assets/Vassets/iassets/massets/ assets/的assets/一assets/些assets/自assets/动assets/功assets/能assets/可assets/能assets/不assets/仅assets/帮assets/不assets/了assets/什assets/么assets/忙assets/assets/反assets/而assets/会assets/拖assets/慢assets/你assets/的assets/编assets/辑assets/速assets/度assets/。assets/有assets/一assets/个assets/ assets/Vassets/iassets/massets/ assets/插assets/件assets/能assets/在assets/文assets/件assets/较assets/大assets/时assets/自assets/动assets/关assets/闭assets/事assets/件assets/处assets/理assets/、assets/撤assets/销assets/、assets/语assets/法assets/加assets/亮assets/等assets/功assets/能assets/assets/用assets/来assets/换assets/取assets/更assets/快assets/的assets/处assets/理assets/速assets/度assets/和assets/更assets/短assets/的assets/响assets/应assets/时assets/间assets/。assets/这assets/个assets/插assets/件assets/就assets/是assets/ assets/[assets/vassets/iassets/massets/-assets/sassets/cassets/rassets/iassets/passets/tassets/sassets//assets/Lassets/aassets/rassets/gassets/eassets/Fassets/iassets/lassets/eassets/]assets/(assets/hassets/tassets/tassets/passets/sassets/:assets//assets//assets/gassets/iassets/tassets/hassets/uassets/bassets/.assets/cassets/oassets/massets//assets/vassets/iassets/massets/-assets/sassets/cassets/rassets/iassets/passets/tassets/sassets//assets/Lassets/aassets/rassets/gassets/eassets/Fassets/iassets/lassets/eassets/)assets/。assets/
assets/
assets/这assets/个assets/插assets/件assets/的assets/功assets/能assets/比assets/较assets/简assets/单assets/assets/唯assets/一assets/需assets/要assets/配assets/置assets/的assets/就assets/是assets/多assets/大assets/算assets/大assets/。assets/你assets/只assets/需assets/要assets/在assets/ assets/vassets/iassets/massets/rassets/cassets/ assets/配assets/置assets/文assets/件assets/中assets/把assets/你assets/对assets/大assets/文assets/件assets/的assets/阈assets/值assets/assets/以assets/ assets/Massets/Bassets/ assets/为assets/单assets/位assets/assets/写assets/到assets/ assets/assets/gassets/:assets/Lassets/aassets/rassets/gassets/eassets/Fassets/iassets/lassets/eassets/assets/ assets/变assets/量assets/里assets/即assets/可assets/。assets/比assets/如assets/assets/如assets/果assets/你assets/认assets/为assets/超assets/过assets/ assets/1assets/0assets/0assets/Massets/Bassets/ assets/算assets/大assets/文assets/件assets/assets/那assets/我assets/们assets/这assets/样assets/写assets/就assets/可assets/以assets/了assets/assets/
assets/
assets/ assets/ assets/ assets/ assets/lassets/eassets/tassets/ assets/gassets/:assets/Lassets/aassets/rassets/gassets/eassets/Fassets/iassets/lassets/eassets/ assets/=assets/ assets/1assets/0assets/0assets/
assets/ assets/ assets/ assets/ assets/
assets/
assets/#assets/#assets/#assets/ assets/Massets/aassets/rassets/kassets/dassets/oassets/wassets/nassets/ assets/Passets/rassets/eassets/vassets/iassets/eassets/wassets/
assets/
assets/你assets/如assets/果assets/像assets/我assets/一assets/样assets/常assets/常assets/写assets/ assets/Massets/aassets/rassets/kassets/dassets/oassets/wassets/nassets/ assets/的assets/话assets/assets/你assets/应assets/该assets/会assets/喜assets/欢assets/ assets/[assets/Massets/aassets/rassets/kassets/dassets/oassets/wassets/nassets/ assets/Passets/rassets/eassets/vassets/iassets/eassets/wassets/]assets/(assets/hassets/tassets/tassets/passets/sassets/:assets//assets//assets/gassets/iassets/tassets/hassets/uassets/bassets/.assets/cassets/oassets/massets//assets/iassets/aassets/massets/cassets/cassets/oassets//assets/massets/aassets/rassets/kassets/dassets/oassets/wassets/nassets/-assets/passets/rassets/eassets/vassets/iassets/eassets/wassets/.assets/nassets/vassets/iassets/massets/)assets/ assets/这assets/个assets/插assets/件assets/。assets/Massets/aassets/rassets/kassets/dassets/oassets/wassets/nassets/ assets/本assets/来assets/最assets/适assets/用assets/的assets/场assets/景assets/就assets/是assets/浏assets/览assets/器assets/assets/纯assets/文assets/本assets/的assets/ assets/Vassets/iassets/massets/ assets/只assets/能assets/编assets/辑assets/assets/没assets/有assets/好assets/的assets/预assets/览assets/终assets/究assets/是assets/很assets/不assets/足assets/的assets/。assets/Massets/aassets/rassets/kassets/dassets/oassets/wassets/nassets/ assets/Passets/rassets/eassets/vassets/iassets/eassets/wassets/ assets/解assets/决assets/了assets/这assets/个assets/问assets/题assets/assets/让assets/你assets/在assets/编assets/辑assets/的assets/同assets/时assets/assets/可assets/以assets/在assets/浏assets/览assets/器assets/里assets/看assets/到assets/实assets/际assets/的assets/渲assets/染assets/效assets/果assets/。assets/更assets/令assets/我assets/吃assets/惊assets/的assets/是assets/assets/这assets/个assets/预assets/览assets/是assets/完assets/全assets/实assets/时assets/、assets/同assets/步assets/的assets/assets/无assets/需assets/存assets/盘assets/assets/而assets/且assets/预assets/览assets/页assets/面assets/随assets/着assets/光assets/标assets/在assets/ assets/Vassets/iassets/massets/ assets/里assets/移assets/动assets/而assets/跟assets/着assets/滚assets/动assets/assets/效assets/果assets/相assets/当assets/酷assets/。assets/你assets/可assets/以assets/直assets/接assets/到assets/ assets/Massets/aassets/rassets/kassets/dassets/oassets/wassets/nassets/ assets/Passets/rassets/eassets/vassets/iassets/eassets/wassets/ assets/的assets/主assets/页assets/上assets/看assets/一assets/下assets/官assets/方assets/的assets/示assets/意assets/图assets/assets/我assets/就assets/不assets/在assets/这assets/里assets/放assets/动assets/图assets/了assets/。assets/
assets/
assets/这assets/个assets/插assets/件assets/唯assets/一assets/需assets/要assets/特assets/别assets/注assets/意assets/的assets/是assets/assets/你assets/不assets/能assets/直assets/接assets/把assets/ assets/iassets/aassets/massets/cassets/cassets/oassets//assets/massets/aassets/rassets/kassets/dassets/oassets/wassets/nassets/-assets/passets/rassets/eassets/vassets/iassets/eassets/wassets/.assets/nassets/vassets/iassets/massets/ assets/放assets/到assets/你assets/的assets/包assets/管assets/理assets/器assets/里assets/了assets/事assets/。assets/原assets/因assets/是assets/它assets/里assets/面assets/包assets/含assets/了assets/需assets/要assets/编assets/译assets/的assets/前assets/端assets/组assets/件assets/assets/需assets/要assets/下assets/载assets/或assets/编assets/译assets/才assets/行assets/。assets/在assets/它assets/的assets/主assets/页assets/上assets/描assets/述assets/了assets/在assets/不assets/同assets/包assets/管assets/理assets/器assets/里assets/的assets/安assets/装assets/方assets/式assets/assets/你assets/只assets/要assets/跟assets/着assets/照assets/做assets/就assets/行assets/。assets/
assets/
assets/它assets/的assets/配assets/置assets/在assets/主assets/页assets/上assets/也assets/有assets/列assets/表assets/assets/但assets/默assets/认assets/设assets/置assets/就assets/已assets/经assets/完assets/全assets/可assets/用assets/了assets/。assets/如assets/果assets/有assets/需assets/求assets/的assets/话assets/assets/你assets/可assets/以assets/修assets/改assets/其assets/中assets/部assets/分assets/值assets/assets/如assets/ assets/assets/gassets/:assets/massets/kassets/dassets/passets/_assets/bassets/rassets/oassets/wassets/sassets/eassets/rassets/assets/ assets/可assets/以assets/用assets/来assets/设assets/定assets/你assets/希assets/望assets/打assets/开assets/页assets/面assets/的assets/浏assets/览assets/器assets/assets/我assets/目assets/前assets/设assets/的assets/是assets/ assets/assets/'assets/fassets/iassets/rassets/eassets/fassets/oassets/xassets/'assets/assets/assets/。assets/
assets/
assets/#assets/#assets/#assets/ assets/Cassets/aassets/lassets/eassets/nassets/dassets/aassets/rassets/
assets/
assets/[assets/Cassets/aassets/lassets/eassets/nassets/dassets/aassets/rassets/]assets/(assets/hassets/tassets/tassets/passets/sassets/:assets//assets//assets/gassets/iassets/tassets/hassets/uassets/bassets/.assets/cassets/oassets/massets//assets/massets/aassets/tassets/tassets/nassets//assets/cassets/aassets/lassets/eassets/nassets/dassets/aassets/rassets/-assets/vassets/iassets/massets/)assets/ assets/是assets/一assets/个assets/很assets/简assets/单assets/的assets/显assets/示assets/日assets/历assets/的assets/ assets/Vassets/iassets/massets/ assets/插assets/件assets/assets/在assets/包assets/管assets/理assets/器assets/里assets/的assets/名assets/字assets/是assets/ assets/massets/aassets/tassets/tassets/nassets//assets/cassets/aassets/lassets/eassets/nassets/dassets/aassets/rassets/-assets/vassets/iassets/massets/。assets/它assets/的assets/功assets/能assets/应assets/该assets/就assets/不assets/需assets/要assets/解assets/释assets/了assets/assets/效assets/果assets/可assets/以assets/直assets/接assets/查assets/看assets/下assets/图assets/。assets/
assets/
assets/!assets/[assets/Fassets/iassets/gassets/Xassets/5assets/.assets/4assets/]assets/(assets/aassets/sassets/sassets/eassets/tassets/sassets//assets/bassets/0assets/2assets/0assets/fassets/9assets/5assets/bassets/eassets/4assets/cassets/0assets/eassets/eassets/3assets/7assets/6assets/8assets/3assets/bassets/2assets/1assets/7assets/8assets/eassets/fassets/7assets/cassets/cassets/bassets/eassets/aassets/.assets/passets/nassets/gassets/ assets/“assets/:assets/Cassets/aassets/lassets/eassets/nassets/dassets/aassets/rassets/Hassets/ assets/产assets/生assets/的assets/水assets/平assets/日assets/历assets/“assets/)assets/
assets/
assets/这assets/个assets/插assets/件assets/支assets/持assets/一assets/些assets/不assets/同assets/的assets/样assets/式assets/和assets/分assets/割assets/方assets/式assets/assets/上assets/图assets/中assets/就assets/是assets/用assets/ assets/assets/:assets/Cassets/aassets/lassets/eassets/nassets/dassets/aassets/rassets/Hassets/assets/assets/或assets/正assets/常assets/模assets/式assets/命assets/令assets/ assets/assets/cassets/aassets/Lassets/assets/assets/进assets/行assets/的assets/横assets/向assets/分assets/割assets/assets/同assets/时assets/横assets/向assets/显assets/示assets/日assets/历assets/assets/你assets/也assets/可assets/以assets/用assets/ assets/assets/:assets/Cassets/aassets/lassets/eassets/nassets/dassets/aassets/rassets/assets/assets/或assets/正assets/常assets/模assets/式assets/命assets/令assets/ assets/assets/cassets/aassets/lassets/assets/assets/进assets/行assets/纵assets/向assets/分assets/割assets/和assets/日assets/历assets/显assets/示assets/。assets/此assets/外assets/assets/它assets/还assets/支assets/持assets/其assets/他assets/一assets/些assets/命assets/令assets/和assets/组assets/合assets/。assets/鉴assets/于assets/这assets/个assets/插assets/件assets/的assets/的assets/帮assets/助assets/文assets/件assets/也assets/不assets/长assets/assets/如assets/果assets/你assets/对assets/相assets/关assets/功assets/能assets/有assets/兴assets/趣assets/的assets/话assets/assets/就assets/请assets/你assets/自assets/己assets/去assets/看assets/一assets/下assets/了assets/。assets/
assets/
assets/#assets/#assets/#assets/ assets/Massets/aassets/tassets/rassets/iassets/xassets/
assets/
assets/上assets/面assets/介assets/绍assets/的assets/插assets/件assets/assets/不assets/管assets/对assets/你assets/有assets/没assets/有assets/用assets/assets/都assets/可assets/以assets/说assets/是assets/“assets/有assets/用assets/”assets/的assets/。assets/插assets/件assets/也assets/不assets/一assets/定assets/要assets/做assets/有assets/用assets/的assets/事assets/assets/我assets/的assets/机assets/器assets/一assets/直assets/装assets/着assets/下assets/面assets/这assets/个assets/“assets/没assets/用assets/”assets/的assets/插assets/件assets/assets/[assets/uassets/gassets/uassets/uassets/-assets/oassets/rassets/gassets//assets/vassets/iassets/massets/-assets/massets/aassets/tassets/rassets/iassets/xassets/-assets/sassets/cassets/rassets/eassets/eassets/nassets/sassets/aassets/vassets/eassets/rassets/]assets/(assets/hassets/tassets/tassets/passets/sassets/:assets//assets//assets/gassets/iassets/tassets/hassets/uassets/bassets/.assets/cassets/oassets/massets//assets/uassets/gassets/uassets/uassets/-assets/oassets/rassets/gassets//assets/vassets/iassets/massets/-assets/massets/aassets/tassets/rassets/iassets/xassets/-assets/sassets/cassets/rassets/eassets/eassets/nassets/sassets/aassets/vassets/eassets/rassets/)assets/。assets/
assets/
assets/它assets/的assets/效assets/果assets/不assets/用assets/解assets/释assets/assets/直assets/接assets/看assets/下assets/面assets/的assets/动assets/图assets/就assets/好assets/。assets/-assets/
assets/-assets/
assets/!assets/[assets/Fassets/iassets/gassets/Xassets/5assets/.assets/5assets/]assets/(assets/hassets/tassets/tassets/passets/sassets/:assets//assets//assets/sassets/tassets/aassets/tassets/iassets/cassets/0assets/0assets/1assets/.assets/gassets/eassets/eassets/kassets/bassets/aassets/nassets/gassets/.assets/oassets/rassets/gassets//assets/rassets/eassets/sassets/oassets/uassets/rassets/cassets/eassets//assets/iassets/massets/aassets/gassets/eassets//assets/dassets/9assets//assets/7assets/fassets//assets/dassets/9assets/6assets/aassets/5assets/bassets/5assets/eassets/1assets/9assets/3assets/9assets/5assets/1assets/fassets/dassets/8assets/eassets/3assets/9assets/4assets/3assets/yassets/yassets/3assets/bassets/9assets/7assets/dassets/6assets/7assets/fassets/.assets/gassets/iassets/fassets/?assets/wassets/hassets/=assets/1assets/1assets/4assets/2assets/*assets/9assets/5assets/6assets/ assets/“assets/Massets/aassets/tassets/rassets/iassets/xassets/!assets/“assets/)assets/
assets/
assets/#assets/#assets/#assets/ assets/Kassets/iassets/lassets/lassets/eassets/rassets/sassets/hassets/eassets/eassets/passets/
assets/
assets/Vassets/iassets/massets/ assets/脚assets/本assets/不assets/仅assets/可assets/以assets/做assets/没assets/用assets/的assets/事assets/情assets/assets/还assets/可assets/以assets/更assets/进assets/一assets/步assets/做assets/娱assets/乐assets/的assets/事assets/情assets/。assets/比assets/如assets/assets/Vassets/iassets/massets/ assets/的assets/作assets/者assets/ assets/Bassets/rassets/aassets/massets/ assets/亲assets/自assets/操assets/刀assets/写assets/了assets/这assets/个assets/“assets/愚assets/蠢assets/的assets/游assets/戏assets/”assets/assets/[assets/vassets/iassets/massets//assets/kassets/iassets/lassets/lassets/eassets/rassets/sassets/hassets/eassets/eassets/passets/]assets/(assets/hassets/tassets/tassets/passets/sassets/:assets//assets//assets/gassets/iassets/tassets/hassets/uassets/bassets/.assets/cassets/oassets/massets//assets/vassets/iassets/massets//assets/kassets/iassets/lassets/lassets/eassets/rassets/sassets/hassets/eassets/eassets/passets/)assets/。assets/
assets/
assets/这assets/当assets/然assets/不assets/是assets/一assets/个assets/真assets/正assets/非assets/常assets/好assets/玩assets/的assets/游assets/戏assets/assets/不assets/过assets/我assets/也assets/玩assets/通assets/关assets/了assets/。assets/你assets/不assets/妨assets/也assets/试assets/试assets/assets/小assets/提assets/示assets/assets/屏assets/幕assets/拉assets/高assets/点assets/assets/按assets/键assets/重assets/复assets/速assets/度assets/快assets/点assets/assets/重assets/复assets/前assets/延assets/迟assets/短assets/一assets/点assets/assets/这assets/样assets/更assets/有assets/助assets/于assets/你assets/打assets/好assets/这assets/个assets/游assets/戏assets/。assets/
assets/
assets/当assets/然assets/assets/这assets/个assets/插assets/件assets/的assets/本assets/来assets/意assets/义assets/就assets/不assets/是assets/一assets/个assets/游assets/戏assets/assets/而assets/是assets/要assets/演assets/示assets/ assets/Vassets/iassets/massets/ assets/8assets/.assets/2assets/ assets/的assets/下assets/列assets/新assets/功assets/能assets/assets/
assets/
assets/*assets/ assets/ assets/ assets/带assets/颜assets/色assets/和assets/掩assets/码assets/的assets/的assets/弹assets/出assets/窗assets/口assets/
assets/*assets/ assets/ assets/ assets/用assets/于assets/高assets/亮assets/文assets/本assets/的assets/文assets/本assets/属assets/性assets/
assets/*assets/ assets/ assets/ assets/声assets/音assets/
assets/
assets/如assets/果assets/你assets/想assets/用assets/这assets/些assets/功assets/能assets/的assets/话assets/assets/就assets/可assets/以assets/去assets/看assets/看assets/这assets/个assets/插assets/件assets/的assets/源assets/码assets/。assets/这assets/也assets/算assets/一assets/种assets/寓assets/教assets/于assets/乐assets/吧assets/assets/
assets/
assets/技assets/巧assets/
assets/-assets/-assets/
assets/
assets/#assets/#assets/#assets/ assets/行assets/过assets/滤assets/
assets/
assets/在assets/编assets/辑assets/日assets/志assets/等assets/类assets/型assets/的assets/文assets/本assets/时assets/assets/我assets/们assets/往assets/往assets/想assets/过assets/滤assets/出assets/我assets/们assets/感assets/兴assets/趣assets/的assets/内assets/容assets/。assets/这assets/时assets/assets/我assets/们assets/可assets/以assets/用assets/正assets/则assets/表assets/达assets/式assets/assets/但assets/使assets/用assets/ assets/assets/:assets/sassets/assets/ assets/命assets/令assets/并assets/不assets/是assets/一assets/种assets/最assets/高assets/效assets/的assets/方assets/式assets/。assets/如assets/果assets/你assets/感assets/兴assets/趣assets/的assets/每assets/一assets/行assets/都assets/可assets/以assets/跟assets/某assets/个assets/正assets/则assets/表assets/达assets/式assets/模assets/式assets/匹assets/配assets/assets/如assets/日assets/期assets/、assets/某assets/关assets/键assets/字assets/等assets/assets/assets/最assets/高assets/效assets/的assets/命assets/令assets/应assets/该assets/是assets/assets/
assets/
assets/ assets/ assets/ assets/ assets/:assets/vassets//assets/匹assets/配assets/模assets/式assets//assets/dassets/
assets/ assets/ assets/ assets/ assets/
assets/
assets/稍assets/微assets/解assets/释assets/一assets/下assets/assets/assets/:assets/vassets/assets/ assets/命assets/令assets/assets/可assets/以assets/查assets/看assets/帮assets/助assets/ assets/[assets/assets/:assets/hassets/eassets/lassets/passets/ assets/:assets/vassets/assets/]assets/(assets/hassets/tassets/tassets/passets/sassets/:assets//assets//assets/yassets/iassets/aassets/nassets/wassets/iassets/lassets/lassets/iassets/sassets/.assets/gassets/iassets/tassets/hassets/uassets/bassets/.assets/iassets/oassets//assets/vassets/iassets/massets/cassets/dassets/oassets/cassets//assets/dassets/oassets/cassets//assets/rassets/eassets/passets/eassets/aassets/tassets/.assets/hassets/tassets/massets/lassets/#assets/:assets/vassets/)assets/assets/可assets/以assets/用assets/来assets/找assets/出assets/不assets/符assets/合assets/匹assets/配assets/模assets/式assets/的assets/行assets/assets/对assets/比assets/一assets/下assets/ assets/assets/gassets/rassets/eassets/passets/ assets/-assets/vassets/assets/ assets/命assets/令assets/assets/assets/然assets/后assets/执assets/行assets/后assets/面assets/的assets/动assets/作assets/。assets/所assets/以assets/上assets/面assets/的assets/命assets/令assets/就assets/是assets/找assets/出assets/不assets/满assets/足assets/匹assets/配assets/模assets/式assets/的assets/行assets/assets/然assets/后assets/执assets/行assets/删assets/除assets/assets/assets/dassets/assets/assets/。assets/
assets/
assets/顺assets/便assets/说assets/一assets/下assets/assets/如assets/果assets/你assets/需assets/要assets/找assets/出assets/符assets/合assets/匹assets/配assets/模assets/式assets/的assets/行assets/assets/需assets/要assets/的assets/命assets/令assets/是assets/ assets/assets/:assets/gassets/assets/assets/可assets/以assets/查assets/看assets/帮assets/助assets/ assets/[assets/assets/:assets/hassets/eassets/lassets/passets/ assets/:assets/gassets/assets/]assets/(assets/hassets/tassets/tassets/passets/sassets/:assets//assets//assets/yassets/iassets/aassets/nassets/wassets/iassets/lassets/lassets/iassets/sassets/.assets/gassets/iassets/tassets/hassets/uassets/bassets/.assets/iassets/oassets//assets/vassets/iassets/massets/cassets/dassets/oassets/cassets//assets/dassets/oassets/cassets//assets/rassets/eassets/passets/eassets/aassets/tassets/.assets/hassets/tassets/massets/lassets/#assets/:assets/gassets/)assets/assets/。assets/
assets/
assets/#assets/#assets/#assets/ assets/自assets/动assets/关assets/闭assets/最assets/后assets/一assets/个assets/ assets/qassets/uassets/iassets/cassets/kassets/fassets/iassets/xassets/ assets/窗assets/口assets/
assets/
assets/我assets/们assets/已assets/经assets/讨assets/论assets/到assets/很assets/多assets/功assets/能assets/会assets/用assets/到assets/ assets/qassets/uassets/iassets/cassets/kassets/fassets/iassets/xassets/ assets/窗assets/口assets/。assets/如assets/果assets/打assets/开assets/ assets/qassets/uassets/iassets/cassets/kassets/fassets/iassets/xassets/ assets/窗assets/口assets/后assets/assets/你assets/关assets/闭assets/了assets/编assets/辑assets/的assets/主assets/窗assets/口assets/assets/那assets/ assets/qassets/uassets/iassets/cassets/kassets/fassets/iassets/xassets/ assets/窗assets/口assets/可assets/能assets/就assets/成assets/为assets/这assets/个assets/ assets/Vassets/iassets/massets/ assets/会assets/话assets/里assets/剩assets/下assets/的assets/唯assets/一assets/窗assets/口assets/了assets/assets/而assets/这assets/个assets/窗assets/口assets/多assets/半assets/你assets/完assets/全assets/不assets/再assets/需assets/要assets/。assets/你assets/会assets/希assets/望assets/assets/此assets/时assets/ assets/Vassets/iassets/massets/ assets/应assets/该assets/自assets/动assets/关assets/闭assets/这assets/个assets/窗assets/口assets/直assets/接assets/退assets/出assets/。assets/这assets/当assets/然assets/是assets/个assets/可assets/以assets/用assets/程assets/序assets/自assets/动assets/化assets/的assets/事assets/情assets/assets/所assets/以assets/我assets/们assets/也assets/应assets/该assets/这assets/样assets/做assets/assets/用assets/下assets/面assets/的assets/脚assets/本assets/放assets/到assets/ assets/vassets/iassets/massets/rassets/cassets/ assets/配assets/置assets/文assets/件assets/里assets/就assets/可assets/以assets/做assets/到assets/assets/
assets/
assets/ assets/ assets/ assets/ assets/aassets/uassets/gassets/ assets/Qassets/Fassets/Cassets/lassets/oassets/sassets/eassets/
assets/ assets/ assets/ assets/ assets/ assets/ assets/aassets/uassets/!assets/
assets/ assets/ assets/ assets/ assets/ assets/ assets/aassets/uassets/ assets/Wassets/iassets/nassets/Eassets/nassets/tassets/eassets/rassets/ assets/*assets/ assets/ assets/iassets/fassets/ assets/wassets/iassets/nassets/nassets/rassets/(assets/assets/$assets/assets/)assets/ assets/=assets/=assets/ assets/1assets/ assets/&assets/&assets/ assets/&assets/bassets/uassets/fassets/tassets/yassets/passets/eassets/ assets/=assets/=assets/ assets/“assets/qassets/uassets/iassets/cassets/kassets/fassets/iassets/xassets/“assets/|assets/qassets/|assets/eassets/nassets/dassets/iassets/fassets/
assets/ assets/ assets/ assets/ assets/aassets/uassets/gassets/ assets/Eassets/Nassets/Dassets/
assets/ assets/ assets/ assets/ assets/
assets/
assets/你assets/只assets/要assets/查assets/一assets/下assets/ assets/assets/wassets/iassets/nassets/nassets/rassets/assets/ assets/函assets/数assets/的assets/帮assets/助assets/assets/[assets/assets/:assets/hassets/eassets/lassets/passets/ assets/wassets/iassets/nassets/nassets/rassets/(assets/)assets/assets/]assets/(assets/hassets/tassets/tassets/passets/sassets/:assets//assets//assets/yassets/iassets/aassets/nassets/wassets/iassets/lassets/lassets/iassets/sassets/.assets/gassets/iassets/tassets/hassets/uassets/bassets/.assets/iassets/oassets//assets/vassets/iassets/massets/cassets/dassets/oassets/cassets//assets/dassets/oassets/cassets//assets/eassets/vassets/aassets/lassets/.assets/hassets/tassets/massets/lassets/#assets/wassets/iassets/nassets/nassets/rassets/(assets/)assets/)assets/assets/就assets/很assets/容assets/易assets/理assets/解assets/了assets/assets/代assets/码assets/的assets/意assets/思assets/还assets/是assets/非assets/常assets/清assets/楚assets/的assets/assets/如assets/果assets/窗assets/口assets/的assets/数assets/量assets/是assets/ assets/1assets/ assets/并assets/且assets/缓assets/冲assets/区assets/类assets/型assets/是assets/ assets/qassets/uassets/iassets/cassets/kassets/fassets/iassets/xassets/ assets/的assets/话assets/assets/那assets/就assets/退assets/出assets/ assets/Vassets/iassets/massets/。assets/为assets/了assets/确assets/保assets/重assets/复assets/执assets/行assets/这assets/段assets/代assets/码assets/没assets/有assets/问assets/题assets/assets/它assets/有assets/一assets/个assets/自assets/己assets/的assets/自assets/动assets/命assets/令assets/组assets/assets/并assets/会assets/在assets/清assets/除assets/这assets/个assets/自assets/动assets/命assets/令assets/组assets/的assets/所assets/有assets/自assets/动assets/命assets/令assets/后assets/在assets/进assets/入assets/窗assets/口assets/assets/Wassets/iassets/nassets/Eassets/nassets/tassets/eassets/rassets/assets/这assets/个assets/事assets/件assets/中assets/进assets/行assets/上assets/面assets/的assets/检assets/查assets/。assets/
assets/
assets/assets/本assets/技assets/巧assets/来assets/自assets/这assets/个assets/ assets/[assets/Sassets/tassets/aassets/cassets/kassets/ assets/Oassets/vassets/eassets/rassets/fassets/lassets/oassets/wassets/ assets/回assets/答assets/]assets/(assets/hassets/tassets/tassets/passets/sassets/:assets//assets//assets/sassets/tassets/aassets/cassets/kassets/oassets/vassets/eassets/rassets/fassets/lassets/oassets/wassets/.assets/cassets/oassets/massets//assets/aassets//assets/7assets/4assets/7assets/7assets/0assets/5assets/6assets//assets/8assets/1assets/6assets/9assets/9assets/9assets/)assets/。assets/assets/
assets/
assets/#assets/#assets/#assets/ assets/Hassets/oassets/massets/eassets/ assets/键assets/的assets/行assets/为assets/
assets/
assets/对assets/于assets/大assets/部assets/分assets/现assets/代assets/的assets/编assets/辑assets/器assets/assets/Hassets/oassets/massets/eassets/ assets/键assets/的assets/行assets/为assets/通assets/常assets/是assets/assets/
assets/
assets/*assets/ assets/ assets/ assets/当assets/光assets/标assets/处assets/于assets/本assets/行assets/第assets/一assets/个assets/非assets/空assets/白assets/字assets/符assets/上assets/时assets/assets/跳assets/转assets/到assets/行assets/首assets/
assets/*assets/ assets/ assets/ assets/否assets/则assets/assets/跳assets/转assets/到assets/本assets/行assets/第assets/一assets/个assets/非assets/空assets/白assets/字assets/符assets/上assets/
assets/
assets/虽assets/然assets/ assets/Vassets/iassets/massets/ assets/的assets/行assets/为assets/不assets/是assets/这assets/样assets/assets/但assets/如assets/果assets/你assets/希assets/望assets/配assets/置assets/出assets/这assets/样assets/的assets/行assets/为assets/assets/也assets/不assets/麻assets/烦assets/assets/把assets/下assets/面assets/的assets/代assets/码assets/加assets/入assets/到assets/你assets/的assets/ assets/vassets/iassets/massets/rassets/cassets/ assets/配assets/置assets/文assets/件assets/即assets/可assets/assets/
assets/
assets/ assets/ assets/ assets/ assets/fassets/uassets/nassets/cassets/tassets/iassets/oassets/nassets/!assets/ assets/Gassets/oassets/Tassets/oassets/Fassets/iassets/rassets/sassets/tassets/Nassets/oassets/nassets/Bassets/lassets/aassets/nassets/kassets/Oassets/rassets/Fassets/iassets/rassets/sassets/tassets/Cassets/oassets/lassets/uassets/massets/nassets/(assets/)assets/
assets/ assets/ assets/ assets/ assets/ assets/ assets/lassets/eassets/tassets/ assets/cassets/uassets/rassets/_assets/cassets/oassets/lassets/ assets/=assets/ assets/cassets/oassets/lassets/(assets/assets/.assets/assets/)assets/
assets/ assets/ assets/ assets/ assets/ assets/ assets/nassets/oassets/rassets/massets/aassets/lassets/!assets/ assets/^assets/
assets/ assets/ assets/ assets/ assets/ assets/ assets/iassets/fassets/ assets/cassets/uassets/rassets/_assets/cassets/oassets/lassets/ assets/!assets/=assets/ assets/1assets/ assets/&assets/&assets/ assets/cassets/uassets/rassets/_assets/cassets/oassets/lassets/ assets/=assets/=assets/ assets/cassets/oassets/lassets/(assets/assets/.assets/assets/)assets/
assets/ assets/ assets/ assets/ assets/ assets/ assets/ assets/ assets/nassets/oassets/rassets/massets/aassets/lassets/!assets/ assets/0assets/
assets/ assets/ assets/ assets/ assets/ assets/ assets/eassets/nassets/dassets/iassets/fassets/
assets/ assets/ assets/ assets/ assets/ assets/ assets/rassets/eassets/tassets/uassets/rassets/nassets/ assets/assets/assets/
assets/ assets/ assets/ assets/ assets/eassets/nassets/dassets/fassets/uassets/nassets/cassets/tassets/iassets/oassets/nassets/
assets/ assets/ assets/ assets/ assets/
assets/ assets/ assets/ assets/ assets/nassets/nassets/oassets/rassets/eassets/massets/aassets/passets/ assets/assets/ assets/assets/ assets/:assets/cassets/aassets/lassets/lassets/ assets/Gassets/oassets/Tassets/oassets/Fassets/iassets/rassets/sassets/tassets/Nassets/oassets/nassets/Bassets/lassets/aassets/nassets/kassets/Oassets/rassets/Fassets/iassets/rassets/sassets/tassets/Cassets/oassets/lassets/uassets/massets/nassets/(assets/)assets/assets/
assets/ assets/ assets/ assets/ assets/iassets/nassets/oassets/rassets/eassets/massets/aassets/passets/ assets/assets/ assets/assets/ assets/assets/=assets/Gassets/oassets/Tassets/oassets/Fassets/iassets/rassets/sassets/tassets/Nassets/oassets/nassets/Bassets/lassets/aassets/nassets/kassets/Oassets/rassets/Fassets/iassets/rassets/sassets/tassets/Cassets/oassets/lassets/uassets/massets/nassets/(assets/)assets/assets/
assets/ assets/ assets/ assets/ assets/
assets/
assets/在assets/这assets/个assets/代assets/码assets/里assets/assets/assets/cassets/oassets/lassets/(assets/'assets/.assets/'assets/)assets/assets/ assets/用assets/来assets/获assets/取assets/光assets/标assets/所assets/在assets/的assets/列assets/号assets/assets/然assets/后assets/我assets/们assets/跳assets/转assets/到assets/第assets/一assets/个assets/非assets/空assets/白assets/字assets/符assets/处assets/assets/assets/^assets/assets/assets/assets/随assets/后assets/检assets/查assets/是assets/不assets/是assets/我assets/们assets/不assets/在assets/第assets/ assets/1assets/ assets/列assets/并assets/且assets/列assets/号assets/没assets/有assets/变assets/化assets/。assets/如assets/果assets/是assets/的assets/话assets/assets/说assets/明assets/第assets/一assets/个assets/非assets/空assets/白assets/字assets/符assets/不assets/在assets/行assets/首assets/并assets/且assets/当assets/前assets/光assets/标assets/已assets/经assets/在assets/第assets/一assets/个assets/非assets/空assets/白assets/字assets/符assets/处assets/了assets/assets/那assets/我assets/们assets/就assets/跳assets/转assets/到assets/行assets/首assets/去assets/assets/assets/0assets/assets/assets/。assets/
assets/
assets/另assets/外assets/要assets/注assets/意assets/assets/我assets/们assets/这assets/儿assets/使assets/用assets/了assets/ assets/assets/nassets/oassets/rassets/massets/aassets/lassets/!assets/assets/ assets/而assets/不assets/是assets/ assets/assets/nassets/oassets/rassets/massets/aassets/lassets/assets/。assets/这assets/两assets/者assets/的assets/区assets/别assets/是assets/assets/用assets/了assets/ assets/assets/!assets/assets/ assets/的assets/ assets/assets/nassets/oassets/rassets/massets/aassets/lassets/assets/ assets/命assets/令assets/会assets/忽assets/略assets/键assets/映assets/射assets/assets/否assets/则assets/ assets/assets/nassets/oassets/rassets/massets/aassets/lassets/assets/ assets/就assets/跟assets/正assets/常assets/按assets/键assets/一assets/样assets/了assets/。assets/为assets/了assets/防assets/止assets/其assets/他assets/地assets/方assets/定assets/义assets/了assets/键assets/映assets/射assets/导assets/致assets/行assets/为assets/变assets/化assets/assets/一assets/般assets/推assets/荐assets/ assets/assets/nassets/oassets/rassets/massets/aassets/lassets/!assets/assets/ assets/命assets/令assets/而assets/不assets/是assets/ assets/assets/nassets/oassets/rassets/massets/aassets/lassets/assets/ assets/命assets/令assets/。assets/
assets/
assets/我assets/们assets/这assets/样assets/就assets/修assets/改assets/了assets/正assets/常assets/模assets/式assets/和assets/插assets/入assets/模assets/式assets/下assets/的assets/ assets/Hassets/oassets/massets/eassets/ assets/键assets/的assets/行assets/为assets/。assets/目assets/前assets/assets/在assets/可assets/视assets/模assets/式assets/下assets/这assets/个assets/方assets/式assets/不assets/适assets/用assets/assets/你assets/仍assets/然assets/只assets/能assets/手assets/工assets/选assets/择assets/合assets/适assets/的assets/ assets/assets/0assets/assets/ assets/或assets/ assets/assets/^assets/assets/ assets/命assets/令assets/。assets/
assets/
assets/#assets/#assets/#assets/ assets/查assets/看assets/光assets/标assets/下assets/字assets/符assets/的assets/内assets/码assets/
assets/
assets/有assets/很assets/多assets/字assets/符assets/是assets/很assets/相assets/似assets/的assets/assets/在assets/ assets/Vassets/iassets/massets/ assets/使assets/用assets/的assets/等assets/宽assets/字assets/体assets/中assets/尤assets/其assets/如assets/此assets/。assets/比assets/如assets/assets/光assets/看assets/字assets/形assets/assets/你assets/能assets/区assets/分assets/下assets/面assets/这assets/些assets/字assets/符assets/分assets/别assets/是assets/什assets/么assets/吗assets/assets/
assets/
assets/*assets/ assets/ assets/ assets/assets/-assets/ assets/assets/ assets/—assets/ assets/assets/ assets/─assets/assets/
assets/
assets/这assets/些assets/字assets/符assets/看assets/起assets/来assets/虽assets/然assets/相assets/似assets/assets/但assets/它assets/们assets/的assets/意assets/义assets/是assets/完assets/全assets/不assets/同assets/的assets/。assets/特assets/别assets/是assets/在assets/源assets/代assets/码assets/中assets/assets/如assets/果assets/你assets/一assets/不assets/小assets/心assets/混assets/入assets/了assets/一assets/个assets/相assets/似assets/的assets/字assets/符assets/assets/字assets/处assets/理assets/器assets/有assets/时assets/候assets/会assets/自assets/动assets/替assets/换assets/一assets/些assets/ assets/Aassets/Sassets/Cassets/Iassets/Iassets/ assets/字assets/符assets/assets/造assets/成assets/这assets/种assets/问assets/题assets/assets/assets/代assets/码assets/运assets/行assets/就assets/会assets/出assets/错assets/了assets/。assets/这assets/时assets/assets/assets/gassets/aassets/assets/ assets/命assets/令assets/就assets/可assets/以assets/帮assets/上assets/忙assets/。assets/
assets/
assets/下assets/图assets/展assets/示assets/了assets/ assets/assets/gassets/aassets/assets/ assets/命assets/令assets/的assets/一assets/次assets/执assets/行assets/结assets/果assets/assets/
assets/
assets/!assets/[assets/Fassets/iassets/gassets/Xassets/5assets/.assets/6assets/]assets/(assets/hassets/tassets/tassets/passets/sassets/:assets//assets//assets/sassets/tassets/aassets/tassets/iassets/cassets/0assets/0assets/1assets/.assets/gassets/eassets/eassets/kassets/bassets/aassets/nassets/gassets/.assets/oassets/rassets/gassets//assets/rassets/eassets/sassets/oassets/uassets/rassets/cassets/eassets//assets/iassets/massets/aassets/gassets/eassets//assets/aassets/1assets//assets/7assets/4assets//assets/aassets/1assets/9assets/3assets/fassets/0assets/dassets/4assets/4assets/6assets/fassets/fassets/7assets/dassets/5assets/eassets/dassets/cassets/eassets/bassets/fassets/bassets/cassets/0assets/eassets/7assets/4assets/9assets/4assets/7assets/7assets/4assets/.assets/passets/nassets/gassets/?assets/wassets/hassets/=assets/1assets/0assets/9assets/2assets/*assets/2assets/4assets/3assets/ assets/“assets/执assets/行assets/ assets/gassets/aassets/ assets/命assets/令assets/的assets/结assets/果assets/“assets/)assets/
assets/
assets/我assets/们assets/可assets/以assets/看assets/到assets/这assets/个assets/字assets/符assets/实assets/际assets/上assets/是assets/ assets/Uassets/nassets/iassets/cassets/oassets/dassets/eassets/ assets/字assets/符assets/ assets/Uassets/+assets/2assets/0assets/1assets/3assets/assets/十assets/进assets/制assets/和assets/八assets/进assets/制assets/数assets/值assets/分assets/别assets/是assets/ assets/8assets/2assets/1assets/1assets/ assets/和assets/ assets/2assets/0assets/0assets/2assets/3assets/assets/assets/短assets/破assets/折assets/号assets/。assets/这assets/个assets/字assets/符assets/在assets/很assets/多assets/键assets/盘assets/上assets/不assets/能assets/直assets/接assets/打assets/出assets/来assets/assets/因assets/而assets/ assets/Vassets/iassets/massets/ assets/提assets/供assets/了assets/二assets/合assets/字assets/母assets/assets/dassets/iassets/gassets/rassets/aassets/passets/hassets/assets/assets/可assets/以assets/用assets/ assets/assets/<assets/Cassets/-assets/Kassets/>assets/-assets/Nassets/assets/ assets/的assets/方assets/式assets/来assets/输assets/入assets/。assets/
assets/
assets/#assets/#assets/#assets/ assets/为assets/什assets/么assets/是assets/ assets/4assets/2assets/assets/
assets/
assets/好assets/吧assets/assets/最assets/后assets/这assets/个assets/不assets/是assets/技assets/巧assets/。assets/你assets/可assets/能assets/会assets/奇assets/怪assets/assets/为assets/什assets/么assets/在assets/讲assets/编assets/程assets/的assets/很assets/多assets/代assets/码assets/里assets/会assets/出assets/现assets/ assets/4assets/2assets/ assets/这assets/个assets/数assets/字assets/呢assets/assets/那assets/其assets/实assets/是assets/因assets/为assets/一assets/个assets/科assets/幻assets/小assets/说assets/的assets/“assets/梗assets/”assets/。assets/事assets/实assets/上assets/assets/我assets/在assets/这assets/个assets/课assets/程assets/里assets/也assets/用assets/到assets/了assets/ assets/4assets/2assets/。assets/要assets/想assets/获assets/取assets/进assets/一assets/步assets/的assets/信assets/息assets/assets/请assets/在assets/ assets/Vassets/iassets/massets/ assets/里assets/输assets/入assets/assets/
assets/
assets/ assets/ assets/ assets/ assets/:assets/hassets/eassets/lassets/passets/ assets/4assets/2assets/
assets/ assets/ assets/ assets/ assets/
assets/
assets/内assets/容assets/小assets/结assets/
assets/-assets/-assets/-assets/-assets/
assets/
assets/本assets/讲assets/的assets/内assets/容assets/比assets/较assets/零assets/散assets/assets/我assets/为assets/你assets/介assets/绍assets/了assets/一assets/些assets/我assets/认assets/为assets/值assets/得assets/介assets/绍assets/的assets/插assets/件assets/和assets/技assets/巧assets/assets/每assets/一assets/则assets/篇assets/幅assets/都assets/较assets/短assets/assets/我assets/也assets/就assets/不assets/再assets/正assets/式assets/总assets/结assets/了assets/。assets/
assets/
assets/我assets/分assets/享assets/的assets/这assets/些assets/内assets/容assets/大assets/部assets/分assets/是assets/有assets/用assets/的assets/assets/但assets/也assets/有assets/一assets/部assets/分assets/可assets/以assets/认assets/为assets/基assets/本assets/是assets/“assets/无assets/用assets/”assets/的assets/。assets/生assets/活assets/里assets/如assets/果assets/要assets/求assets/每assets/一assets/件assets/事assets/情assets/都assets/有assets/用assets/assets/岂assets/不assets/是assets/会assets/变assets/得assets/非assets/常assets/无assets/趣assets/assets/对assets/我assets/来assets/说assets/assets/编assets/程assets/和assets/编assets/程assets/工assets/具assets/也assets/是assets/如assets/此assets/。assets/
assets/
assets/课assets/后assets/练assets/习assets/
assets/-assets/-assets/-assets/-assets/
assets/
assets/今assets/天assets/介assets/绍assets/的assets/插assets/件assets/和assets/技assets/巧assets/assets/虽assets/说assets/不assets/能assets/算assets/必assets/需assets/assets/也assets/是assets/非assets/常assets/有assets/意assets/义assets/的assets/。assets/强assets/烈assets/建assets/议assets/你assets/挑assets/至assets/少assets/三assets/个assets/来assets/安assets/装assets//assets/实assets/验assets/一assets/下assets/。assets/如assets/有assets/任assets/何assets/问assets/题assets/或assets/意assets/见assets/assets/欢assets/迎assets/留assets/言assets/和assets/我assets/交assets/流assets/。assets/
assets/
assets/我assets/是assets/吴assets/咏assets/炜assets/assets/我assets/们assets/下assets/一assets/讲assets/再assets/见assets/assets/

View File

@ -0,0 +1,136 @@
因收到Google相关通知网站将会择期关闭。相关通知内容
结束语 Vim 森林探秘,一切才刚刚开始
你好,我是吴咏炜。
我们的课程到这里就结束了,而你的学习旅程,到这儿只能算是一个小小的休息站。
学习的度
对于一个持续发展了 30 年的编辑器,我们显然不可能在一门短小精悍的课程里完整地覆盖它的所有功能。不过,我从来就没打算介绍 Vim 的一切。如果把 Vim 比作一片大森林,我只是一个导游,为你制定了一条旅游路线,带你绕过沼泽地和陷阱,攀上了几座峰顶,让你能够领略到若干美景。如果想在林中长久地居住下去,熟稔各条秘径,你仍然要靠自己去探索。
Vim 的作者 Bram 这么告诫人们不要走到两个极端上去 [Moolenaar 2007]
你需要马上把文本准备好。所以没有时间读文档或学习新命令。——你会一直使用原始的命令。
你想学编辑器提供的所有功能,并在任何时候都能使用最高效的命令。——你会浪费很多时间学习很多你永远不会用到的东西。
前者的问题很明显,如果你不学习,那你只能使用初级的功能,所以效率一定很低。后者的问题可能不那么明显了:实际上,除了多花时间之外,你很难培养出良好的习惯,形成“肌肉记忆”。而这,恰恰是高效工作的关键之一——不需要想,就知道怎么做,从而可以把头脑和精力投入到更重要的问题上。
在这个课程里,我也只是告诉你基本的原则和技巧,并培养你基本的编辑习惯。回头,在遇到实际问题时,你会需要使用搜索引擎、讨论组等工具来找到问题的答案。
学习、积累和分享
我学习 Vim原本是处于 Linux 下开发的需要而后慢慢成为一种习惯。回过头来看Vim 就和 Unix 一样,老而弥坚,经久不衰,不管时代怎样变化,它们却一直没有过时。而同时代我用过的其他 Windows 上的编辑器,如 EditPlus 和 UltraEdit现在虽然都还在但是应该已经很少有人提起了吧……
经过几年的学习和使用,我居然也就可以给别人分享我的经验了。我先是在 IBM developerWorks 上发了一系列的三篇 Vim 文章,后面又在 SHLUGShanghai Linux User Group的活动中分享了我的 Vim 使用经验。事后,我在网上看到别人觉得我这个分享做得最好,也是非常的欣慰。
时间快进到今年的年初,我做完了我在极客时间的第一门课程《现代 C++ 实战 30 讲》。当编辑问我还有什么其他可分享的课程时,我立刻想到了 Vim。于是就有了这个课程。我很喜欢知识分享的过程因为准备的过程同时也是自我梳理和刷新的过程毕竟要给别人讲就不能像自己用的时候那样不求甚解了。让我没预料到的是在课程中我还向某些积极的同学学了一两手为了不让你们太骄傲我就不点名了😉。知识分享真是一种非常好的活动于人于己都非常有利。建议大家在工作中也可以多多考虑😇。它唯一的缺点就是会让自己变得非常忙碌、周末没有休息时间而已😂。
高效编辑的诀窍
现在回到 Vim。我们来考虑一下这个“元”问题怎么样可以进行高效的编辑
事实上Bram 早就回答过这个问题了。这跟一般的效率改进计划没有本质的区别。我们要做的是:
发现低效的根源
找出更快的方法
形成新的习惯
Vim 的功能也是围绕着这样的模式开发出来的。
比如我们要找出当前文件中某个符号的使用在任何编辑器里都会提供一个搜索的功能。但每次都使用搜索实际上也是有点麻烦的。Vim 里的 * 搜索键和搜索加亮,这两个你目前应当已经习以为常的功能,就是为了解决这种低效而诞生的。而对你,现在更快的方法,就是在配置中启用搜索加亮(目前我们的基本配置里已经启用),并使用 * 来搜索光标下的单词。
又比如我们有一个非常长的函数名打起来又费力又容易出错。这时候我们需要找出更快的方法那就是自动完成。Vim 内置的改进方法是 <C-N><C-P> 命令。而我们学到现在的就该知道YCM 还提供了更现代的模糊完成引擎。用好内置命令,或安装合适的插件,就是我们需要形成的新习惯。
说到这儿,你也看到了,不仅我们的课程不是高效编辑的终点,而且就连 Vim 也不是。Vim 一直在发展,但有人嫌 Vim 发展得不够激进,另外搞了 Neovim。我们这个课程完全没有讨论 Neovim主要是不想在一个已经很复杂的课题上再增加复杂性同时也是因为 Neovim 虽然看起来势头不错,但远没到可以尘埃落定、一定会在将来替代 Vim 的程度再加上两者并不完全兼容……反过来Neovim 倒是刺激 Vim 更快地添加新功能了)。
抛开 Neovim 不谈,我们还有插件:正是这些辛勤的插件作者,才使得 Vim 真正更为强大,成为一个特别高效的编辑器。就像上面讨论的,有了 YCM我们找到了一个比 Vim 内置功能更加简单、直观、高效的方法。我们也应该去用好这样的新工具,提升自己的工作效率。
要提高自己的编辑效率,需要时时刻刻注意自己有哪儿效率特别低,出现了不必要的重复,然后找到更好的办法来改进,并确保自己形成新的习惯。
这个新的习惯是什么呢?可以是掌握了 Vim 里的一个之前不熟悉的功能,可以是用上了一个新的插件,也可以是……为 Vim 社区贡献一个新的插件!
这也恰恰是我在开篇词里提到的“懒惰”。所谓懒惰,就是我们要不让自己做低效的重复工作。而要不做低效的重复工作,我们就需要开动自己的脑子,监测自己的工作方式,找出问题点,予以改进,并坚持下去。懒惰的手段就是高效;反过来说也可以,高效的目的就是懒惰。
期待你在 Vim 森林里找出自己的“传送门”,能够慵懒地一抬腿,就飞速到达任何自己想去的地方。
我们后会有期!
《Vim 实用技巧必知必会》课程结束了,这里有一份毕业问卷,题目不多,希望你能花两分钟填一下。十分期待能听到你说一说,你对这个课程的想法和建议。-
](https://jinshuju.net/f/vUVK4d)
参考资料
作为写作的基本规矩,我最后列一下我参考过的资料(插件和之前文中直接给出的链接则不再重复)。希望它们也能帮到你,让你在下面的旅途中再上一层楼:
Allen, Leo. 2016. “Why Vim is so much better than Atom”.
Arthur, Barry. 2014. “Learning the tool of Vim”.
Bringhurst, Robert. 2012. The Elements of Typographic Style, 4th ed. Hartley & Marks Publishers.
Irwin, Conrad. 2013. “Bracketed paste mode”.
Kochkov, Anton. 2019. “Terminal Colors”.
Leonard, Andrew. 2000. “BSD Unix: Power to the people, from the code”.
Moolenaar, Bram. 2000. “The continuing history of Vim”.
Moolenaar, Bram. 2002. “Vim, an open-source text editor”.
Moolenaar, Bram. 2007. “7 habits for effective text editing 2.0”. YouTube video.
Moolenaar, Bram. 2018. “Vim: Recent developments”.
Neil, Drew. 2015. Practical Vim, 2nd ed. OReilly. 中文版杨源、车文隆译《Vim 实用技巧》人民邮电出版社2014第一版2016第二版
Neil, Drew. 2018. Modern Vim: Craft your development Environment with Vim 8 and Neovim. Pragmatic Bookshelf. 中文版:死月译《精通 Vim用 Vim 8 和 Neovim 实现高效开发》电子工业出版社2020。
Ornbo, George. 2019. “Vim: you dont need NERDtree or (maybe) netrw”.
Osipov, Ruslan. 2018. Mastering Vim. Packt. 中文版王文涛译《Vim 8 文本处理实战》人民邮电出版社2020。
Robbins, Arnold, Elbert Hannah, and Linda Lamb. 2008. Learning the vi and Vim Editors, 7th ed. OReilly.
Salus, Peter H. 1994. A Quarter Century of UNIX. Addison-Wesley.
Schneider, Peter A. 2018. Answer to “How do I disable the weird characters from bracketed paste mode on the Mac OS X default terminal?”.
Stack Overflow. 2015. “2015 developer survey”, section “Technology > Text editor”.
Stack Overflow. 2019. “Developer survey results 2019”, section “Technology > Development environments and tools”.
Target, Sinclair. 2018. “Where Vim came from”.
Vance, Ashlee. 2003. “Bill Joys greatest gift to man the vi editor”.
Vim Online.
Wikipedia. “Berkeley Software Distribution”.
Wikipedia. “Bill Joy”.
Wikipedia. “ex (text editor)”.
Wikipedia. “Version 6 Unix”.
Wikipedia. “vi”.
Wikipedia. “Vim”.
Wikipedia. “Visual editor”.