I want to write zsh completions for a program with the following calling convention:
program [generaloptions] operation [operationoptions]
where operation
is one of --install
, --upgrade
...
What I have so far, are the general options and the operation options. My code looks something like this:
local generaloptions; generaloptions=(...)
local installoptions; installoptions=(...)
local upgradeoptions; upgradeoptions=(...)
case "$state" in
(install)
_arguments -s \
"$installoptions[@]" \
&& ret=0
(upgrade)
_arguments -s \
"$upgradeoption[@]" \
&& ret=0
*)
_arguments -s \
"$generaloptions[@]" \
'--install[...]: :->install' \
'--upgrade[...]: :->upgrade' \
&& ret=0
The problem is, after I type the operation and the first operation option, the state gets reset to the *)
case.
$ program --install --installoption --<tab>
list of general options
How can I set the next state to be the same as the old? Which command has similar calling conventions, so I can look at the code of the completion for this command?
The main problem is that the operations start with a --
, so it is harder to find them in the arguments. In git for example all subcommands are only a word without dashes. So git solves this problem something like this:
So git dispatches in every call to the completion function (this was what I meant with "holding the state").
The way I solved this problem was by looking through many completion functions and finding a command that had a similar calling convention. The command that I found the most useful is pacman
. Here is what I extracted from that:
# This somehow disassembles the commandline options
args=( ${${${(M)words:#-*}#-}:#-*}
case $args in
*i)
_arguments -s \
${installoptions} \
'(-i[...]' \
&& ret=0
;;
*u)
_arguments -s \
${upgradeoption} \
'-u[...]' \
&& ret=0
;;
*)
case ${(M)words:#--*} in
*--install*)
_arguments -s \
${installoptions} \
'--install[...]' \
&& ret=0
;;
*--upgrade*)
_arguments -s \
${upgradeoption} \
'--upgrade[...]' \
&& ret=0
;;
*)
_arguments -s \
{generaloptions} \
&& ret=0
;;
esac
esac
I know, there is a lot of dublication, but I think you get the point. Also notice, I moved the --install
and --upgrade
options from the general case to the operation case. If you don't do that, you loose the argument if you want complete after --install
or --upgrade