Ugrep作成日記その3-表示と検索関係をいじる

前置き


外部プログラムでGrepを使ってみよう、という試み。どちらかというと勉強のためという要素が強いです。
前回より、いろいろと機能を付けてみた。

付けた機能

  • 連結表示
  • 検索ワードのハイライト表示

連結表示


howmで、@を押すとメモの連結表示ができる。
これを、ugrepでもやってみようと。

まず、検索結果が表示されているバッファを一時ファイルにして保存。そうしないとread-lineが使えないからだ。
そして、一時ファイルからread-lineで各行を取り出し、パスを抽出。
取り出したパスはリストにして保存。
そのリストから、insert-fileでリストのパスの内容を出力。

四つのコマンドからなっていて、一々説明すると

split-path
(defun split-path (line)
  (setq splits (split-string line #\:))
  (concat (car splits) ":" (cadr splits)))

与えられた行からパスを取得
grepでは、検索結果を
D:\data1\TTD/075-2.txt:日記−200x1027(wed)
のように出力するから、":"をセパレーターにして、最初(D)と二つめの要素(\data1\TTD/075-2.txt)を":"でつなげばパスができることになる。

make-filename-list
(defun make-filename-list (file)
  (with-open-file (in file :direction :input)
    (let ((word-list (make-list 0))
	word)
      (while (setq word (read-line in nil))
	(setq word (split-path word))
	(unless (equal word (car word-list))
	  (setq word-list (cons word word-list))))
      (setq *filename-list* word-list))))

与えられたファイルから、split-pathも利用してパスのリストを作成する。それはグローバル変数*filename-list*に入れる。
与えられるファイルとして想定しているのは、grepの検索結果がずらずら並んだファイル。
同じファイル内に検索語が複数あった場合、表示されるパスに重複がでることになる。そうすると、同じファイルをいくつも連結表示するということになる。そこでそれを避けるため、同じファイル名の場合にはリストに追加しないようにした。

insert-list
(defun insert-list (file-list)
    (dolist (x file-list)
      (insert-file x)))

リストのパスを、全て今のバッファに表示するためのもの

view-all-list
(defun view-all-list ()
  (interactive)
  (unless (equal (window-buffer (selected-window)) *ugrep-list*)
    (other-window))
  (let ((tmp-file (make-temp-file-name)))
    (write-file tmp-file)
    (make-filename-list tmp-file)
    (other-window)
    (switch-to-buffer "all-list-view")
    (insert-list *filename-list*)
    (delete-file tmp-file))
  (set-buffer-fold-width nil)
  (setq need-not-save t))

本体

という構成です。

その他


日本語で検索できない語があることに気づいた。これがあのダメ文字ってやつだろうか。ヒューマニズムと検索をしても、検索してくれない。
解決策ないか少し探してみるけど、難しそうならどうしようかなぁ。文字関連の問題って、書庫関係の話みたく面倒なイメージがあるんだよなぁ。

ugrep.l

;;; -*- Mode: Lisp; Package: EDITOR -*-
;;; ugrep.l
;;; ver0.24

(provide "ugrep")

(in-package "editor")

(export '(ugrep
	  ugrep-dialog
	  ugrep-split-window
	  exec-ugrep
	  scan-ugrepfile
	  scan-ugrepfileR
	  highlight-search-word
	  view-all-list
	  *ugrep-mode-hook*
	  *search-ugrepdialog-template* *ugrep-search-word* *ugrep-mode-map*))

(defvar *ugrep-search-word* nil)
(defvar *ugrep-search-dir* nil)
(defvar *ugrep-mode-hook* nil)
(defvar *ugrep-list* nil)
(defvar *ugrep-view* nil)


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

(defvar *search-ugrepdialog-template*
  '(dialog 0 0 271 65
    (: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 ref "参照(&R)..." #x50010000 214 41 50 14))))

;; ダイアログ

(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*))
		  `(list (ref :related dir :directory-name-dialog (:title "参照"))))
    (setq *ugrep-search-word* (cdr (assoc 'pat data)))
    (setq *ugrep-search-dir* (cdr (assoc 'dir data)))
    )
  )


;; ウィンドウの分割とバッファの表示

(defun ugrep-split-window (file list-buffer)
  (interactive)
  (delete-other-windows)
  (setq *ugrep-list* (switch-to-buffer list-buffer))
  (insert-file-contents file)
  (split-window -25 nil)
  (set-buffer-fold-width nil)
  (setq need-not-save t))


;; 外部コマンドの実行


(defun exec-ugrep (command key dir temp-file)
  (call-process (concat command " " key " " dir "/*") :output temp-file :show :hide :wait t))



;; 検索語のハイライト表示


(defun highlight-search-word ()
  (interactive)
    (delete-text-attributes 'search-word)
  (save-excursion
    (goto-char (point-min))
    (while (scan-buffer *ugrep-search-word* :regexp t :no-dup nil :case-fold t :tail t)
      (set-text-attribute (point) (- (point) (length *ugrep-search-word*))  'search-word :bold t :foreground 4 )
      (goto-eol)
      )
    )
)

;; 行頭のファイルパスを取得

(defvar *file-path* nil)
(defun get-path ()
  (interactive)
    (save-excursion
      (goto-eol)
      (let* ((line (buffer-substring (point) (progn (goto-bol)(point))))
	     (splits (split-string line #\:)))
	(setq *file-path* (concat (car splits) ":" (cadr splits))))))

;; ファイルパスが存在するかどうかで分岐

(defvar *ugrep-old-pathname* nil)
(defun ugrep-open-file ()
  (interactive)
  (get-path)
  (cond ((equal *file-path* *ugrep-old-pathname*)
	   (progn (other-window)
	     (set-buffer *ugrep-view*)
	     (scan-buffer *ugrep-search-word* :no-dup t :tail t)
	     (other-window)
	     (message "~A は同じ" *ugrep-old-pathname*)))
	  ((file-exist-p *file-path*) 
	   (progn (other-window)
	     (switch-to-buffer *ugrep-view*)
	     (erase-buffer(selected-buffer))
	     (insert-file-contents *file-path*)
	     (highlight-search-word)
	     (scan-buffer *ugrep-search-word* :no-dup t)
	     (setq need-not-save t)
	     (setq *ugrep-old-pathname* *file-path*)
	     (message "~A " *ugrep-old-pathname*)
	     (other-window)))
	(t (message "~A はファイルなのか?" *file-path*))))


;; 次のファイルを開く

(defun ugrep-open-file-next ()
  (interactive)
  (unless (equal (window-buffer (selected-window)) *ugrep-list*)
    (other-window))
  (next-line)
  (ugrep-open-file))

(defun ugrep-open-file-this ()
  (interactive)
  (unless (equal (window-buffer (selected-window)) *ugrep-list*)
    (other-window))
  (ugrep-open-file))

;; 連結表示

(defun split-path (line)
  (setq splits (split-string line #\:))
  (concat (car splits) ":" (cadr splits)))

(defvar *filename-list* nil)

(defun make-filename-list (file)
  (with-open-file (in file :direction :input)
    (let ((word-list (make-list 0))
	word)
      (while (setq word (read-line in nil))
	(setq word (split-path word))
	(unless (equal word (car word-list))
	  (setq word-list (cons word word-list))))
      (setq *filename-list* word-list))))

(defun insert-list (file-list)
    (dolist (x file-list)
      (insert-file x)))


(defun view-all-list ()
  (interactive)
  (unless (equal (window-buffer (selected-window)) *ugrep-list*)
    (other-window))
  (let ((tmp-file (make-temp-file-name)))
    (write-file tmp-file)
    (make-filename-list tmp-file)
    (other-window)
    (switch-to-buffer "all-list-view")
    (insert-list *filename-list*)
    (delete-file tmp-file))
  (set-buffer-fold-width nil)
  (setq need-not-save t))

;; 検索
	  
(defun scan-ugrepfile ()
  (interactive)
  (unless (equal (buffer-name (selected-buffer)) (buffer-name *ugrep-view*))
    (other-window))
  (scan-buffer *ugrep-search-word* :reverse t :no-dup t)
  )
(defun scan-ugrepfileR ()
  (interactive)
  (unless (equal (buffer-name (selected-buffer)) (buffer-name *ugrep-view*))
    (other-window))
  (scan-buffer *ugrep-search-word* :reverse t :no-dup t)
  )


;; 本体

(defun ugrep ()
  (interactive)
  (ugrep-dialog)
  (let ((command "grep.exe")
	(key *ugrep-search-word*)
	(dir *ugrep-search-dir*)
	(tmp-file (make-temp-file-name))
	(buffer1 "view-list")
	(buffer2 "view-file"))
    (exec-ugrep command key dir tmp-file)
    (ugrep-split-window tmp-file buffer1)
    (switch-to-buffer buffer1)
    (ugrep-mode)
    (setq *ugrep-view* (switch-to-buffer buffer2))
    (ugrep-mode)
    (delete-file tmp-file))
  (ugrep-open-file-this)
  )

;; キーマップ

(defun ugrep-mode ()
  (interactive)
  (kill-all-local-variables)
  (setq buffer-mode 'ugrep-mode)
  (setq mode-name "Ugrep")
  (use-keymap *ugrep-mode-map*)
  (make-local-variable 'need-not-save)
  (setq need-not-save t)
  (make-local-variable 'auto-save)
  (setq auto-save nil)
  (run-hooks '*ugrep-mode-hook*))

(defvar *ugrep-mode-map* nil)
  (unless *ugrep-mode-map*
    (setq *ugrep-mode-map* (make-sparse-keymap))
    (define-key *ugrep-mode-map* #\F10 'ugrep-open-file-this)
    (define-key *ugrep-mode-map* #\F11 'ugrep-open-file-next)
    (define-key *ugrep-mode-map* #\F3 'scan-ugrepfile)
    (define-key *ugrep-mode-map* #\S-F3 'scan-ugrepfileR)
    (define-key *ugrep-mode-map* #\@ 'view-all-list))

サブフォルダを検索したい場合


本体である ugrep () で、

(let ((command "grep.exe")

の行を

(let ((command "grep.exe -r")

に。外部コマンドの実行の、

(defun exec-ugrep (command key dir temp-file)
  (call-process (concat command " " key " " dir "/*") :output temp-file :show :hide :wait t))

(defun exec-ugrep (command key dir temp-file)
  (call-process (concat command " " key " " dir) :output temp-file :show :hide :wait t))

に変えるとサブフォルダを検索するようになる。grepのオプションで "-r"をつけるとサブフォルダを検索するようになって、その場合には、fenrirで渡すパスにくっつけていた"/*"がいらなくなる、ということですね。
チェックボタンだとかで実装するほうがいいんですが、面倒そうなんだよねえ。