Emacs 心路历程(上)
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 + CEDET,CEDET 是一个看起来堪称完美的方案,光是看截图都足以让人咽口水。
(btw,现在看到这张图片,依旧很吸引人)
CEDET 其实不是一个插件,而是一堆插件(cedet、eieio、semantic、speedbar 等等),插件之间有版本依赖关系, 还要考虑不支持 GUI 的情况。
忘记当时是因为 Emacs 没有完善的包管理机制(不能轻松的解决版本依赖问题),还是自己不知道这些。但是可以确定的是,
当时的 Github 还没有这么盛行,装插件的方式,还是在 sourceforge 上下载 .el
文件,然后手动 (load-path)
。
配置 C++ 对我帮助比较大的网站有两个:
令人伤心的是,这两个网站现在都已经访问不了了。
当我费劲了力气把 CEDET 搭建好之后,发现太慢了,根本没法用,大部分功能都用不到。
几乎每一个刚开始使用 Emacs 的人,总想着把它配置成一个 IDE,总是想着看他能不能干这个,能不能干那个,却忘了自己到底需要的是什么。
作为一个程序工作者,每天都在写代码,不管是 IDE 还是 Emacs 这样的文本编辑器,常用的功能其实很少,能解决好编辑、复制、 粘贴、查找、保存等基本功能就已经解决了 60% 的问题,再解决代码的语义分析、支持补全、跳转等就可以解决 30% 的问题, 剩下的 10% 是一些调试,重构等工作。所以我们往往在折腾的时候把自己 80% 的精力放在了不足 20% 的功能上,而那些功能只是看起来酷酷的。
后来,我终于明白了, 这样的文本编辑器的优势在于编辑,也就是说它是在解决那 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 的)。
毫不夸张的说,在使用 Emacs 的过程中,我有无数次想要切换到其它的编辑器,比如 Sublime Text,Atom,VS Code, 甚至我把编程环境都配置好了,最后都是卡在了 Buffer 管理和窗口切换上,Emacs 的多窗口实在太好用了。
另外一个让我真正开始喜欢 Emacs 的插件是 helm,当时应该还没有这个网站,我看的资料是:Emacs mini manual。 看到里面的 gif 图之后惊呆了,没想到 Emacs 还这么用。
Helm 是极大提升 Emacs 效率的一个插件。在此之前我一直用 =ido+smex=,但是 Helm 除了在文件打开和 Buffer 切换这块有极大提升之外,还在本文件和工程搜索,以及书签等。
其中最喜欢的也是最常用的有两个快捷键: helm-occur
, helm-show-kill-ring
;
前者用来在本文档中搜索(可替代 C-s
),后者可以显示历史的复制记录。
在 Helm 的基础上扩展了很多使用的插件,比如:helm-is-git,helm-projectile。后来 helm 逐渐成了一个体系。
上面这些插件再结合 highlight-symbol,auto-complete,yasnippet,expant-region.el 等插件,组成了一个堪称完美的编辑器。
后来我整理了一篇文章:Emacs 简易教程,相关的配置在文章中有比较细致的说明。
在后面的四年时间里,所有与编辑工作(C/C++,Python/Django,JS/HTML/CSS/JSX,Go)都在终端 + Emacs上进行, 养成一套自己的工作流,偶尔也会有些问题,但整体还是很爽的。