Emacs に Flymake を導入

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)))

コメントを残す

メールアドレスが公開されることはありません。