2010年5月18日

Emacs で文法チェック

Emacs でプログラムを書く人々は、リアルタイムに文法チェックをするために flymake を使うらしい。

以前聞き覚えがあるが、何をする物か分からないのでスルーした奴だが、結構使えるものだったのかぁ。

なので flymake の設定をしてみた。

Quick Start

詳細は info マニュアルに書かれているが、 使い始めの設定は次の通りになる。この状態で、何もせずに Perl/PHP の文法チェックは行なえる(perl/php コマンドがあればの話だが)。

(require 'flymake)

;; GUIの警告は表示しない
(setq flymake-gui-warnings-enabled nil)

;; 全てのファイルで flymakeを有効化
(add-hook 'find-file-hook 'flymake-find-file-hook)

;; M-p/M-n で警告/エラー行の移動
(global-set-key "\M-p" 'flymake-goto-prev-error)
(global-set-key "\M-n" 'flymake-goto-next-error)

;; 警告エラー行の表示
(global-set-key "\C-cd" 'flymake-display-err-menu-for-current-line)

次に C/C++ で文法チェックを可能にするには、 Makefile を使い、次の様な check-syntax ルールを追加すればよい。

PHONY: check-syntax
check-syntax:
    $(CC) -Wall -Wextra -pedantic -fsyntax-only $(CHK_SOURCES)

ここまですると、編集途中にリアルタイムに文法エラー/警告の部分に色が付き、分かり易い。

なるほど、、、これは素晴らしい。

以下の設定は、好きずきだと思う

警告エラー行の表示のカスタマイズ

警告エラー行の表示は、 プラットホーム毎のポップアップメニュー実装が使われるが、 色々検索すれば出てくるが、2通りのカスタマイズが良く知られている。

  1. Minibufに表示する。
  2. popup.el(auto-complete に含まれている)のpopup-tipsで表示する。

;; Minibuf に出力
(defun my-flymake-display-err-minibuf-for-current-line ()
  "Displays the error/warning for the current line in the minibuffer"
  (interactive)
  (let* ((line-no            (flymake-current-line-no))
         (line-err-info-list (nth 0 (flymake-find-err-info flymake-err-info line-no)))
         (count              (length line-err-info-list)))
    (while (> count 0)
      (when line-err-info-list
        (let* ((text       (flymake-ler-text (nth (1- count) line-err-info-list)))
               (line       (flymake-ler-line (nth (1- count) line-err-info-list))))
          (message "[%s] %s" line text)))
      (setq count (1- count)))))

;; popup.el を使って tip として表示
(defun my-flymake-display-err-popup.el-for-current-line ()
  "Display a menu with errors/warnings for current line if it has errors and/or warnings."
  (interactive)
  (let* ((line-no            (flymake-current-line-no))
         (line-err-info-list (nth 0 (flymake-find-err-info flymake-err-info line-no)))
         (menu-data          (flymake-make-err-menu-data line-no line-err-info-list)))
    (if menu-data
      (popup-tip (mapconcat '(lambda (e) (nth 0 e))
                            (nth 1 menu-data)
                            "\n")))
    ))

僕としては popup.el を使うのが結構好み

check-syntaxターゲットルール

先に述べて Makefile の check-syntaxターゲットルールでは一種類の文法チェックプログラムしか使えない。。。 gcc でチェックできるものは全てチェック出来るから、気を回す必要は無いかもしれない。

GNU make を使えば、拡張子毎に文法チェックプログラムを選べるルールの書き方がある。

CHECKSYNTAX.c = $(CC) $(CFLAGS) $(CPPFLAGS) -Wall -Wextra -pedantic -fsyntax-only
CHECKSYNTAX.cc = $(CXX) $(CXXFLAGS) $(CPPFLAGS) -Wall -Wextra -pedantic -fsyntax-only

check-syntax: $(addsuffix -check-syntax,$(CHK_SOURCES))

%.c-check-syntax:
	$(CHECKSYNTAX.c) $*.c

%.cc-check-syntax:
	$(CHECKSYNTAX.cc) $*.cc

まぁ、ここまで書く奴はいないかぁ。。。

Makefileが無くてもC/C++の文法チェック

Makefileが無ければ、直接 gcc で文法チェックをし、 Makefileがあればcheck-syntaxターゲットルールを使いたい場合は、 次の設定をすれば良い。

(defun flymake-simple-generic-init (cmd &optional opts)
  (let* ((temp-file  (flymake-init-create-temp-buffer-copy
                      'flymake-create-temp-inplace))
         (local-file (file-relative-name
                      temp-file
                      (file-name-directory buffer-file-name))))
    (list cmd (append opts (list local-file)))))

;; Makefile が無くてもC/C++のチェック
(defun flymake-simple-make-or-generic-init (cmd &optional opts)
  (if (file-exists-p "Makefile")
      (flymake-simple-make-init)
    (flymake-simple-generic-init cmd opts)))

(defun flymake-c-init ()
  (flymake-simple-make-or-generic-init
   "gcc" '("-Wall" "-Wextra" "-pedantic" "-fsyntax-only")))

(defun flymake-cc-init ()
  (flymake-simple-make-or-generic-init
   "g++" '("-Wall" "-Wextra" "-pedantic" "-fsyntax-only")))

(push '("\\.[cC]\\'" flymake-c-init) flymake-allowed-file-name-masks)
(push '("\\.\\(?:cc\|cpp\|CC\|CPP\\)\\'" flymake-cc-init) flymake-allowed-file-name-masks)

まとめ

取り敢えず、次いでに ruby/bash の文法チェックを追加したのが、 今現在の設定になっている。

45flymake.el

あとは Python/Java/HTML/CSS をチェックするのがあれば良いが、、、 これくらい素のflymakeに含まれていればいいのになぁ。。。

あとSemanticかぁ。。。

追記 (2010/05/24)

暫く使ってみたが、基本動作で2点だけイマイチな挙動を修正しないと、ストレスが溜まりまくる。

  1. ファイルもしくは上位ディレクトリに書き込み権限が無い場合、messageを吐いて開くのを諦めてしまう。。。
  2. 文法チェックプログラムが無い場合 flymake がオフになるが、それでも何かのプロセス?が残って、後々無意味な y の連打をしないといけない。。。
これも、素のflymakeで用意してくれれば良いのに、、、で場当たり的に次の設定を追加した。。。

;; flymake を使えない場合をチェック
(defadvice flymake-can-syntax-check-file
  (after my-flymake-can-syntax-check-file activate)
  (cond
   ((not ad-return-value))
   ;; tramp 経由であれば、無効
   ((and (fboundp 'tramp-list-remote-buffers)
         (memq (current-buffer) (tramp-list-remote-buffers)))
    (setq ad-return-value nil))
   ;; 書き込み不可ならば、flymakeは無効
   ((not (file-writable-p buffer-file-name))
    (setq ad-return-value nil))
   ;; flymake で使われるコマンドが無ければ無効
   ((let ((cmd (nth 0 (prog1
                          (funcall (flymake-get-init-function buffer-file-name))
                        (funcall (flymake-get-cleanup-function buffer-file-name))))))
      (and cmd (not (executable-find cmd))))
    (setq ad-return-value nil))
   ))

追記 (2010/05/25)

上の修正は、ちょっと上手く行かない場合があったので修正っと。

追記 (2010/05/27)

tramp経由の場合も、イマイチ上手く動かないので、除外に加えてみた。

0 件のコメント:

Cocoa Emacs 24.3 構築 (2013/03版)

暫く使っている Cocoa Emacs を更新していなかったので、24.3 に上げてみた。 当てるパッチは inline patch と ポップアップフリーズ対応パッチ くらい。 24.3 には既にフルスクリーン実装が入っているので、よく使われているフルスクリーンパッチは外し...