tasuwo blog

EmacsにおけるC,C++の環境を整える

| Comments

大昔に取り組んでいた自作OS、せっかくなので再挑戦してみようと考えた.CLionとかそこらへんのIDEを使用しても良かったけれど,せっかくだからEmacs で C, C++ の環境を整えるたいので,メモしとく.
基本的にC/C++ Development Environment for Emacsに全部書いてあったのでつまんでみる.

helm+helm-gtags もしくは ggtags を使う.
自分は helm-gtags を使うことになるだろう.

1.GTAGS

プロジェクトのルートディレクトリで gtags コマンドを実行すると,以下のファイル群が生成されるはず.

1
2
3
4
$ cd /path/to/project/root
$ gtags
$ ls G*
GPATH   GRTAGS  GTAGS

それぞれ以下の情報を保持している.

  • GTAGS : 定義
  • GRTAGS : 参照
  • GPATH : パス名

2.基本操作

作業を快適にするために把握しておくべきEmacsにおける基本操作は以下

コマンド コマンド名 概要
C-M-f forward-sexp 閉じカッコの前に行く
C-M-b backward-sexp 閉じカッコの後ろに行く
C-M-k kill-sexp 閉じカッコ内を削除する
C-M-,C-M-@ mark-sexp 閉じカッコ内を選択する
C-M-a beginning-of-defun 関数の前に行く
C-M-e end-of-defun 関数の後ろに行く
C-M-h mark-defun 関数を選択する

3.定義参照

3.1.バッファ内参照

ggtags-mode

Imenuを使用する
(setq-local imenu-create-index-function #'ggtags-build-imenu-index)

helm

moo-jump-localを使用する

3.2.プロジェクト内参照

ggtags-mode

コマンド コマンド名 概要
M-. ggtags-finde-tag-dwim ・定義にポイントしていれば参照先を表示する
・参照にポイントしてれば定義を表示する
・include ヘッダーをポイントしていればそのヘッダーを表示する
・その他の場所であれば定義,参照一覧が表示され,絞り込みができる
M-, pop-tag-mark ジャンプ元へ戻る
M-n, M-p 候補内移動
M-g s 候補内検索

helm-mode

コマンド コマンド名 概要
M-. helm-gtags-dwim ggtags-find-tag-dwim と一緒
M-, tags-loop-continue pop-tag-mark と一緒
C-j helm-gtags-select 空白部分で M-. するのと一緒.定義や山椒を一覧から絞り込み&ジャンプできる

4.参照元ジャンプ

gtags-mode

ggtags-find-reference, ggtags-find-tag-dwimを使う

helm-gtags

コマンド コマンド名 概要
C-c g r helm-gtags-find-rtag ・関数内で呼び出したら,その関数についての参照先を検索する
・関数名上で呼び出したら,参照先のリストを表示する
・変数名にポイントしていたら,なにもしない
C-c g s htlm-gtags-find-symbol 変数名ポイント時に参照元を検索する
C-c g a htlm-gtags-tagas-in-this-function 現在の関数が参照する関数一覧

5.ファイル検索

ggtags-mode

ggtags-find-file

helm-gtags

helm-gtags-find-files

正直Projectile使ったほうが良いとのこと.

6.過去に訪れたタグへジャンプ

ggatgs-mode

ggtags-view-tag-history(C-c g h)

helm-gtags

helm-gtags-show-stack

7.Speedbar

ソースツリーを見れるパッケージ.ただのソースツリーではなくて,戻り値や関数なども一覧できるのが便利っぽい.

コマンド 操作
SPC 子ノードを開く
RET ノードを別ウインドウで開く
U 親ノードへ移動
n,p ノードを上下移動
M-n,M-p 現在の階層内でノードを上下移動
b Speedbarのバッファリストに戻る
f ファイルリストに戻る

7.1.sr-speedbar

Speedbarを便利にするパッケージ.

  • 起動/終了
    • sr-speedbar-open, sr-speedbar-toggle : 開く
    • sr-speedbar-cloe, sr-speedbar-toggle : 閉じる
  • 改善点
    • フレームの代わりにEmacs windowを使用する
    • C-x 1でSpeedbarを除くすべてのウインドウを削除する
    • C-x oでSpeedbarに移動するのを防ぐ(sr-speedbar-skip-other-window-pをtにする)

8.Company-mode (補完)

company-mode を使う.company-mode はEmacsのための補完フレームワーク.

1
2
(require 'company)
(add-hook 'after-init-hook 'global-company-mode)

8.2.使い方

コマンド 操作
M-n,M-p 候補移動
RET,TAB 候補決定
C-s,C-r,C-o 候補検索
M-(数値) 候補簡易選択
選択中候補のドキュメントを表示
C-w 選択中候補のソースコード表示

company-backendsで候補に使用するリソースを指定する.

8.3.C言語の補完

C言語でcompanyの補完を利用するためには,以下を記述する.

1
2
3
(delete 'company-semantic company-backends)
(define-key c-mode-map [(tab)] 'company-complete)
(define-key c++-mode-map [(tab)] 'company-complete)

上記の設定では,company-semanticを削除している.理由は後述.company-semanticについてはCEDITの項で詳しく説明する.

companyの補完として,以下の二つが働く.

8.3.1.company-clang

補完候補の取得のためにclangを使用する.プロジェクトではなく,ヘッダファイルによって補完を行う.デフォルトではcompany-clangcompany-semanticのサブセットであるため,上記設定を行っていれば他に特別な設定はいらない.
上記せて血でcompany-semanticを削除したのは,そうしないとcompany-completecompany-clangではなくcompany-semanticを使用してしまうため.これは,company-backends内の優先度がそうなっているため生じる.
補完候補をプロジェクト内から取得するためには,.dir-locals.elをプロジェクトルートに配置する必要がある.

1
2
((nil . ((company-clang-arguments . ("-I/home/<user>/project_root/include1/"
                                     "-I/home/<user>/project_root/include2/")))))

helmを使っているなら,C-x C-fによるファイル検索中に,対象ファイル選択状態からC-c iによって絶対パスを挿入できる.
nilを指定すると設定をすべてのサブディレクトリ,ファイルに適用し,non-nilであれば設定を適用するメジャーモードを指定できる.company-clang-argumentsはインクルードパスを指定するリストである.

8.3.2comapny-gtags

GNU GlobalGTAGSから補完候補を取得する.プロジェクトによる補完を行うことができる.

8.4.ヘッダーの補完

プロジェクト内のヘッダーを補完したいなら,company-c-headersを使用する.以下のようにcompany-backendsに追加すれば良い.

1
(add-to-list 'company-backends 'company-c-headers)

C++でヘッダーの補完を行いたいならば,パスを追加する必要がある.company-c-headerはシステムのインクルードパスとして/usr/include//usr/local/include/しか含んでいない.例としては以下のように追加する.

1
(add-to-list 'company-c-headers-path-system "/usr/include/c++/4.8/")

9.CEDET

CEDETはCollection of Emacs Development Environment Toolsの略称.CEDETのデメリットは,Emacs Lispで書かれているため,Emacsのパフォーマンスに影響すること.23.2以降のEmacsにはマージされているので,インストールの必要はない.
最新版は以下のようにダウンロードすれば良い.

1
2
3
4
5
$ git clone http://git.code.sf.net/p/cedet/git cedet
$ cd cedet
$ make # wait for it to complete
$ cd contrib
$ make

Emacs からロードする.
1
2
(load-file (concat user-emacs-directory "/cedet/cedet-devel-load.el"))
(load-file (concat user-emacs-directory "cedet/contrib/cedet-contrib-load.el"))

9.1.Semanticマイナーモード

Semanticは,ソースコードパーサを利用して構文を考慮した補完を行ってくれるパッケージ.

9.1.1セットアップ

1
2
3
4
5
6
7
(require 'cc-mode)
(require 'semantic)

(global-semanticdb-minor-mode 1)
(global-semantic-idle-scheduler-mode 1)

(semantic-mode 1)

9.1.2.semantic-mode

Semantic-modeでは,Emacsは現在のバッファをパースする.シンボルにカーソルを合わせるとsemanticはすべてのincludeファイルを読みに行くので,たまに時間がかかる.しかし一回パースすれば終わりなので,問題はない.

9.1.3.パスの追加

Semantic のデフォルトのインクルードパスはsemantic-dependency-system-include-pathに格納されており,追加したい場合は以下のようにする.

1
2
3
(semantic-add-system-include "/usr/include/boost" 'c++-mode)
(semantic-add-system-include "~/linux/kernel")
(semantic-add-system-include "~/linux/include")

9.1.4.company-modeにおけるSemantic-mode

company-modeにはcompany-semanticコマンドがあり,これがSemanticDBを補完候補の取得に利用する.company-semanticの良いところは,semantic-ia-complete-symbolが改善されているところ.元は1文字以上タイプしていなければ補完を検索してくれなかったが,company-semanticではプレフィクスなしで補完してくれる.

  • global-sematicdb-minor-mode
    • パース結果をキャッシュする.キャッシュ結果はsemanticdb-default-save-directory変数内のパスに保存されるが,デフォルトでは~/.emacs.d/semanticdbいかに保持される
  • global-semantic-idle-scheduler-mode
    • このモードが有効になっていると,バッファーが期限切れになっていた時,ユーザがタイプしていない間にパーサをし直す.これがオフだと,バッファはコマンドによって手動でパースし直さなければならない

9.2.CEDETのその他の機能

Semanticがソースコードをパースし作成したデータベースは,コードの補完の他にも様々な使い道がある.コードナビゲーションや定義元・参照元ジャンプなど.

9.2.1.Senator

CEDETの一部で,SEmainticNAvigaTORの略称.

9.2.2.デバッグ

GDBとかGUDとかがあるらしい.

あとは気が向いたら.

Comments