最近在读《Linux/UNIX系统编程手册》,里面有一些系统调用 mac 不支持,所以还是得在 Linux 环境下面搞,于是又得折腾 emacs 的 c/c++ 开发环境,正好看见了 cmake-ide,features 是很不错的,看作者在 cppcon 的 lighting talk 也不错(作者对代码编辑器的需求跟我差不多),问题是作者自己展示的时候都翻车了,而我配置这个插件的时候,也没有成功。所以决定仔细读一下源代码。

细节

这个 package 的入口函数是 cmake-ide-setup 它把 cide--mode-hook 加到 c-mode-hookc++-mode-hook1。此外还有一个 cide--before-save 会被添加到 before-save-hook

cide--mode-hook 先把 cmake-ide-maybe-run-cmake 加到 find-file-hook, 然后是 cmake-ide-maybe-start-rdm。我们先看后者。

首先是 interactive2,无他,告诉我们可以交互调用并且没有需要手动输入的入参。然后判断是否存在 rtags(所以实际你没有装 rtags 的话这个函数就不用干活了),这个判断使用了 emacs 内置的 featurep 函数。然后判断你有没有设置 comp-db-file – 一个配置 rtags 的 json 文件,或者你有没有设置 project-dir,这两个条件任意成立一个,加上且安装了 rtags 的话3,进入下一步的逻辑。

然后我们用函数 cide--process-running-p 判断是否有 rdm 进程在跑,要是有,那这个函数也可以退出了,如果没有,那么我们就把 rdm 跑起来。

判断 rdm 有没有起来的时候,作者先用内置的 get-process 函数,然后又自己写了一个复杂的 cide--system-process-running-p4

(defun cide--system-process-running-p (name)
  "If a process called NAME is running on the system."
  (let* ((all-args (mapcar (lambda (x) (cdr (assq 'args (process-attributes x)))) (list-system-processes)))
         (match-args (cide--filter (lambda (x) (cide--string-match (concat "\\b" name "\\b") x)) all-args)))
    (not (null match-args))))

分析一下这段代码,关于 let* 可以参考这个回答,它的作用就是递归绑定变量名,这样,后面的变量可以使用前面的变量,如这里,match-args 绑定的时候使用了 all-args 的值。mapcar 将一个函数作用到列表上,获得一个新的列表。 list-system-processes 获取系统的 PID 列表,前面应该 lambda 比较复杂,它先用 process-attributes 获取对应进程的属性,然后用 assq 获取命令行参数,最后返回进程名5。最后就是找有没有名为 name 的进程。

如果判断 rdm 没有启动,那么先创建一个 buf(使用 get-buffer-create 函数)。

注释

  1. 其实这里还用到了 emacs lisp 的 sharp quote, 不过看 https://emacs.stackexchange.com/questions/3595/when-to-sharp-quote-a-lambda-expression 用不用是等价的。其实不加也行。但我自己在 scratch 里面运行的结果却好像是三者都相同,真是难解。 

  2. Just ‘(interactive)’ means pass no arguments to the command when calling interactively. –Emacs Manual 

  3. 这里用自然语言来描述一个 and 加一个 or 组合出来的 bool 表达式真是辛苦。 

  4. 个人感觉是因为这个 get-process 太难用了,我在 mac 上手动测试一下,基本判断不到有啥进程存在。 

  5. 实际上我在 mac 中,因为 process-attributes 总是不包含 args 属性,所以返回总是 nil。