基于 Emacs 的 company 模式并配合 semantic 文法分析器,实现 Emacs 的 C/C++ 代码自动补全。
关于 Emacs 的代码自动补全
代码自动补全的功能,对于使用 Emacs 写代码的程序员而言其重要性不言而喻的,但是搜了一些所谓的 “Emacs 完美的 C++ 自动补全” 的文章,丝毫也没有感觉到美,反而只是让我不断的追忆 MS 的 Visual Stdio (.net) 的 C/C++ 代码自动补全功能,所以干脆就只当没这回事。一个本性就有些懒惰的人,最好不要面对太多容易令自己失望的东西。
"在 Emacs 下用 C/C++ 编程" 较为详细地讲述了使用 Emacs 内置的 hippie-expand 模式并配合 semantic 来解决 C/C++ 代码自动补全问题。在我知道 company-mode 项目之前,个解决方案或许是最好的选择了。实际上,我不满意 hippie-expand + semantic 组合,主要是因为配置有些麻烦,并且补全的界面也有些丑陋。
Company-mode 是什么
Company-mode(Complete anything-mode)是 Emacs 的代码自动补全扩展包,它的主要工作是配合许许多多的处理后端来实现比较方便并且清晰的代码补全。可以在一份简短的视频中获得对 company-mode 的直观认识。
要使用 company-mode 来解决 Emacs 的 C/C++ 代码自动补全问题,可选择的处理后端(tag 工具)有 etags、gtags 和 semantic。由于我之前从未用过这些工具,所以只好对比了一些资料并依赖直觉选择了 semantic,但愿未有选错
如果面对的问题是 c/c++ 之外的代码补全,那么 company-mode 扩展包中提供的那些后端或许可以满足大部分需求了,这意味着不用像 C/C++ 那样折腾。
安装所需要的软件包
炫耀一下我用的 gentoo 可以很方便地安装 company-mode 和 semantic(当然你也有足够的理由来炫耀你的系统):
$ echo "app-emacs/company-mode semantic" | sudo tee -a /etc/portage/package.use
$ sudo emerge company-mode
由于开启了 company-mode 的 "semantic" USE 标识,会自动依赖安装 cedet(它包含 semantic)。
配置,配置……很容易令人厌倦
要在 Emacs 中生活的好,前提是得好好学习,天天配置。我这个 Emacs 世界的穷人,却每天都希望眼前不要出现那些括号套括号的代码,大有穷且益坚之势。
因为穷,所以也很容易感恩。Gentoo 在安装 emacs 的扩展包之后,会在 /usr/share/emacs/site-gentoo.el 文件中自动添加所安装扩展包的加载代码,因此我可以在自己的 .emacs 配置文件中省却类似以下的代码:
(load "/usr/share/emacs/site-lisp/cedet/common/cedet" nil t)
(add-to-list 'load-path "/usr/share/emacs/site-lisp/company-mode")
(autoload 'company-mode "company" nil t)
真好。
下面,先来配置一下 semantic,目的是让它可以帮助我们更好的分析出现在 Emacs 中的 C/C++ 代码,从中提取出那些有用的 tag。将下面代码写入个人的 .emacs 配置文件中。
(setq semanticdb-default-save-directory "~/.emacs.d/semanticdb")
(semantic-load-enable-code-helpers)
上面的第 1 行代码是设定 semantic 生成的 tag 文件保存目录(请手动建立该目录)。如果不设置这个目录的话,据说 semantic 会将生成的 tag 文件放在当前的工作目录。第 2 行代码是设置 semantic 分析代码文法的方式,据说共有 5 种级别的分析方式,详见“在 Emacs 中使用 semantic”,对于 C/C++ 代码自动补全问题而言,选择 semantic-load-enable-code-helpers 没错。
配置完 semantic 之后,紧接着就是配置 company-mode 了,配置代码只有一行:
(setq company-idle-delay t)
这行代码是告诉 company-mode 扩展包,在弹出自动补全窗口之时莫要犹豫。如果不是很苛求自动补全窗口的弹出速度(假如你不认为 1 秒种有多么长),这行配置代码完全可以不需要的。
这令人厌倦的配置工作终于做完了,好累
体验一下 company-mode
现在,可以用 emacs 编辑一份 C 程序源文件,内容大致如下:
#include
typedef struct tagPoint {
double x;
double y;
double z;
} Point;
int main (void)
{
Point *pt = (Point *) malloc (sizeof (Point));
pt->x = 0.0;
pt->y = 0.0;
pt->z = 0.0;
return 0;
}
在打开 C 文件之后,需要使用 “M-x company-mode" 命令开启 company-mode,这样自动补全才可以工作。下面看看我在编辑代码过程中的截图:
感觉很舒服。所以再花点时间进一步配置一下。
只针对 C/C++ 的配置
因为只是想在 C/C++ 代码编辑过程中启用 company-mode 和 semantic,所以前面所讲的配置虽然可以工作,但是有点铺张浪费。可以利用 Emacs 的 hook 函数,将上述配置代码插入 C/C++ 编辑模式中。这样,只有在使用 Emacs 编辑 C/C++ 源文件时,有关 company-mode 和 semantic 的配置才会生效。
现在请冷酷将前面的配置代码从 .emacs 文件中清除掉,换上下面稍微有点复杂的配置。
(add-hook 'c-mode-hook
(lambda ()
(setq semanticdb-default-save-directory "~/.emacs.d/semanticdb")
(semantic-load-enable-code-helpers)
(setq company-idle-delay t)
(company-mode)))
上述配置是针对 C 语言的模式。如果你是用 C++ 模式,那么就将 c-mode-hook 替换为 c++-mode-hook 吧。
上述配置代码的作用是定义了一个匿名的 elisp 函数(lambda 函数),然后这个函数插入到 c-mode-hook 的函数列表中。当 Emacs 编辑 C 文件时,便会自动调用那个 lambda 函数,从而 semantic 和 company-mode 的一些设置代码开始工作,最后并自动启用 company 模式。