2006-12-18

Emacs で C 言語プログラミングを始める人へのイントロダクション

Emacs エディターで C 言語のプログラムを書く人向けに、入門用の解説がないように思う。そこで、知っておくと便利な機能をまとめてみた。

読者は、Emacs の操作とカスタマイズが最低限できる人を対象にしている。つまり、C-x C-f といったショートカット・キーが使えて、.emacs の設定ファイルがいじれる人。各機能について、基本的な使い方とその効果、あと最低限の設定について書き出した。

目次

  1. ソースの色付け
  2. インデント
  3. アラインメント
  4. コメント
  5. info マニュアル
  6. スペル・チェック
  7. タグ・ジャンプ
  8. 関数名の補完入力
  9. コンパイルとエラー行ジャンプ
  10. ChangeLog ファイル

1. ソースの色付け

Emacs は、C 言語のソース・ファイルを解析して、iffor といったキーワードに対して、自動で色を付ける。

色付け

色を付けることでソースにメリハリが生まれ、可読性が上がる。また、スクリーン・ショットを見て分かる通り、コメントの閉じ忘れにも気付き易くなるメリットがある。

設定

Emacs 21 のユーザーは、以下のコードを .emacs に追加する。

;; 自動で色を付ける設定
(global-font-lock-mode t)

Emacs 22 以降では、デフォールトで色が付くので、設定の必要はない。

2. インデント

Emacs は C 言語のソースを解析して、構文に沿ったインデントを行なう。ユーザーは、ソース・コードを書く時に、インデントをどれだけ深くするか考える必要がない。インデントは、TAB キーに割り当てられている。

例えば次のコードで、TAB を打ってみる (TAB は、インデントしたい行の上なら、どこで打っても構わない)。

void foo (void)
{
    Foo();
  if (CONDITION){
  bar();
}
    Bar();
}

すると、下のように適切なインデントが為される。if 文の中は一つ深めのインデントになり、if の閉じ括弧も if と同じレベルにインデントされているのが分かる。また、インデントが深すぎた foo();bar(); が、ちゃんとインデントされていることも注目したい。

void foo (void)
{
  Foo();
  if (CONDITION){
    bar();
  }
  Bar();
}

インデントが C 言語の構文を解析してくれるおかげで、if 文の括弧の閉じ忘れなどに気がつき易くなるメリットがある。構文を理解しないインデントでは、こういうことは出来ない。

例えば、下のコードでは、二つ目の if 文の閉じ括弧を忘れてしまった。コードの書き手は、if 文を閉じたものと思い込んでいるので、else 文を一つ目の if 文と同じレベルに (手で) インデントしている。

void foo (void)
  if (CONDITION){
    bar();
    if (CONDITION II){
      ...
      barbar();
  } else {

Emacs のインデント機能を使っていれば、else 文が一つ深くインデントされ、前の if 文が閉じられていないことに気づける。

関数全体のインデント

if 文の条件文を見直すなどして、ブロックのインデントが大きく変わることがある (ブロック全体のインデントが一つ下がるなど...)。そのような場合、各行に対してインデントを行なうのは面倒。代わりに関数全体をインデントするコマンドを使おう。

C-c C-q (c-indent-defun)
関数全体をインデントし直す
自動インデント用の設定

次のコードを .emacs に追加すると、自動改行+インデント機能が加わり、幸せになれる。

(add-hook 'c-mode-common-hook
          '(lambda ()
             ;; センテンスの終了である ';' を入力したら、自動改行+インデント
             (c-toggle-auto-hungry-state 1)
             ;; RET キーで自動改行+インデント
             (define-key c-mode-base-map "\C-m" 'newline-and-indent)
))

3. アラインメント

Emacs には、連続する代入式などを整列 (alignment) させる機能がある。実行には、整列させたいコードをリージョンで囲み M-x align する。

例えば、次のソース・コードに対して M-x align すると...

  /* Initialize options */
  opt.encoding = AUTO;
  opt.output_format = PLAIN;
  opt.case_insensitive = false;
  opt.byte_insensitive = false;
  opt.count_escape = false;
  opt.merge = false;
  opt.overwrite = false;
  opt.print_0 = false;
  opt.print_sort = false;
  opt.sort_reverse = false;
  opt.verbose = false;

下記のように = 以降が同じ深さで揃えられ可読性が高まる。

  /* Initialize options */
  opt.encoding         = AUTO;
  opt.output_format    = PLAIN;
  opt.case_insensitive = false;
  opt.byte_insensitive = false;
  opt.count_escape     = false;
  opt.merge            = false;
  opt.overwrite        = false;
  opt.print_0          = false;
  opt.print_sort       = false;
  opt.sort_reverse     = false;
  opt.verbose          = false;

4. コメント

Emacs にはコメント・アウト用のコマンドが用意されている。通常使うのは、次の三つ。

M-; (comment-dwim)
コメント・コード /* */ を埋め込み、文脈に合わせてインデントを行なう。
M-x comment-region
リージョンをコメント・アウトする。
M-x uncomment-region
リージョンのコメントを解除する。

C 言語のコメント書式は、コメントのネストに向いていない。例えば次のコード:

  foo();  /* ホゲホゲする関数 */
  bar();  /* フガフガする関数 */

これを、foo(); の前の行に /* を、bar(); の次の行に */ を置いてコメント・アウトしてみやう。すると、次のようにハイライトされる。

コメント・アウト

見て分かるように、関数 foo(); の後ろにある */ でコメントが終わってしまっている。このコメント・アウトのやり方では、関数 bar(); はコメント・アウトされない。

コメントをネストさせる場合は、次のようにする。

/*
   foo();  /\* ホゲホゲする関数 *\/
   bar();  /\* フガフガする関数 *\/
*/

Emacs のコメント・アウト機能は、こうしたコメントのネストにも対応している。たいした手間ではないからと、手でコメント・アウトなどせず、積極的にエディターのコメント機能を使うようにしませう。

5. info マニュアル

Emacs には、C の標準関数のリファレンス (英語) を参照する機能がある。C-h C-i (Emacs 22 以降では C-h S) で、Describe symbols (default ***): と出てくるので関数名を入力、すると関数の説明を読むことができる。なお、*** にはカーソル下にある関数名がデフォールト値として入る。

スクリーン・ショットは、関数 strtol のヘルプを表示させた所。

info の参照

Emacs には、info と呼ばれるドキュメント・システムが搭載されており、各種マニュアルを読むことができる。ここで参照している info マニュアルは The GNU C Library Reference Manual (libc)。英語だけど、libc が提供している関数 (C の標準関数含む) を丁寧に解説している。

6. スペル・チェック

Emacs には ispell というスペル・チェッカーを呼び出す機能がある。よく使う ispell コマンドは以下の 3 つ。

M-$ (ispell-word)
カーソル下のワードのスペル・チェックを行なう。
M-x ispell-region
リージョンに対してスペル・チェックを行なう。
M-x ispell-comments-and-strings
コメントと文字列だけを対象にスペル・チェックを行なう。

ispell-comments-and-strings はバッファー内のコメントと文字列だけを対象にスペル・チェックを行なう。プログラムをリリースする前に、一度は実行するようにしませう。

ispell の使い方

スクリーン・ショットは、Charactor のタイポを ispell で修正している所。

ispell

別バッファーに修正候補が表示されるので、その中で正しい綴の番号 (or 記号) を入力する。スクリーン・ショットの場合、(0) 番目の Character が正しいので、0 を入力。

ispell では、この他に次のコマンドが使える。

  (スペース・キー)
一度だけ正しい単語として受け入れる
i
正しい単語として辞書に登録する
a
正しい単語として、このセッションの間受け入れる
r
正しい単語をユーザーが入力する
q
ispell を終了する
flyspell-mode

Emacs には、自動的に ispell を実行する flyspell-mode と呼ばれるマイナー・モードがある。このモードをオンにしておくと、辞書にない単語を入力したらその単語がハイライトされて、スペルミスを知らせてくれる。単語の修正には、その単語の上で M-$ (ispell-word) を実行する。

C 言語の編集中は、flyspell の実行範囲を文字列・コメント内に限定する flyspell-prog-mode を使う。設定は以下の通り。

(add-hook 'c-mode-common-hook
          '(lambda ()
             ;; flyspell-prog-mode をオンにする
             (flyspell-prog-mode)
))
関連ページ

新しい関数名や変数名を決める時に、ispell を使って英単語補完する方法もある。

7. タグ・ジャンプ

Emacs には、タグ・ジャンプと言って、関数の定義へとジャンプする機能がある。関数が、別のファイルでで定義されていたら、Emacs はそのファイルを開いてその関数定義部分へとジャンプする。

タグ・ジャンプと呼ばれるのは、この機能を使うために予め TAGS というタグ・ファイルを作ることに寄る。

TAGS の作成

タグ・ファイルを作成するには、コマンド・ラインで次のようにする。

$ etags *.[ch]

(上級者向け) もし、サブ・ディレクトリーをいくつか作っているなら、トップ・ディレクトリーで次のようにする。

$ find . -name "*.[chCH]" -print | etags -

TAGS ファイルは、新しく関数を定義する度に作り直す必要がある。Makefile に次のように書いておくと、make し直す度に自動的に TAGS ファイルが更新されるので楽。

SRC = ソース・ファイル1.c ソース・ファイル2.c ...
TAGS: $(SRC)
 etags *.[ch]
タグ・ジャンプの実行

タグ・ジャンプは M-. に割り当てられている。

関数の上で M-. すると、Find tag: (default カーソル下の関数名) と出るので、RET を打つ。これで、関数の定義へとジャンプする。

なお、タグ・ジャンプを行なう時、最初に一回だけ Visit tags table: (delaut TAG) と出てくる。どのタグ・ファイルを読み込むのか聞いているのだけど、普通はそのまま RET を打てばいい。

タグ・ジャンプに関するコマンドで、よく使うのは以下の通り。

C-x 4 .
別ウィンドウを開いて、タグ・ジャンプする。
C-x 5 .
別フレームを開いて、タグ・ジャンプする。
M-*
タグ・ジャンプする前の位置へ戻る。

8. 関数名の補完入力

Emacs は、TAGS ファイルを利用して、関数名を補完することができる。関数の最初何文字かを入力して M-TAB と入力する。すると、関数名の続きが補完入力される。もし、候補が複数ある場合は別バッファーに候補一覧が表示される。

補完入力を使うと、長い関数名をつけるも面倒でなくなるし、タイポもなくなる。

TAGS ファイルの作成方法については、前節「タグ・ジャンプの実行」を参照のこと。なお、関数の補完をする場合、最初に一度だけタグ・ファイルを読み込む必要がある。タグ・ジャンプを一度でも実行していれば、タグ・ファイルは読み込まれているけど、そうでない場合は次のコマンドを実行する。

M-x visit-tags-table
標準関数の補完入力

strtolfscanf のような C 言語の標準関数を補完入力する場合は、C-u M-TAB を使う。

C-u M-TAB は、libc のマニュアルから関数の補完を行なっている。従って、libc の提供する関数 (ex. ferror_unlocked) の補完も可能。

9. コンパイルとエラー行ジャンプ

Emacs 使いは、Emacs から離れることなくコンパイルを行なう。

コンパイル

コンパイルをしたくなったら、M-x compile と入力する。すると

Compile command: make -k

と出てくる。Makefile を書いていれば、そのまま RET を打つ。Makefile が何か分からない人は、make -k を消して、gcc ファイル名 と打つ。

スクリーン・ショットは、M-x compile を実行した所。

コンパイル

画面が二つに分かれ、コンパイル・ログが表示される。見ての通りエラーがある。

エラー行ジャンプ

C-x ` と打つと、エラーのある行にジャンプする。エラーを修正したら、再び C-x ` を打つ。すると、次のエラー行へとジャンプする。これをエラーが無くなるまで繰り返す。

ソースの修正が終わったら、再び M-x compile しませう。

Emacs には、このエラー行ジャンプ機能があるので、M-x goto-line (指定行ジャンプ・コマンド) の需要が小さい。

設定

コンパイルの度に M-x compile と入力するのは面倒臭い。ぼくは、次のコードを .emacs に入れて、C-c c から compile コマンドを実行するようにしている。

;; C-c c で compile コマンドを呼び出す
(define-key mode-specific-map "c" 'compile)
関連ページ

10. ChangeLog ファイル

Emacs には ChangeLog (変更履歴ファイル) を書くための機能も揃わっている。

変更を加えた関数にカーソルを持っていき、C-x 4 a と打つ。すると、ChangeLog ファイルにその関数名が自動的に入力され、変更履歴を書ける。

スクリーン・ショットは、関数 fib に対する ChangeLog を書いているところ。

ChangeLog

ChangeLog ファイルは、後でソースをどう編集したのかを知るのにとても便利。半年も経つと、過去の自分が別人のように思うことが多い。変更の目的・手段等をメモしておくようにしませう。

その他

この他、Emacs には、二つのバッファーを比較する ediff 機能、二つのファイルをマージする emerge 機能、RCS や CVS などのバージョン管理システムとの連携機能、デバッグ・ツール gdb との連携機能がある。ただし、これらの機能は、C プログラミングを始めたばかりの人達は使わなさそうなので、解説は割合する。

機会があれば、また後日、レビューを書きませう。

あとがき

C 言語を対象に、Emacs の便利な機能を解説した。しかし、ここで紹介したほとんどの機能は C 言語に限定したものじゃあない。ソースのハイライトからコメント・アウト、スペル・チェックにタグ・ジャンプ、コンパイルにエラー行ジャンプといった機能は、C++ でも Perl でも Ruby でも JavaScript でも使える。info さえ用意されていれば、マニュアルの参照も標準関数の補完入力もできる。

本エントリーを読んで、プログラミングに Emacs で何が出来るか、雰囲気だけでも掴んでもらえれば嬉しい。

15 comments:

  1. Emacs は 6, 7 年くらい前までよく使っていましたが、アラインメントや C の標準関数のリファレンスが参照できる機能は知りませんでした。
    今はもうほとんどプログラムは書かないのですが(書いても使っているのは vi(vim) だったりして)、改めて Emacs の奥深さに感心しますね。

    ReplyDelete
  2. そう言って頂けると、この記事を書いた甲斐もあるってものです。ありがとうございます。
    私は Emacs が好きでしょうがないのですが、それでもまだ知らない事が山積みです :) こんな感じに Emacs に興味を持つ人を増やせたら嬉しいです。

    ReplyDelete
  3. Xkeymacsを使ってWindowsのほとんどの操作をEmacsライクに使っていますが、EmacsでCのソースを書くことは無かったので参考になりました。

    私もEmacs大好きなのでこの特集はありがたかったです。

    ReplyDelete
  4. エラー行ジャンプは「C-`」ではなく「C-x `」ではないでしょうか。

    ReplyDelete
  5. うわー。穴があったら入りたい。
    おっしゃる通りです。修正しました。

    ReplyDelete
  6. 素朴な疑問なんですが、コンパイルした後、できたファイルを実行するにはどうするんでしょう?
    なにか便利な仕組みが用意されているのでしょうか?

    ReplyDelete
  7. Emacs には、M-x shell や M-x eshell とすることで、サブ・シェルを起動する機能があります。この機能を使えば、Emacs から離れることなしに、できたファイルを実行することができます。

    過去記事では、ここら辺が参考になるかと。
    - clmemo@aka: Emacs の subshell で ls に色を付けない
    - clmemo@aka: Emacs で複数 subshell を起動する
    - clmemo@aka: .emacs_SHELL という shell の設定ファイル
    - 徒然な覚書 : ls on M-x shell

    ReplyDelete
  8. ありがとうございます。
    シェルを使うとなると、vi でサスペンドして実行ファイル起動、fg するのと大して変わりませんね。
    それすら面倒に感じる場合に、最速かつ汎用的な方法はないものかと考えているのですが。

    ReplyDelete
  9. > シェルを使うとなると、vi でサスペンドして...

    parasporospa は、「それすら面倒に感じる」領域に入ってしまわれているのですね ^^; その領域の人を唸らせる回答をするのは難しいですが :p
    # 一行コマンドを実行する M-! (shell-command) はもちろんご存じですよね。

    Makefile に実行コマンドまで書いておくというのはどうでしょう。
    下のような Makefile を書いておき

    foo: foo.o
    $(CC) $(CFLAGS) $^ -o $@
    exec-foo: foo
    ./foo&

    Emacs で「M-x compile RET make exec-foo」とすれば、コンパイル後、続けてファイルの実行までやってくれます。対話的に実行するのに比べると自由度が低くなりますが、絞切り型の処理ならば快適度は高くなるかと。

    ReplyDelete
  10. 自分は秀丸エディタを使用しています

    インデントで自動で改行とか
    結構便利です

    お金はフリーソフト製作者
    ということで一応免除
    してもらえますし・・・

    色分けもできます

    でもEmacsの色分けきれいですね
    自分は色分けで設定しつくすの
    結構めんどくさくて・・・

    秀丸で色指定の無いものを
    自分でどんどんカスタマイズして
    つかってるといったところでしょうか・・・

    ReplyDelete
  11. annkokunokizinn さん、こんにちは。

    秀丸もよいソフトウェアですよね。私も (Emacs が難しくて使えないという) 友人には秀丸をすすめています :)

    ReplyDelete
  12. たいへん参考になりました
    ありがとうございます

    ReplyDelete
  13. コメントありがとうございます。
    記事が参考になって、こちらも嬉しいです。

    ReplyDelete
  14. 私はC言語を書くときにEmacsを使っていますが、全く機能を使っておらず、宝の持ち腐れでした。

    とても参考になりました。ありがとうございます。

    ReplyDelete
  15. Kozonoyuki さん、はじめまして。Emacs は機能がありすぎて、目が回りそうですよね。この記事が一助になったと聞き、うれしく思います。

    ReplyDelete