GNU EmacsのTips...
g/RE/pの真似ごとをする
(2000.09.27)
(バッファを操る・その1、そしてマクロ)

概要

 カレントバッファに対して、エディターedないしexの、g/RE/pと同じ ようなことをする。

 コマンドの中で(作業用に)バッファを作ってみたいなあ、そうする といろいろ自由が利いてよさそうだなあ、第一面白いだろうなあ、今後 のためにも手がけておきたいなあ、というきわめて不純な動機で書いて みたものです。

 grepモードってのが有名ですが、Mule for Windowsで試してみてどー もうまく使えず、それ以来敬遠しています(かわいそう、なんでしょう ね)。けっきょくシェルウィンドウからgrepを走らせる毎日。
 ま、それはそれでいいんですが、ちょうどedのように、現在着目して いるバッファに対して広域指令でパターン照合を行ない結果を印字する、 なんてことができるといいかもな、ということで、それらしいものを書 いてみました。

 M-x g/re/p <RET>
と実行すると、
 Pattern?:
と聞いてきます。探索したい文型を入力すると、結果が*g-re-p:result* というバッファに表示されます。
 例によってエラーや例外の処理は考えに入れてません。

適用範囲

GNU Emacs 19.34 (Mule 2.3)
GNU Emacs 20.5.1

解説

 バッファを作るためにcreate-file-bufferという関数 を呼んでいます。このg/re/pコマンドを何回実行しても同じ結果バッファ を使いたいので、もし同名のバッファがあればそれを使い、ない時だけ 新規作成、という風にしました。
 delete-regionは結果バッファが既存の場合に、前回の 実行結果を消去するため。このときにset-bufferでカレ ントバッファを切り替えている(筈)ため、もとのカレントバッファに 戻します。こういうのはsave-excursionを使うのがいい のかも知れません。

 それから、単に結果を別のバッファに表示するだけじゃつまらないの で、パターンが出現する行を数え上げ、その行数をエコー領域に表示す るようにしてみました。
 数え上げ自体は簡単なことですが、せっかくだからマクロ(Emacs Lispのマクロね)を作って使ってみました。incfという ヤツです。

 文型が見つかった行を結果バッファに格納するために行内を動き回っ た挙げ句、次の行頭(きっと)に移動してそのままループを続けますが、 これは意図してのことです。g/RE/pとしては文型に合致する行があれば 目的は達成しているので、次の行からの探索に進んでもかまわないとい う考えです。

 なお、次の行に進む時にnext-lineを使うと、行末に改 行文字がない場合にエラーとなってしまいます。ので、 forward-lineを使うようにしています。
(実は既に掲載済みのvi 風カットアンドペーストでは、next-lineを使ってい ました。が、今回こっそり直してあります)

コード

  1: (defmacro incf (a)
  2:  `(setq ,a (1+ ,a))
  3:  )
  4: 
  5: (defun g/re/p ()
  6:  (interactive)
  7:  (let ((patter)
  8:     (npat 0)
  9:     (here)
 10:     (end (point-max))
 11:     (bufname "*g-re-p:result*")  ; name of result buffer
 12:     (momo)             ; result buffer
 13:     (cur (current-buffer))     ; current buffer
 14:     )
 15:   (if (setq momo (get-buffer bufname))
 16:     ()
 17:    (setq momo (create-file-buffer bufname)))
 18:   (set-buffer momo)
 19:   (delete-region (point-min) (point-max))  ; clean up result buffer
 20:   (set-buffer cur)
 21:   (setq pattern (read-input "Pattern?: "))
 22:   (save-excursion
 23:    (let ((from)
 24:       (to)
 25:       )
 26:     (beginning-of-buffer)
 27:     (while (and (setq here (search-forward-regexp pattern end t))
 28:           (> end here))
 29:      (beginning-of-line) (setq from (point))
 30:      (forward-line 1) (setq to (point))
 31:      (append-to-buffer momo from to)
 32:      (incf npat)
 33:      )
 34:     )
 35:    (switch-to-buffer momo)
 36:    (message (format "Pattern %s found in %d line(s)" pattern npat))
 37:    ))
 38:  )

補足

 バッファを作って自分で表示させてみると、なんか、Emacs Lispプロ グラマーになったって感じがしますね(笑)。それに、けっこう長い。 ってもまだ直線的なフローでしかありませんが。

 結果バッファに格納する各行の先頭に、行番号をつけられるといいな と思います。でもそこまでやるならgrepモードを使えるようにするんだ ろうなあ。

 なお、結果バッファをしまっている変数のmomoという 名前は、fooやbarと同じで、ぼくにとっての〈名称変数〉です。気にし ないでください。

(2000.09.27)

GNU EmacsのTipsへ
参考図書へ
コンピューター言語研究所へ
トップページへ
(C) ©Copyright Noboru HIWAMATA (nulpleno). All rights reserved.