thorで作ったコマンドラインツールのZsh補完スクリプトを自動生成してくれるthor-zsh_completionでホクホクしてサクサクしてみる

自作コマンドラインツールでも補完してサクサク使いたい！
ホクホク顔になりたい！
でもツールを作るたびに補完用の設定を手書きするのはめんどくさい！
という方は多いのではないでしょうか。

そんな方には @labocho さん作の [thor-zsh_completion](https://github.com/labocho/thor-zsh_completion) という gem があります。
※この gem は thor を前提にしています。

## <i class='fa fa-check-circle'></i> 前提
thor って何？という方は以下をご確認ください。

[Ruby の CLI ツールの作成を支援する、 thor gem の基本について](http://qiita.com/tbpgr/items/10a5c236cfb528c76ef5)

## <i class='fa fa-flask'></i> サンプル
以下のリポジトリの内容をベースに試します。

[tbpgr/fizzbuzz](https://github.com/tbpgr/fizzbuzz)

### <i class='fa fa-cube'></i> 変更前
thor を使った CLI の定義部分は以下のようになっています。

```ruby:lib/cli.rb
require 'thor'

# Fizzbuzz::Models
module Fizzbuzz
  # CLI
  #
  # FizzBuzzのコマンドライン
  class CLI < Thor
    class_option :help, type: :boolean, aliases: '-h', desc: 'help message.'
    class_option :version, type: :boolean, desc: 'version'
    class_option :debug, type: :boolean, aliases: '-d', desc: 'debug mode'

    desc 'fizzbuzz', 'Get fizzbuzz result from limit number'
    def fizzbuzz(limit)
      print Fizzbuzz.fizzbuzz(limit).join(',')
      exit
    rescue => e
      output_error_if_debug_mode(e)
      exit(false)
    end

    desc 'version', 'version'
    def version
      puts Fizzbuzz::VERSION
    end

    private

    def output_error_if_debug_mode(e)
      return unless options[:debug]
      STDERR.puts(e.message)
      STDERR.puts(e.backtrace)
    end
  end
end
```

### <i class='fa fa-cube'></i> 変更後
* gemspec に thor-zsh_completion を追加

```ruby:fizzbuzz.gemspec
# 略
spec.add_runtime_dependency 'thor-zsh_completion'
# 略
```

```ruby:lib/cli.rb
require 'thor'
# :new: 追加
require 'thor/zsh_completion'

# Fizzbuzz::Models
module Fizzbuzz
  # CLI
  #
  # FizzBuzzのコマンドライン
  class CLI < Thor
    # :new: 追加
    include ZshCompletion::Command
```

### <i class='fa fa-terminal'></i> デモ
* zsh-completion コマンドが増えます

```sh
$ fizzbuzz
Commands:
  fizzbuzz fizzbuzz        # Get fizzbuzz result from limit number
  fizzbuzz help [COMMAND]  # Describe available commands or one specific command
  fizzbuzz version         # version
  fizzbuzz zsh-completion  # Print zsh completion script

Options:
  -h, [--help], [--no-help]        # help message.
      [--version], [--no-version]  # version
  -d, [--debug], [--no-debug]      # debug mode
```

* zsh-completion コマンドを実行します

```sh
$ fizzbuzz zsh-completion
#compdef fizzbuzz

local state

_fizzbuzz() {
  __fizzbuzz
}

__fizzbuzz() {
  readonly local DEPTH=2

  case $CURRENT in
    $DEPTH)
      _arguments \
        '*: :->subcommands'

      case $state in
        subcommands)
          _values \
            'subcommand' \
            'zsh-completion[Print zsh completion script]' \
            'fizzbuzz[Get fizzbuzz result from limit number]' \
            'version[version]' \
            ;
          ;;
      esac
      ;;
    *)
      case $words[$DEPTH] in
        zsh-completion)
          __fizzbuzz_zsh-completion
          ;;
        fizzbuzz)
          __fizzbuzz_fizzbuzz
          ;;
        version)
          __fizzbuzz_version
          ;;
        *)
          # if does not match any subcommand
          # complete rest arguments
          _files
          ;;
      esac
      ;;
  esac
}


__fizzbuzz_zsh-completion() {
  _arguments \
    {--help,--h}help message. \
    {--version}version \
    {--debug,--d}debug mode \
    {--name,-n} \
    '*: :->rest'

  case $state in
    rest)
      # complete rest arguments
      _files
      ;;
  esac
}

__fizzbuzz_fizzbuzz() {
  _arguments \
    {--help,--h}help message. \
    {--version}version \
    {--debug,--d}debug mode \
    '*: :->rest'

  case $state in
    rest)
      # complete rest arguments
      _files
      ;;
  esac
}

__fizzbuzz_version() {
  _arguments \
    {--help,--h}help message. \
    {--version}version \
    {--debug,--d}debug mode \
    '*: :->rest'

  case $state in
    rest)
      # complete rest arguments
      _files
      ;;
  esac
}


compdef _fizzbuzz fizzbuzz
```

* 生成したコードを zsh の設定に追加します

私は prezto を使っているので、以下のように指定します

```sh
$ fizzbuzz zsh-completion > ~/.zprezto/modules/completion/external/src/_fizzbuzz
$ exec $SHELL -l
```

* 動作確認

コマンドがサジェストされるようになりました。

![demo.gif](https://qiita-image-store.s3.amazonaws.com/0/43480/dc647d5c-f0d3-4340-1682-fd6793680799.gif)

## <i class="fa fa-book" style="font-size:1em;"></i> 外部資料
* [thor-zsh_completion](https://github.com/labocho/thor-zsh_completion)
* [Ruby の CLI ツールの作成を支援する、 thor gem の基本について](http://qiita.com/tbpgr/items/10a5c236cfb528c76ef5)