Fish用の補完スクリプトを実際に作ってみる(4種)

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

Bashの補完スクリプトの作り方をおさえたところで、
早速、4種のコマンドについての補完スクリプトを作成してみましょう。

## vil

https://github.com/nil-two/vil

Vim scriptでテキストを処理するコマンドです。
このコマンドの使い方は次のようになっています。

```
$ vil [OPTION]... {script-only-if-no-other-script} [input-file]...

Options:
  -n, --quiet, --silent
                 suppress automatic printing of buffer
  -e script, --expression=script
                 add the script to the commands to be executed
  -f script-file, --file=script-file
                 add the contents of script-file to the commands to be executed
  --help
                 display this help and exit
  --version
                 display version information and exit
```

オプションと入力ファイルのパスを受け取る、よくある形のコマンドですね。
通常の引数は何もしなくてもファイルパスが補完されるようになっているので、通常の引数については手を加えず、
-s、-lを指定して補完するオプションを追加するだけでよさそうです。
気を付ける点としては次のことぐらいでしょうか。

- -e、--expressionが任意の値を取るオプションであること
- -f、--fileがファイルパスを取るオプションであること

まず、ヘルプ表示に従って各オプションの定義を記述します。

```fish
complete -c vil -s n -l quiet -l silent -d 'Suppress automatic printing of buffer'
complete -c vil -s e -l expression -x -d 'Add the script to the commands to be executed'
complete -c vil -s f -l file -r -d 'Add the contents of script-file to the commands to be executed'
complete -c vil -l help -d 'Display this help and exit'
complete -c vil -l version -d 'Display version information and exit'
```

これで完成です。
今回はオプションを定義するだけで済むものなので、これ以上することはありません。
ただ、解説としてはこれだけだと分かりづらいと思うので、各行についてコメントで解説を付けたものを示します。

```fish
# -c vil     vilを対象に補完の設定をする
# -s n       -nというオプションを補完候補に追加
# -l quiet   --quietというオプションを補完候補に追加
# -l silent  --silentというオプションを補完候補に追加
# -d ...     これらのオプションの説明はSuppress automatic printing of bufferとする
#
# -n、--quiet、--silentは同時に指定したため、これらのオプションは同じオプションとして扱う
complete -c vil -s n -l quiet -l silent -d 'Suppress automatic printing of buffer'

# -c vil         vilを対象に補完の設定をする
# -s e           -eというオプションを補完候補に追加
# -l expression  --expressionというオプションを補完候補に追加
# -x             これらのオプションは値を取るオプションとする。加えて、オプションの値の補完候補からファイルパスを抜く
# -d ...         これらのオプションの説明はSuppress automatic printing of bufferとする
#
# -e、--expressionは同時に指定したため、これらのオプションは同じオプションとして扱う
complete -c vil -s e -l expression -x -d 'Add the script to the commands to be executed'

# -c vil   vilを対象に補完の設定をする
# -s f     -fというオプションを補完候補に追加
# -l file  --fileというオプションを補完候補に追加
# -r       これらのオプションは値を取るオプションとする(-rと-fのセットの-xではなく-r単体を指定しているため、オプションの値の補完候補はファイルパスとなる)
# -d ...   これらのオプションの説明はAdd the contents of script-file to the commands to be executedとする
#
# -f、--fileは同時に指定したため、これらのオプションは同じオプションとして扱う
complete -c vil -s f -l file -r -d 'Add the contents of script-file to the commands to be executed'

# -c vil   vilを対象に補完の設定をする
# -l help  --helpというオプションを補完候補に追加
# -d ...   このオプションの説明はDisplay this help and exitとする
complete -c vil -l help -d 'Display this help and exit'

# -c vil      vilを対象に補完の設定をする
# -l version  --helpというオプションを補完候補に追加
# -d ...      このオプションの説明はDisplay version information and exitとする
complete -c vil -l version -d 'Display version information and exit'
```

また、別例として補完関数のみで作ったバージョンを示します。
printfを使ってタブ区切りの出力をしている箇所がありますが、これは1行をタブで区切って2列にすると1列目が補完候補、2列目が補完候補の説明として解釈されるようになっているためこうしています。
この作り方でも動くのですが、この作り方では-s、-l、-oを使ったときの恩恵が受けられないため、このケースではこの作り方にはしないほうがいいと思います。

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

    switch $prev
        case -e --expression
            return
        case -f --file
            __fish_complete_path
            return
    end
    switch $cur
        case '-*'
            printf '%s\t%s\n' '--quiet'      'Suppress automatic printing of buffer'
            printf '%s\t%s\n' '--silent'     'Suppress automatic printing of buffer'
            printf '%s\t%s\n' '--expression' 'Add the script to the commands to be executed'
            printf '%s\t%s\n' '--file'       'Add the contents of script-file to the commands to be executed'
            printf '%s\t%s\n' '--help'       'Display this help and exit'
            printf '%s\t%s\n' '--version'    'Display version information and exit'
        case '*'
            __fish_complete_path
    end
end
complete -c vil -xa '(__complete_vil)'
```

## nano

https://www.nano-editor.org/

ターミナルで動くテキストエディタです。
このコマンドの使い方は次のようになっています。

```
Usage: nano [OPTIONS] [[+LINE[,COLUMN]] FILE]...

To place the cursor on a specific line of a file, put the line number with
a '+' before the filename.  The column number can be added after a comma.
When a filename is '-', nano reads data from standard input.

Option		GNU long option		Meaning
 -A		--smarthome		Enable smart home key
 -B		--backup		Save backups of existing files
 -C <dir>	--backupdir=<dir>	Directory for saving unique backup files
 -D		--boldtext		Use bold instead of reverse video text
 -E		--tabstospaces		Convert typed tabs to spaces
 -F		--multibuffer		Read a file into a new buffer by default
 -G		--locking		Use (vim-style) lock files
 -H		--historylog		Log & read search/replace string history
 -I		--ignorercfiles		Don't look at nanorc files
 -J <number>	--guidestripe=<number>	Show a guiding bar at this column
 -K		--rawsequences		Fix numeric keypad key confusion problem
 -L		--nonewlines		Don't add an automatic newline
 -M		--trimblanks		Trim tail spaces when hard-wrapping
 -N		--noconvert		Don't convert files from DOS/Mac format
 -P		--positionlog		Log & read location of cursor position
 -Q <regex>	--quotestr=<regex>	Regular expression to match quoting
 -R		--restricted		Restricted mode
 -T <#cols>	--tabsize=<#cols>	Set width of a tab to #cols columns
 -U		--quickblank		Do quick statusbar blanking
 -V		--version		Print version information and exit
 -W		--wordbounds		Detect word boundaries more accurately
 -X <str>	--wordchars=<str>	Which other characters are word parts
 -Y <name>	--syntax=<name>		Syntax definition to use for coloring
 -Z		--zap			Let Bsp and Del erase a marked region
 -a		--atblanks		When soft-wrapping, do it at whitespace
 -b		--breaklonglines	Automatically hard-wrap overlong lines
 -c		--constantshow		Constantly show cursor position
 -d		--rebinddelete		Fix Backspace/Delete confusion problem
 -e		--emptyline		Keep the line below the title bar empty
 -g		--showcursor		Show cursor in file browser & help text
 -h		--help			Show this help text and exit
 -i		--autoindent		Automatically indent new lines
 -j		--jumpyscrolling	Scroll per half-screen, not per line
 -k		--cutfromcursor		Cut from cursor to end of line
 -l		--linenumbers		Show line numbers in front of the text
 -m		--mouse			Enable the use of the mouse
 -n		--noread		Do not read the file (only write it)
 -o <dir>	--operatingdir=<dir>	Set operating directory
 -p		--preserve		Preserve XON (^Q) and XOFF (^S) keys
 -r <#cols>	--fill=<#cols>		Set width for hard-wrap and justify
 -s <prog>	--speller=<prog>	Enable alternate speller
 -t		--tempfile		Auto save on exit, don't prompt
 -u		--unix			Save a file by default in Unix format
 -v		--view			View mode (read-only)
 -w		--nowrap		Don't hard-wrap long lines [default]
 -x		--nohelp		Don't show the two help lines
 -y		--afterends		Make Ctrl+Right stop at word ends
 -z		--suspend		Enable suspension
 -$		--softwrap		Enable soft line wrapping
```

結構な分量がありますね。
しかし、オプションと入力ファイルのパスを受け取る、よくある形のコマンドであることに変わりはありません。
多くのオプションがありますが、一つ一つ地道に定義していきましょう。
気を付ける点としては次のことぐらいでしょうか。

- -C、--backupdirがディレクトリパスを値を取るオプションであること
- -J、--guidestripeが任意の値を取るオプションであること
- -Q、--quotestrが任意の値を取るオプションであること
- -T、--tabsizeが任意の値を取るオプションであること
- -X、--wordcharsが任意の値を取るオプションであること
- -Y、--syntaxが任意の値を取るオプションであること
- -o、--operatingdirがディレクトリパスを取るオプションであること
- -r、--fillが任意の値を取るオプションであること
- -s、--spellerがコマンド名を取るオプションであること

-Y、--syntaxのオプションの値としてシンタックス名を補完したいところなのですが、シンタックス名一覧を取得する手段が見当たらないので、その点は妥協します。

まず、ヘルプ表示に従って各オプションの定義を記述します。

```fish
complete -c nano -s A -l smarthome -d 'Enable smart home key'
complete -c nano -s B -l backup -d 'Save backups of existing files'
complete -c nano -s C -l backupdir -xa '(__fish_complete_directories)' -d 'for saving unique backup files'
complete -c nano -s D -l boldtext -d 'Use bold instead of reverse video text'
complete -c nano -s E -l tabstospaces -d 'Convert typed tabs to spaces'
complete -c nano -s F -l multibuffer -d 'Read a file into a new buffer by default'
complete -c nano -s G -l locking -d 'Use (vim-style) lock files'
complete -c nano -s H -l historylog -d 'Log & read search/replace string history'
complete -c nano -s I -l ignorercfiles -d 'Don'"'"'t look at nanorc files'
complete -c nano -s J -l guidestripe -x -d 'a guiding bar at this column'
complete -c nano -s K -l rawsequences -d 'Fix numeric keypad key confusion problem'
complete -c nano -s L -l nonewlines -d 'Don'"'"'t add an automatic newline'
complete -c nano -s M -l trimblanks -d 'Trim tail spaces when hard-wrapping'
complete -c nano -s N -l noconvert -d 'Don'"'"'t convert files from DOS/Mac format'
complete -c nano -s P -l positionlog -d 'Log & read location of cursor position'
complete -c nano -s Q -l quotestr -x -d 'expression to match quoting'
complete -c nano -s R -l restricted -d 'Restricted mode'
complete -c nano -s T -l tabsize -x -d 'width of a tab to #cols columns'
complete -c nano -s U -l quickblank -d 'Do quick statusbar blanking'
complete -c nano -s V -l version -d 'Print version information and exit'
complete -c nano -s W -l wordbounds -d 'Detect word boundaries more accurately'
complete -c nano -s X -l wordchars -x -d 'Which other characters are word parts'
complete -c nano -s Y -l syntax -x -d 'Syntax definition to use for coloring'
complete -c nano -s Z -l zap -d 'Let Bsp and Del erase a marked region'
complete -c nano -s a -l atblanks -d 'When soft-wrapping, do it at whitespace'
complete -c nano -s b -l breaklonglines -d 'Automatically hard-wrap overlong lines'
complete -c nano -s c -l constantshow -d 'Constantly show cursor position'
complete -c nano -s d -l rebinddelete -d 'Fix Backspace/Delete confusion problem'
complete -c nano -s e -l emptyline -d 'Keep the line below the title bar empty'
complete -c nano -s g -l showcursor -d 'Show cursor in file browser & help text'
complete -c nano -s h -l help -d 'Show this help text and exit'
complete -c nano -s i -l autoindent -d 'Automatically indent new lines'
complete -c nano -s j -l jumpyscrolling -d 'Scroll per half-screen, not per line'
complete -c nano -s k -l cutfromcursor -d 'Cut from cursor to end of line'
complete -c nano -s l -l linenumbers -d 'Show line numbers in front of the text'
complete -c nano -s m -l mouse -d 'Enable the use of the mouse'
complete -c nano -s n -l noread -d 'Do not read the file (only write it)'
complete -c nano -s o -l operatingdir -xa '(__fish_complete_directories)' -d 'Set operating directory'
complete -c nano -s p -l preserve -d 'Preserve XON (^Q) and XOFF (^S) keys'
complete -c nano -s r -l fill -x -d 'width for hard-wrap and justify'
complete -c nano -s s -l speller -xa '(__fish_complete_command)' -d 'Enable alternate speller'
complete -c nano -s t -l tempfile -d 'Auto save on exit, don'"'"'t prompt'
complete -c nano -s u -l unix -d 'Save a file by default in Unix format'
complete -c nano -s v -l view -d 'View mode (read-only)'
complete -c nano -s w -l nowrap -d 'Don'"'"'t hard-wrap long lines [default]'
complete -c nano -s x -l nohelp -d 'Don'"'"'t show the two help lines'
complete -c nano -s y -l afterends -d 'Make Ctrl+Right stop at word ends'
complete -c nano -s z -l suspend -d 'Enable suspension'
complete -c nano -s '$' -l softwrap -d 'Enable soft line wrapping'
```

これで完成です。
今回もオプションを定義するだけで済むものなので、これ以上することはありません。
新しい点としては次のことぐらいでしょうか。

- -C、--backupdir、-o、--operatingdirのオプションの値としてディレクトリパスを補完するために\_\_fish\_complete\_directoriesを-aオプションの値に指定していること
- -s、--spellerのオプションの値としてコマンド名を補完するために\_\_fish\_complete\_commandを-aオプションの値に指定していること

また、別例として補完関数のみで作ったバージョンを示します。
こちらもこの作り方でも動くのですが、やはり-s、-l、-oを使ったときの恩恵が受けられないため、このケースでもこの作り方にはしないほうがいいと思います。

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

    switch $prev
        case -C --backupdir
            __fish_complete_directories
            return
        case -J --guidestripe
            return
        case -Q --quotestr
            return
        case -T --tabsize
            return
        case -X --wordchars
            return
        case -Y --syntax
            return
        case -o --operatingdir
            __fish_complete_directories
            return
        case -r --fill
            return
        case -s --speller
            __fish_complete_command
            return
    end
    switch $cur
        case '-*'
            printf '%s\t%s\n' '--smarthome'      'Enable smart home key'
            printf '%s\t%s\n' '--backup'         'Save backups of existing files'
            printf '%s\t%s\n' '--backupdir'      'Directory for saving unique backup files'
            printf '%s\t%s\n' '--boldtext'       'Use bold instead of reverse video text'
            printf '%s\t%s\n' '--tabstospaces'   'Convert typed tabs to spaces'
            printf '%s\t%s\n' '--multibuffer'    'Read a file into a new buffer by default'
            printf '%s\t%s\n' '--locking'        'Use (vim-style) lock files'
            printf '%s\t%s\n' '--historylog'     'Log & read search/replace string history'
            printf '%s\t%s\n' '--ignorercfiles'  'Don'"'"'t look at nanorc files'
            printf '%s\t%s\n' '--guidestripe'    'Show a guiding bar at this column'
            printf '%s\t%s\n' '--rawsequences'   'Fix numeric keypad key confusion problem'
            printf '%s\t%s\n' '--nonewlines'     'Don'"'"'t add an automatic newline'
            printf '%s\t%s\n' '--trimblanks'     'Trim tail spaces when hard-wrapping'
            printf '%s\t%s\n' '--noconvert'      'Don'"'"'t convert files from DOS/Mac format'
            printf '%s\t%s\n' '--positionlog'    'Log & read location of cursor position'
            printf '%s\t%s\n' '--quotestr'       'Regular expression to match quoting'
            printf '%s\t%s\n' '--restricted'     'Restricted mode'
            printf '%s\t%s\n' '--tabsize'        'Set width of a tab to #cols columns'
            printf '%s\t%s\n' '--quickblank'     'Do quick statusbar blanking'
            printf '%s\t%s\n' '--version'        'Print version information and exit'
            printf '%s\t%s\n' '--wordbounds'     'Detect word boundaries more accurately'
            printf '%s\t%s\n' '--wordchars'      'Which other characters are word parts'
            printf '%s\t%s\n' '--syntax'         'Syntax definition to use for coloring'
            printf '%s\t%s\n' '--zap'            'Let Bsp and Del erase a marked region'
            printf '%s\t%s\n' '--atblanks'       'When soft-wrapping, do it at whitespace'
            printf '%s\t%s\n' '--breaklonglines' 'Automatically hard-wrap overlong lines'
            printf '%s\t%s\n' '--constantshow'   'Constantly show cursor position'
            printf '%s\t%s\n' '--rebinddelete'   'Fix Backspace/Delete confusion problem'
            printf '%s\t%s\n' '--emptyline'      'Keep the line below the title bar empty'
            printf '%s\t%s\n' '--showcursor'     'Show cursor in file browser & help text'
            printf '%s\t%s\n' '--help'           'Show this help text and exit'
            printf '%s\t%s\n' '--autoindent'     'Automatically indent new lines'
            printf '%s\t%s\n' '--jumpyscrolling' 'Scroll per half-screen, not per line'
            printf '%s\t%s\n' '--cutfromcursor'  'Cut from cursor to end of line'
            printf '%s\t%s\n' '--linenumbers'    'Show line numbers in front of the text'
            printf '%s\t%s\n' '--mouse'          'Enable the use of the mouse'
            printf '%s\t%s\n' '--noread'         'Do not read the file (only write it)'
            printf '%s\t%s\n' '--operatingdir'   'Set operating directory'
            printf '%s\t%s\n' '--preserve'       'Preserve XON (^Q) and XOFF (^S) keys'
            printf '%s\t%s\n' '--fill'           'Set width for hard-wrap and justify'
            printf '%s\t%s\n' '--speller'        'Enable alternate speller'
            printf '%s\t%s\n' '--tempfile'       'Auto save on exit, don'"'"'t prompt'
            printf '%s\t%s\n' '--unix'           'Save a file by default in Unix format'
            printf '%s\t%s\n' '--view'           'View mode (read-only)'
            printf '%s\t%s\n' '--nowrap'         'Don'"'"'t hard-wrap long lines [default]'
            printf '%s\t%s\n' '--nohelp'         'Don'"'"'t show the two help lines'
            printf '%s\t%s\n' '--afterends'      'Make Ctrl+Right stop at word ends'
            printf '%s\t%s\n' '--suspend'        'Enable suspension'
            printf '%s\t%s\n' '--softwrap'       'Enable soft line wrapping'
        case '*'
            __fish_complete_path
    end
end
complete -c nano -xa '(__complete_nano)'
```

## vack

https://github.com/nil-two/vack

Vimのプラグインを管理するコマンドです。
このコマンドの使い方は次のようになっています。

```
usage: vack <command> [...]
manage Vim plugins.

commands:
  i|install [-os] <repository>...   # install the plugins
  u|update [-a] <plugin>...         # update the plugins
  r|remove <plugin>...              # remove the plugins
  l|list [-aos]                     # list installed plugins
  p|path [-a] <plugin>...           # show the path of the plugins
  e|enable <plugin>...              # move the plugins to start
  d|disable <plugin>...             # move the plugins to opt
  I|init                            # create the plugin directory
  h|help                            # print usage

environment-variables:
  VACKPATH   # the plugin directory (default: $HOME/.vim/pack/vack)
```

サブコマンドを取り、サブコマンドごとに違った引数を取る、これもよくある形のコマンドですね。
-nを使えば、第一引数のときだけの補完候補を追加したり、サブコマンドごとの補完候補を追加したりといったことができるので、-nを使うようにしてみましょう。

まず、このコマンドはファイルパスを引数に取らないため、-xを指定してファイルパスを補完候補に含めないようにします。

```fish
complete -c vack -x
```

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

```fish
complete -c vack -x
complete -c vack -n '__fish_use_subcommand' -xa install -d 'Install the plugins'
complete -c vack -n '__fish_use_subcommand' -xa update -d 'Update the plugins'
complete -c vack -n '__fish_use_subcommand' -xa remove -d 'Remove the plugins'
complete -c vack -n '__fish_use_subcommand' -xa list -d 'List installed plugins'
complete -c vack -n '__fish_use_subcommand' -xa path -d 'Show install directory of the plugins'
complete -c vack -n '__fish_use_subcommand' -xa enable -d 'Move the plugins to start'
complete -c vack -n '__fish_use_subcommand' -xa disable -d 'Move the plugins to opt'
complete -c vack -n '__fish_use_subcommand' -xa init -d 'Create the plugin directory'
complete -c vack -n '__fish_use_subcommand' -xa help -d 'Show this help message'
```

これでサブコマンドが補完されるようになりました。
\_\_fish\_use\_subcommandは第一引数のときに正常終了する関数となっており、第一引数の位置のときにセットで指定した補完候補が追加されます。

次に、各サブコマンドの通常の引数を補完するようにします。
各サブコマンドの通常の引数は次のものになっているので、これを補完します。

- update、remove、path→プラグイン名
- enable→起動時に読み込まれないプラグイン名
- disable→起動時に読み込まれるプラグイン名

```fish
complete -c vack -x
complete -c vack -n '__fish_use_subcommand' -xa install -d 'Install the plugins'
complete -c vack -n '__fish_use_subcommand' -xa update -d 'Update the plugins'
complete -c vack -n '__fish_seen_subcommand_from u update' -xa '(vack list)' -d 'Plugin'
complete -c vack -n '__fish_use_subcommand' -xa remove -d 'Remove the plugins'
complete -c vack -n '__fish_seen_subcommand_from r remove' -xa '(vack list)' -d 'Plugin'
complete -c vack -n '__fish_use_subcommand' -xa list -d 'List installed plugins'
complete -c vack -n '__fish_use_subcommand' -xa path -d 'Show install directory of the plugins'
complete -c vack -n '__fish_seen_subcommand_from p path' -xa '(vack list)' -d 'Plugin'
complete -c vack -n '__fish_use_subcommand' -xa enable -d 'Move the plugins to start'
complete -c vack -n '__fish_seen_subcommand_from e enable' -xa '(vack list -o)' -d 'Plugin'
complete -c vack -n '__fish_use_subcommand' -xa disable -d 'Move the plugins to opt'
complete -c vack -n '__fish_seen_subcommand_from d disable' -xa '(vack list -s)' -d 'Plugin'
complete -c vack -n '__fish_use_subcommand' -xa init -d 'Create the plugin directory'
complete -c vack -n '__fish_use_subcommand' -xa help -d 'Show this help message'
```

次に、各サブコマンドの通常の引数が補完されるようになりました。
\_\_fish\_use\_subcommandは指定した引数のいずれかがサブコマンドの位置にあるときに正常終了する関数となっており、サブコマンドが指定したサブコマンドだったときにセットで指定した補完候補が追加されます。
また、今回はupdate、remove、path、enable、disableの補完候補としてvack listの出力結果を指定しています。
このように指定しておくと、vack listの出力結果が改行区切りの補完候補として扱われるようになります。

最後に各サブコマンドのショートオプションを補完するようにします。

```fish
complete -c vack -x
complete -c vack -n '__fish_use_subcommand' -xa install -d 'Install the plugins'
complete -c vack -n '__fish_seen_subcommand_from i install' -s o -d 'Install as opt plugin'
complete -c vack -n '__fish_seen_subcommand_from i install' -s s -d 'Install as start plugin'
complete -c vack -n '__fish_use_subcommand' -xa update -d 'Update the plugins'
complete -c vack -n '__fish_seen_subcommand_from u update' -xa '(vack list)' -d 'Plugin'
complete -c vack -n '__fish_seen_subcommand_from u update' -s a -d 'Update all plugins'
complete -c vack -n '__fish_use_subcommand' -xa remove -d 'Remove the plugins'
complete -c vack -n '__fish_seen_subcommand_from r remove' -xa '(vack list)' -d 'Plugin'
complete -c vack -n '__fish_use_subcommand' -xa list -d 'List installed plugins'
complete -c vack -n '__fish_seen_subcommand_from l list' -s a -d 'List all plugins'
complete -c vack -n '__fish_seen_subcommand_from l list' -s o -d 'List opt plugins'
complete -c vack -n '__fish_seen_subcommand_from l list' -s s -d 'List start plugins'
complete -c vack -n '__fish_use_subcommand' -xa path -d 'Show install directory of the plugins'
complete -c vack -n '__fish_seen_subcommand_from p path' -xa '(vack list)' -d 'Plugin'
complete -c vack -n '__fish_seen_subcommand_from p path' -s a -d 'Show install directory of the all plugins'
complete -c vack -n '__fish_use_subcommand' -xa enable -d 'Move the plugins to start'
complete -c vack -n '__fish_seen_subcommand_from e enable' -xa '(vack list -o)' -d 'Plugin'
complete -c vack -n '__fish_use_subcommand' -xa disable -d 'Move the plugins to opt'
complete -c vack -n '__fish_seen_subcommand_from d disable' -xa '(vack list -s)' -d 'Plugin'
complete -c vack -n '__fish_use_subcommand' -xa init -d 'Create the plugin directory'
complete -c vack -n '__fish_use_subcommand' -xa help -d 'Show this help message'
```

これで完成です。
このように、-nは通常の引数の補完のとき、オプションの補完のときの両方で使うことができます。

また、別例として補完関数のみで作ったバージョンを示します。
コマンドの出力結果に説明を付与するときはsedやawkを使って1行1行に同じ説明を付与するようにします。
今回は-s、-l、-oを使ったときの恩恵をそこまで必要としないため、こちらの作り方をとってしまっても問題ないと思います。

```fish
function __complete_vack
    set -l cur (commandline -tc)
    set -l words (commandline -pco)
    set -l cword (count $words)

    switch $cword
        case 1
            printf '%s\t%s\n' 'install' 'Install the plugins'
            printf '%s\t%s\n' 'update'  'Update the plugins'
            printf '%s\t%s\n' 'remove'  'Remove the plugins'
            printf '%s\t%s\n' 'list'    'List installed plugins'
            printf '%s\t%s\n' 'path'    'Show install directory of the plugin'
            printf '%s\t%s\n' 'enable'  'Move the plugins to start'
            printf '%s\t%s\n' 'disable' 'Move the plugins to opt'
            printf '%s\t%s\n' 'init'    'Create the plugin directory'
            printf '%s\t%s\n' 'help'    'Show this help message'
        case '*'
            switch $words[2]
                case i install
                    switch $cur
                        case '-*'
                            printf '%s\t%s\n' '-o' 'Install as opt plugin'
                            printf '%s\t%s\n' '-s' 'Install as start plugins'
                        case '*'
                    end
                case u update
                    switch $cur
                        case '-*'
                            printf '%s\t%s\n' '-a' 'Update all plugins'
                        case '*'
                            vack list | awk '{printf "%s\t%s\n", $0, "Plugin"}'
                    end
                case r remove
                    switch $cur
                        case '-*'
                        case '*'
                            vack list | awk '{printf "%s\t%s\n", $0, "Plugin"}'
                    end
                case l list
                    switch $cur
                        case '-*'
                            printf '%s\t%s\n' '-a' 'List all plugins'
                            printf '%s\t%s\n' '-o' 'List opt plugins'
                            printf '%s\t%s\n' '-s' 'List start plugins'
                        case '*'
                    end
                case p path
                    switch $cur
                        case '-*'
                            printf '%s\t%s\n' '-a' 'Show install directory of the all plugins'
                        case '*'
                            vack list | awk '{printf "%s\t%s\n", $0, "Plugin"}'
                    end
                case e enable
                    switch $cur
                        case '-*'
                        case '*'
                            vack list -o | awk '{printf "%s\t%s\n", $0, "Plugin"}'
                    end
                case d disable
                    switch $cur
                        case '-*'
                        case '*'
                            vack list -s | awk '{printf "%s\t%s\n", $0, "Plugin"}'
                    end
                case i init
                case h help
            end
    end
end
```

## memo

https://github.com/mattn/memo

メモを管理するコマンドです。
このコマンドの使い方は次のようになっています。

```
NAME:
   memo - Memo Life For You

USAGE:
   memo [global options] command [command options] [arguments...]

VERSION:
   0.0.13

COMMANDS:
   new, n     create memo
   list, l    list memo
   edit, e    edit memo
   cat, v     view memo
   delete, d  delete memo
   grep, g    grep memo
   config, c  configure
   serve, s   start http server
   help, h    Shows a list of commands or help for one command

GLOBAL OPTIONS:
   --help, -h     show help (default: false)
   --version, -v  print the version (default: false)

------------------------------------------------------------------

NAME:
   memo new - create memo

USAGE:
   memo new [command options] [arguments...]

OPTIONS:
   --help, -h  show help (default: false)
   
------------------------------------------------------------------

NAME:
   memo list - list memo

USAGE:
   memo list [command options] [arguments...]

OPTIONS:
   --fullpath       show file path (default: false)
   --format string  print the result using a Go template string
   --help, -h       show help (default: false)

------------------------------------------------------------------

NAME:
   memo edit - edit memo

USAGE:
   memo edit [command options] [arguments...]

OPTIONS:
   --help, -h  show help (default: false)
   
------------------------------------------------------------------

NAME:
   memo cat - view memo

USAGE:
   memo cat [command options] [arguments...]

OPTIONS:
   --help, -h  show help (default: false)

------------------------------------------------------------------

NAME:
   memo delete - delete memo

USAGE:
   memo delete [command options] [arguments...]

OPTIONS:
   --help, -h  show help (default: false)
   
------------------------------------------------------------------

NAME:
   memo grep - grep memo

USAGE:
   memo grep [command options] [arguments...]

OPTIONS:
   --help, -h  show help (default: false)
   
------------------------------------------------------------------

NAME:
   memo config - configure

USAGE:
   memo config [command options] [arguments...]

OPTIONS:
   --cat       cat the file (default: false)
   --help, -h  show help (default: false)
   
------------------------------------------------------------------

NAME:
   memo serve - start http server

USAGE:
   memo serve [command options] [arguments...]

OPTIONS:
   --addr value  server address (default: ":8080")
   --help, -h    show help (default: false)
   
------------------------------------------------------------------

NAME:
    - Shows a list of commands or help for one command

USAGE:
    [command options] [command]

OPTIONS:
   --help, -h  show help (default: false)
```

サブコマンドを取り、サブコマンドもオプションを取る、複合的なコマンドになっています。
分量がありますが、複雑な引数は取らないので一つ一つ見ていけば大丈夫です。

真新しいことは何もありません。
今回は次の点に気を付けて、一気に作ってしまいましょう。

- 第一引数にはサブコマンド以外にも-h、--help、-v、--versionを補完候補に取るようになっていること
- edit、cat、deleteは通常の引数にメモ名を引数に取るようになっていること
- memo listの--formatが任意の値を取るオプションであること
- memo serveの--addrが任意の値を取るオプションであること

```fish
complete -c memo -x
complete -c memo -n '__fish_no_arguments' -s h -l help -d 'Show help'
complete -c memo -n '__fish_no_arguments' -s v -l version -d 'Print the version'
complete -c memo -n '__fish_use_subcommand' -xa new -d 'Create memo'
complete -c memo -n '__fish_seen_subcommand_from n new' -s h -l help -d 'Show help'
complete -c memo -n '__fish_use_subcommand' -xa list -d 'List memo'
complete -c memo -n '__fish_seen_subcommand_from l list' -l fullpath -d 'Show file path'
complete -c memo -n '__fish_seen_subcommand_from l list' -l format -x -d 'Print the result using a Go template string'
complete -c memo -n '__fish_seen_subcommand_from l list' -s h -l help -d 'Show help'
complete -c memo -n '__fish_use_subcommand' -xa edit -d 'Edit memo'
complete -c memo -n '__fish_seen_subcommand_from e edit' -xa '(memo list)' -d 'Memo'
complete -c memo -n '__fish_seen_subcommand_from e edit' -s h -l help -d 'Show help'
complete -c memo -n '__fish_use_subcommand' -xa cat -d 'View memo'
complete -c memo -n '__fish_seen_subcommand_from v cat' -xa '(memo list)' -d 'Memo'
complete -c memo -n '__fish_seen_subcommand_from v cat' -s h -l help -d 'Show help'
complete -c memo -n '__fish_use_subcommand' -xa delete -d 'Delete memo'
complete -c memo -n '__fish_seen_subcommand_from d delete' -xa '(memo list)' -d 'Memo'
complete -c memo -n '__fish_seen_subcommand_from d delete' -s h -l help -d 'Show help'
complete -c memo -n '__fish_use_subcommand' -xa grep -d 'Grep memo'
complete -c memo -n '__fish_seen_subcommand_from g grep' -s h -l help -d 'Show help'
complete -c memo -n '__fish_use_subcommand' -xa config -d 'Configure'
complete -c memo -n '__fish_seen_subcommand_from c config' -l cat -d 'Cat the file'
complete -c memo -n '__fish_seen_subcommand_from c config' -s h -l help -d 'Show help'
complete -c memo -n '__fish_use_subcommand' -xa serve -d 'Start http server'
complete -c memo -n '__fish_seen_subcommand_from s serve' -l addr -x -d 'Server address'
complete -c memo -n '__fish_seen_subcommand_from s serve' -s h -l help -d 'Show help'
complete -c memo -n '__fish_use_subcommand' -xa help -d 'Shows a list of commands or help for one command'
complete -c memo -n '__fish_seen_subcommand_from h help' -xa 'new list edit cat delete grep config serve help' -d 'Subcommand'
complete -c memo -n '__fish_seen_subcommand_from h help' -s h -l help -d 'Show help'
```

これで完成です。
\_\_fish\_no\_argumentsは第一引数のときに正常終了する関数となっており、第一引数の位置のときにセットで指定した補完候補が追加されます。
\_\_fish\_use\_subcommandと名前こそ違うものの実装は同じものなので、ニュアンスで使い分けるようにします。

また、別例として補完関数のみで作ったバージョンを示します。
今回はショートオプションとロングオプションを取るようになっているので、このケースでは可能であればこの作り方にはしないほうがいいと思います。

```fish
function __complete_memo
    set -l cur (commandline -tc)
    set -l words (commandline -pco)
    set -l cword (count $words)

    switch $cword
        case 1
            switch $cur
                case '-*'
                    printf '%s\t%s\n' '--help'    'Show help'
                    printf '%s\t%s\n' '--version' 'Print the version'
                case '*'
                    printf '%s\t%s\n' 'new'    'Create memo'
                    printf '%s\t%s\n' 'list'   'List memo'
                    printf '%s\t%s\n' 'edit'   'Edit memo'
                    printf '%s\t%s\n' 'cat'    'View memo'
                    printf '%s\t%s\n' 'delete' 'Delete memo'
                    printf '%s\t%s\n' 'grep'   'Grep memo'
                    printf '%s\t%s\n' 'config' 'Configure'
                    printf '%s\t%s\n' 'serve'  'Start http server'
                    printf '%s\t%s\n' 'help'   'Shows a list of commands or help for one command'
            end
        case '*'
            switch $words[2]
                case n new
                    switch $cur
                        case '-*'
                            printf '%s\t%s\n' '--help' 'Show help'
                        case '*'
                    end
                case l list
                    switch $cur
                        case '-*'
                            printf '%s\t%s\n' '--fullpath' 'Show file path'
                            printf '%s\t%s\n' '--format'   'Print the result using a Go template string'
                            printf '%s\t%s\n' '--help'     'Show help'
                        case '*'
                    end
                case e edit
                    switch $cur
                        case '-*'
                            printf '%s\t%s\n' '--help' 'Show help'
                        case '*'
                            memo list | awk '{printf "%s\t%s\n", $0, "Memo"}'
                    end
                case v cat
                    switch $cur
                        case '-*'
                            printf '%s\t%s\n' '--help' 'Show help'
                        case '*'
                            memo list | awk '{printf "%s\t%s\n", $0, "Memo"}'
                    end
                case d delete
                    switch $cur
                        case '-*'
                            printf '%s\t%s\n' '--help' 'Show help'
                        case '*'
                            memo list | awk '{printf "%s\t%s\n", $0, "Memo"}'
                    end
                case g grep
                    switch $cur
                        case '-*'
                            printf '%s\t%s\n' '--help' 'Show help'
                        case '*'
                    end
                case c config
                    switch $cur
                        case '-*'
                            printf '%s\t%s\n' '--cat'  'Cat the file'
                            printf '%s\t%s\n' '--help' 'Show help'
                        case '*'
                    end
                case s serve
                    switch $cur
                        case '-*'
                            printf '%s\t%s\n' '--addr' 'Server address'
                            printf '%s\t%s\n' '--help' 'Show help'
                        case '*'
                    end
                case h help
                    switch $cur
                        case '-*'
                            printf '%s\t%s\n' '--help' 'Show help'
                        case '*'
                            printf '%s\t%s\n' 'new'    'Subcommand'
                            printf '%s\t%s\n' 'list'   'Subcommand'
                            printf '%s\t%s\n' 'edit'   'Subcommand'
                            printf '%s\t%s\n' 'cat'    'Subcommand'
                            printf '%s\t%s\n' 'delete' 'Subcommand'
                            printf '%s\t%s\n' 'grep'   'Subcommand'
                            printf '%s\t%s\n' 'config' 'Subcommand'
                            printf '%s\t%s\n' 'serve'  'Subcommand'
                            printf '%s\t%s\n' 'help'   'Subcommand'
                    end
            end
    end
end
complete -c memo -xa '(__complete_memo)'
```