コマンドラインでの選択的インターフェイスについて考える

##はじめに

ここで言う選択的インターフェイスというのは、EmacsでのAnything的なツールのことを言います。この点については、イメージ画像を見たほうが分かりやすいでしょう。

このようなツールは、様々な応用可能性があります。

選択的インターフェイスでは、パイプで繋ぐことにより、出力結果を選択できるようになります。そして、選択されたものが最終的に出力されることになります。

この出力を使うことによって、様々なツールの仲介を任せたり、値を取得したりすることができます。

##percol

`python`で書かれた選択的インターフェイスツールです。

[https://github.com/mooz/percol](https://github.com/mooz/percol)

###percolのインストール

```bash:
$ sudo pip install percol
```

または、

```bash:
$ git clone https://github.com/mooz/percol

$ cd !$:t

$ sudo python setup.py install
```

###percolの基本的な使い方

```bash:
$ ls | percol
```

最初から特定の文字列にマッチさせます。

```bash:
$ ls ~ | percol --query="s"
```

プロンプトの左をカスタマイズする。

```bash:
$ ls | percol --right-prompt=prompt1
```


###percolのキーバインド

percolでは、設定ファイルを作ることでキーバインドを設定することもできます。

```bash:
$ mkdir ~/.percol.d

$ vim ~/.percol.d/rc.py
```


```python:~/.percol.d/rc.py
# https://gist.github.com/mitukiii/4234173
import sys, commands
from percol.command import SelectorCommand
from percol.key import SPECIAL_KEYS
from percol.finder import FinderMultiQueryMigemo, FinderMultiQueryRegex

## prompt
# Case Insensitive / Match Method に応じてプロンプトに表示
def dynamic_prompt():
    prompt = ur""
    if percol.model.finder.__class__ == FinderMultiQueryMigemo:
        prompt += "[Migemo]"
    elif percol.model.finder.__class__ == FinderMultiQueryRegex:
        prompt += "[Regexp]"
    else:
        prompt += "[String]"
    if percol.model.finder.case_insensitive:
        prompt += "[a]"
    else:
        prompt += "[A]"
    prompt += "> %q"
    return prompt

percol.view.__class__.PROMPT = property(lambda self: dynamic_prompt())

## migemo
# Mac と Ubuntu で辞書のパスを変える
if sys.platform == "darwin":
    FinderMultiQueryMigemo.dictionary_path = "/usr/local/Cellar/cmigemo/20110227/share/migemo/utf-8/migemo-dict"
else:
    FinderMultiQueryMigemo.dictionary_path = "/usr/local/share/migemo/utf-8/migemo-dict"

## kill
# Mac の場合は kill（yank）をクリップボードと共有する
if sys.platform == "darwin":
    def copy_end_of_line_as_kill(self):
        commands.getoutput("echo " + self.model.query[self.model.caret:] + " | pbcopy")
        self.model.query  = self.model.query[:self.model.caret]

    def paste_as_yank(self):
        self.model.insert_string(commands.getoutput("pbpaste"))

    SelectorCommand.kill_end_of_line = copy_end_of_line_as_kill
    SelectorCommand.yank = paste_as_yank

## 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 入れて zsh と組み合わせたら超便利](https://gist.github.com/mitukiii/4234173)

[ターミナル版anything的なpercolをzawの代わりに試してみた](http://shibayu36.hatenablog.com/entry/2013/10/06/184146)


###percolの応用的な使い方

####percolで複数行選択する

複数行を選択する場合は、`percol.command.toggle_mark_and_next()`を使います。デフォルトでは、`C-S`に設定されているようです。

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

私の場合は、以下の設定ファイルを書いています。複数行マークとマークの解除です。

```python:~/.percol.d/rc.py
    "C-n" : lambda percol: percol.command.toggle_mark_and_next(),
    "C-p" : lambda percol: percol.command.unmark_all(),
```

また、区切り文字を設定するには、以下のコマンドで行けます。

```bash:
$ ls | percol | tr '\n' ';'
```

####percolのzsh補完

GitHubからcloneする場合は、最初から用意されているみたいです。

[https://github.com/mooz/percol/blob/master/tools/zsh/_percol](https://github.com/mooz/percol/blob/master/tools/zsh/_percol)


```bash:
$ curl -o ~/.zsh/functions/_percol https://raw.githubusercontent.com/mooz/percol/master/tools/zsh/_percol

$ exec $SHELL
```


####percolでcdする

[cdr](http://blog.n-z.jp/blog/2013-11-12-zsh-cdr.html)ができれば、以下の方法で移動を簡単にできるようです。

```bash:~/.zshrc
# http://piyopiyoducky.net/blog/2013/08/17/cdr-with-percol/
### search a destination from cdr list
function percol-get-destination-from-cdr() {
    cdr -l | \
        sed -e 's/^[[:digit:]]*[[:blank:]]*//' | \
        percol --match-method migemo --query "$LBUFFER"
}

### search a destination from cdr list and cd the destination
function percol-cdr() {
    local destination="$(percol-get-destination-from-cdr)"
    if [ -n "$destination" ]; then
        BUFFER="cd $destination"
        zle accept-line
    else
        zle reset-prompt
    fi
}
zle -N percol-cdr
bindkey '^xb' percol-cdr
```

それ以外はこちら。

```bash:~/.zshrc
# https://github.com/shibayu36/config-file/blob/master/.zsh/percol-sources/cdr.zsh
function percol-cdr () {
    local selected_dir=$(cdr -l | awk '{ print $2 }' | percol)
    if [ -n "$selected_dir" ]; then
        BUFFER="cd ${selected_dir}"
        zle accept-line
    fi
    zle clear-screen
}
zle -N percol-cdr
bindkey '^@' percol-cdr
```

参考:

[percolで最近行ったdirectoryにcdするやつを書きました](http://shibayu36.hatenablog.com/entry/2013/10/07/185048)

[Zshのcdrとpercolで最近移動したディレクトリにAnythingライクに移動する - PiyoPiyoDucky](http://piyopiyoducky.net/blog/2013/08/17/cdr-with-percol/)

[autojumpからpercolベースのディレクトリジャンプに移行する | Web scratch](http://efcl.info/2013/1230/res3580/)


####percolとghq

自身のディレクトリ構造を使いやすくします。

```bash:
$ go get github.com/motemen/ghq
```

詳しくは以下の記事で紹介されています。

[ghq + percol + docc | SOTA](http://deeeet.com/writing/2014/06/09/docc/)

[ghqを使ったローカルリポジトリの統一的・効率的な管理について - delirious thoughts](http://blog.kentarok.org/entry/2014/06/03/135300)

[ghq コマンドの zsh 補完ファイルを修正したので、その過程を解説する - Qiita](http://qiita.com/mollifier/items/8cca799b384c0a69db99)


####percolとgit

gitコマンドを使いやすくするためにもpercolは大活躍です。

詳しくは以下の記事で紹介されています。

[git-issue + git-flow + percol](https://gist.github.com/azu/d3fb5f970e4ff945ead4)

[percolとgit-issueを使って、redmineのチケットをブランチに切って作業するのが捗る設定 | Technology-Gym](http://tech-gym.com/2012/07/shell/825.html)

[percolを使って、gitのコミットハッシュ値をクリップボードにコピーする - Qiita](http://qiita.com/hfm/items/ad1641225991d56373d2)

[Git - プルリクエストを自動補完してcheckoutする - Qiita](http://qiita.com/yuku_t/items/f53a9d3ea92614b0927d)


##peco

`percol`を元に`golang`で書かれた選択的インターフェイスツールです。

[https://github.com/lestrrat/peco](https://github.com/lestrrat/peco)


設定ファイルは以下の様な感じに編集できます。

```golang:~/.peco/config.json
{
    "Keymap": {
        "C-p": "peco.SelectPrevious",
        "C-n": "peco.SelectNext",
        "C-c": "peco.Cancel"
    }
}
```

詳しくは以下の記事で紹介されています。

[Windows のコマンドプロンプトを10倍便利にするコマンド「peco」](http://mattn.kaoriya.net/software/peco.htm)

[pecoを導入してzshのhistoryに使うようにした - さよならインターネット](http://blog.kenjiskywalker.org/blog/2014/06/12/peco/)