svnの補完関数を書こう(2) -サブコマンド-

svnは、第一引数にサブコマンドを取る。

$ svn add hoge.c
$ svn commit

なので、まずはこのサブコマンドの補完関数を用意する。

完成するとこうなる。

$ svn ^D
Subversion command
?         cleanup   diff      log       pe        propedit  rename    switch
add       co        export    ls        pedit     propget   resolved  unlock
ann       commit    h         merge     pg        proplist  revert    up
annotate  copy      help      mkdir     pget      propset   rm        update
blame     cp        import    move      pl        ps        st
cat       del       info      mv        plist     pset      stat
checkout  delete    list      pd        praise    remove    status
ci        di        lock      pdel      propdel   ren       sw

KSH_AUTOLOADへの対応

まずは移植性を上げるための工夫を。
補完関数は自動的にロード(autoload)される必要がある。しかしその自動ロードの挙動は、KSH_AUTOLOADオプションが設定されているかどうかで異なる。
KSH_AUTOLOADが設定されていない場合、_fnの内容はファイル_fnに記述されてた内容となる。
しかし、設定されている場合は、_fnに記述された_fn関数の中身になる。

_fn(){
  # KSH_AUTOLOADが設定されている場合
}
# KSH_AUTOLOADが設定されていない場合

なので、KSH_AUTOLOADが設定されているかどうかにかかわらず動くようにするために、次のようにする。

#compdef svn colorsvn
_svn(){
  # 補完関数の中身
}
_svn "$@"

サブコマンドの補完

まずは、サブコマンドを補完するべきかどうかを調べる必要がある。
そのためにCURRENTというパラメータを調べる。これには、今何個目の引数に対する補完を行っているかが格納されている。

# ((...))で算術演算が行える
if ((CURRENT > 2)); then
  # サブコマンドに対する補完
else
  # サブコマンドの補完
fi

あとはサブコマンドのリストをシステムに渡してやればいい。そのために_describeという関数を用いる。これは、

_describe -t <タグ名> <説明> <補完リスト>

という形式をとる。ただし補完リストは実行時に展開されるので、候補のリストではなく、候補のリストを格納した変数名を渡してやる。

なので、次のようになる。

local -a cmd
cmd=(add commit)
_describe -t svn-command "Subversion command" cmd

ただし、全部のサブコマンドを列挙するのは面倒なのでsvn helpとsedをつかって横着する。

local line
local -a cmd
_call_program help-command svn help \
	| sed -n '/^ /s/[(), ]/ /gp' \
	| while read -A line; do
	    cmd=($line $cmd)
	done
_describe -t svn-command "Subversion command" cmd

_call_programを使えば、エキスパートはHackしやすくなるらしいですよ、奥さん。

結果

#compdef svn colorsvn
_svn(){
    if ((CURRENT > 2)); then
	# サブコマンドに対する補完
    else
	# サブコマンドの補完
	local line
	local -a cmd
	_call_program help-command svn help \
	    | sed -n '/^ /s/[(), ]/ /gp' \
	    | while read -A line; do
	    cmd=($line $cmd)
	done
	_describe -t svn-command 'Subversion command' cmd
    fi
}
 
_svn "$@"