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通りのカスタマイズが良く知られている。
- Minibufに表示する。
- 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点だけイマイチな挙動を修正しないと、ストレスが溜まりまくる。
- ファイルもしくは上位ディレクトリに書き込み権限が無い場合、messageを吐いて開くのを諦めてしまう。。。
- 文法チェックプログラムが無い場合 flymake がオフになるが、それでも何かのプロセス?が残って、後々無意味な y の連打をしないといけない。。。
;; 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 件のコメント:
コメントを投稿