Help us understand the problem. What is going on with this article?

zshの補完ファイルの中で呼ばれているコマンドを差し替えてカスタマイズする

More than 5 years have passed since last update.

何がしたいか

Qiitaに次の投稿がありました。

Capistranoのタスクをzshで補完したい - Qiita

zsh-completions/_capを使うと、zshでcapコマンドの補完ができるようになります。しかし、capコマンドをグローバルにインストールしたのではなくbundle installでインストールしている状態ではうまく補完ができない、という問題です。これを解決します。

なんでだめなのか

なぜうまくいかないのでしょうか。_capの中身を見てみると分かります。

src/_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 -Tbundle exec cap -Tに書き換えることです。ただし、この修正をしても将来_capファイルが更新されたときに上書きされてしまうので、その都度改めて直す必要があります。

実は、これには別の修正方法があります。元の_capファイルをもう一度よく見てみると、_call_program commands cap -Tというふうに書かれています。この_call_programは、補完関数から呼び出す外部コマンドを差し替え出来るようにするための仕組みです。

_call_programは「_call_program <タグ名> <コマンド>」という形式で使います。そして、<コマンド>を呼び出すが、もし<タグ名>によって別のコマンドが指定されていたら代わりにそれを呼び出す、という処理を行います。

今回の例の場合は、タグ名は「commands」、コマンドは「cap -T」です。このコマンドを「bundle exec cap -T」に差し替えればうまくいきます。

具体的には、次の行を.zshrcに書きます。

.zshrc
zstyle ':completion::*:cap:*:commands' command 'bundle exec cap -T'

これで_call_programで呼び出されるコマンドがbundle exec cap -Tに置き換わります。さっそく試してみましょう。

zsh_cap.png

うまく補完されています。

指定方法についてのもう少し詳しい説明

さきほどのコマンドを差し替える方法について、もう少し詳しく説明します。この指定方法は、補完対象となるコマンド名などによって書き方が変わります。具体的には、次の形式で指定します。

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]は現在のコマンドラインの先頭の単語を表します)。

_rake
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に追加します。

.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はこうやって補完の動作をカスタマイズできるようになっているので、試してみてください。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした