rcmdnk's blog
Last update

Completion (English Edition)

Bashの補完について に引き続き、 ZshのTabを押した時に出る補完の自作等について。

Zsh補完

compinit

Zshで補完を有効にするためにはまず、

autoload -U compinit
compinit

を実行する必要があります。

compinitが色々設定をしたりする関数なんですが、 autoloadしてから呼んであげる必要があります。

autoloadについては以下のページが丁寧に説明されてて分かりやすいです。

.zshrcで見かけるautoloadの意味と使い方 - Qiita

有効にするために取り敢えずこれを.zshrcなんかに書いておきます。

ちなみに

zsh compinit: insecure directories, run compaudit for list.
Ignore insecure directories and continue [y] or abort compinit [n]?

みたいなWARNINGが出ることがありますが(特にCygwinで出る)、 簡単にこれを避けるためにはこの様なチェックを入れないようにcompinit -u を使う事が一つあります。

もう少ししっかりやるには該当のディレクトリ (大概は/usr/local/share/zsh/site-functions)のパーミッションを変更

$ sudo chown -R root:root /usr/local/share/zsh/site-functions
$ sudo chmod -R 755 /usr/local/share/zsh/site-functions

してあげると治ることがあります。

zsh completion - zsh compinit: insecure directories - Stack Overflow

comopdef

あるコマンドに対して補完を出来るようにしてあげるには

$ compdef _comp_func cmd

compdefコマンドを使います。

これでcmdというコマンドに対する補完を_comp_func で決めることになります。

Bashのcompleteコマンドと違って -Fとか使わず最初の引数に補完を決める関数を渡します。

その後に補完したいコマンドを与えます。 複数コマンドもその後に

$ compdef _func cmd1 cmd2 cmd3

と書いていけばまとめて同じ補完が指定できます。

補完補助関数

compadd

補完の指定は

compadd aaa bbb ccc

の様にcompaddに補完したいものを引数として渡してあげればOK。

このコマンドに渡しただけ補完が増えるので 補完を指定する関数内で

compadd aaa bbb ccc
compadd ddd eee fff

とするとこれら6つ全てが補完に入ります。

Zshの補完の場合は、これで指定するだけで 途中まで書いた状態でのTabでの絞込など勝手にやってくれます。

補助関数

Zshのcompaddはかなり高機能で、 補完の表示時にも全体の説明を加えたり、 補完の文字列毎に説明を載せたり することが出来ます。

これらをcompaddのオプションで指定して直接使うことも出来ますが、 Zshには色々と便利に補完を決めるための補助関数が用意されています 1。 これらの関数の中では最終的にcompaddを使って 補完を決めています。

以下、いくつかよく使うものについて。

  • _files

引数を与えなければ現在のディレクトリのファイルやディレクトリを 補完するようになります。

-W <PATH>でパスを指定してその中の物だけ、を指定することも出来ます。

_path_filesという関数もあって、_filesはこれの更なる ラッパー関数みたいなもので、_path_filesから -g <pattern>(patternにマッチするものだけを補完)と -/(ディレクトリだけを補完) のオプションを除いたものになっています。

  • _values

    _values “description of completion” aaa bbb ccc

とすると、aaa bbb cccの補完を加えます。

最初のdescription…は全体の説明になります。

このdescriptionの部分を補完時に表示させるには 予め.zshrcとかで

zstyle ':completion:*' format '%B%d%b'

とかしておく必要が有ります。 zsytleでZshの色々な表示設定が出来、ここでは 補完時の表示設定で、formatは全体の説明部分の表示設定になります。

%Bは太文字開始、%bが太文字終了、 間の%dが補完の関数で決められたdescriptionの内容になります。 他にも%U~%uでアンダーラインを引いたり色々変更することも出来ます。

また、それぞれの補完の説明などを加えるには、

_values "description of completion" 'aaa[explain aaa]' 'bbb[explain bbb]'

みたいにすると[]内がaaaの説明として出ます(全体をクォートする必要あり)。

$ cmd <Tab>
description of completion
aaa  -- explain aaa
bbb  -- explain bbb

こんな感じ。

  • _arguments

主に-Xみたいなハイフン付きのオプションを解析するのに使います。

_arguments '-a' '--aaa'

とすると これらの補完が有効になります。

_valuesの様に、-a[description of -a]とすると説明が出ます。

また、{-a,--aaa}'[description of -a/--aaa]とすると-a--aaaを 同じ意味のオプションとしてまとめられます。 (この場合はクォートは{}の後から)。

_argumentsの書き方としてはいくつかありますが、その1つとして

'-a:messsage for -a:->aaa'

の様な書き方があります。

この書き方をすると、最初の-aがオプション、:で区切った次がメッセージ、最後がアクションになります。

メッセージは_valuesのdescriptionと同じように補完リストを表示する時に 上に表示されるメッセージです。(要らなければ空欄に。)

最後のアクションは-aが書かれた次の引数の補完で、 単に文字列を与えたければ(aaa bbb ccc)みたいに配列を与えればOK。

ここに関数を与えることも出来、

'-a:message for -a:a_func'

としておいて、

1
2
3
a_func () {
    compadd aaa bbb ccc
}

みたいな関数を用意してあげても同じことです。

さらに->を使う書き方をすると-aがある場合に、この部分でstateという変数にaaaを代入することになります。

_argumentsを設定する項目を書いた後、その後でこのstateを使って色々場合分けが出来る様になる、とういこと。

1
2
3
4
5
6
7
_arguments '-a::->aaa' '-b::->bbb'
case "$state" in
  aaa)
    compadd a_1 a_2 a_3;;
  bbb)
    compadd b_1 b_2 b_3;;
esac

みたいにすると、-aと書いた次のところではa_1a_2a_3 が補完に使われます。

words, CURRENT

Zshの補完補助関数の中でも BashのCOMP_WORDSCOMP_CWORDに当たるものがあり、 それぞれwordsCURRENTという変数に入っています。

ただし、CURRENTはコマンドの位置が0ではなく1になってるので、 COMP_CWORDに比べて1つ大きな数字になります。

また、Zshは通常配列の番号が0ではなく1から始まります。 setopt ksharraysとするとKshやBashの様に 0から始まる配列になりますが、この辺も注意が必要です。

配列をlocalで使う時に直接local a=(...)と書くとエラーが出るので 最初に宣言だけして後で代入する必要があるところも ちょっとしたハマりどころ。

BashとZshの違いでのハマりどころ

1
2
3
4
_comp_func() {
  compadd ${words[@]} $CURRENT
}
compdef _comp_func test_cmd

みたいな物をsourceしてみると

$ test_cmd <Tab>
2 test_cmd
$ test_cmd aaa<Tab>
# This puts one space, as 'aaa' is completed.
$ test_cmd aaa <Tab>
3 aaa test_cmd
$ test_cmd aaa bbb <Tab>
4 aaa bbb test_cmd
$ test_cmd aaa <Tab> bbb
3 aaa bbb test_cmd

な感じでBashの時より1つ大きな数字が出ます。

zsh-completions

zsh-completions はZshの補完を拡張してくれる設定が入ったパッケージです。

上のGitHubのREADMEに従ってファイルを配置するか、 MacならHomebrewで

$ brew install zsh-completions

/usr/local/share/zsh-completionsに 補完ファイルが入ります。

これを有効にするには

1
2
3
fpath=(/usr/local/share/zsh-completions $fpath)
autoload -U compinit
compinit

とします。

ここでfpathは補完用のスクリプトファイルが入った ディレクトリ群の配列で、 これに必要なディレクトリを加えたあと、 compinitをすることで ~/.zcompdumpというファイルがアップデートされ、 このファイルに従って補完が行われる様になります。

Homebrewで色々インストールすると、 他のパッケージ内からのZsh補完用スクリプトは 上のzsh-completionsではなく、 /usr/local/share/zsh/zsh-site-functionsの方に入ります。

これらを有効にするためには

1
2
3
4
5
6
7
8
for d in "/share/zsh-completions" "/share/zsh/zsh-site-functions";do
  brew_completion=$(brew --prefix 2>/dev/null)$d
  if [ $? -eq 0 ] && [ -d "$brew_completion" ];then
    fpath=($brew_completion $fpath)
  fi
done
autoload -Uz compinit
compinit

みたいにしてあげておけばOK。

MacのHomebrewでZsh補完のファイルをインストールするFormulaを作る

上にも書いたとおり、通常HomebrewでZsh補完ファイルをインストール しようとすると share/zsh/zsh-site-functionsなディレクトリにインストールされます。

なので、パッケージ内にshare/zsh/zsh-site-functions/_my_comp_file みたいなファイルを作り、補完関数の定義を書きます。 (この位置も名前も実用上は何でも良い。)

Bashの時同様、

1
2
3
4
5
6
  option "without-completions", "Disable bash/zsh completions"
  def install
    if build.with? "completions"
      zsh_completion.install "share/zsh/zsh-site-functions/_my_comp_file"
    end
  end

みたいな感じに書いてあげればOK。 zsh_completionprefix+”share/zsh/zsh-site-functions” になります(prefixは通常/usr/local/

この辺も最近 Brew-file で色々アップデートしたのでBrew-fileのFormulaや補完関数も参考になるかも。

homebrew-file/brew-file.rb

homebrew-file/share/zsh/site-functions/_brew-file

追記: 2016/02/01

Zshの補完についてもう少し付け足し。

追記ここまで

Sponsored Links
Sponsored Links

« Bashの補完について Brew-fileにコマンド補完を追加 »

}