fish shell の補完で補完関数を用いる方法と設計思想

## 経緯

この前、[fish shell で補完の実装 - complete](http://qiita.com/sosuke/items/4e763afb9e46f82ea9ea)という記事を書きました。

ざっと目を通していただけるとありがたいのですが、まとめると以下です。

### 前回のまとめ

`fish`の補完を定義する`complete`コマンドでは
`-a`オプションで補完候補の単語を指定するのですが
サブコマンドなどで補完候補を変えるには
`-n`オプションで指定する関数によって場合分けをする。

```
# fish completion example

complete -c {コマンド} -n "{現在のコマンドに対する引数が０個の時０を返す関数}" -a "{引数が０個の時補完したい候補}"
complete -c {コマンド} -n "{現在のコマンドに対する引数が１個の時０を返す関数}" -a "{引数が１個の時補完したい候補}"
```
という話でした。

### `fish`では補完関数による候補生成ができないのか？

[Shell Script Advent Calendar 2015](http://qiita.com/advent-calendar/2015/shell-script) 15日目の [@akinomyoga](http://qiita.com/akinomyoga)さんの記事内にて。

> `fish` では補完関数による候補生成ができないのですかね…??

そうですよね。できて欲しいです。
[fish-shellのcompletionsフォルダ](https://github.com/fish-shell/fish-shell/tree/b9b39f7c31d2514788f7b3bbacb966ad15578b5e/share/completions)のソースを見てもされていないので、考えもしませんでした。すいません。

## `bash`と`fish`の補完の比較

`bash`の[complete](https://www.gnu.org/software/bash/manual/html_node/Programmable-Completion-Builtins.html)

>
complete [-abcdefgjksuv] [-o comp-option] [-DE] [-A action] [-G globpat] [-W wordlist]
[-F function] [-C command] [-X filterpat]
[-P prefix] [-S suffix] name [name …]

`fish`の[complete](http://fishshell.com/docs/current/commands.html#complete)

>
complete ( -c | --command | -p | --path ) COMMAND
　        [( -s | --short-option ) SHORT_OPTION]
　        [( -l | --long-option | -o | --old-option ) LONG_OPTION]
　        [( -a | --arguments ) OPTION_ARGUMENTS]
　        [( -w | --wraps ) WRAPPED_COMMAND]
　        [( -d | --description ) DESCRIPTION]


今回注目するオプションだけ表にまとめると

|オプションの機能|bash|fish|
|:-:|:-:|:-:|
|候補単語を指定|-W|-a|
|補完関数を指定|-F|なし|
|候補の説明を付加|なし|-d|

となります。

ただし、`-a`オプションでコマンド置換によってコマンドの出力を候補単語に指定すれば補完関数を指定することと同様なことが可能です。

### コマンド置換による補完関数の代替

```fish
function demo
    echo "demo"
end

function __demo_subcommand
    set cmd (commandline -opc)
    if [ (count $cmd) -eq 2 ]
        ls
    else
        set -l DEMO_SUBCOMMAND yes no
        for SUB in $DEMO_SUBCOMMAND
            echo $SUB
        end
    end
end

complete -f -c demo -a "(__demo_subcommand)"
```

上記のコードを実行すると`demo`コマンドの補完が以下のようになりました。

![https://gyazo.com/20311389a5e7d74f8baf1c66ab186ad4](https://i.gyazo.com/20311389a5e7d74f8baf1c66ab186ad4.gif)

補完関数による候補生成ができました。

では、なぜ本家でやられていないのか？

## `fish`の設計思想

[fish: Design document](http://fishshell.com/docs/current/design.html)

`fish`の設計思想の中の１つに`The law of discoverability`というものがあり、以下のように記されています。

>
The law of discoverability
>
A program should be designed to make its features as easy as possible to discover for the user.
...
Examples:
　　Everything should be tab-completable, and every tab completion should have a description.

__すべての補完候補に説明がついているべき__ ということらしいです。

### `-d` オプション

上のデモでのコード下記のように一部変えてみます。

```fish
complete -f -c demo -a "(__demo_subcommand)" -d "demo sub command"
```

![https://gyazo.com/2f15213ceb99c530c45207f45b3282a4](https://i.gyazo.com/2f15213ceb99c530c45207f45b3282a4.gif)

候補の後ろに説明はつきましたが、すべて同じです。

つまり、候補ごとに説明を付加するために、候補ごとに`complete`を実行していたから、補完関数を用いる必要がなかったのですね。

納得しました。

## 最後に

これで、また少し`fish`が少しわかった気がします。
最初から設計思想読めよって話ですね汗

あと、[zsh-completions](https://github.com/zsh-users/zsh-completions)のようなものがあればいいなあと思いました。