$shibayu36->blog;

クラスター株式会社のソフトウェアエンジニアです。エンジニアリングや読書などについて書いています。

ターミナル版anything的なpercolをzawの代わりに試してみた

 emacsを使っているとterminalでもanything的にいろいろやりたくなるんだけど、そういう時にこれまでzawというツールを使ってきた。

 zaw結構便利なんだけど問題点もある。

  • 読み込む行数が増えてくると遅くなる
    • 履歴検索で10万行とか行くと動かないので致命的
  • zshに完全に紐付いてしまって、気軽には使えない

 で、この前YAPCid:moozさんと話してて、percolという便利ツール作ってると聞いたので、試してみた。

percolとは

 紹介記事などがあるので、それを参考に。

 簡単に言うとterminal版anythingもしくはUnite。標準入力で与えたものを行単位で絞込み、行を選択すると最後に標準出力でその行を出力するだけ。このインターフェースのお陰で適当にpipeでつなげるだけでも便利に使えるようになっている。

 以下は履歴検索して複数選択すると、標準出力にそれらが出力される様子。
f:id:shiba_yu36:20131006143100g:plain

インストール

 pipで入れるらしい(https://github.com/mooz/percol#pypi)。brewpythonをインストールしていると、pipコマンドが付いてくるのでそれで入れる。

$ pip install percol

設定

 まずemacs風に扱うために~/.percol.d/rc.pyを編集。

## keymap
# Mac で delete(backspace)が効くようにする
SPECIAL_KEYS.update({
    127: '<backspace>'
})
percol.import_keymap({
    "C-a" : lambda percol: percol.command.beginning_of_line(),
    "C-e" : lambda percol: percol.command.end_of_line(),
    "C-b" : lambda percol: percol.command.backward_char(),
    "C-f" : lambda percol: percol.command.forward_char(),
    "C-d" : lambda percol: percol.command.delete_forward_char(),
    "C-h" : lambda percol: percol.command.delete_backward_char(),
    "C-k" : lambda percol: percol.command.kill_end_of_line(),
    "C-y" : lambda percol: percol.command.yank(),
    "C-n" : lambda percol: percol.command.select_next(),
    "C-p" : lambda percol: percol.command.select_previous(),
    "C-v" : lambda percol: percol.command.select_next_page(),
    "M-v" : lambda percol: percol.command.select_previous_page(),
    "M-<" : lambda percol: percol.command.select_top(),
    "M->" : lambda percol: percol.command.select_bottom(),
    "C-m" : lambda percol: percol.finish(),
    "C-j" : lambda percol: percol.finish(),
    "C-g" : lambda percol: percol.cancel(),
    "M-c" : lambda percol: percol.command.toggle_case_sensitive(),
    "M-m" : lambda percol: percol.command.toggle_finder(FinderMultiQueryMigemo),
    "M-r" : lambda percol: percol.command.toggle_finder(FinderMultiQueryRegex)
})

 次にpercolでいろいろ関数を作りやすいようにする。.zshrcにpercolを使った便利ツール書きまくると管理できなくなるので、特定ディレクトリにpercolのsourceを置いておいてそれを読み込む設定を書く。

# ~/.zshrc
# setting for percol
source ~/.zsh/percol.zsh
# ~/.zsh/percol.zsh
# load percol sources
for f (~/.zsh/percol-sources/*) source "${f}"

上記の設定をそれぞれ作っておけば、~/.zsh/percol-sources/以下のファイルはzsh起動時に全てsourceしてくれるので、このディレクトリに便利コマンド設定を作っておけば良い。

履歴検索をpercolで

 とりあえず履歴検索してコマンドを選択できるようにする。https://gist.github.com/mitukiii/4234173 が非常に参考になる。

 ~/.zsh/percol-sources/select-history.zshというファイルを作り、以下をコピペ。

function percol-select-history() {
    local tac
    if which tac > /dev/null; then
        tac="tac"
    else
        tac="tail -r"
    fi
    BUFFER=$(history -n 1 | \
        eval $tac | \
        percol --query "$LBUFFER")
    CURSOR=$#BUFFER
    zle clear-screen
}
zle -N percol-select-history

 あとは適当に.zshrcなりにkeybindを追加。

bindkey '^r' percol-select-history


 これでC-rを押したら、履歴検索してターミナルに表示するということが出来るようになった。

git-recent-branchesをpercolで

 以前gitのbranchを更新順に並べてcheckoutできるやつをzawで作った。http://shibayu36.hatenablog.com/entry/2013/06/04/203833 。これをpercolでやってみる。

 ~/.zsh/percol-sources/git-recent-branches.zsh というファイルを作り、以下をコピペ。

function percol-git-recent-branches () {
    local selected_branch=$(git for-each-ref --format='%(refname)' --sort=-committerdate refs/heads | \
        perl -pne 's{^refs/heads/}{}' | \
        percol --query "$LBUFFER")
    if [ -n "$selected_branch" ]; then
        BUFFER="git checkout ${selected_branch}"
        zle accept-line
    fi
    zle clear-screen
}
zle -N percol-git-recent-branches

function percol-git-recent-all-branches () {
    local selected_branch=$(git for-each-ref --format='%(refname)' --sort=-committerdate refs/heads refs/remotes | \
        perl -pne 's{^refs/(heads|remotes)/}{}' | \
        percol --query "$LBUFFER")
    if [ -n "$selected_branch" ]; then
        BUFFER="git checkout -t ${selected_branch}"
        zle accept-line
    fi
    zle clear-screen
}
zle -N percol-git-recent-all-branches

 あとは適当に.zshrcにキーバインドを追加。

bindkey '^x^b' percol-git-recent-branches
bindkey '^xb' percol-git-recent-all-branches

 これでブランチを選択してcheckout出来るようになった。
f:id:shiba_yu36:20131006145014g:plain

プロセス検索をpercolで

 あとpercolは標準入力を受け取って標準出力に表示するだけなので、プラグインみたいなのを作らなくても適当にコマンドラインでpipeに渡すだけでも便利に使える。

 例えばプロセス検索して選択してkillするやつは以下のように書ける。便利ですね。

$ ps ax | percol | awk '{ print $1 }' | xargs kill

f:id:shiba_yu36:20131006150335g:plain

まとめ

 percolの紹介しました。zawと比べて拡張性には欠けますが、シンプルなお陰でパイプでいい感じにコマンド作れて非常に便利です。あと速いのでzawとかで履歴検索が出来なかった人は使ってみると良さそうに思いました。