应 lgn21st 之邀,写一篇有关Emacs 开发 Rails 应用的 Wiki. 深感任务相当庞大, 好在现在还没有上班,时间相对充裕。虽然已经很晚了,但明天可以睡个懒觉。 立即行动,把这两天的战斗结果记录下来。
这几天突击研究了几天有关 Emacs 中的自动补全 (autocomplete) 以及 snippet 输入。
小有心得,在同一个快捷键之上 (tab), 堪称完美的实现自动补全与 snippet 插入的功能。
赶紧记下来备忘,在这里分享给大家。
我认为针对任何其他编辑器,都应该具有一定的通用性,从此再也不用为了使用什么样的 key 来定义 snippet 发愁了。很爽~~
在 Emacs 下面,自动补全使用的插件叫做 auto-complete, snippet 插件叫做 yasnippet.
你可以在以下 github 找到最新的软件版本: https://github.com/m2ym/auto-complete https://github.com/capitaomorte/yasnippet
首先说下在 Emacs 以上两个插件中存在的提示菜单,总共有三种。
其中两种是自动补全 (autocomplete) 提供的,而另一种是 yasnippet 插件提供的。
自动补全有两个菜单: 一个是光标后面的阴影,会告诉你如果按下 tab 键,会补全为什么单词. 另一个是如果等待 0.8 秒后,什么也没做,弹出一个文本菜单,这个菜单按照一下优先顺序决定扩展:
(注意,以上两个菜单,在 Emacs 中,可以设定在输入多少个字符后才允许激活,很明显, 我的设定是 4 个字符,默认情况下,这个选项是 2)
yasnippet 有一个菜单,只有在使用同样的 key, 但是具有不同的 name 的情况下,才会弹出这个菜单. 用于选择同样的 key 下的不同的不同的 snippet.
例如:键入 should, 再次键入 tab, 会弹出一个 snippet 菜单,会有各种 should 的片段注释, 在这里,你可以根据不同的 name, 来选择扩展对应的 snippet.
具体的要求是:
如果键入的字符数量小于等于 3 个,此时,如果存在对应的 snippet key. 例如存 sh 扩展为 should,则扩展该 snippet. (注意,也可以选择这一步直接扩展 snippet, 即,sh => 直接调用所有 should 开头的 snippet 菜单)
如果键入的字符超过 3 个,此时,在光标所在行,会弹出阴影,告诉你按下 tab 后会补全成什么单词. 此时的优先顺序是:当前模式词典 => yasnippet => 当前 buffer 的上下文) 要求此时按下 tab, 应该总是扩展为阴影提示的字符。 例如:你键入 shou, 会看到后面有阴影提示 ld, 此时立即按下 tab 键,应该总是自动完成 should. 但此时又分为两种情况:
通过以上两种方式,已经自动补全为 should, 此时再次按下 tab, 会弹出一个 yasnippet 选择菜单 会有各种以 should 开头的 snippet 方案弹出,根据 name 的不同,你可以选择一个,然后扩展它。
(这里需要注意的是:完全可以直接通过 键入 sh => 按下 tab 展开 snippet 菜单,此时必须同时满足以下条件:
唧唧歪歪一大堆,最终的效果就是类似这样的:
键入 sh => 按下 tab, 调用跟 should 有关的 Rspec snippet 菜单. 键入 shou => 按下 tab, 展开为 should, 也许你想自己输入自己的 should 定义,此时就不用再次键入 tab 了. 键入 should => 按下 tab, 调用跟 should 有关的 Rspec snippet 菜单,跟 sh 一样。
方案也介绍完了,有人会问,至于费这么大劲儿么?原先自带的不就挺好么?
我要说 我真的是很不喜欢老外的单词首字母简写的那种方案. 我承认这种的确简单,也许会快一点点。
可是就拿 Rspec 当中常见的的 should_not 来说 shnp, shnm, shnre, shnrt, shns, shnb, shkof, shbio, shnbc, shnbr, shne, 这还没完..., 这一大堆东西,你第一次使用,快捷键你记得住吗?你知道是什么意思吗?
而且很明显,这种方式及其容易冲突,有时候你不得不做妥协,例如两个 snippet 的首字母一样,你可能必须 让其中一个稍稍改变一下。这造成的后果是:也许你熟练了,觉得听自然,但是让别人用起来,会很晕。 怎么一会儿这样,一会儿那样啊?的确,使用这种方式,如果 snippet 太多,重复性高,你很难归纳出一个 有共性的东西,
我要的是:以后不管新加多少 snippet, 都可以采用此种方式来推断出你要采用的别名是什么的完整解决方案。
所以,最后的结论是:
所有的 snippet, 都采用完整的激活字符。例如:Ruby 中的 classify 方法,我需要扩展代码块的使用方式, 对应的 key 就是 classify, 只不过,无论你是键入 cla, 或者是键入 class, 在按下 tab, 都会自动补全为 classify, 然后再次 tab, 就自动补全 snippet 了。
针对就有高度相似的 snippet, 使用同样的 key, 例如,跟 render 有关的所有 snippet, 激活 key 都是 render, 在弹出菜单里选择需要的 snippet 就是了。