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で渡すパスにくっつけていた"/*"がいらなくなる、ということですね。
チェックボタンだとかで実装するほうがいいんですが、面倒そうなんだよねえ。