C/C++開発で有用なauto-complete-clang-asyncパッケージの使い方をまとめました。
Table of Contents
1 auto-complete-clang-asyncパッケージとは
C/C++のヘッダファイルを解析し補完候補を表示するemacsパッケージです。
独自のインクルードパスを追加することで、標準ヘッダファイル以外のものでも補完候補を表示することが可能になります。
補完候補の表示にはclang-completeというコマンドが必要になります。
2 auto-complete-clang-asyncパッケージのインストール
M-x package-list-packages経由でインストールします。
auto-complete-c... 0.5 installed Auto Completion source for clang for GNU Emacs
3 clang-completeのインストール
emacs-clang-complete-asyncのGitHubからソースコードを取得します。
3.1 Ubuntu 14.04
aptでClang/LLVMのdevelパッケージをインストールしてclang-completeをビルドします。
$ sudo apt-get install -y clang llvm-dev libclang-dev $ git clone https://github.com/Golevka/emacs-clang-complete-async $ cd emacs-clang-complete-async $ make $ cp clang-complete ~/bin/
3.2 OSX
XCodeに含まれるClangではヘッダファイルが不足している為、HomebrewでClang/LLVMをビルドしてインストールし、clang-completeをビルドします。
Clang/LLVMのビルドに時間が掛かります。
$ brew install --with-clang --all-targets --rtti --universal --jit llvm $ git clone https://github.com/Golevka/emacs-clang-complete-async $ cd emacs-clang-complete-async $ make LLVM_CONFIG=/usr/local/opt/llvm/bin/llvm-config CC=/usr/local/opt/llvm/bin/clang $ cp clang-complete ~/bin/
4 .emacsの設定
auto-complete-clang-asyncをロードした後で、c++-modeのフックスクリプトに値を設定します。
(require 'auto-complete-clang-async)
(add-hook 'c++-mode-hook
'(lambda()
(setq ac-clang-complete-executable "~/bin/clang-complete")
(setq ac-sources '(ac-source-clang-async))
(ac-clang-launch-completion-process)))
以上の設定でclang-completeに組み込まれているインクルードパスを探索した補完候補の表示が可能になります。
5 独自のインクルードパスの設定
独自にインクルードパスを追加している場合はカスタム変数のac-clang-cflagsを設定してac-clang-update-cmdlineargsを呼ぶか、ac-clang-set-cflagasを呼ぶ必要があります。
5.1 .emacsでcustom-set-variablesを用いる
.emacsでcustom-set-variablesを用いた場合はac-clang-udpate-cmdlineargsは不要です。
(custom-set-variables
'(ac-clang-cflags '("-I/path/to/include"))
)
5.2 ac-clang-set-cflagsを用いる
この関数はreadでCFLAGSの値を入力します。
M-x ac-clang-set-cflags New cflags: -I/path/to/include
5.3 ソースツリーにelispファイルを置いて独自のインクルードパスを設定する
.dir-localsを用いることでタブ幅などをソースツリー独自の設定にすることができます。
しかし、.dir-localsではac-clang-cflagsの値をどうもうまく設定できないようです(custom-set-variablesの値は設定できない?)。
いろいろ探してみると、StackExchangeに素晴らしい回答が。
.emacsに以下の記述をしておき、各ソースツリーに.setting.elを用意しておくことで、各ソースツリーで独自に適用するelispを書くことができるようになります。
ただし、find-file-hookはc++-mode-hook等の後に動作します(.setting.elでcustom-set-variableを実行してもelispの初期化処理は終わっているので無意味です)。
(defun recursive-load-dir-settings (currentfile)
(let ((lds-dir (locate-dominating-file currentfile ".settings.el")))
(when lds-dir
(progn
(load-file (concat lds-dir ".settings.el"))
(recursive-load-dir-settings
(file-truename(concat lds-dir "..")))))))
(defun load-dir-settings()
(interactive)
(when buffer-file-name
(recursive-load-dir-settings buffer-file-name)))
(add-hook 'find-file-hook 'load-dir-settings)
ソースツリーに以下の.setting.elを置きます。
インクルードパスは.setting.elのあるディレクトリからの相対パスです。インクルードパスが複数ある場合はlist内に追加してください。
(when (equal major-mode 'c++-mode)
(setq ac-clang-cflags
(list (concat "-I" (expand-file-name
(concat lds-dir "path/to/include")))))
(ac-clang-update-cmdlineargs)
(print ac-clang-cflags)
)
この方法が一番しっくりきました。
6 実行例
以下の構成のソースツリーを用意します。
$ tree .
.
|-- include
| `-- myclass.h
`-- src
`-- main.cc
2 directories, 2 files
.settings.elにincludeディレクトリのパスを通します。
$ cat .settings.el
(when (equal major-mode 'c++-mode)
(setq ac-clang-cflags
(list (concat "-I" (expand-file-name
(concat lds-dir "include")))))
(ac-clang-update-cmdlineargs)
(print ac-clang-cflags)
)
myclass.hの中身は以下の通りです。getValueメソッドを定義してます。
$ cat include/myclass.h
#ifndef __MYCLASS_H
#define __MYCLASS_H
class myclass {
private:
int mValue;
public:
myclass(int value) : mValue(value) {}
int getValue() { return mValue; }
}
#endif /** __MY_CLASS_H */
この状態でmyclassの変数で補完候補が表示されます。
変数を定義する際にmyclassというクラス名も補完候補として利用してます。
iostreamのライブラリも補完候補として表示されます。