Subscribed unsubscribe Subscribe Subscribe

anything-grepでackを使うとマッチした部分のカラーリングが反映出来ない

anything-grep.elを使うと、シェルコマンドとしてgrepなどの検索をかけて、その結果をanythingで絞り込むことができる。ディレクトリ以下のファイルすべてを一発で検索、などできるのが魅力。
EmacsWiki: anything-grep.el 現在の最新版は1.27。

例えば、anything-regexpの場合

これは、カレントバッファに対して正規表現で絞り込みをかけることができる。Regexpに、

[[:digit:]]\{4,\}

とか指定すると、マッチした行が、マッチしている部分がカラーリングされて候補に出てくる。

anything-grepだと

ターミナルで

ack --nogroup '\d{4}'

とかやると、ちゃんと色がつくけど、

anything-grepのコマンドで同じように検索かけた場合、結果は候補としてでてくるけど、マッチしている部分のカラーリングはされていない

ackコマンドでなく

grep -nH -e '[[:digit:]]{4\}' *.el

とやっても同じ結果だった。

原因

追いかけてみたところ、マッチした部分のカラーリングはagrep-fontify関数で行っているようだ。ここで検索コマンドから返ってきた結果を加工している。

  (while (re-search-forward "\\(\033\\[01;31m\\)\\(.*?\\)\\(\033\\[[0-9]*m\\)" nil t)
    (put-text-property (match-beginning 2) (match-end 2) 'face  grep-match-face)
    (replace-match "" t t nil 1)
    (replace-match "" t t nil 3))

この部分で、マッチしてきた部分のfaceをgrep-match-faceにセットし、それ以降エスケープシーケンスを除去している。つまり、この正規表現にマッチするようにコマンドから出力されていないといけない、ということ。
grepを使った場合は、agrep-do-grep関数内で

    (when (eq grep-highlight-matches t)
      ;; Modify `process-environment' locally bound in `call-process-shell-command'.
      (setenv "GREP_OPTIONS" (concat (getenv "GREP_OPTIONS") " --color=always"))
      ;; for GNU grep 2.5.1
      (setenv "GREP_COLOR" "01;31")
      ;; for GNU grep 2.5.1-cvs
      (setenv "GREP_COLORS" "mt=01;31:fn=:ln=:bn=:se=:ml=:cx=:ne"))

という定義があった。grep-highlight-matchestであれば勝手に環境変数でオプションを調整してエスケープシーケンスを出すようにしてくれるようだ。

(custom-set-variables '(grep-highlight-matches t))

とやったら、grepコマンドを使った検索で結果のマッチ部分がカラーリングされるようになった。

しかしackでは

ackの場合、検索結果のマッチ部分は\e[01;31mのエスケープシーケンスではカラーリングされない。デフォルトではblack on_yellow、つまり\e[30;43mとなっている。これはオプションもしくは環境変数で変えることができるが、指定出来るのはTerm::ANSIColorのATTRIBUTESに登録されているものに限られるようだ。

sub print_match_or_context {
...
                 s/$regex/Term::ANSIColor::colored( substr($_, $-[0], $+[0] - $-[0]), $ENV{ACK_COLOR_MATCH} )/eg ) {
...
}

で、この$ENV{ACK_COLOR_MATCH}は"bold red"を指定することでgrepと同じカラーリングにできるのだけど…

$ ack -h --color --color-match='bold red' --nogroup '\d{4}' | hexdump -C | head
00000000  3b 3b 20 43 6f 70 79 72  69 67 68 74 20 28 43 29  |;; Copyright (C)|
00000010  20 1b 5b 31 3b 33 31 6d  32 30 30 37 1b 5b 30 6d  | .[1;31m2007.[0m|
00000020  20 7e 20 1b 5b 31 3b 33  31 6d 32 30 31 31 1b 5b  | ~ .[1;31m2011.[|
00000030  30 6d 2c 20 54 61 73 73  69 6c 6f 20 48 6f 72 6e  |0m, Tassilo Horn|
00000040  2c 20 61 6c 6c 20 72 69  67 68 74 73 20 72 65 73  |, all rights res|
00000050  65 72 76 65 64 2e 1b 5b  30 6d 1b 5b 4b 0a 3b 3b  |erved..[0m.[K.;;|
00000060  20 43 6f 70 79 72 69 67  68 74 20 28 43 29 20 1b  | Copyright (C) .|
00000070  5b 31 3b 33 31 6d 32 30  30 39 1b 5b 30 6d 2c 20  |[1;31m2009.[0m, |
00000080  41 6e 64 79 20 53 74 65  77 61 72 74 2c 20 61 6c  |Andy Stewart, al|
00000090  6c 20 72 69 67 68 74 73  20 72 65 73 65 72 76 65  |l rights reserve|

残念なことに出力されるエスケープシーケンスは\e[1;31mとなっていて、agrep-fontifyで使われている正規表現にはマッチしない。
ためしにagrep-fontifyの部分の定義を

317c317
<   (while (re-search-forward "\\(\033\\[01;31m\\)\\(.*?\\)\\(\033\\[[0-9]*m\\)" nil t)
---
>   (while (re-search-forward "\\(\033\\[1;31m\\)\\(.*?\\)\\(\033\\[[0-9]*m\\)" nil t)

と変更してやって、

ack --nogroup --color-match='bold red' '\d{4}'

という検索コマンドを指定したところ、マッチ部分がカラーリングされるようになった。

解決策

ackでの検索結果のマッチカラーリングを実現させるには。
ack側では、App::AckTerm::ANSIColorを使ってカラーリングの指定をしていて、その中では

               'bold'           => 1,

と数値の1で指定されてしまっているので、\e[01;31mを出力させるようにするのは難しい。
anything-grep.elのagrep-fontifyに処理が行く前にdefadviceを使ってコマンド実行結果を先に加工するのが良いだろうか。
この正規表現部分をカスタム変数にして自由に設定出来るようにid:rubikitchさんにお願いしてみましょうか。
他になにか良い方法あるかなぁ…