Emacs 心路历程(上)

emacs-startup.png

Figure 1: JerryZhang

在使用 Emacs 过程中,从不缺折腾,每次折腾都让人想放弃使用它,但是折腾过后又会觉得很爽。隔一段时间我都会折腾一次,但是比较大范围的折腾,只有两次。


第一次大范围折腾是在 2014 年,以前 Emacs 只是玩玩,业余时间写写代码、写写文章,并没有作为工作的一部分。虽然写 C++ 有几年了,但是都只是在 Windows 环境下一直用 VS 系列(VC++6.0,VS2015,VS2010,VS2012 我都很熟)做开发。2014 年刚换了工作,开始从事游戏服务器开发,虽然依然是在 Windows 下,但是需要远程连到 CentOS 上开发(Xshell)。心想着总算有 Emacs 的用武之地了,没想到的时候搭建 C++ 开发环境比我想象的要复杂的多。

印象中,有几个月的时间都在与 Emacs 做斗争,尝试各种网上的 Emacs C++ IDE 方案,不断的尝试,然后不断的推翻配置。当时比较流行的方案有:ECB

是一个看起来堪称完美的方案,光是看截图都足以让人咽口水。

ecb.png

(btw,现在看到这张图片,依旧很吸引人)

CEDET 其实不是一个插件,而是一堆插件(cedet、eieio、semantic、speedbar 等等),插件之间有版本依赖关系,还要考虑不支持 GUI 的情况。

忘记当时是因为 Emacs 没有完善的包管理机制(不能轻松的解决版本依赖问题),还是自己不知道这些。但是可以确定的是,当时的 Github 还没有这么盛行,装插件的方式,还是在 sourceforge 上下载 .el 文件,然后手动 =(load-path)=。配置 C++ 对我帮助比较大的网站有两个:

  1. Emacs 中文网
  2. 曹乐的主页

令人伤心的是,这两个网站现在都已经访问不了了。

当我费劲了力气把 CEDET 搭建好之后,发现太慢了,根本没法用,大部分功能都用不到。

几乎每一个刚开始使用 Emacs 的人,总想着把它配置成一个 IDE,总是想着看他能不能干这个,能不能干那个,却忘了自己到底需要的是什么。

作为一个程序工作者,每天都在写代码,不管是 IDE 还是 Emacs 这样的文本编辑器,常用的功能其实很少,能解决好编辑、复制、粘贴、查找、保存等基本功能就已经解决了 60% 的问题,再解决代码的语义分析、支持补全、跳转等就可以解决 30% 的问题,剩下的 10% 是一些调试,重构等工作。所以我们往往在折腾的时候把自己 80% 的精力放在了不足 20% 的功能上,而那些功能只是看起来酷酷的。

后来,我终于明白了,Emacs 这样的文本编辑器的优势在于编辑,也就是说它是在解决那 60% 的问题,其编辑效能远不是 VS 这样的 IDE 可以比拟的。而剩下的 40% 则是 IDE 的天下,Emacs 无论装多少插件也比不上 IDE。所以,我们要做的是权衡:搞定常用的 60%,尽可能的解决剩下的 40%,实在解决不了用其他方法来填充。比如 gdb 的确可以跟 Emacs 融合做 Debug,但是直接用命令行来做也插不了多少。

最后的配置方案是: =xcscope + etags + c++-mode=,

尽管 xcscope 比 cedet 差一些,但实际上就这些功能都用的很少,写代码最多就是跳转到定义返回,还有有文件和对应源文件之间的跳转,我当时绑定了一个快捷键:=(global-set-key [(f9)] 'ff-find-other-file)=。

在 Emacs 下写 C/C++ 是我第一次耗费很大的精力去折腾 Emacs,之后写 Python,写 Golang 都有过折腾,但是都不会那么复杂。为什么呢?

因为 C++ 语法比较复杂,灵活性很强,尤其是模板嵌套的缩进一层好搞,两层就很麻烦了,何况多层。我印象中最后模板的缩进都没有做的特别好。另外一个原因是没有统一业内的规范,C/C++ 程序员历经了太多的代,主要的两个流派:Linux 和 Microsoft,是完全不同的风格。我经历过 Windows 下的 C++ 开发,也经历过 Linux 下的 C++ 开发,别说是编码风格了,文化和技术手段都差很多(这个值得我再写一篇文章)。

因为这些导致网上对与 C++ 的配置五花八门,我印象比较深的是 C++ 中函数的括号位置,默认的情况下 Emacs 是这样的:

function()
  {}

因为很多传统的 C++ 程序员,比如说 云风 曾经说过括号放在下面是没有缩进的,相同的言论在代码大全里也说过。所以 Linux 的风格是这样的(他们认为这样才是有缩进的):

function() {
}

但实际上我们当时用的编码风格需求是这样的:

function()
{
}

最后在 Emacs indent for C++ class method? 找到函数括号缩进的解决方案。

类似的问题还有很多,但为什么 Python 和 Golang 没有那么复杂呢?因为他们都有业内比较权威的编码规范,比如 Python 有 PEP 8,Go 做的更彻底,直接官方提供了代码格式化工具 gofmt。大家都是相同的规范,在社区就很容易就找到配置文件参考。

(实际上由于 Python 依靠空格缩进来定义代码块这种方式为 IDE 和编辑器都带来了很大的困难,因为它没有办法来判断你的下一行是属于上一个代码块的还是下一个,这会导致每次的缩进都参考上一行,如果想新起代码块总是需要手动调整)

C++ 配置搞定了之后,面对一个终端,在鼠标束手无策的情况下,完全在 Emacs 下工作还是有一定问题。尤其是分屏情况下窗口切换,Emacs 默认情况下的快捷键是 =C-x o=,两个窗口还可以,多个窗口的情况下很难准确的定位到自己想要的窗口。

后来找到了 window-numbering.el,使用 Meta + 1/2/3/... 的方式进行窗口切换,对于当时来讲,简直就是救星,太好用了。但是 Meta + 数组键盘 这种快捷键组合通常会跟终端模拟器的快捷键冲突,比如 Xshell,还有 Linux 的各种终端,直到用上 MacOS 之后,才解决这个问题(因为 Mac 下是用 Command 作为 Leading-Key 的)。

windows-number.gif

毫不夸张的说,在使用 Emacs 的过程中,我有无数次想要切换到其它的编辑器,比如 Sublime TextAtomVS Code,甚至我把编程环境都配置好了,最后都是卡在了 Buffer 管理和窗口切换上,Emacs 的多窗口实在太好用了。

helm-grep-ag-persistent.png

另外一个让我真正开始喜欢 Emacs 的插件是 helm,当时应该还没有这个网站,我看的资料是:Emacs mini manual。看到里面的 gif 图之后惊呆了,没想到 Emacs 还这么用。

Helm 是极大提升 Emacs 效率的一个插件。在此之前我一直用 =ido+smex=,但是 Helm 除了在文件打开和 Buffer 切换这块有极大提升之外,还在本文件和工程搜索,以及书签等。

其中最喜欢的也是最常用的有两个快捷键:=helm-occur=,=helm-show-kill-ring=;前者用来在本文档中搜索(可替代 =C-s=),后者可以显示历史的复制记录。

在 Helm 的基础上扩展了很多使用的插件,比如:helm-is-githelm-projectile。后来 helm 逐渐成了一个体系。

上面这些插件再结合 highlight-symbolauto-completeyasnippetexpant-region.el 等插件,组成了一个堪称完美的编辑器。

后来我整理了一篇文章:Emacs 简易教程,相关的配置在文章中有比较细致的说明。

在后面的四年时间里,所有与编辑工作(C/C++,Python/Django,JS/HTML/CSS/JSX,Go)都在 终端 + Emacs 上进行,养成一套自己的工作流,偶尔也会有些问题,但整体还是很爽的。

Date: 2019-06-17 23:17:00

Author: JerryZhang