GNU GLOBAL on Emacsのタグ選択を見易くする
GNU GLOBALは素晴しいタグツールだが表示に問題がある。
Emacsで使うと、複数の候補が見付かったときに *GTAGS SELECT* バッファに候補を表示してくれるのだが、これがとてつもなく見辛い。たとば以下は「M-x gtags-find-with-grep undef」をやった結果表示されたバッファの内容なのだが、検索しているタグが「undef」なのはわかりきってるし、スペースが無駄に多い上、ファイル名がフルパスで表示されているがために、肝心のソースコードが見えなくなっいる!いちいち右スクロールしないといけないんじゃ、正直使いものにならない。
undef 969 /m/home/nobackup/compile/ruby19/array.c if (rpl = undef 1794 /m/home/nobackup/compile/ruby19/array.c rb_ary_spl undef 2451 /m/home/nobackup/compile/ruby19/array.c return Qu undef 2483 /m/home/nobackup/compile/ruby19/array.c if (v != undef 696 /m/home/nobackup/compile/ruby19/bignum.c #undef MASK_0 undef 697 /m/home/nobackup/compile/ruby19/bignum.c #undef MASK_3 undef 698 /m/home/nobackup/compile/ruby19/bignum.c #undef MASK_5 undef 145 /m/home/nobackup/compile/ruby19/blockinlining.c undef 174 /m/home/nobackup/compile/ruby19/blockinlining.c undef 199 /m/home/nobackup/compile/ruby19/blockinlining.c if (va undef 207 /m/home/nobackup/compile/ruby19/blockinlining.c return Q undef 245 /m/home/nobackup/compile/ruby19/blockinlining.c undef 274 /m/home/nobackup/compile/ruby19/blockinlining.c undef 318 /m/home/nobackup/compile/ruby19/blockinlining.c if (va
そこでgtags.elを調べてみたら、gtags-goto-tag関数にてたんに「global -axg undef」の結果を表示しているだけだとわかった。
そうだとわかれば解決は簡単。-aオプションで「なぜか」フルパスで表示しているので取り除く。なぜフルパスで表示しているのかはっきり言って意味不明だ。だいたいgtags-goto-tag内でgenerate-new-bufferしているけど、default-directoryは作成時点の値が受け継がれるはず。相対パスで十分じゃないか。それなら同じディレクトリにあるファイルはファイル名だけになるから。もしフルパスでないといけない理由があるならば、overlayを使って表示を相対パスにすりゃいいだけの話。
そして、わかりきってるタグ名はいらないので表示しない。その部分をput-text-propertyでinvisibleにしてしまえばよし。そのためにはglobal -xgの結果表示フォーマットを知る必要がある。GNU GLOBALのソースをとってきて調べてみたらlibutil/pathconvert.c内にこんな行を見付けた。なるほど、16桁なんだな。ならば行頭から16桁までを非表示にすればいい。
case FORMAT_CTAGS_X: fprintf(cv->op, "%-16s %4d %-16s %s", tag, lineno, convert_pathname(cv, path), rest);
問題点を修正してgtags-goto-tagを再定義したのがコレ。.emacsで「(require 'gtags)」を入れた後に入れてくれ。
(defun gtags-goto-tag (tagname flag) (let (save prefix buffer lines) (setq save (current-buffer)) (cond ((equal flag "P") (setq prefix "(P)")) ((equal flag "g") (setq prefix "(GREP)")) ((equal flag "I") (setq prefix "(IDUTILS)")) ((equal flag "s") (setq prefix "(S)")) ((equal flag "r") (setq prefix "(R)")) (t (setq prefix "(D)"))) ;; load tag (setq buffer (generate-new-buffer (generate-new-buffer-name (concat "*GTAGS SELECT* " prefix tagname)))) (set-buffer buffer) (message "Searching %s ..." tagname) (if (not (= 0 (call-process "global" nil t nil (concat "-x" flag) tagname))) ; remove '-a' option (progn (message (buffer-substring (point-min)(1- (point-max)))) (gtags-pop-context)) (goto-char (point-min)) (setq lines (count-lines (point-min) (point-max))) (cond ((= 0 lines) (cond ((equal flag "P") (message "%s: path not found" tagname)) ((equal flag "g") (message "%s: pattern not found" tagname)) ((equal flag "I") (message "%s: token not found" tagname)) ((equal flag "s") (message "%s: symbol not found" tagname)) (t (message "%s: tag not found" tagname))) (gtags-pop-context) (kill-buffer buffer) (set-buffer save)) ((= 1 lines) (message "Searching %s ... Done" tagname) (gtags-select-it t)) (t ;; added by rubikitch (goto-char (point-min)) (while (not (eobp)) (put-text-property (point) (+ 16 (point)) 'invisible t) (forward-line 1)) (goto-char (point-min)) (switch-to-buffer buffer) (gtags-select-mode)))))) (defun gtags-select-it (delete) (let (line file) ;; get context from current tag line (beginning-of-line) (if (not (looking-at "[^ \t]+[ \t]+\\([0-9]+\\)[ \t]\\([^ \t]+\\)[ \t]")) (gtags-pop-context) (setq line (string-to-number (gtags-match-string 1))) (setq file (expand-file-name (gtags-match-string 2))) (if delete (kill-buffer (current-buffer))) ;; move to the context (if gtags-read-only (find-file-read-only file) (find-file file)) (setq gtags-current-buffer (current-buffer)) (goto-line line) (gtags-mode 1))))
この結果 *GTAGS SELECT* は見やすくなった。これで安心してGNU GLOBALを使えるぜ。
969 array.c if (rpl == Qundef) { 1794 array.c rb_ary_splice(ary, pos, len, Qundef); /* Qnil/rb_ary_ 2451 array.c return Qundef; 2483 array.c if (v != Qundef) return v; 696 bignum.c #undef MASK_0f 697 bignum.c #undef MASK_33 698 bignum.c #undef MASK_55 145 blockinlining.c Qundef); 174 blockinlining.c Qundef)); 199 blockinlining.c if (val == Qundef) { 207 blockinlining.c return Qundef; 245 blockinlining.c Qundef); 274 blockinlining.c Qundef)); 318 blockinlining.c if (val == Qundef) {
追記
フルパス表記をやめたため、gtags-select-itも変更の必要があった。カレントバッファを削除するとdefault-directoryが変わってしまうため、ファイル名を取得ときにexpand-file-nameする必要がでてきた。