A Non-fashionable Development Environment for Emacs
0. Preliminary
本文简单介绍一种 Emacs 开发环境配置,它的特点是简单高效,依赖轻量,所有开发语言通用,大规模项目下也流畅。
1. General Requirements
通用的开发环境无非是满足以下要求:
-
语意补全(Semantic completion)
-
代码跳转(Jump to definition)
-
代码浏览(Code navigation)
-
文档提示(Document)
-
实时语法检测(Real-time syntax checking)
语意补全的要求很高,对我个人而言能力弱一些的补全也能接受。实时语法检测几乎无法完成,也不是很必要,尤其是大规模、分布式的项目。但是代码跳转和代码浏览是刚需。
1.1 Package list
语意补全 | 代码跳转 | 代码浏览 | 文档提示 | 语法检测 | |
---|---|---|---|---|---|
lsp-mode |
Y | Y | Y | Y | Y |
ctags + citre |
N | Y | Y | N | N |
cscope |
N | Y | Y | N | N |
要实现以上功能最著名的包莫过于 lsp-mode
,LSP 是 Language Server Protocol 的简称,是微软在开发 VS Code 时提出来的,现在已经成为了众多 IDE 配置开发环境的标准。lsp-mode
是 Emacs 使用 LSP 的模式。但这篇文章并不讲它,因为官方文档已经阐述得足够详细了,简单讲讲另外几个。
2. ctags + citre
[Universal Ctags]:ctags
最早是为 C 语言生成 tags(索引)的工具,经过发展目前支持 140 多种语言。它能够为源代码的变量、函数、类、结构体等构建索引,排序后写入到 tags 文件中。Universal Ctags 是目前仍在维护的分支。
[citre]:citre
是 Emacs 的 tags 前端。它主要借助 readtags
(From Universal Ctags)工具读取 tags 的内容展示到 Emacs 中。由于 tags 有序,readtags
用二分查找读取,速度是有保证的。
2.1 Installation of ctags
根据官网的文档编译安装即可,没什么特殊的要求,安装完成后可以得到:
$ ctags --version | head 1
Universal Ctags 5.9.0(2fd9b0c7), Copyright (C) 2015-2022 Universal Ctags Team
$ readtags --version
5.9.0
2.2 Installation of citre
这个包开箱即用,用官网推荐的、没几行的配置就能用:
(use-package citre
:defer t
:init
(require 'citre)
(require 'citre-config)
(global-set-key (kbd "M-.") 'citre-jump)
(global-set-key (kbd "M-,") 'citre-jump-back)
(global-set-key (kbd "M-P") 'citre-peek)
(setq
;; Set these if readtags/ctags is not in your PATH.
citre-readtags-program "/opt/homebrew/bin/readtags"
citre-ctags-program "/opt/homebrew/bin/ctags"
citre-auto-enable-citre-mode-modes '(prog-mode))
)
二进制文件的路径根据自己环境配置即可,最重要的三个操作:跳转定义、跳出定义和小窗查看,我分别绑定了M-.
M-,
和M-P
。
2.3 Generate tags
以 Linux 内核源码为例,它足够庞大,用来验证速度合适,在源码目录下执行:
$ ctags --exclude=build --languages=c,c++ -R
tags 文件会生成在本目录下:
$ du -h ./tags
1.0G ./tags
2.4 Now, it should look like…
citre-peek
:打开小窗查看函数的定义。
complete-at-point
:配合 company-mode
实现补全。
速度很快,可以用 in a blink 来形容。做不到语意补全,但也够用。
3. cscope
[cscope]:cscope
是一个和 ctags
类似的工具,原理也差不多,最早诞生于贝尔实验室。
[xcscope]:xcscope
是 Emacs 与 cscope
对接的包。
3.1 Generate cscope.files
在 Linux 源码目录下执行,命令的含义是找到所有 .c / .h / .cpp 文件(可以自定义加上别的)并对它们构建索引:
$ find ./ -name "*.c" -o -name "*.h" -o -name "*.cpp" | tee cscope.files
$ cscope -Rbkq -i cscope.files
3.2 Installation of xcscope
同样开箱即用的包:
(use-package xcscope
:init
(setq cscope-index-recursively 1)
:config
(cscope-setup))
3.3 Now, it should look like…
cscope-find-functions-calling-this-function
:找到所有调用该函数的地方(并不是简单的 Grep 或 Ag 搜索)。
可以看到搜索大约花费 1.7 秒,当然不同函数调用次数不同,时间会有一定的差异,但对于这么大的项目来说算得上合格(大部分时候我会配合 projectile-ag
来使用)。
Summary
-
两个包(citre 和 xcscope)的配置加在一起不超过 20 行,外加 3 行的执行命令,就能够丝滑地对 Linux 这样大的项目进行代码跳转和浏览,我觉得是相当高效率了。投入小,回报大,很符合降本增效的理念(:p)。
-
两个包还有很多其它功能,我所介绍的不足十分之一,用得好的话阅读代码会非常方便。当然它也有缺点,毕竟构建的索引是静态的,如果代码更新频繁还需要同时更新索引文件。
-
本文提供一种能够快速浏览陌生项目的思路。比如拿到一个新语言的项目,但一时间没那么快配置好对应的 LSP Client,那么本文提供的方法或许能帮上忙。