何がしたいか
Qiitaに次の投稿がありました。
Capistranoのタスクをzshで補完したい - Qiita
zsh-completions/_capを使うと、zshでcapコマンドの補完ができるようになります。しかし、capコマンドをグローバルにインストールしたのではなくbundle install
でインストールしている状態ではうまく補完ができない、という問題です。これを解決します。
なんでだめなのか
なぜうまくいかないのでしょうか。_capの中身を見てみると分かります。
cmds=( ${(f)"$(_call_program commands cap -T 2> /dev/null | sed -e '/ # /!d; s/:/\\:/g; s/cap \([A-Za-z0-9\\:_-]*\) .*# /\1:/')"} )
このcap -T
という箇所がポイントです。cap -T
は、Capistranoのタスク一覧を表示するコマンドです。
% cap -T
cap deploy # Deploy a new release
cap deploy:check # Check required files and directories exist
cap deploy:check:directories # Check shared and release directories exist
(...以下省略...)
_capファイルの中では、cap -T
を呼び出してタスク一覧を取得して、それを元に補完候補を作成しています。capがグローバルにインストールされているときはこれで問題ないのですが、bundle install
でインストールしているときはcap -T
を実行してもエラーで実行できません(bundle exec cap -T
としないと実行できません)。そのために補完候補が作成できていなかったのです。
どうやって対応するか
それでは、この問題にどうやって対応すればよいのでしょうか。一つは_capファイルの中身のcap -T
をbundle exec cap -T
に書き換えることです。ただし、この修正をしても将来_capファイルが更新されたときに上書きされてしまうので、その都度改めて直す必要があります。
実は、これには別の修正方法があります。元の_capファイルをもう一度よく見てみると、_call_program commands cap -T
というふうに書かれています。この_call_program
は、補完関数から呼び出す外部コマンドを差し替え出来るようにするための仕組みです。
_call_programは「_call_program <タグ名> <コマンド>」という形式で使います。そして、<コマンド>を呼び出すが、もし<タグ名>によって別のコマンドが指定されていたら代わりにそれを呼び出す、という処理を行います。
今回の例の場合は、タグ名は「commands」、コマンドは「cap -T」です。このコマンドを「bundle exec cap -T」に差し替えればうまくいきます。
具体的には、次の行を.zshrcに書きます。
zstyle ':completion::*:cap:*:commands' command 'bundle exec cap -T'
これで_call_programで呼び出されるコマンドがbundle exec cap -T
に置き換わります。さっそく試してみましょう。
うまく補完されています。
指定方法についてのもう少し詳しい説明
さきほどのコマンドを差し替える方法について、もう少し詳しく説明します。この指定方法は、補完対象となるコマンド名などによって書き方が変わります。具体的には、次の形式で指定します。
zstyle ':completion::*:<補完コマンド名>:*:<タグ名>' command <呼び出したいコマンド>
タグ名は補完ファイルの中を見て確認します。今回の例の場合は<補完コマンド名>はcap、タグ名はcommandsです。<呼び出したいコマンド>は、使用している環境によって変わります。今回の例はbundle exec capでcapコマンドを起動するようになっているので「bundle exec cap -T」と指定しました。
他の例を見てみましょう。rakeの補完ファイルはzshに標準で含まれています。たとえば僕のUbuntu環境では、/usr/share/zsh/functions/Completion/Unix/_rakeに補完ファイルがあります。これもcapコマンドと同じように、bundle installでインストールしているとrakeのタスクが補完されません。
_rakeファイルの中を見ると、次のように書かれています($words[1]
は現在のコマンドラインの先頭の単語を表します)。
targets=( ${${(f)"$(_call_program targets $words[1] -sT $opt_args[(I)(-N|--nosearch)] ${(kv)opt_args[(I)(-f|--rakefile)]} 2>/dev/null)"}/(#b)rake ([^ ]##) ##\# (*)/${${match[1]}//:/\\:}:${match[2]:l}}
_call_programが使われているので、_capのときと同じ要領でコマンドを差し替えできます。次の行を.zshrcに追加します。
zstyle ':completion::*:rake:*:targets' command 'bundle exec rake -sT $opt_args[(I)(-N|--nosearch)] ${(kv)opt_args[(I)(-f|--rakefile)]}'
少し長いですが、元の$words[1]
のところをbundle exec rake
に置き換えただけです。これでbundle installしているときにうまく補完できるようになりました。
zshはこうやって補完の動作をカスタマイズできるようになっているので、試してみてください。