Emacs/Vim 深度比较
生命在于折腾,折腾完了 Atom Editor,开始跟着陈斌大婶和 purcell的配置折腾 Emacs,比较下。很多人都在比较键位,比较插件,这是十分肤浅的,我们比较点深入的东西:
代码结构
- Emacs 源代码:eLisp 79%, C 21%
- Vim 源代码:C 52%, VimScript 48%
从代码结构上来讲,Emacs的代码最多的是 elisp,C代码只是一个微内核,Vim 里C代码还是大头。当然不排除 24.X, 25.X以后 Emacs源代码里带了好几个重量级的包,而 Vim向来比较精简一些,官方没带啥大点的插件有关。去除自带插件后,Emacs的 elisp代码比例应该会下降很多,不过总体来说,Emacs有更多组件使用 elisp开发而成,也就是说可以被用户修改或者替换的地方比 Vim要多,当然速度也会相应慢一点(比如 Emacs新打开上万行的文件连续按住PageDown时cpu 100%占满),不过比较大 JB,Atom Editor来说,还是快不少。
系统接口
大框架基本类似:
- Vim 可以操作: buffer, window, tabpage, 光标,marker, region 跳转表等等。
- Emacs 可以操作:buffer, window, frame, 光标,marker,region 异步进程 等等。
Vim 有 local,Emacs有 mode,Vim有事件触发,Emacs有各种钩子,基本大框架类似的。
键位设置也都很灵活,会配置的话,可以把 Emacs键位全部弄成 Vim的,比如 Evil,或者Vim里面也可以配制成进去就自动进入插入模式,全部用 Emacs键位。
具体到比如 buffer 或者窗口里面,Emacs的窗口或者 buffer /window 属性更多一些,Vim也有一些 Emacs没有的基础设施,比如 jumplist, quickfix之类的,不过 Emacs也可以用插件实现,实现 jumplist没问题,比较独立,但每个插件实现一个类似的 quickfix的东西,实在是比较蛋疼。
脚本语言
VimScript 类似 Lua,但更啰嗦些,没啥好说的,要上手可能就是2-3天的样子,主要是实际编写中熟悉各种 Vim内部结构体系,以及api。
EmacsLisp 类似 clisp,稍微有些区别而已。clisp中的向量: (vector 1 2 3 4) 在 elisp中可以用中括号来表示:[1 2 3 4] 这样对减少小括号数量有一些帮助,代码读起来没那么拗口,可惜实际写 elisp的过程中,向量用的并不多,各种插件用的最多的还是 list,alist,plist 几种容器,小括号还是满天飞,如果不借助编辑器的匹配,缩进,还有彩虹括号(不同层次的括号用不同颜色的小插件)代码很难阅读,自己写好写点,看别人代码要对括号对半天(特别是他们把很多写一行的话)。
Lisp变量可视范围比较好用,局部变量可以 “遮盖住” 全局变量,比如你在 VimScript里面要临时跳转到一个新目录需要这么写:
let cd = haslocaldir()? "cd" : "lcd" let saved = getcwd() silent! exec cd "my new directory" ...... silent! exec cd saved
而 elisp里面,利用局部变量遮盖特性,简单的:
(let ((default-directory "my new directory")) ..... )
在这个 let的作用范围内,buffer的全局变量 default-directory
被新定义的同名局部变量给遮盖住了,里面的代码(包括调用外面定义的函数)取这个名字的变量,都会取到你新定义的变量,作用范围结束就恢复了,这样比 vimscript方便不少,类似的用法还有 (with-selected-window new-window...)
把当前窗口设置成 new-window并做一些事情,当离开代码作用域以后,自动恢复成先前的窗口,如果VimScript来做这事情,又需要保存前窗口,然后跳转,最后又恢复。
VimScript在 vim6.0及以前的时代里比 EmacsLisp弱很多,连个 List,HashTable之类的容器都没有,怎么写复杂的程序嘛,Vim7.0以后补充了字典和列表还有三元式,简单面向对象等,终于可以写点复杂的东西了,而 Vim下很多传奇性的插件,其实也正是 7.0以后才出现的。Vim8.0以后又进一步补充了匿名函数和 partial function (类似简陋版的 curry 函数)等等特性,除了啰嗦外,写点复杂东西问题不大,比如前久有人用 VimScript写了一个 C编译器,可以在Vim里跑 C脚本。
Lisp里面的宏类似C++模版加强版,C++模版只有一层,定义好模版,然后生成代码。Lisp的宏可以先用程序生成模版,再由模版生成代码,类似模版的模版。不过实际使用中,为了不让自己的代码飞的太远,难以维护,大部分插件开发者在 elisp里面最多就是 require一下 common lisp的兼容包,用一些 common lisp里面诸如 loop, for, incf, case 之类的通用宏,自己定义也限于定义一些小工具,没有人丧心病狂的搞一些影响程序结构的东西,定义成另外一门语言。
大部分插件开发者实际上还是把它当作充满括号的 lua 来用,除去一些便利性外,麻烦也还是挺多的。
异步进程
早期的 Vim没有异步进程操作,导致很多插件没有 Emacs那么顺畅,比如生成 tag或者编译,Vim只有傻等着,Emacs可以同时做其他事情,Vim8.0以后异步接口和后台时钟等也比较完善了,假以时日各大常见插件能逐步发挥 8.0特性的时候,相信也能带来一致的体验,包括纯vimscript实现的 shell,以及 gdb集成,发邮件等,问题不大。
Recent Comments