Search code examples
zshansibleoh-my-zshtab-completion

Zsh completions with multiple repeated options


I am attempting to bend zsh, my shell of choice, to my will, and am completely at a loss on the syntax and operation of completions. My use case is this: I wish to have completions for 'ansible-playbook' under the '-e' option support three variations:

  • Normal file completion: ansible-playbook -e vars/file_name.yml
  • Prepended file completion: ansible-playbook -e @vars/file_name.yml
  • Arbitrary strings: ansible-playbook -e key=value

I started out with https://github.com/zsh-users/zsh-completions/blob/master/src/_ansible-playbook which worked decently, but required modifications to support the prefixed file pathing. To achieve this I altered the following lines (the -e line):

...
"(-D --diff)"{-D,--diff}"[when changing (small files and templates, show the diff in those. Works great with --check)]"\
"(-e --extra-vars)"{-e,--extra-vars}"[EXTRA_VARS set additional variables as key=value or YAML/JSON]:extra vars:(EXTRA_VARS)"\
'--flush-cache[clear the fact cache]'\

to this:

...
"(-D --diff)"{-D,--diff}"[when changing (small files and templates, show the diff in those. Works great with --check)]"\
"(-e --extra-vars)"{-e,--extra-vars}"[EXTRA_VARS set additional variables as key=value or YAML/JSON]:extra vars:__at_files"\
'--flush-cache[clear the fact cache]'\

and added the '__at_files' function:

__at_files () {
  compset -P @; _files
}

This may be very noobish, but for someone that has never encountered this before, I was pleased that this solved my problem, or so I thought.

This fails me if I have multiple '-e' parameters, which is totally a supported model (similar to how docker allows multiple -v or -p arguments). What this means is that the first '-e' parameter will have my prefixed completion work, but any '-e' parameters after that point become 'dumb' and only allow for normal '_files' completion from what I can tell. So the following will not complete properly:

ansible-playbook -e key=value -e @vars/file

but this would complete for the file itself:

ansible-playbook -e key=value -e vars/file

Did I mess up? I see the same type of behavior for this particular completion plugin's '-M' option (it also becomes 'dumb' and does basic file completion). I may have simply not searched for the correct terminology or combination of terms, or perhaps in the rather complicated documentation missed what covers this, but again, with only a few days experience digging into this, I'm lost.


Solution

  • If multiple -e options are valid, the _arguments specification should start with * so instead of:

    "(-e --extra-vars)"{-e,--extra-vars}"[EXTR ....
    

    use:

    \*{-e,--extra-vars}"[EXTR ...
    

    The (-e --extra-vars) part indicates a list of options that can not follow the one being specified. So that isn't needed anymore because it is presumably valid to do, e.g.:

    ansible-playbook -e key-value --extra-vars @vars/file