■ Emacs 全般で利用できる設定


【お知らせ】


<2018/09/12 追記>
rubikitch 氏による pgreper というコマンドもあるようです。本設定と似た目的のコマンドですが、より高性能なコマンドのようです。今更ではありますが、紹介させていただきます。

<2018/08/28 追記>
helm-ag パッケージの helm-do-ag コマンドも and検索に対応していることを知りました。こちらは、ag コマンドの正規表現の指定方法を工夫することにより、実現しているようです。

<2018/08/28 追記>
現在の helm の grep 機能は and検索をサポートしていることが分かりました。本設定は意味のないものとなっていたようです。このため、本設定のメンテナンスを終了としたいと思います。本ページの設定を参考とし、helm-grep-default-command 等の変数の変更を行っていた利用者が居りましたら、デフォルト値(変数を設定しない状態)に戻していただければと思います。

<2018/08/28 追記>
helm の仕様変更によるものだと思うのですが、helm-do-grep での and検索ができなくなりました。調査はしていますが、そもそも helm-do-grep の機能自体がなくなっていますので、helm-ag へ移行すべき時なのかと思っています。

<2016/02/02 追記>
Twitter の情報によると、helm-do-grep の機能は、helm-find-files のアクションで grep を選択することにより利用できるそうです。
本設定でのキーバインドでは、
  • C-x C-f で helm-find-files を起動した後、TAB または C-i でアクションに移動し、grep を選択
  • C-x C-f で helm-find-files を起動した後、C-s でカーソルのある箇所のファイルを検索(ディレクトリも指定可)
  • C-x C-f で helm-find-files を起動した後、C-u C-s でファイル種別を指定して検索
となるようです。

<2015/12/08 追記>
helm package から helm-do-grep の機能が削除されました。とりあえず、次の commit で削除された helm-do-grep 関数の定義と変数の定義を本設定内で行うと、helm-do-grep を利用することができます。もしくは、helm-ag を使うのが良いのではないかと思います。
https://github.com/emacs-helm/helm/commit/013ee11b695f52c68b0a0648dbd143b3592e2ef3
https://github.com/emacs-helm/helm/commit/f1519ad9d7a13cf00854a6096413ccfe7472dda0

<2015/07/23 追記>
helm の grepコマンド設定について、andgコマンド の有無を判定して動作するように変更しました。
この対応で、trampでリモート接続した際でも grepコマンド が利用できるようになりました。

【本題】


  • 【重要】 MinGW版 Emacs で ack を使う場合は、「fakecygpty を使うための設定」の設定も行ってください。
  • 【重要】 「helm を使うための設定」に追加してご利用ください。特に、helm-before-initialize-hook の helm-source-grep に対して行っている candidate-number-limit の設定は、速度対策のために重要な設定です。

helm の helm-do-grep で、空白文字で区切られた検索文字を and検索 するための設定です。

helm-ag(http://rubikitch.com/2015/02/09/helm-ag/)の利用により同様なことは可能ですが、一つ目の検索文字列から容易に指定し直せるのは便利かと思います。但し、migemo は使えません。

まず最初に、以下の perlスクリプトを andg という名称で Emacs からコマンドの検索パスが通っているディレクトリ(~/bin など)に格納してください。
必要であれば実行権を付けてください。( gnupack with Cygwin 11.00 の環境では不要です。 )

# ・Cygwin で lgrep コマンドを利用する場合、Cygwin に入っていなければインストールしてください。(lv パッケージに含まれています。)
# ・lgrep が出力する漢字は utf-8 に設定されていますので、適宜変更してください。
# ・lgrep は最終行が改行で終わっていないファイルの最終行にマッチすると誤作動します。
#  (検索行に改行が補完されないため、次のファイルの検索行があれば後ろにつながってしまう。grep では補完される。)
#  これは lgrep の仕様ですので、そのような挙動となることをご理解のうえ、利用してください。

andg
#!/usr/bin/perl
# -*- mode: perl; coding: utf-8-unix -*-

#####################################################################################
## ■ 空白文字で区切られた検索文字を and検索 するためのコマンド
##
##   Usage: andg [-hi] [-e] "PATTERN [PATTERN...]" FILES or DIRECTORIES
##
## ※ 空白がエスケープされているか二つ並んでいる場合は、その空白を一文字の検索文字と
##    みなします。
#####################################################################################

# ■検索用のコマンドを設定する
# ・$grep_command1 には最初に実行するコマンドを、$grep_command2 には二番目以降に
#   フィルターのために実行するコマンドを指定してください
# ・検索のオプション(特に漢字指定箇所)は適宜見直してください
# ・$grep_command1 では、検索したファイル名と行番号が表示されるようにしてください
#   (ファイル名の表示は、lgrep のように複数ファイルの検索でファイル名表示となる
#     コマンドもあるため、実行するコマンドに /dev/null を追加することでも対応して
#     います。)
# ・-i(case insensitive)オプションが使える場合は、そのオプションを指定する位置に
#   %c を“マイナスを付けずに”指定してください。
# ・以下では、lgrep 以外リカーシブ検索が可能な設定としています。(lgrep にはこの
#   オプションがないため。)リカーシブ検索が可能な場合、検索対象に DIRECTORIES が
#   指定可能となります。

# for lgrep
$grep_command1 = "lgrep -n -Au8 -Ia +i %c -";
$grep_command2 = "lgrep -Au8 +i %c -";

# for grep
# $grep_command1 = "grep -n -r %c --color=never --";
# $grep_command2 = "grep %c --color=never --";

# for ack
# $grep_command1 = "ack %c --no-group --no-color --";
# $grep_command2 = "ack %c --no-color --";

# for ag
# $grep_command1 = "ag %c --nogroup --nocolor --";
# $grep_command2 = "ag %c --nocolor --";

#####################################################################################

use File::Basename;
use Getopt::Std;

# Usage を表示し、終了する
sub usage_exit {
    print STDERR "Usage: ", basename($0), " [-hi] [-e] \"PATTERN [PATTERN...]\" FILES or DIRECTORIES\n";
    exit(1);
}

# オプションの取得
if (getopts("hie:", \%opts) == null) {
    usage_exit();
}

# オプションの判定
foreach $key (keys %opts) {
    if ($key eq "e") {
        # 検索文字列を設定する
        $pattern = $opts{$key};
    }
    elsif ($key eq "i") {
        # 大文字小文字を区別する
        $grep_command1 =~ s/%c/-i/;
        $grep_command2 =~ s/%c/-i/;
    }
    elsif ($key eq "h") {
        usage_exit();
    }
}

# 検索文字列を変数に格納する
if (!defined($pattern)) {
    $pattern = $ARGV[0];
    shift(@ARGV);
}

# 引数の残りが1つ以上なければ終了する
if ($#ARGV < 0) {
    usage_exit();
}

# 検索文字列の最後が中途半端な¥サインで終わっていたら終了する
if ($pattern =~ /[^\\]\\$/) {
    usage_exit();
}

# 指定されなかったオプション指定文字を削除する
$grep_command1 =~ s/%.//g;
$grep_command2 =~ s/%.//g;

# 検索文字列の区切り文字から除外する空白文字のパターンを正規表現で指定する
$space_regexp = qr/[\\ ] /;

# エスケープされた空白文字を一旦置き換える
$pattern =~ s/$space_regexp/\0\0/g;

# 検索文字列の前後の空白文字を削除する
$pattern =~ s/^ *(.*?) *$/$1/;

# 検索文字列を空白文字区切りで配列にし、置き換えていたエスケープ空白文字を元に戻す
@patterns = map { s/\0\0/ /g; $_ } split(/ +/, $pattern);

# 検索するファイルの配列を空白区切りの文字列にする
$files = '"' . join('" "', @ARGV) . '"';

# 検索用のコマンドを構成する
$command = "$grep_command1 \"$patterns[0]\" $files /dev/null";
while ($#patterns > 0) {
    shift(@patterns);
    # 二回目以降の検索ではファイル名と行番号の部分をパスして検索する
    $command = "$command | $grep_command2 \"^[^:][^:]*:[^:][^:]*:.*$patterns[0]\"";
}

# 検索コマンドを実行する
system($command);
# print $command; # for debug

利用方法は次のとおりとなります。
andg の第一引数を空白文字で分割し、分割した全てのパターンで and検索 します。
但し、空白がエスケープされているか二つ並んでいる場合は、その空白を一文字の検索文字とみなします。
andg "PATTERN [PATTERN...]" FILE...

# 一般的な使い方
<sample1-1> andg "pattern" *.txt
<sample1-2> andg "pattern1 pattern2" *.txt

# 上記と同じ(pattern が -(マイナス)から始まってもOK。-e の代わりに -- でも良い)
<sample2-1> andg -e "pattern" *.txt
<sample2-2> andg -e "pattern1 pattern2" *.txt

# 大文字と小文字を区別しない場合
<sample3-1> andg -i "pattern" *.txt
<sample3-2> andg -i "pattern1 pattern2" *.txt

つぎに helm の設定を以下のとおりに追加もしくは変更します。(bash 用のスクリプトとなっています。)
注)以下の設定は現在正しく動作しません。本ページの初めにある、<2018/08/28 追記> の内容を確認ください。
;; grepコマンドのパラメータを指定する
;; ・helm-grep-default-command で xargs を使っているのは、%f が二箇所で展開されるのを防ぐためです。
(setq helm-grep-default-command
      (concat "find %f -maxdepth 0 -print0 |"
              "if which andg > /dev/null 2>&1; then "
                  "xargs -0 andg $(if [ -n '%c' ]; then echo -n '-i'; fi) -e %p;"
              "else "
                  "xargs -0 " (replace-regexp-in-string "%f" "" helm-grep-default-command) ";"
              "fi"))

(setq helm-grep-default-recurse-command
      (concat "if which andg > /dev/null 2>&1; then "
                  "find %f -type d \\( -name '.svn' -o -name '.git' \\) -prune -o -type f "
                          "-name \"$(echo -n '%e' |"
                                    "sed -r -e 's/--include=([^ ]*) --exclude.*/\\1/' "
                                           "-e 's/\\\\//g')\" "
                          "-print0 |"
                  "xargs -0 andg $(if [ -n '%c' ]; then echo -n '-i'; fi) -e %p;"
              "else "
                  helm-grep-default-recurse-command ";"
              "fi"))

;; helm-grep-highlight-match を multi-match モードで強制的に起動する
(advice-add 'helm-grep-highlight-match
            :around (lambda (orig-fun &rest args)
                      (apply orig-fun `(,(nth 0 args) t))))


<変更履歴>
  • 2013/08/25 このページを作成した。
  • 2013/09/08 grep.pl の文字列比較方法が間違っていたのを修正した。("==" -> "eq")
  • 2013/09/08 $grep_command2 で使う正規表現を grep でも使える正規表現に見直しした。
  • 2013/09/09 -i(case insensitive)オプションをサポートした。
  • 2013/09/10 grep.pl を agrep (and grep の略)に名称変更した。
  • 2013/10/21 agrep の検索文字が -(マイナス)で始まるときにエラーとなる問題を対策した。
  • 2013/10/21 検索文字列を指定するオプション -e をサポートした。
  • 2013/10/22 helm or anything での利用において、存在しないファイルを指定した場合のエラー出力の影響を回避する設定を追加した。
  • 2013/11/05 agrep を andg に再度名称変更した。
  • 2013/12/01 検索の高速化のために、検索件数を絞る対応をした。
  • 2015/07/23 helm や anything の grepコマンド設定について、andgコマンド の有無を判定して動作するように変更した。
  • 2015/07/30 helm-grep-default-command、anything-c-grep-default-command の設定の見直しを行った。
  • 2015/09/10 advice を Emacs-24.4 以降の書式に見直した。また、anything の設定箇所を削除した。


最終更新:2020年08月07日 09:57