Emacs における C, C++ 系モードのインデント設定

C, C++ などのコードを Emacs で書いていて、 「インデントを自動でやってくれるのはいいけど、 自分が望んでいるものと違う」 ということはないでしょうか?
C, C++ を始め、 Java 、 JavaScript 、 C# などのモードは cc-mode を元に作られています。 この cc-mode ではインデントを細やかに設定できる分、やり方が少しわかりづらいです。
今回はこの C, C++ 言語系のモードでのインデントの設定方法について説明したいと思います。

また、インデントの設定が適切にできていれば、 {, ; などのキーを入力した時に自動で改行、インデントしてくれるという便利な機能も使えます。

設定方法とバッファーローカル変数

最初に cc-mode 関連の設定を行う方法について説明します。 Emacs で設定を行う場合には 2 通りの方法があります。

カスタマイズ

cc-mode 関連の設定項目はカスタマイズでは C グループに属するので、次の手順で設定します。
  1. M-x customize-group [RET] c で C group のカスタマイズ画面を開く
  2. 設定したい項目を変更
  3. 設定([Set ..])、保存([Sava ..])

設定ファイルの記述とバッファーローカル

カスタマイズを使わない場合は設定ファイルに記述するのですが、 ここで注意点があります。
それは設定する変数がバッファーローカルかどうかという点です。 通常、変数に設定した値は emacs 全体で共通に使われますが、 バッファーローカルな変数はバッファーごとに固有の値を持ちます。
次章で紹介する機能の ON/OFF 的な設定値は通常の変数ですが、 インデントの設定値の多くはバッファーローカルな変数となっています。

バッファーローカルな変数を設定ファイルで設定する場合には、モードのホックを使って バッファーを開いた時に設定する必要があります。
;; init.el だけに設定されてしまう
(setq c-basic-offset 4)

;; ホックを使った設定
(defun my-c-c++-mode-init ()
  (setq c-basic-offset 4)
  )
(add-hook 'c-mode-hook 'my-c-c++-mode-init)
(add-hook 'c++-mode-hook 'my-c-c++-mode-init)
面倒かもしれませんが、インデント量のような値はモードごとに変えたいため、 このようになっています。


また、カスタマイズで設定した値はバッファーローカルにならないということも注意する必要があります。
これはバッファーローカルな変数でも全てのモードで共通でも構わないという時には楽ですが、 逆にモードで個別に設定したいという場合にはカスタマイズからは設定できないようになっています。


この記事では基本的にカスタマイズ、設定ファイルの両方を説明していますが、 設定する場合は次のような方針で行って下さい。
  • バッファーローカルではない変数(機能の ON/OFF など)
    • カスタマイズ、設定ファイルのどちらでも可
  • バッファーローカル(インデント関連の設定など)
    • 共通した値を使用 → カスタマイズ
    • モードごとに設定 → 設定ファイル
ただし、設定ファイルからであればどの変数でもバッファーローカルにすることは可能です。
(defun my-c-c++-mode-init ()
  ;; 変数をバッファーローカルに
  (make-local-variable 'c-tab-always-indent)
  (setq c-tab-always-indent nil)
  )

インデントの設定を使う Emacs の機能

インデントのスタイルを使う機能を大きく分けると 2 つです。
  1. オフセット(インデント量)を使う機能
  2. 自動改行、インデント

オフセット(インデント量)を使う機能

[Tab] によるインデント、 C-M-\ のインデント整形、 C-j の改行 + インデントなどオフセット(インデント量)を使う機能です。 オフセットはコーディングする上で最低限していないといけない設定でしょう。

[Tab] キーの挙動の変更

[Tab] キーでインデントですが、これは C Tab Always Indent(c-tab-always-indent) の設定を変えると、 挙動を変えることができます。

動作
t 常にインデント
nil 左端(文字の前)ではインデント、それ以外はタブの挿入
t, nil 以外 コメント、文字列などではタブ、それ以外はインデント

この値が t の場合、 Tab キーを押しても インデントをあわせるだけで、タブは挿入できません。 t, nil 以外( 1 など)の場合もコメントなど特定の領域以外はタブを挿入できません。
それらの設定でタブを挿入したい場合には C-q [Tab]などを使います。

c-tab-always-indent はバッファーローカルではないため、カスタマイズ、設定ファイルのどちらでも設定しても同じです。

カスタマイズ :
emacs_indent_tab_always_indent.png

設定ファイル :
(setq c-tab-always-indent t)

自動改行、インデント

{, ; などのキーを入力すると自動で改行、インデントをしてくれる機能です。 カスタマイズで設定するか、設定ファイルに以下を追記すると有効になります。
(setq c-auto-newline t)
これが有効になっているとモード行のモードを表すところに "/la" が付きます。 正確には "/a" の自動改行と "/l" の electric-state (インデント)は別なのですが、 ほぼセットだと思って下さい。
このモードで改行やインデントをさせずに { などを入力したい場合は C-q { といったように入力します。 特に : はネームスペース等でも使うので、"::"(コロン 2 つ)を入力する C-c : も用意されています。

この機能は { 等で改行するので、スタイルは改行位置などもちゃんと設定する必要があります。 自動改行を有効にしなければ、オフセット量だけでも十分ですが、 コーディングが非常に楽になる機能なので、ぜひ使ってみてください。

空白を一度に削除

ついでなので、 hungry-delete の機能も紹介します。
これは [Back Space] などで空白を削除すると 空白以外が出てくるところまで一度に削除してくれます。

以下の記述を追記していると有効になります。
(setq c-hungry-delete-key t)
有効になっているとモード行に "/h" が付きます。

スタイルの設定

インデントの設定では、まずベースとするスタイルを指定します。

スタイルの種類

選択可能なスタイルを次の表にあげます。 ただし、バージョンによって変わるかも知れないので、 正確には C-h v c-style-alist で確認して下さい。

スタイル 説明
gnu GNU スタイル
k&r C の古典 『 K&R 』のスタイル。
標準の ANSI C と文法自体少し違うので、最近ではほとんど使われてない。 本自体も今の版はこのスタイルではない。
bsd BSD/Allman スタイル。
Visual Studio のスタイルはこれがベース。
whitesmith 古くからある商用コンパイラー Whitesmiths のスタイル
stroustrup C++の作者であるストロヴストルップ『プログラミング言語C++』のスタイル
ellemtel 『Programming in C++, Rules and Recommendations』で規定されているスタイル
linux Linux カーネルのコードで使われているスタイル
java java-mode で使われる Java のコーディングスタイル
awk awk-mode で使われる AWK のコーディングスタイル。
python Python モードのスタイルではなく、 Python の C 拡張モジュールを書く際のスタイル
user ユーザー定義用の特殊なスタイル。(後述)

ここで挙げたもののうち、主要なものは Wikipedia に説明があります。

スタイルの設定方法

スタイルの設定はカスタマイズを使った方法で説明します。


C グループの C Default Style でモードごとのスタイルを設定します。 これはモードごとに対応するスタイルを設定する変数ですので、バッファーローカルではありません。
emacs_indent_style.png

C#、 JavaScript など後からパッケージで追加するようなモードは出てませんが、 各 lisp ファイル内で定義されているはずですので、ここで設定する必要はありません。


各モードに指定するスタイル名は前節のものを指定します。 インデントなど指定したスタイルで問題がなければ、それで設定は完了です。
そうでなければ、個別に設定していく必要があります。 例えば、 Visual Studio 風のスタイルにしたい場合、 "bsd" が近いのですが、他の設定もしないと綺麗に一致はしません。

基本オフセット量の設定

基本オフセットというのは、 1 段インデントをする際のサイズです。

カスタマイズの場合、C Basic Offset のパラメーターで設定します。
emacs_indent_basic_offset.png


c-basic-offset はバッファーローカルな変数ですが、カスタマイズで設定した場合には、 cc-mode を元にした全てのモードで同じ値が使用されます。
モード別で指定するためには設定ファイルでロード時に設定にします。
(defun my-c-c++-mode-init ()
  (setq c-basic-offset 4)
  )

(add-hook 'c-mode-hook 'my-c-c++-mode-init)
(add-hook 'c++-mode-hook 'my-c-c++-mode-init)

タブ幅

先ほどの基本オフセットというのはタブのサイズとは別ものです。 必ずしもタブ一つがインデントの一段というわけではありません。
タブ幅が 8 、 基本オフセットも 8 であれば 1 段でタブ1つですが、 タブ幅が 8 、 基本オフセットが 4 ならば 2 段でタブ 1 つとなります。

タブ幅を変更したいという場合は以前の記事を見て下さい。 なお、タブ幅(tab-width)もバッファーローカル変数です。

タブの代わりに空白

タブとインデントの関連でもう一つ補足しておきます。
indent-tabs-mode の値に nil を設定するとインデントの際にタブを使わずに、 空白(スペース)のみ使用するようにできます。 たまに 「タブは使うな」 といったコーディングルールがあったりするので、そういった場合に設定します。

カスタマイズでも設定できるのですが、 全モードで使われると影響が大きいので、 設定ファイルで設定した方がいいでしょう。
(defun my-c-c++-mode-init ()
  (setq indent-tabs-mode nil)
  )

;; 略 

インデント量の設定

インデントの状況別のオフセット量は C Offsets Alist(c-offsets-alist)で設定します。

emacs_indent_offsets.png

オフセット値

オフセットとして設定する値によく使うのは 3 つです。
意味
0 インデントなし
+ 1 段インデント
- 1 段インデントを戻す

この +, - で増減する単位が先ほどの c-basic-offset のサイズです。

これ以外にも数値、記号、関数など色々使えます。 その他の値については C-h v [RET] c-offsets-alist で確認して下さい。

Syntax

オフセット値は状況ごとに設定できるようになっています。 そのうちの幾つかを紹介します。
if, while ブロック
制御文のブロックです。スタイルごとに違いがでやすいところだと思います。
emacs_indent_offset_if.png
switch ブロック
制御文の中でも switch 文には case ラベルの設定もあります。
emacs_indent_offset_switch.png
クラス内関数
関数の定義のブロックは defun-open なのですが、 クラス内のインライン定義の場合は inline-open になります。
emacs_indent_offset_inline.png
上記以外の項目についても、以下のサイトに説明があります。

スタイルの構成

ここで、スタイルの中身について説明します。

スタイルは継承することができ、 構成は継承元のスタイル名と 変数-値 ペアのリストからなっています。
`("bsd" ;; <- 継承元スタイル
  ;; (変数 . 値) のリスト
  (c-basic-offset . 4)
  (tab-width . 4)
  (c-offsets-alist . ((inline-open . 0)
                      (substatement-open . 0)
                      (case-label . 0)))
  (c-hanging-braces-alist . ((inline-open         before after)
                             (block-open          before after)
                             (substatement-open   before after)))
  (c-cleanup-list . (defun-close-semi
                      list-close-comma
                      scope-operator
                      compact-empty-funcall)))
変数-値のリストを継承元から設定していき、同じ変数の場合は対象のスタイルの値で上書きされます。
例えば、 次節の c-hanging-braces-alist などは継承元で設定されていても、 それは全く使われず、今のスタイルのリストが使用されます。

ただし、 c-offsets-alist は特別でリストの中身も継承されます。
defun-open の値が継承元で設定されていて、今のスタイルで設定されていないとすると、 継承元の値が使われます。


個々のスタイルでどのような値が設定されていかについても C-h v c-style-alist で見ることができます。

user スタイル

c-offsets-alist にはもう一つ特別な点があり、 カスタマイズで設定した値は user スタイルの値として設定されます。

user スタイルはすべてのスタイルの継承元です。
そのため、カスタマイズで設定された値がすべてのモードにそのまま適用されることはありませんが、 設定値はスタイルが優先されます。 スタイルの設定から漏れているものをここで設定する感じです。


なお、設定ファイルからであれば、ロード時に値を変更することもできます。
(defun my-c-c++-mode-init ()
  (setq c-offsets-alist '((statement-case-open . +)
                          (case-label . 0)))
  )
また、自作のスタイルを作成することもできます。 自作のスタイルのサンプルはこの記事の最後にあげていますので、 そちらをご覧ください。

自動改行、インデントのための設定

機能で紹介した自動改行、インデントを使う場合、 改行位置などの設定も必要となります。

改行位置

{ などのキーを押した時に改行を入れるかどうかを指定するため 3 つの変数があります。
キー 変数名
{ } (ブレース) c-hanging-braces-alist
: (コロン) c-hanging-colons-alist
, ; (カンマ、セミコロン) c-hanging-semi&comma-criteria

emacs_indent_hanging.png


これらは対象ごとに before, after を指定したり、複雑な場合は関数で指定します。
substatement-open を例にすると次のようになります。
after
if (a == 0) {
    foo();
}
befor
if (a == 0) 
{   foo();
    bar(); }
befor after
if (a == 0) 
{
    foo();
}
設定する対象の名前は c-offsets-alist の対象の名前とほとんど同じなので、 そちらの説明をご覧下さい。


3 つの変数はどれもバッファーローカルなので、設定ファイルの場合はロード時に設定するようにします。
(defun my-c-c++-mode-init ()
  (setq c-hanging-braces-alist '((class-open before after)
                                 (class-close before)))
  )

改行位置(C#)

C# モード (csharp-mode) の場合、 { を押してインデントが上手くいかないことがあります。
これはインデントの設定があっていないのではなく、 csharp-mode では { のキー割り当てを拡張したコマンドにしていて、 それが上手く動いていないためです。
手っ取り早く治す場合、 { の割り当てを他のモードと同じ c-electric-brace にすると治ります。
(defun my-csharp-mode-init ()
  (local-set-key "{" 'c-electric-brace)
  )
(add-hook 'csharp-mode-hook 'my-csharp-mode-init)

Clean up

c-cleanup-list は "} else {" のようにつなげるかどうかや "foo ()" のように関数の前の空白を入れるかどうかなどの設定です。 これを設定していると自動インデントの際に空白、改行を削除(追加)してくれます。
emacs_indent_cleanup.png

Clean up の説明は、英語ですが Emacs のマニュアルがサンプル付きで分かりやすいです。 こちらの変数もバッファーローカルです。
(defun my-c-c++-mode-init ()
  (setq c-cleanup-list '(scope-operator
                         compact-empty-funcall))
  )

カスタムスタイル

デフォルトで用意されたスタイルに合わない場合、個別に設定していきます。 これを自分でスタイルを作ってまとめて設定することも可能です。
開発しているプロジェクトのコーディングスタイルがある場合は、 「カスタムスタイルを用意して、メンバーにそれを使ってもらう」 といった際に使えると思います。

サンプルを兼ねて、使いそうなカスタムスタイルを 2 つ挙げたいと思います。

Google コーディングスタイル

Google は C++ のコーディングガイドを公開しており、 そこの「書式」の章でインデントのスタイルも規定しています。 emacs_indent_google.png

そこでは Emacs 用の設定ファイル(google-c-style.el)も公開されています。 使いたい場合はリンクを [名前を付けてリンク先を保存] でダウンロードして下さい。

取得した lisp ファイルは load-path の通ったフォルダーに置き、 ~/.emacs.d/init.el に以下の記述を行います。
(defun my-c-c++-mode-init ()
  (require 'google-c-style)
  (google-set-c-style)
  ;; (google-make-newline-indent)
  )

(add-hook 'c-mode-hook 'my-c-c++-mode-init)
(add-hook 'c++-mode-hook 'my-c-c++-mode-init)
因みに google-make-newline-indent を記述すると [Enter] キーで改行とインデントを一度に行うようになります。
ただ、改行するときに C-j を押せばいいだけなので、 設定する必要はないと思います。

Visual Studio のスタイル

Windows で開発していると、自分以外のプロジェクトメンバーが Visual Studio を使っているという驚きの状況に陥ることがあります。 どうやら IDE でコード書いている人がいるというウワサは本当みたいです。

仕方がないので、私はインデントを Visual Studio 風に設定しています。
しかし、スタイルを "bsd" しても、他の設定もしないと一致しません。 そこで、 Google スタイルのようにまとめて設定するファイルを作成してみました。 リンク先をダウンロード([名前を付けてリンク先を保存])して、 パスの通ったフォルダーにおいて、以下の記述を ~/.emacs.d/init.el に記述して下さい。
(autoload 'vs-set-c-style "vs-set-c-style")
(add-hook 'c-mode-hook 'vs-set-c-style)
(add-hook 'c++-mode-hook 'vs-set-c-style)


関連記事
スポンサーサイト
Prev.    Category    Next 

Facebook コメント


コメント

コメントの投稿

Font & Icon
非公開コメント

このページをシェア
アクセスカウンター
アクセスランキング
[ジャンルランキング]
コンピュータ
43位
アクセスランキングを見る>>

[サブジャンルランキング]
プログラミング
3位
アクセスランキングを見る>>
カレンダー(アーカイブ)
プルダウン 降順 昇順 年別

05月 | 2017年06月 | 07月
- - - - 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 -


はてな新着記事
はてな人気記事
ブロとも申請フォーム
プロフィール

yohshiy

Author:yohshiy
職業プログラマー。
仕事は主に C++ ですが、軽い言語マニアなので、色々使っています。

はてブ:yohshiy のブックマーク
Twitter:@yohshiy

サイト紹介
プログラミング好きのブログです。プログラミング関連の話題や公開ソフトの開発記などを雑多に書いてます。ただ、たまに英語やネット系の話になることも。