Fish用の補完スクリプトの作り方

1. Fish用の補完スクリプトの作り方
2. [Fish用の補完スクリプトを実際に作ってみる(4種)](https://qiita.com/nil2/items/a341210a5e87d6dbcfa0)

Fishにはプログラム補完という機能があり、
このプログラム補完によって、タブ補完起動時にコマンドに応じた補完がされるようになっています。

コマンドに応じた補完は全てのコマンドについて用意されている訳ではありませんが、
補完スクリプトを作成することで、コマンドに応じた補完を追加することが可能です。
また、コマンドに対応したmanがあれば、コマンドに応じた補完が自動生成されるようにもなっています。

この記事では、この補完スクリプトの作り方について解説します。

## 準備

次のものが必要です。

- Fish

最初からFishが入っているケースはまれですが、
Fishはほとんどの環境でパッケージマネージャ経由で入手できるようになっています。
Fishが入っていなければパッケージマネージャでFishをインストールしてください。

## 補完スクリプトの作り方

補完スクリプトは単なるFishスクリプトです。
補完スクリプトとして成立させるには次のものを含める必要があります。

1. completeコマンドでの補完の設定

例を見てみましょう。
noteという、次のようにシェルスクリプトで書かれたコマンドがあったとします。
このコマンドは`note edit <name>`でメモの編集、`note remove <name>`でメモの削除、`note list`でメモの一覧表示をする、というコマンドです。

```shell:note
#!/bin/sh
set -eu

cmd=$1
shift
case $cmd in
    edit)
        "${EDITOR:-vi}" -- "$HOME/.note/$1"
        ;;
    remove)
        rm -- "$HOME/.note/$1"
        ;;
    list)
        ls -- "$HOME/.note"
        ;;
    *)
        printf "%s\n" "${0##*/}: $cmd: no such sub command" >&2
        ;;
esac
```

このnoteに対する補完スクリプトは次のようになります。
補完スクリプトの作り方にはおおまかに分けて3通りの作り方があるので、3つの例を紹介します。

```fish:note.fish
# 1. completeのみで作る作り方
complete -c note -x

complete -c note -n '__fish_use_subcommand' -xa edit -d 'Edit note'
complete -c note -n '__fish_seen_subcommand_from edit' -xa '(note list)' -d 'Note'

complete -c note -n '__fish_use_subcommand' -xa remove -d 'Remove note'
complete -c note -n '__fish_seen_subcommand_from remove' -xa '(note list)' -d 'Note'

complete -c note -n '__fish_use_subcommand' -xa list -d 'List notes'
```

```fish:note.fish
# 2. completeとヘルパ関数で作る作り方
function __complete_note_commands
    printf '%s\t%s\n' 'edit'   'Edit note'
    printf '%s\t%s\n' 'remove' 'Remove note'
    printf '%s\t%s\n' 'list'   'List notes'
end

function __complete_note_list
    note list | awk '{printf "%s\t%s\n", $0, "Note"}'
end

complete -c note -x
complete -c note -n '__fish_use_subcommand' -xa '(__complete_note_commands)'
complete -c note -n '__fish_seen_subcommand_from edit remove' -xa '(__complete_note_list)'
```

```fish:note.fish
# 3. 補完関数のみで作る作り方
function __complete_note
    set -l words (commandline -pco)
    set -l cword (count $words)

    switch $cword
        case 1
            printf '%s\t%s\n' 'edit'   'Edit note'
            printf '%s\t%s\n' 'remove' 'Remove note'
            printf '%s\t%s\n' 'list'   'List notes'
        case '*'
            switch $words[2]
                case edit
                    note list | awk '{printf "%s\t%s\n", $0, "Note"}'
                case remove
                    note list | awk '{printf "%s\t%s\n", $0, "Note"}'
                case list
            end
    end
end
complete -c note -xa '(__complete_note)'
```

**1. completeのみで作る作り方**は、completeコマンドだけで補完を実装する作り方です。
複雑なコマンドでなければこの作り方で済み、デフォルトで考慮されるあれこれの恩恵にもあずかれるため、基本的にはこの作り方で作ります

**2. completeとヘルパ関数で作る作り方**は、1に加えて、completeコマンドの-aオプション、-nオプションで利用する関数を定義しながら作る作り方です。
1の作り方ではごちゃごちゃする場合にこの作り方で作ります。1の作り方から移行可能なほか、デフォルトで考慮されるあれこれの恩恵にもあずかれます。

**3. 補完関数で全て作る作り方**は、補完の全てを担う補完関数を定義して作る作り方です。
デフォルトで考慮されるあれこれの恩恵にあずかれなくなりますが、dd用の補完やip用の補完のようにcompleteコマンド主軸では作りづらい補完はこの作り方で作ります。

このように作り方は色々ありますが、
基本的には1か2、どちらかきれいにまとまる方を選び、
1か2で作るのが難しい場合に3を選ぶので、作り方を迷うことはそうありません。

いずれの作り方をとるにせよ、
補完スクリプトではcompleteというコマンドを用いて補完の設定を行います。
補完スクリプトでやることはそれだけです。
補完の設定をするFishスクリプトを作成すれば、補完スクリプトが完成します。

あとは、この補完スクリプトを何かしらの方法で読み込めば、
該当のコマンドについてコマンドに応じた補完がされるようになります。

補完スクリプトを読み込む方法は後述しますが、
基本的には、補完スクリプトを`コマンド名.fish`という名前のファイルとして保存し、
`~/.config/fish/completions`の配下に置くようにします。

そうすることで、該当のコマンドの引数を補完するタイミングで補完スクリプトが自動読み込みされるようになります。

コマンド名が`note`の場合、次の場所に補完スクリプトを配置します。

```
~/.config/fish/completions/note.fish
```

試作の段階では、編集の都度sourceするようにしたほうが試作しやすいので、
完成してから`~/.config/fish/completions`の配下に置くするようにするとよいでしょう。

コマンド名が`note`の場合、次のようなコマンドで補完スクリプトの読み込み・再読み込みができます。
(コマンドはnote.fishを置いているディレクトリで実行します)

```fish
source note.fish
```

補完スクリプトのおおまかな作り方としてはこれぐらいです。
あとは、補完スクリプトで使ういくつかのコマンドを覚えれば、補完スクリプトの自作ができるようになります。
補完スクリプトで使ういくつかのコマンドを一つ一つ見ていきましょう。

### 覚えるもの

覚える必要のあるものは次の2つです。

- commandline
  - コマンドライン情報を取得するコマンド
- complete
  - 補完の設定をするコマンド

別途、複数ある補助関数も覚えておくと便利でしょう。

一つ一つ見ていきましょう。

#### commandline

コマンドライン情報を取得するコマンドです。
補完関数、ヘルパ関数でコマンドラインの情報を取得するために使用します。

```fish
# カーソル位置の引数(文字列)を取得する
commandline -tc

# カーソル位置の一つ前までの引数全体(文字列の配列)を取得する
commandline -pco
```

commandlineコマンドできることはコマンドライン情報の取得だけではありませんが、
基本的には、カーソル位置の引数の取得、引数全体の取得のためだけに使います。

イディオムとして、
補完関数、ヘルパ関数では次のようにしてコマンドライン情報を取得します。
(例なのでcur、words、cword、prevと全て取得していますが、取得するのは必要なものだけで十分です)

```fish
set -l cur (commandline -tc)
set -l words (commandline -pco)
set -l cword (count $words)
set -l prev $words[$cword]
```

このようにすると、
次のコマンドライン情報を得ることができます。

| 変数  | 値                                 |
|-------|------------------------------------|
| cur   | カーソル位置の引数                 |
| words | カーソル位置の一つ前までの引数全体 |
| cword | カーソル位置の引数の番号           |
| prev  | カーソル位置の一つ前の引数         |

#### complete

補完の設定をするコマンドです。
補完スクリプトで補完の定義を行うために使用します。

completeはオプションで設定をする作りになっており、
多くのオプションを取るようになっていますが、作る上では次のオプションをおさえれば大丈夫です。

| オプション  | 概要                         |
|-------------|------------------------------|
| -c          | 設定対象のコマンドの指定     |
| -r -f -x -a | 補完候補(通常の引数)の指定   |
| -s -l -o    | 補完候補(オプション)の指定   |
| -d          | 補完候補の説明の指定         |
| -n          | 補完条件の指定               |
| -w          | 設定を継承するコマンドの指定 |

##### -c (設定対象のコマンドの指定)

-cは設定対象のコマンドを指定するオプションです。
このオプションは単体では使いません。他のオプションとセットで指定します。

```fish
# 基本形式
complete -c 設定対象のコマンド ...

# noteを対象として補完の設定をする
complete -c note ...

# w3mを対象として補完の設定をする
complete -c w3m ...
```

##### -r -f -x -a (補完候補(通常の引数)の指定)

-r、-f、-x、-aは補完候補(通常の引数)を指定するオプションです。
これらのオプションは補完候補をどのようなものにするかによって組み合わせて指定します。

| オプション | 概要                                                                                   |
|------------|----------------------------------------------------------------------------------------|
| -r         | 値を取るオプションとして設定する(後述する-s、-l、-oとセットで指定したときのみ機能する) |
| -f         | ファイルパスを補完候補から抜く                                                         |
| -x         | -rと-fを同時に指定にする                                                               |
| -a         | 指定した値を補完候補に追加する                                                         |

```fish
# 基本形式
complete -c 設定対象のコマンド 補完候補(通常の引数)の指定のオプション

# noteの引数の補完候補からファイルパスを抜く(補完候補: 無し)
complete -c note -x

# noteの引数の補完候補からファイルパスを抜き、edit、remove、listを追加する(補完候補: edit、remove、list)
complete -c note -xa 'edit remove list'

# noteの引数の補完候補からファイルパスを抜き、note listの出力結果を追加する(補完候補: note listの実行結果(行単位))
complete -c note -xa '(note list)'

# noteの引数の補完候補からファイルパスを抜き、ディレクトリパスを追加する(補完候補: ディレクトリパス)
complete -c note -xa '(__fish_complete_directories)'
```

-rはオプションを値を取るオプションとして設定するオプションです。
-rは-s、-l、-oとセットで指定したときのみ機能するオプションですので後ほど解説します。-rは-f、-a、-xとだけ組み合わせても無視されます。
おおまかには指定のオプションが値を取る場合に指定します。

-fは補完候補からファイルパスを抜くオプションです。
デフォルトではファイルパスが補完候補となっているため、このオプションを指定しなければファイルパスが補完候補に加わることとなります。
基本的には、-f単体で使うと補完候補が無くなってしまうため、後述する-aとセットで使用します。

-xは-rと-fを同時に指定するオプションです。
オプションに`-r -f`を指定することと、`-x`を指定することは等価となります。
後述する-aを指定する際には-rと-fをセットで指定することが多いため、-r、-fを使わず-xを使うことが多いです。

-aは補完候補を追加するオプションです。
-aの値にはスペース区切りの単語一覧、または括弧でくくったコマンドを指定します。
-aの値にスペース区切りの単語一覧を指定した場合は、単語一覧がそのままその補完候補一覧となり、
-aの値に括弧でくくったコマンドを指定した場合は、コマンドの出力が改行区切りの補完候補一覧となります。
加えて、コマンドの出力の1行がタブで2列に区切られていた場合、1列目が補完候補、2列目が補完候補の説明として扱われます。
補完候補の説明は後述する-dで指定できるのですが、補完候補ごとに説明を変えたい場合はコマンドの出力をタブ区切りにする必要があります。
-aの値に指定するコマンドには、通常のコマンドを使うか、定義済みの補助関数を使うか、自分で定義したヘルパ関数を使うことになります。

補完候補(通常の引数)はこれらのオプションを組み合わせて指定します。
とはいえ、組み合わせ方の種類は数個だけです。ケース別の組み合わせ方は次のようになります。

| ケース                   | 組み合わせ |
|--------------------------|------------|
| ファイルパスを引数にとる | 無指定     |
| 指定の値を引数にとる     | -xa        |
| 任意の値を引数にとる     | -x         |

##### -s -l -o (補完候補(オプション)の指定)

-s、-l、-oは補完候補(オプション)を指定するオプションです。

| オプション | 概要                                                    |
|------------|---------------------------------------------------------|
| -s         | ショートオプション(-オプション文字)を補完候補に追加する |
| -l         | ロングオプション(--オプション名)を補完候補に追加する    |
| -o         | オールドオプション(-オプション名)を補完候補に追加する   |

```fish
# 基本形式
complete -c 設定対象のコマンド 補完候補(オプション)の指定のオプション

# sedの引数の補完候補に-s、--quiet、--silentを追加する(引数が-で始まっていたときの補完候補: -s、--quiet、--silent)
complete -c sed -s n -l quiet -l silent

# sedの引数の補完候補に-e、--expressionを追加する(引数が-で始まっていたときの補完候補: -e、--expression)
# 加えて、-e、--expressionを任意の文字列を値に取るオプションとする(一つ前の引数が-e、--expressionだったときの補完候補: 無し)
complete -c sed -s e -l expression -x

# sedの引数の補完候補に-f、--fileを追加する(引数が-で始まっていたときの補完候補: -f、--file)
# 加えて、-f、--fileをファイルパスを値に取るオプションとする(一つ前の引数が-f、--fileだったときの補完候補: ファイルパス)
complete -c sed -s e -l expression -r

# grepの引数の補完候補に--binary-filesを追加する(引数が-で始まっていたときの補完候補: --binary-files)
# 加えて、--binary-filesをwithout-match、text、binaryのいずれかを値に取るオプションとする(一つ前の引数が--binary-filesだったときの補完候補: without-match、text、binary)
complete -c grep -l binary-files -xa 'without-match text binary'

# w3mの引数の補完候補に-helpを追加する(引数が-で始まっていたときの補完候補: -help)
complete -c w3m -o help
```

-sはショートオプションを補完候補に追加するオプションです。
引数が`-`で始まっていたとき、`-a`や`-b`のように`"-" + 一文字`で構成されるオプションが補完されるようになります。

-lはロングオプションを補完候補に追加するオプションです。
引数が`-`で始まっていたとき、`--help`や`--version`のように`"--" + 文字列``で構成されるオプションが補完されるようになります。

-oはオールドオプションを補完候補に追加するオプションです。
引数が`-`で始まっていたとき、`-help`や`-version`のように`"-" + 文字列``で構成されるオプションが補完されるようになります。

補完候補(オプション)はこれらのオプションで指定します。
オプションを補完させたいだけなら-aオプションでオプションを補完候補に追加するという手も取れるのですが、
-s、-l、-oを使うことで、次の考慮を受けられるため、オプションを補完候補に追加する際には-aではなく-s、-l、-oを指定します。

1. カーソル位置の引数が`-`で始まっていたときのみオプションを補完をする
2. カーソル位置より前の引数に`--`があった場合、オプションを補完しない
3. カーソル位置のショートオプションが値を取らないオプションだった場合、別のショートオプションを繋げて補完する
4. 別名のショートオプション、ロングオプション、オールドオプションがあった場合にそれらを一つの補完候補にまとめる
5. -rが指定されていた場合、オプションの値の位置(値を取るショートオプションの次の引数、値を取るロングオプション、オールドオプションの次の引数、`--ロングオプション名=`の続きの引数、`-オールドオプション名=`の続きの引数)にて-aで指定した値、またはファイルパスを補完をする

-s、-l、-oとセットで-rを指定すると、
オプションの値の位置にて、-aで指定した値、またはファイルパスが補完されるようになります。
オプションの値は通常の引数と同様に、-f、-r、-x、-aを組み合わせて指定します。
とはいえ、こちらも組み合わせ方の種類は数個だけです。ケース別の組み合わせ方は次のようになります。

| ケース                       | 組み合わせ       |
|------------------------------|------------------|
| 値を取らないオプション       | -s、-l、-oのみ   |
| ファイルパスを取るオプション | -s、-l、-o + -r  |
| 指定の値を取るオプション     | -s、-l、-o + -xa |
| 任意の値を取るオプション     | -s、-l、-o + -x  |

##### -d (補完候補の説明)

-dは補完候補の説明を指定するオプションです。

```fish
# 基本形式
complete -c 設定対象のコマンド 補完候補の指定のオプション -d 補完候補の説明

# noteの引数の補完候補からファイルパスを抜き、note listの出力結果を追加する(補完候補: note listの実行結果(行単位))
# 補完候補(通常の引数)の説明はNoteとする
complete -c note -xa '(note list)' -d 'Note'

# sedの引数の補完候補に-e、--expressionを追加する(引数が-で始まっていたときの補完候補: -e、--expression)
# 加えて、-e、--expressionを任意の文字列を値に取るオプションとする(一つ前の引数が-e、--expressionだったときの補完候補: 無し)
# 補完候補(オプション)の説明はAdd the script to the commands to be executedとする
complete -c sed -s e -l expression -x -d 'Add the script to the commands to be executed'

# grepの引数の補完候補に--binary-filesを追加する(引数が-で始まっていたときの補完候補: --binary-files)
# 加えて、--binary-filesをwithout-match、text、binaryのいずれかを値に取るオプションとする(一つ前の引数が--binary-filesだったときの補完候補: without-match、text、binary)
# 補完候補(オプション)の説明はAssume that binary files are TYPEとする
# 補完候補(オプションの値)の説明はwithout-matchはSkip binary files、textはProcess binary file as text、binaryはProcess binary file as binaryとする
function __complete_grep_binary_files
  printf '%s\t%s\n' 'without-match' 'Skip binary files'
  printf '%s\t%s\n' 'text'          'Process binary file as text'
  printf '%s\t%s\n' 'binary'        'Process binary file as binary'
end
complete -c grep -l binary-files -xa (__complete_grep_binary_files)' -d 'Assume that binary files are TYPE'
```

-dを指定すると、通常時は補完候補に説明が追加され、
-s、-l、-oがセットで指定されていた場合はオプションに説明が追加されます。
-s、-l、-oで指定したオプションの値に説明を追加することは-dではできません。
-s、-l、-oで指定したオプションの値に説明を追加したい場合は、-aの値に括弧でくくったコマンドを指定し、コマンドの出力を補完候補と補完候補の説明をタブ区切りにしたものにする必要があります。

##### -n (補完条件の指定)

-nは補完候補を追加する条件を指定するオプションです。

```fish
# 基本形式
complete -c 設定対象のコマンド -n 補完条件 補完候補の指定のオプション

# カーソル位置がnoteの最初の引数のとき、noteの引数の補完候補からファイルパスを抜き、editを追加する(補完候補: edit)
# 補完候補(通常の引数)の説明はEdit noteとする
complete -c note -n '__fish_use_subcommand' -xa edit -d 'Edit note'

# noteの最初の引数がeditのとき、noteの引数の補完候補からファイルパスを抜き、note listの出力結果を追加する(補完候補: note listの実行結果(行単位))
# 補完候補(通常の引数)の説明はNoteとする
complete -c note -n '__fish_seen_subcommand_from edit' -xa '(note list)' -d 'Note'
```

-nを指定すると、-nの値で指定したコマンドを実行して正常終了するときのみ、補完候補の追加が行われるようになります。
-nの値に指定するコマンドには、通常のコマンドを使うか、定義済みの補助関数を使うか、自分で定義したヘルパ関数を使うことになります。

##### -w (設定を継承するコマンドの指定)

-wは補完の設定を引き継ぐコマンドを指定するオプションです。
このオプションは既存のコマンドの拡張となるコマンドに対する補完を作る際に使用します。

```
# 基本形式
complete -c 設定対象のコマンド -w 設定の引継ぎ元のコマンド

# gitの補完の設定をhubに引き継ぐ
complete -c hub -w git

# lsの補完の設定をmylsに引き継ぐ
complete -c myls -w ls
```

#### 補助関数

Fishには-a、-nで利用できる補助関数が最初からいくつか定義されています。
シンプルな補完であれば、-a、-nで利用する関数は次の定義済みの補助関数だけで済むようになっています(利用数は公式の補完スクリプトで使われている回数です)

もしこの中に無ければ自分で補助関数を定義する必要があります。
また、定義済みの補助関数については公式のドキュメントが無いので、補助関数の詳細を知るにはFishの公式リポジトリのshare/functions配下のソースを読む必要があります。

##### -aで利用する補助関数

| 関数名                          | 引数                 | 利用数 | 補完候補                       |
|---------------------------------|----------------------|--------|--------------------------------|
| \_\_fish\_complete\_path        | 無し                 | 4      | ファイルパス                   |
| \_\_fish\_complete\_suffix      | 接尾辞               | 121    | 指定の接尾辞を持つファイルパス |
| \_\_fish\_complete\_directories | 無し                 | 124    | ディレクトリパス               |
| \_\_fish\_complete\_users       | 無し                 | 45     | ユーザ名                       |
| \_\_fish\_complete\_groups      | 無し                 | 17     | グループ名                     |
| \_\_fish\_complete\_pids        | 無し                 | 12     | プロセスID                     |
| \_\_fish\_complete\_command     | 無し                 | 10     | コマンド名                     |
| \_\_fish\_complete\_subcommand  | 無し                 | 23     | サブコマンド                   |
| \_\_fish\_complete\_list        | 区切り文字、補完関数 | 25     | 任意のリスト                   |
| \_\_fish\_print\_packages       | 無し                 | 34     | パッケージ名                   |
| \_\_fish\_print\_hostnames      | 無し                 | 27     | ホスト名                       |
| \_\_fish\_print\_encodings      | 無し                 | 14     | 文字コード名                   |
| \_\_fish\_print\_filesystems    | 無し                 | 6      | ファイルシステム名             |
| \_\_fish\_print\_interfaces     | 無し                 | 1      | ネットワークインターフェース名 |

ほとんどが-aで指定するだけで使えるものとなっています。
\_\_fish\_complete\_subcommandはwinptrやsudoのように別のコマンドを引数に取るコマンド用のものです。
\_\_fish\_complete\_listはカンマ区切りやコロン区切りの引数を取るコマンド用のものです。こちらは区切り文字と一要素一要素を補完する補完関数を引数にとります。

##### -nで利用する補助関数

| 関数名                           | 引数                                               | 利用数 | 条件                                         |
|----------------------------------|----------------------------------------------------|--------|----------------------------------------------|
| \_\_fish\_is\_first\_arg         | 無し                                               | 22     | 最初の引数である                             |
| \_\_fish\_is\_first\_token       | 無し                                               | 140    | 最初の引数である(オプションはスキップ)       |
| \_\_fish\_no\_arguments          | 無し                                               | 70     | 最初の引数である(オプションはスキップ)       |
| \_\_fish\_use\_subcommand        | 無し                                               | 925    | 最初の引数である(オプションはスキップ)       |
| \_\_fish\_seen\_subcommand\_from | サブコマンド名(複数可)                             | 2457   | サブコマンドが指定のサブコマンドである       |
| \_\_fish\_seen\_argument         | -s ショートオプション、-l ロングオプション(複数可) | 29     | 指定のオプションのいずれかを既に取っている   |
| \_\_fish\_contains\_opt          | ロングオプション、-s ショートオプション(複数可)    | 204    | 指定のオプションのいずれかを既に取っている   |
| \_\_fish\_not\_contain\_opt      | ロングオプション、-s ショートオプション(複数可)    | 82     | 指定のオプションのいずれかを既に取っていない |
| \_\_fish\_prev\_arg\_in          | 一つ前の引数(複数可)                               | 19     | 一つ前の引数が指定の値のいずれかである       |

\_\_fish\_is\_first\_token、\_\_fish\_no\_arguments、\_\_fish\_use\_subcommandは名前こそ違うものの、実質的な条件は全く同じです。
\_\_fish\_seen\_argument、\_\_fish\_contains\_optも同じです。
名前だけ違うものについてはどれを使っても問題ありませんが、コードを読む上で一番自然だと思うものを使うといいと思います。

### サンプル

これらのことを踏まえて、noteコマンド用の補完スクリプトを1から作ってみます。
まず、このコマンドはファイルパスを引数に取らないため、-xを指定してファイルパスを補完候補に含めないようにします。

```fish
complete -c note -x
```

これで何も補完されないようになりました
次に、第一引数ではサブコマンドを補完するようにします。

```fish
complete -c note -x
complete -c note -n '__fish_use_subcommand' -xa edit -d 'Edit note'
complete -c note -n '__fish_use_subcommand' -xa remove -d 'Remove note'
complete -c note -n '__fish_use_subcommand' -xa list -d 'List notes'
```

これでサブコマンドが補完されるようになりました。
次にサブコマンドがedit、removeだったときにnote listの実行結果を補完候補に追加するようにします

```
complete -c note -x
complete -c note -n '__fish_use_subcommand' -xa edit -d 'Edit note'
complete -c note -n '__fish_seen_subcommand_from edit' -xa '(note list)' -d 'Note'
complete -c note -n '__fish_use_subcommand' -xa remove -d 'Remove note'
complete -c note -n '__fish_seen_subcommand_from remove' -xa '(note list)' -d 'Note'
complete -c note -n '__fish_use_subcommand' -xa list -d 'List notes'
```

これで完成です。
今回はサブコマンドを取るコマンドなので-s、-l、-oが登場していません。
-s、-l、-oを使った例は[Fish用の補完スクリプトを実際に作ってみる(4種)]()にありますので、そちらを参照してみてください。

## 補完スクリプトの提供の仕方

補完スクリプトが完成したら、
それを提供できる形にしておきましょう。

補完スクリプトを提供するやり方にはいくつか種類があります。

基本的には後述するファイル式の方法がおすすめですが、
補完スクリプトを提供するための方法は大まかに分けて2つあるので、場合によって使い分けるとよいと思います。

### 1. ファイル式

補完スクリプトをファイルとして配布し、
決められた場所に置いてもらうことで読み込んでもらう方法です。

ユーザに次のディレクトリにファイルを置いてもらうことで、
ユーザの環境で該当のコマンドについての補完が有効になります。[^1] [^2]

[^1]: /etc/fish/completionsに置いてもよいのですが、/etc配下はシステムのディレクトリであるためおすすめしません。自分が管轄のシステム共通のものとして置く分にはいいと思います。
[^2]: /usr/share/fish/vendor_completions.dに置いてもよいのですが、/usr配下はシステムのディレクトリであるためおすすめしません。make installやパッケージマネージャ経由で置く分にはいいと思います。その場合は配置先のディレクトリは直パスではなく、pkg-config --variable completionsdir fishで出力されるパスを指定してください。

```
~/.config/fish/completions
(正確には$XDG_CONFIG_HOME/fish/completions)
```

補完スクリプトのファイル名は次の形式の名前にします。

```
コマンド名.fish
```

例として、noteというコマンドに対応する補完スクリプトであれば、次の場所に置くことになるでしょう。

```
~/.config/fish/completions/note.fish
```

基本的にはこの方法がおすすめです。

### 2. source式

コマンドに補完スクリプトの出力機能を組み込み、
補完スクリプトをプロセス置換とsourceで読み込んでもらう方法です。

ユーザに次のような設定を~/.config/fish/config.fish等に書いてもらうことで、
ユーザの環境で該当のコマンドについての補完が有効になります。

```fish
type -q コマンド; and source (補完スクリプトを出力するためのコマンド | psub)
```

これはtypeでコマンドの有無をチェックし、
コマンドがあれば、補完スクリプトを出力するコマンドを実行、プロセス置換で出力を一時ファイルに保存し、sourceで読み込む、という設定です。

例として、noteというコマンドが`note complete シェル名`としたときに補完スクリプトを出力するようになっていたとすれば、
次のような設定を書くことになるでしょう。

```fish
type -q note; and source (note complete fish | psub)
```

コマンドに機能を追加する必要があるため、
無理にこの方法を採用することもないと思いますが、
rbenvのように設定ファイルでevalをしてもらうタイミングがあるコマンドであるとか、
コマンドラインアプリを作るライブラリに補完スクリプトを生成する機能があるとかであれば、
この方法も選択肢に入れていいのかなと思います。

## まとめ

このように、Fish用の補完スクリプトは作る上で覚えることが少なく、
自作が結構しやすいものとなっています。

補完スクリプトの作り方、提供の仕方についてはこれぐらいなので、
あとは、この記事の続きとなる[Fish用の補完スクリプトを実際に作ってみる(4種)](https://qiita.com/nil2/items/a341210a5e87d6dbcfa0)や、
下記の参考リンクにある情報を参照してFish用の補完を作ってみてください。

## 参考リンク

### 公式

Fishの公式リポジトリ
https://github.com/fish-shell/fish-shell

Fishの公式ドキュメント
https://fishshell.com/docs/current/index.html

補完の作り方についての公式ドキュメント
https://fishshell.com/docs/current/index.html#writing-your-own-completions

commandlineについての公式ドキュメント
https://fishshell.com/docs/current/cmds/commandline.html

completeについての公式ドキュメント
https://fishshell.com/docs/current/cmds/complete.html

Fishの公式リポジトリにはshare/completions配下に大量のサンプルがあり、とても参考になると思います。
公式のドキュメントにある補完の作り方、commandline、completeについてのドキュメントも参考になると思います。ただ、こちらについては後述する日本語訳のものを参照して問題ないと思います。
補助関数については公式のドキュメントが無いので、補助関数の詳細を知るにはFishの公式リポジトリのshare/functions配下のソースを読む必要があります。

また、次のFishのマニュアルの日本語訳も参考になると思います。
少しバージョンが古めですが、補完回りの仕組みは大きく変わっていないので、こちらを見ても問題ないと思います。
Fishスクリプトの書き方についても参考になると思います。

全訳！fishシェル普及計画(公式ドキュメントの日本語訳)
http://fish.rubikitch.com/

補完の作り方についての公式ドキュメントの日本語訳
http://fish.rubikitch.com/document-ja#655806bc25d47ab50429cd006ca797be

commandlineについての公式ドキュメントの日本語訳
http://fish.rubikitch.com/commandline/

completeについての公式ドキュメントの日本語訳
http://fish.rubikitch.com/complete/

### 記事

Fishの補完についてのまとめの記事もあります。
こちらも参考になると思います。

fishの補完設定覚え書き
https://qiita.com/ryotako/items/31f9c9153bece58f2d98

fish shell で補完の実装 - complete
https://qiita.com/sosuke/items/4e763afb9e46f82ea9ea

fish shell の補完で補完関数を用いる方法と設計思想
https://qiita.com/sosuke/items/a0b516652241be856c4a

Oracle Cloud Infrastructure CLI用のfish補完を作ってみた
https://qiita.com/sugimount/items/fb71920f846175633d68