最近 percol をヘビーに使っています。percol は入力された文字列を部分一致かつ AND 検索で絞り込んでくれるコマンドなので、ファイル名の絞り込み、補完に使えない わけがないと思っていました。
以前のエントリでファイル名の補完として insert-file-by-percol
というのを紹介
しましたが、少々機能不足でした。今回は空気を読んでファイル名を補完、絞り込みし
てくれる zsh のコマンド complete-filename-by-percol
を書きましたので紹介します。
動作
complete-filename-by-percol
は何を絞り込みの候補とするかを、空気を読んで変え
てくれます。カーソル位置にある引数を ARG
とすると以下のものが絞り込みの候補に
なります。
ARG
が空白の場合- カレントディレクトリを ls したファイル群
ARG
が一意なディレクトリの場合ARG
を ls したファイル群
- その他の場合
ARG*
(* はグロブです)に合致するファイル群
また絞り込みの候補の数に応じて動作を変えてくれます。
- 絞り込みの候補が0個の場合
- エラーメッセージを表示してコマンド終了
- 絞り込みの候補が1個の場合
- その候補をコマンドラインに挿入してコマンド終了
- 絞り込みの候補が2個以上の場合
- percol を使って絞り込みし、選択されたものをコマンドラインに挿入してコマン ド終了。複数ファイルをマークした場合は、全てのファイルが挿入されます
ファイル名中の空白や記号に関しては適切にエスケープしてあるので、問題なく絞り 込みできるはずです。
動画
まずはどんな動作をするか見てもらったほうが早いと思うので、動画をとってみました。
http://www.youtube.com/watch?v=SpujuVQfjuk (できれば後ほど埋め込む)
動画ではわかりにくいかもしれませんが Ctrl+j
で補完コマンドが発動しています。
complete-filename-by-percol
のコード
空気を読んでくれるが故に条件分岐が多く少々長くなってしまいましたが、以下がその
コードです。これを .zshrc に書いておくと使えるようになります。ショートカットキー
は Ctrl+j
にしましたが、お好みのキーへ設定してください。
詳細解説
上で「カーソル位置にある引数を ARG
とする」と説明しましたが、正確には「引数
上か、引数の右隣の空白にカーソルがある場合その引数を ARG
とする」で
す。~complete-filename-by-percol~ でディレクトリを補完すると、その右隣の空白に
カーソルが移動するのでもう1回 Ctrl+j
を押せばそのディレクトリをさらに掘るこ
とができます。percol の決定も Ctrl+j
でできるので、 Ctrl+j
を連打すればど
んどんディレクトリを掘り進められます。とても楽です。
complete-filename-by-percol
はファイルよりも一意なディレクトリを優先します。
たとえば
$ ls -F example/ example.tar.gz
というよくあるディレクトリを考えます。
$ ls example
このようなコマンドライン上で example 上にカーソルがあるとします。この状態で
complete-filename-by-percol を発動すると、example と example.tar.gz の絞り込み
ではなく、example ディレクトリ中のファイルの絞り込みになります。これはディレク
トリを掘り進めるほうが需要があるだろうと思った為です。example.tar.gz を補完し
たければ TAB
などでやるといいでしょう。example と example.tar.gz が両方共ファ
イルであれば、単純に percol による絞り込みになります。
関数の中で split-shell-arguments
や modify-current-argument
を用いているの
で、autoload する必要があります。これらのコマンドが、シェルの引数を空白や記号
のエスケープを考慮してパースしてくれるので、非常に楽に書くことができました。そ
のへんのパースを自分で書くのは骨が折れそうです。これらのコマンドについては
man zshcontrib
をご覧ください。
また1行目の set no_nomatch
についてです。zsh は標準ではグロブの展開に失敗し
た(グロブに合致する候補が1つもない)場合には zsh がエラーを出してコマンドが終
了してしまいます。なので ARG*
に合致する候補が1つもない場合、コマンドが途中
で終了してしまいます。 set no_nomatch
することにより zsh によるエラーを避け
ることができます(かわりに ls のエラーになります)。候補が1つもないということ
は補完する必要がないので、あまりコマンドの主要な動作に影響がないといえばないの
ですが、精神衛生上設定しておくと安心です。マッチするパターンがない時のグロブの
動作 -おもてなしの空間でこのオプションについて説明があります。man zshoptions
もどうぞ。
あらかじめ候補の数を知るために ls を実行する必要があるので、実行時間的に大丈夫 かな?と思っていました。試しに5000ファイルあるディレクトリで実行してみましたが、 0.5秒程度で絞り込みが始まったので、大概の場合は大丈夫であろうと思います。 Linux であれば。Windows、というか Cygwin だとちょっとつらいかもしれないです。
既知の問題点としては、隠しファイルの .
が付いているファイル群を補完しようと
して .
の上でこのコマンドを実行してもうまく動きません。そのディレクトリにあ
るファイル全てが絞り込みの対象となります。また $HOME
は必ず ~
に置換します
ので、 /home/user
のままにしておくということはできません。 ARG
で場合分け
すればいいんですが、煩雑になりそうだったのでやってません。 /home/user
のまま
にしておきたいという需要はそんなにないですよね?
まとめ
空気を読んでファイル名を補完してくれる complete-filename-by-percol
コマンド
を紹介しました。近年 zsh の補完機能にもだいぶ慣れて依存度も高くなっていました
が、補完候補が多いとタブだけで補完するのもめんどくさいなぁ、と思っていまし
た。~complete-filename-by-percol~ を使えばその不満を解消し、高速かつ効率的にファ
イル名を補完できるようになります。
ディレクトリ中のファイル数が多いければ多いほど、このコマンドによる効率アップが
効いてくると思います。使い始めてまだ1週間程度ですが、すでに依存しはじめていま
す。おそらくこのコマンドの恩恵を一番受けるのは、ディレクトリ名に日本語が多用さ
れており、ディレクトリを掘るのがめんどくさい!というような人ではなかろうかと思
います。もし日本語なんかいらね、という人は --match-method
を regex にすると
いいでしょう。
ぜひぜひ皆さんこのコマンドを使ってみてください。頑張って条件分岐を書きました が条件の漏れがあるかもしれませんので、こんな時動かんぞー、とかここはこういう 動作にしたほうがいいんじゃないの?などのご意見、ご感想をお待ちしています。