読者です 読者をやめる 読者になる 読者になる

M-x compileで成功したらウィンドウを閉じる

Emacs + gccで開発するときの話。

EmacsでM-x compileをすると自動的に新しいウィンドウが開き、ビルドの結果を表示するバッファ(*compilation*)が表示されます。ここに警告やエラーのメッセージが表示されるためデバッグには便利なのですが、成功した場合にも*compilation*バッファのウィンドウは開いたままになってしまいます。これを自動で閉じるEmacs Lispコードを書いてみました。

「エラーや警告がなけれがウィンドウを閉じる」という関数をビルドが終わったあとに実行できればよいわけですが、そのための変数compilation-finish-functionsというのが都合よくあったのでこれを使います。複数形になっているとおりこれはリストであり、複数の関数を登録できるようです。なお単数形のcompilation-finish-functionという変数もあるのですが(当然これはリストではありません)、これはEmacs-22.1でobsoleteになったとマニュアルに書いてありました。

登録する関数は2つの引数buf、 strを取ります。bufは*compilation*バッファであり、strはビルドのプロセスがどのように終わったかを表す文字列です。試したところ、成功したら"finished"となり失敗したときは"exited abnormally with code 2"となるようです。

strに"abnormally"という文字列があればビルドに失敗したことが分かるわけですが、これでは警告が起きただけの場合を判定できません。警告が出たとしてもビルドが最後まで行われればgccやmakeが0(成功)を返してしまうためだと思われます。

仕方がないので単純に*compilation*バッファを検索することにしました。gccは警告があれば"warning:"や"警告:"という文字列を出すので、これらの文字列があれば警告が出たとみなします。このため判定を間違えることもあるかもしれません。

ということでできたのがこのコード。

;;;; コンパイルに成功したら*compilation*バッファを閉じる
;;; 指定したバッファに警告の文字列があるか
(defun my-compilation-warning-bufferp (buf)
  (save-current-buffer
	(set-buffer buf)
	(save-excursion
	  (goto-char (point-min))
	  (if (or (search-forward "warning:" nil t)
			  (search-forward "警告:" nil t)) t nil))))
;;; バッファに"abnormally"や警告メッセージがなければウィンドウを閉じる
(defun my-close-compilation-buffer-if-succeeded (buf str)
  (cond ((string-match "abnormally" str)
		 (message "Error!"))
		((if (my-compilation-warning-bufferp buf)
			 (message "Warning!")))
		(t
		 (delete-window (get-buffer-window buf))
		 (message "Succeeded"))))
;;; compile終了時の実行関数に追加する
(if compilation-finish-functions
  (append compilation-finish-functions
		  '(my-close-compilation-buffer-if-succeeded))
  (setq compilation-finish-functions
		'(my-close-compilation-buffer-if-succeeded)))

関数名にmy-とか使っていてちょっと汚い。気になったら変更して使ってください。

追記 (2013/06/02)

だいぶ前に書いたものですがバグがあったので直しました。