Flymake は,リアルタイムでコンパイルエラー・警告を表示してくれる Emacs のプラグイン.つまらないエラーのために何度もコンパイルを書ける手間が省けるので,開発がずっと速くなるし,コードロジックに集中できるようになる.今回はその導入方法まとめ.なお,手元の環境は Cocoa Emacs 24.2.
導入方法
導入方法に関しては,すでに多くのサイトで解説されている.日本語の資料では,次の記事がわかりやすい. Emacs で文法チェック – 年越しそばと初詣は絶対に欠かせない
自分が参考にしたのは次の記事. Emacsで開発環境を整える – Story of Your Life
細かい設定や Hacks などは,EmacsWiki のページがポータルとして使える. FlyMake – EmacsWiki
g++ 4.7 で警告とエラーが区別されない
エラーのハイライトを行う際,デフォルトで警告とエラーが違う色になるはずなのだが,g++ 4.7 を使うとどちらもエラー色で表示される.g++ 4.4 では正しく動作することから,調べてみると,g++ 4.4 と g++ 4.7 ではエラー表示の文法が変わっているようだ.g++ 4.7 でもエラーを正しくパースできるよう,Emacs の初期設定ファイルに次のブロックを追加した.
(add-to-list 'flymake-err-line-patterns ;; g++ 4.7 '("\\([^:]+\\):\\([0-9]+\\):\\(\\([0-9]+\\):\\)? \\(\\(error\\|warning\\): \\(.+\\)\\)" 1 2 4 5))
エラー内容の表示にショートカットキーを設定
エラー内容の表示するのに,毎回コマンドを打つのは面倒だ.初期設定ファイルに次の行を追加して,C-c e でエラー内容を表示するようにした.
(global-set-key "\C-ce" 'flymake-show-and-sit)
上の階層の Makefile を探す
flymake-simple-make を使う場合はデフォルトで上の階層まで Makefile を探しに行くのだが,上の参考記事にならって Makefile なしでも Flymake を使えるようにした場合,上の階層に Makefile がある場合でも Makefile なしの方でエラーチェックが行われる.これを防ぐために,設定中に記述する flymake-simple-make-or-generic-init 関数を次のように変更する.
(defun makefile-exists (dir) (if (file-exists-p (concat dir "Makefile")) t (let ((padir (file-name-directory (directory-file-name dir)))) (if (and (not (string= dir padir)) padir) (makefile-exists padir) nil)))) (defun flymake-simple-make-or-generic-init (cmd &optional opts) (if (makefile-exists default-directory) (flymake-simple-make-init) (flymake-simple-generic-init cmd opts)))
これで,編集ファイルと同階層に Makefile がない場合でも,ディレクトリをたどって Makefile を探すようになる.
初期設定ファイル
最後に,Flymake に関して自分が設定した内容の全文を貼り付けておく.
;;; flymake (require 'flymake) (add-hook 'find-file-hook 'flymake-find-file-hook) (add-to-list 'flymake-err-line-patterns ;; g++ 4.7 '("\\([^:]+\\):\\([0-9]+\\):\\(\\([0-9]+\\):\\)? \\(\\(error\\|warning\\): \\(.+\\)\\)" 1 2 4 5)) (defun flymake-show-and-sit () "Displays the error/warning for the current line in the minibuffer" (interactive) (progn (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* ((file (flymake-ler-file (nth (1- count) line-err-info-list))) (full-file (flymake-ler-full-file (nth (1- count) line-err-info-list))) (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))))) (sit-for 60.0)) (global-set-key "\C-ce" 'flymake-show-and-sit) (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))))) (defun makefile-exists (dir) (if (file-exists-p (concat dir "Makefile")) t (let ((padir (file-name-directory (directory-file-name dir)))) (if (and (not (string= dir padir)) padir) (makefile-exists padir) nil)))) (defun flymake-simple-make-or-generic-init (cmd &optional opts) (if (makefile-exists default-directory) (flymake-simple-make-init) (flymake-simple-generic-init cmd opts))) (defun flymake-c-init () (flymake-simple-make-or-generic-init "gcc" '("-Wall" "-Wextra" "-Wno-trigraphs" "-fopenmp" "-fsyntax-only"))) (defun flymake-cc-init () (flymake-simple-make-or-generic-init "g++" '("-Wall" "-Wextra" "-Wno-trigraphs" "-fopenmp" "-fsyntax-only"))) (push '("\\.c\\'" flymake-c-init) flymake-allowed-file-name-masks) (push '("\\.\\(cc\\|cpp\\|cxx\\|h\\|hpp\\)\\'" flymake-cc-init) flymake-allowed-file-name-masks) ;;; flymake-tex (defun flymake-get-tex-args (file-name) (list "/usr/texbin/platex" (list "-file-line-error" "-draftmode" "-interaction=nonstopmode" file-name)))