打造 Mac OS 的 C++ IDE(Emacs 篇)

IDE 介绍

  本篇文章将从零开始,介绍如何搭建一个好用的 C++ IDE,主要特性:

  • 支持代码语义跳转。
  • 支持代码自动补全。
  • 支持代码语法检查。
  • 支持 cmake。

通用配置

  下面是一些比较有用的设置,打开~/.emacs文件,写入以下内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
;; 启动时不显示信息
(setq inhibit-startup-message t)
;; 显示行号
(global-linum-mode t)
;; 自动补全括号
(electric-pair-mode 1)
;; 按 Ctl-Enter 开始选中文本
(global-set-key (kbd "C-<return>") 'set-mark-command)
;; 设置代码风格
(c-add-style "my-style"
'("stroustrup"
(indent-tabs-mode . nil) ; 使用空格缩进而不是 Tab
(c-basic-offset . 4) ; 使用四个空格缩进
(c-offsets-alist . ((inline-open . 0)
(brace-list-open . 0)
(statement-case-open . +)))))
(defun my-c++-mode-hook ()
(c-set-style "my-style") ; use my-style defined above
(auto-fill-mode)
(c-toggle-auto-hungry-state 1))
(add-hook 'c++-mode-hook 'my-c++-mode-hook)
(add-hook 'c-mode-hook 'my-c++-mode-hook)

软件源设置

  Emacs 24开始支持包管理器,为了加快软件包的下载速度,可以设置国内的软件源。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
;; 设置软件源
(require 'package)
(setq package-archives '(("gnu" . "http://elpa.emacs-china.org/gnu/")
("melpa" . "http://elpa.emacs-china.org/melpa/")))
(package-initialize)
;; 要安装的软件包列表
(setq package-list '(rtags company-rtags clang-format cmake-ide
company irony company-irony markdown-mode
company-irony-c-headers monokai-theme
flycheck flycheck-irony flycheck-rtags flycheck-irony yasnippet))
(unless package-archive-contents
(package-refresh-contents))
;; 安装列表中尚未安装的软件包
(dolist (package package-list)
(unless (package-installed-p package)
(package-install package)))

语义跳转

  在 Mac OS,我们可以用 brew 去安装软件,首先更新一下软件源:

1
$ brew update

  接着安装 rtags,rtags 是可以实现 C++ 语义跳转的工具。安装 rtags 之前需要安装 LLVM 和 cmake:

1
2
$ brew install llvm --with-libcxx --with-clang --without-assertions --with-rtti
$ brew install cmake

  在我的电脑上,llvm的安装位置是/usr/local/Cellar/llvm/4.0.0_1/。接着是安装 rtags:

1
2
3
4
5
$ git clone --recursive https://github.com/Andersbakken/rtags.git
$ cd rtags
$ cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=1 -DLIBCLANG_LLVM_CONFIG_EXECUTABLE=/usr/local/Cellar/llvm/4.0.0_1/bin/llvm-config .
$ make
$ make install

  还可以安装 clang-format,它可以格式化代码风格:

1
$ brew install clang-format

  在.emacs文件中加入 rtags 的配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
;; 启动 company 模式
(require 'company)
(add-hook 'after-init-hook 'global-company-mode)
(require 'rtags)
(require 'company-rtags)
(setq rtags-completions-enabled t)
(eval-after-load 'company
'(add-to-list
'company-backends 'company-rtags))
(setq rtags-autostart-diagnostics t)
(rtags-enable-standard-keybindings)
;; 设置快捷键
(define-key c-mode-base-map (kbd "M-.") (function rtags-find-symbol-at-point))
(define-key c-mode-base-map (kbd "M-,") (function rtags-find-references-at-point))
(define-key c-mode-base-map (kbd "M-;") (function rtags-find-file))
(define-key c-mode-base-map (kbd "C-.") (function rtags-find-symbol))
(define-key c-mode-base-map (kbd "C-,") (function rtags-find-references))

  rtags 在解析项目时,需要一个编译时数据库文件compile_commands.json,这个文件描述了项目所用到的编译选项。如果项目是使用cmake编译的,那只需要给 cmake 提供一个-DCMAKE_EXPORT_COMPILE_COMMANDS=1参数,就可以生成compile_commands.json这个文件了。
  下面的是在一个项目中使用 rtags 的例子:

1
2
3
4
$ rdm &
$ cd /path/to/project/root
$ cmake . -DCMAKE_EXPORT_COMPILE_COMMANDS=1
$ rc -J .

  下面是一些快捷键的作用:

  • M-. 在符号的声明和定义的位置之间跳转。
  • M-, 查找项目中哪些地方引用到这个符号。
  • M-; 根据文件名,自动补全需要查找的文件。
  • C-. 根据符号的名称,查找所有定义这个符号名称的地方。
  • C-, 根据符号的名称,查找所有引用这个符号名称的地方。

语法检查

  语法检查使用 flycheck,在.emacs加入下面的配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
(require 'flycheck)
;; 让 flycheck 支持 C++11
(add-hook 'c++-mode-hook
(lambda () (setq flycheck-clang-language-standard "c++11")))
;; 在 C 和 C++ 模式下打开 flycheck
(add-hook 'c-mode-hook 'flycheck-mode)
(add-hook 'c++-mode-hook 'flycheck-mode)
(require 'flycheck-rtags)
(defun my-flycheck-rtags-setup ()
(flycheck-select-checker 'rtags)
(setq-local flycheck-highlighting-mode nil) ;; RTags creates more accurate overlays.
(setq-local flycheck-check-syntax-automatically nil))
;; c-mode-common-hook is also called by c++-mode
(add-hook 'c-mode-common-hook #'my-flycheck-rtags-setup)

自动补全

  自动补全使用 irony,打开~/.emacs文件,写入以下内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
;; 配置 irony 模式
(require 'irony)
(add-hook 'c++-mode-hook 'irony-mode)
(add-hook 'c-mode-hook 'irony-mode)
(add-hook 'objc-mode-hook 'irony-mode)
(defun my-irony-mode-hook ()
(define-key irony-mode-map [remap completion-at-point]
'irony-completion-at-point-async)
(define-key irony-mode-map [remap complete-symbol]
'irony-completion-at-point-async))
(add-hook 'irony-mode-hook 'my-irony-mode-hook)
(add-hook 'irony-mode-hook 'irony-cdb-autosetup-compile-options)
(require 'company-irony)
(add-hook 'irony-mode-hook 'company-irony-setup-begin-commands)
(setq company-backends (delete 'company-semantic company-backends))
(require 'company-irony-c-headers)
(eval-after-load 'company
'(add-to-list
'company-backends '(company-irony-c-headers company-irony)))
(setq company-idle-delay t
company-minimum-prefix-length 2
company-show-numbers t
company-tooltip-limit 20
company-dabbrev-downcase nil)
(require 'flycheck-irony)
(eval-after-load 'flycheck
'(add-hook 'flycheck-mode-hook #'flycheck-irony-setup))


  除了上面的配置之外,还需要编译 irony,在 Emacs 中运行命令M-x irony-install-server,将出现类似于下面的内容:

1
2
cmake -DCMAKE_INSTALL_PREFIX=/Users/senlin/.emacs.d/irony/ /Users/senlin/.emacs.d/elpa/irony-20161102.1337/server && cmake --build . --use-stderr --config \
Release --target install

  直接运行上面的命令,如果出现错误,可以试着给 cmake 增加下面的参数,然后再运行一次:

1
-DLIBCLANG_INCLUDE_DIR=/usr/local/Cellar/llvm/4.0.0_1/include -DLIBCLANG_LIBRARY=/usr/local/Cellar/llvm/4.0.0_1/lib/libclang.dylib

  如无意外,那么就成功编译好了 irony 了。


  另外可以使用 yasnippet 自动生成有用的代码片段,首先需要将代码片段下载到本地:

1
$ git clone https://github.com/AndreaCrotti/yasnippet-snippets.git ~/.emacs.d/yasnippet-snippets/

  接着在 Emacs 的配置文件写入:

1
2
3
4
(require 'yasnippet)
(add-to-list 'yas-snippet-dirs "~/.emacs.d/yasnippet-snippets/")
(yas-global-mode 1)
(yas-reload-all)

  使用 Emacs 打开一个.cpp文件,在文件中输入main,然后按 Tab 键补全,如无意外,将出现:

1
2
3
4
int main(int argc, char *argv[])
{
return 0;
}

外观配置

  fantasque-sans 是一款非常好看的字体,可以在这里安装。安装好之后,在配置文件中加入:

1
2
;; 设置字体
(set-frame-font "fantasque sans mono:pixelsize=14")

  设置主题:

1
(load-theme 'monokai t)

  开启透明窗口:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
(global-set-key (kbd "<f10>") 'loop-alpha)
;当前窗口和非当前窗口时透明度
(setq alpha-list '((85 75) (100 100)))
(defun loop-alpha ()
(interactive)
(let ((h (car alpha-list)))
((lambda (a ab)
(set-frame-parameter (selected-frame) 'alpha (list a ab))
(add-to-list 'default-frame-alist (cons 'alpha (list a ab))))
(car h) (car (cdr h)))
(setq alpha-list (cdr (append alpha-list (list h))))))
;启动窗口时时自动开启窗口半透明效果
(loop-alpha)

参考资料