先日、shell勉強会で「zawを使ってシェル操作を快適に」というお話を聴いて、自分ももう少しそのあたりの環境を整えよう、と思い立った。
自分が使う選択をしたのは zaw ではなく percol 。
軽く調べてみたかんじでは zawってのは設定してある(もしくは自作する)決められたsourceを使って決められた操作を行うもので、zshに密接に結び付いているツールで。percolはそういうのではなく純粋に「入力をフィルタリングする」だけのツールなので、パイプなどを使って各コマンドと組み合わせることで色々な使い方ができそう。
percolの導入
Python製のツールなので、sudo pip install percol
とかで入る。
READMEに書いてある通り、~/.percol.d/rc.py
に設定ファイルを用意することでプロンプトやキーマップをカスタマイズできる。
自分はできるだけEmacsの helm と同じような操作感にするため こんなかんじにして使ってる。
percol.view.PROMPT = ur"<green>Input:</green> %q" percol.view.RPROMPT = ur"[%i/%I]" percol.import_keymap({ "C-f" : lambda percol: percol.command.forward_char(), "C-b" : lambda percol: percol.command.backward_char(), "C-p" : lambda percol: percol.command.select_previous(), "C-n" : lambda percol: percol.command.select_next(), "C-h" : lambda percol: percol.command.delete_backward_char(), "C-d" : lambda percol: percol.command.delete_forward_char(), "C-k" : lambda percol: percol.command.kill_end_of_line(), "C-a" : lambda percol: percol.command.beginning_of_line(), "C-e" : lambda percol: percol.command.end_of_line(), "C-v" : lambda percol: percol.command.select_next_page(), "M-v" : lambda percol: percol.command.select_previous_page(), "C-j" : lambda percol: percol.finish(), "C-g" : lambda percol: percol.cancel(), })
ちなみに、現在これと同様なツールとしてgoで書かれたpeco
というものが作られているので、もしかしたら将来的にこちらに移行するかもしれない。
percolでコマンド履歴の検索・絞り込み
READMEに書いてあるけれど。
シェルのhistoryをpercolに流し込むことで より素早く簡単にコマンド履歴を遡れる。
zshの場合はfc -l -n 1
で全履歴を表示できる、のかな。
function percol_select_history() { local tac if which tac > /dev/null; then tac="tac" else tac="tail -r" fi BUFFER=$(fc -l -n 1 | eval $tac | percol --query "$LBUFFER") CURSOR=$#BUFFER # move cursor zle -R -c # refresh } zle -N percol_select_history bindkey '^R' percol_select_history
と設定を書いておけば、Ctrl+Rでの履歴検索をanything likeに快適に行うことができる。
あと、重複したコマンド履歴は必要ないので
setopt hist_ignore_all_dups
も指定しておいた。
percolでディレクトリ移動
の記事の通り、「一度でもcdしたことのあるディレクトリに効率よくcdする」ために、 autojump や z を使わずともpercolでディレクトリ訪問履歴を絞り込んで選択して移動、ということができる。
自分はしばらくzを使ってきてた けど、percol使ったほうが全然便利だわ!
上記参照記事ではchpwd_functions
に履歴の記録を仕込んでいるけれど、z.shを使っていれば履歴データが~/.z
に残っているので 折角なのでそれを使うことにした。
function percol_select_directory() { local tac if which tac > /dev/null; then tac="tac" else tac="tail -r" fi local dest=$(_z -r 2>&1 | eval $tac | percol --query "$LBUFFER" | awk '{ print $2 }') if [ -n "${dest}" ]; then cd ${dest} fi zle reset-prompt } zle -N percol_select_directory bindkey "^X^J" percol_select_directory
_z -r
でランキング順?にsortされて最近訪問したディレクトリ一覧が出力されるので、それをpipeでpercolに渡して選択。
percolでtmuxのwindow選択・切り替え
これもREADMEに書いてあるけれど。
bind b split-window "tmux lsw | percol --initial-index $(tmux lsw | awk '/active.$/ {print NR-1}') | cut -d':' -f 1 | xargs tmux select-window -t"
のようにtmux.conf
に書いておくと、tmuxの現在のsessionで開いているwindowをpercolで絞り込み、選択して切り替えができる。
デフォルトで"w"キーにbindされているtmux choose-window
でも似たようなことが出来るのだけど、window数が多いときにpercolならインクリメンタルに絞り込みができるのと、tmux split-window
を使うことで現在のwindowが隠れることなく選択操作ができるようになるのが利点だと思う。
ちなみに、tmux list-windows
で表示される情報はwindow nameやpane情報だったりするのだけど、percolで絞り込むときなどは特に「そのwindowはどのディレクトリで作業しているものか」などもあった方が嬉しいので、
tmux list-windows -F '#{window_index}: #{window_name}#{window_flags} (#{window_panes} panes) #{pane_current_path} #{?window_active, (active),}
のように#{pane_current_path}
を含むformatを指定して表示させるようにした。最終的にtmux.confには
bind-key C-t split-window -c '#{pane_current_path}' "tmux list-windows -F '#{window_index}: #{window_name}#{window_flags} (#{window_panes} panes) #{pane_current_path} #{?window_active, (active),}' | percol --initial-index $(tmux lsw | awk '/active.$/ {print NR-1}') | cut -d':' -f 1 | xargs tmux select-window -t"
と(長いw)。
あと、window nameには通常は実行中のコマンド名が表示されているのだけれど、ssh
で他hostにログインしている場合はただ"ssh"とだけ出るのではなく そのhost情報などを表示するようにした方が分かりやすいし誤操作を防げそう。ということで
など参考にしつつ、自分ではsshコマンドをラップするシェルスクリプトを用意した。
#!/bin/sh if [ -n "$TMUX" ]; then local_command='tmux rename-window $(echo "%r@%n(%h:%p)")' fi command -p ssh -o PermitLocalCommand=yes -o LocalCommand="${local_command}" "$@" if [ -n "$TMUX" ]; then tmux set-window-option -u automatic-rename fi
tmux上でsshを叩いたときだけ、接続先のhost名などの情報を含むwindow nameに更新し、終了したら元通りに。tmux rename-window
で変更した場合はautomatic-rename
オプションがoffになるようなので、それを戻してあげるのが正しい復元方法だと思う。
percolでweechatのbuffer選択
IRC環境として weechatを使っている けれど、これのchannel選択なんかもpercolでできると嬉しい。
の記事を真似てみようとした。
WeeChat はデフォルトでFifo pluginが有効になっていて、起動中のweechatに対して任意のコマンドを送ることができる。
しかし内部の情報を取得するようなことはできなそうで、やろうとするとそういった機能を持つpluginを作るなり導入するなりする必要がありそうだった。
苦肉の策として、「コマンド送信によって現在のchannel(buffer)一覧情報をファイル保存させ、そのファイルを読み込む」という方法で一覧を取得することにした。
for fifo in $HOME/.weechat/weechat_fifo_*; do echo '*/mute layout store' > $fifo echo '*/mute save weechat' > $fifo done
のようにFIFO pipeに対しlayout store
とsave
を送ることで、現在のbuffer情報が~/.weechat
以下に保存される(毎回その結果がweechatのbufferに出力されるのを防ぐためにmute
コマンド経由で)。
あとはそれで保存された設定ファイルを読み込んでpercolに渡し、選択結果を使ったbuffer切り替えコマンドを再度FIFO pipeに流せばよい。
BUFFER=$(grep 'default.buffer' $HOME/.weechat/weechat.conf | cut -d'"' -f2 | percol | cut -d';' -f3) if [ -n "$BUFFER" ]; then for fifo in $HOME/.weechat/weechat_fifo_*; do echo '*/mute buffer' $BUFFER > $fifo done fi
これでだいぶ捗る。