Ugrep作成日記その5−サブディレクトリ検索のチェックボックス実装

やったこと

  • サブディレクトリの検索を選択できるようにした
  • 正規表現での検索(egrep)をできるようにした
  • grepのオプションの関数を別にした

サブディレクトリの検索とダイアログテンプレート


今まで難しそうだなとおもっていたのは、サブディレクトリの検索を選択できるようにするにはdialogをいじらないとダメだから。
xyzzyの場合には、ダイアログのテンプレートの作成と、ダイアログの呼び出しという二つによって成り立っている。dialogとdialog-boxの二つです。


一応リファレンスなどにもそれなりの説明は載っているのだけれど、詳細については資料がない。サイトを見て回っても、VisualC++をつかってそれをxyzzy用にしてほにゃららとか書いてある。けれどVisualC++を使ったことのない僕としてはどうしようもない。
サンプルとしては、たとえばlispフォルダに入っているgrepd.lなどが参考になるけれどあまりにも資料が少なすぎる。みようみまねでやるしかないけれども、それも難しそうだな、と考えていた。


で、結局どうしたかというとgrepd.lをみて見よう見まねで書きました。

;;ダイアログテンプレート


(defvar *search-ugrepdialog-template*
  '(dialog 0 0 271 95
    (:caption "UGrep")
    (:font 9 "MS Pゴシック")
    (:control
     (:static nil "パターン(&P):" #x50020000 7 10 42 8)
     (:combobox pat nil #x50210042 51 8 157 96)
		 (:static nil "フォルダ(&D):" #x50020000 7 27 42 8)
		 (:combobox dir nil #x50210042 51 25 157 96)
     (:button IDOK "検索(&S)" #x50010001 214 7 50 14)
     (:button IDCANCEL "キャンセル" #x50010000 214 24 50 14)
     (:button subdir "SubDir" #x50010003 214 58 50 14)
     (:button regexp "正規表現" #x50010003 214 75 50 14)
     (:button ref "参照(&R)..." #x50010000 214 41 50 14))))


テンプレは、このような書式。captionだとかfontだとかは別にほっておいてもよくて、いじらないといけないのは

     (:static nil "パターン(&P):" #x50020000 7 10 42 8)
     (:combobox pat nil #x50210042 51 8 157 96)
		 (:static nil "フォルダ(&D):" #x50020000 7 27 42 8)
		 (:combobox dir nil #x50210042 51 25 157 96)
     (:button IDOK "検索(&S)" #x50010001 214 7 50 14)
     (:button IDCANCEL "キャンセル" #x50010000 214 24 50 14)
     (:button subdir "SubDir" #x50010003 214 58 50 14)
     (:button regexp "正規表現" #x50010003 214 75 50 14)
     (:button ref "参照(&R)..." #x50010000 214 41 50 14))))

の部位。buttonはボタンでおっけーとして、おそらくつぎのようになっているのだろう。

(:button 結果を入れる変数名 "コントロールに表示する名前" コントロールのもっと細かいもの 位置)

4つの数字がそれぞれ位置とかをあらわしていて、適当に変えれば思い通りの位置におけるのだろう。僕はボタンとかチェックボックス類は右端に並べようとしたが、二番目の数値だけをいじっている。

そして、dialogの後ろにある

0 0 271 95

もきっとコントロール全体、gui全体の大きさとかを決めているんだろう。これも適当にいじればいい。


dialog-boxについては、かくのがめんどうになったから省略します☆ミ

コマンドラインオプションで分岐


チェックボックスの状態によって動作を分岐させないといけないんだけど、突き詰めればそれはgrepにどういうオプションを投げるかという話になる。

grep.exe -F -r D:/bin/hogehoge

あるいは

grep.exe -F D:/bin/hogehoge/*

のどちらか。だから、grepに投げるオプションをどうするかを決める関数grep-cmdをあらたに作り、そこで分岐させようと。

-F -r D:/bin/hogehoge

-F D:/bin/hogehoge/*

のどちらかを結果として返してくれるようなのを作るわけですね。

;; grepの引数


(defun grep-cmd (key dir  &optional subdir regexp)
  (let ((sub-c (if subdir
		    "-r"
		  nil))
	(reg-mode (if regexp
		    "-E"
		    "-F")))
    (if subdir
	(concat " " sub-c " " reg-mode " " key " " dir)
      (concat " " reg-mode " " key " " dir "/*"))))

ヒストリ変数


チェックボックスの状態を記憶するために、ヒストリ変数を使った。次回のxyzzyの立ち上げまで値を保存してくれる便利なものらしい。

(define-history-variable *ugrep-subdir* nil)
(define-history-variable *ugrep-regexp* nil)


チェックボックスの状態とこれを結びつけている。
ugrep-dialogの箇所ですね。


(cons 'subdir *ugrep-subdir*)


みたいにして結びつけ。で、必要なときにはassocで取り出し。

;; ダイアログ

(defun ugrep-dialog ()
  (interactive)
  (setq *ugrep-search-dir* (pop si:*command-line-args*))
  (multiple-value-bind (result data)
      (dialog-box *search-ugrepdialog-template*
		  (list (cons 'dir *ugrep-search-dir*)
			(cons 'subdir *ugrep-subdir*)
			(cons 'regexp *ugrep-regexp*))
		  `(list (ref :related dir :directory-name-dialog (:title "参照"))))
    (setq *ugrep-search-word* (cdr (assoc 'pat data)))
    (setq *ugrep-search-dir* (cdr (assoc 'dir data)))
    (setq *ugrep-subdir* (cdr (assoc 'subdir data)))
    (setq *ugrep-regexp* (cdr (assoc 'regexp data)))
    )
  )

既知の不具合

サブディレクトリ検索とfgrepとがうまく両立しない。サブディレクトリ検索にチェックを入れて、ヒューマニズムとか検索しても検索してくれない。
ググった限りではうまくいきそうなんだけどなぁ。書き方とか何かまちがってるのかなぁ。

正規表現での検索egrep

正規表現で検索する、ということもほとんどないのであれだけど、-Eオプションをつけれるようにしておいた。正規表現チェックボックスがそれ。
ふつうのgrepよりも、高機能らしい。くわしくはわからないのです。

今後やりたいこととか


検索を実行してその結果がウィンドウに出力した後も、Ctrl+Fとか任意のキーを押したら再びダイアログを出して検索を実行するというのをしたい。
ダイアログで検索ワードを入れなかったら、それ以降の動作をキャンセルしてウィンドウを分割したりとかせずスクラッチが開くようにする。
ファイル一覧表示ウィンドウで一番下までいったら上にループ
howmみたく、カーソルと連動してカーソル下のファイルが自動で下のウィンドウに表示されるようにする。