Subject: Re: zsh virsh completion
From: Marko Myllynen <[email-masked]>
To: Daniel Shahaf <[email-masked]>
Cc: zsh workers <[email-masked]>
Date: Mon, 18 Jul 2016 15:06:52 +0300
List-Id: Zsh Workers List <zsh-workers.zsh.org>

Hi,

On 2016-07-13 07:59, Daniel Shahaf wrote:
> Marko Myllynen wrote on Mon, Jul 11, 2016 at 14:52:02 +0300:
>> Below is a patch to implement basic completion for the virsh(1) [1]
>> command from libvirt [2], it completes all virsh commands and their
>> respective options.
> 
> Note there are two or three other _virsh's floating around:
> 
> https://github.com/jplitza/zsh-virsh-autocomplete/blob/master/_virsh
> https://github.com/zsh-users/zsh-completions/blob/master/src/_virsh
> https://github.com/Aso23/zsh_virsh_autocompletion/blob/master/_virsh

I wasn't aware of these. The last one on the list seems to be the first
incarnation, the middle one adds very little to it, the first link
provides the most recent version.

Those repos seem to have some other goodies as well, it's a pity that
they haven't been upstreamed. Many of us work with several systems and
downloading something for each is inconvenient, sometimes even
impossible, so if something is not in upstream and in distro packages,
it's almost like it didn't exist at all, unfortunately.

> Those seem to have features your patch lacks, e.g., completing domain
> (VM name) arguments.

Domain completion is certainly nice, however option completion seems to
be broken (try e.g. virsh perf <tab>) and all lack at least storage
pool and network completions.

Oliver cleaned up and committed my earlier patch, here's an update for
it, this patch implements completion for the rest of the commands that
are part of libvirt (as is with libvirt-2.0 RHEL 7 packages). Few of
them barely deserve completion but are there for completeness sake.
(No virt-admin command accepts filenames so those are not offered.)

---
 Completion/Unix/Command/_libvirt | 89 ++++++++++++++++++++++++++++++++++------
 1 file changed, 76 insertions(+), 13 deletions(-)

diff --git a/Completion/Unix/Command/_libvirt b/Completion/Unix/Command/_libvirt
index a9249b3..2053d15 100644
--- a/Completion/Unix/Command/_libvirt
+++ b/Completion/Unix/Command/_libvirt
@@ -1,34 +1,82 @@
-#compdef virsh
+#compdef virsh virt-admin virt-host-validate virt-pki-validate virt-xml-validate
 
 local curcontext="$curcontext" state line expl ret=1
 
+local exargs="-h --help -V -v --version=short --version=long"
+local -a common_opts interact_cmds
+common_opts=(
+  '(- *)'{-h,--help}'[print help information and exit]'
+  '(- *)'{-v,--version=short}'[print short version information and exit]'
+  '(- *)'{-V,--version=long}'[print long version information and exit]'
+  "(-c --connect $exargs)"{-c+,--connect}'[specify connection URI]:URI:_hosts'
+  "(-d --debug -q --quiet $exargs)"{-d+,--debug}'[set debug level]:level:(0 1 2 3 4)'
+  "(-l --log $exargs)"{-l+,--log}'[specify log file]:file:_files'
+  "(-q --quiet -d --debug $exargs)"{-q,--quiet}'[quiet mode]'
+)
+interact_cmds=(cd echo exit quit connect)
+
 case $service in
   virsh)
     if (( ! $+_cache_virsh_cmds )); then
       _cache_virsh_cmds=( ${${${${(f):-"$(_call_program options virsh help)"}:#*:}/# ##}/ *} )
+      for icmd in $interact_cmds; do
+        _cache_virsh_cmds[$_cache_virsh_cmds[(i)$icmd]]=()
+      done
     fi
     if (( ! $+_cache_virsh_cmdopts )); then
       typeset -gA _cache_virsh_cmdopts
     fi
     _arguments -A "-*" -C -S -s -w \
-      '(- *)'{-h,--help}'[print help information and exit]' \
-      '(- *)'{-v,--version=short}'[print short version information and exit]' \
-      '(- *)'{-V,--version=long}'[print long version information and exit]' \
-      '(-c --connect)'{-c+,--connect}'[specify connection URI]:URI:_hosts' \
-      '(-d --debug)'{-d+,--debug}'[set debug level]:level:(0 1 2 3 4)' \
-      '(-e --escape)'{-e+,--escape}'[set escape sequence for console]:sequence' \
-      '(-k --keepalive-interval)'{-k+,--keepalive-interval}'[set keepalive interval]:interval' \
-      '(-K --keepalive-count)'{-K+,--keepalive-count}'[set keepalive count]:count' \
-      '(-l --log)'{-l+,--log}'[specify log file]:file:_files' \
-      '(-q --quiet)'{-q,--quiet}'[quiet mode]' \
-      '(-r --readonly)'{-r,--readonly}'[connect readonly]' \
-      '(-t --timing)'{-t,--timing}'[print timing information]' \
+      "$common_opts[@]" \
+      "(-e --escape $exargs)"{-e+,--escape}'[set escape sequence for console]:sequence' \
+      "(-k --keepalive-interval $exargs)"{-k+,--keepalive-interval}'[set keepalive interval]:interval' \
+      "(-K --keepalive-count $exargs)"{-K+,--keepalive-count}'[set keepalive count]:count' \
+      "(-r --readonly $exargs)"{-r,--readonly}'[connect readonly]' \
+      "(-t --timing $exargs)"{-t,--timing}'[print timing information]' \
       '1:command:->virsh_cmds' \
       '*:cmdopt:->virsh_cmdopts' && return
       # We accept only virsh command options after the first non-option argument
       # (i.e., the virsh command itself), this makes it so with the -A "-*" above
       [[ -z $state ]] && state=virsh_cmdopts
   ;;
+  virt-admin)
+    if (( ! $+_cache_virt_admin_cmds )); then
+      _cache_virt_admin_cmds=( ${${${${(f):-"$(_call_program options virt-admin help)"}:#*:}/# ##}/ *} )
+      for icmd in $interact_cmds; do
+        _cache_virt_admin_cmds[$_cache_virt_admin_cmds[(i)$icmd]]=()
+      done
+    fi
+    if (( ! $+_cache_virt_admin_cmdopts )); then
+      typeset -gA _cache_virt_admin_cmdopts
+    fi
+    _arguments -A "-*" -C -S -s -w \
+      "$common_opts[@]" \
+      '1:command:->virt_admin_cmds' \
+      '*:cmdopt:->virt_admin_cmdopts' && return
+      # Same as with virsh above
+      [[ -z $state ]] && state=virt_admin_cmdopts
+  ;;
+  virt-host-validate)
+    _arguments -A "-*" -S \
+      '(- *)'{-h,--help}'[print help information and exit]' \
+      '(- *)'{-v,--version}'[print version information and exit]' \
+      '(- *)'{-q,--quiet}'[quiet mode]' \
+      '1:hv-type:(qemu lxc)' && return
+  ;;
+  virt-pki-validate)
+    _arguments -A "-*" -S \
+      '(- *)'{-h,--help}'[print help information and exit]' \
+      '(- *)'{-V,--version}'[print version information and exit]' \
+      && return
+  ;;
+  virt-xml-validate)
+    _arguments -A "-*" -S \
+      '(- *)'{-h,--help}'[print help information and exit]' \
+      '(- *)'{-V,--version}'[print version information and exit]' \
+      '1:file:_files -g "*.xml(-.)"' \
+      '2:schema:(domainsnapshot domain network storagepool storagevol nodedev capability nwfilter secret interface)' \
+      && return
+  ;;
 esac
 
 case $state in
@@ -50,6 +98,21 @@ case $state in
     fi
     _values -w options ${=_cache_virsh_cmdopts[$cmd]} && ret=0
   ;;
+  virt_admin_cmds)
+    _wanted commands expl 'virt-admin command' compadd -a _cache_virt_admin_cmds && ret=0
+  ;;
+  virt_admin_cmdopts)
+    local cmd
+    for (( i = 2; i <= $#words; i++ )); do
+      [[ -n "${_cache_virt_admin_cmds[(r)$words[$i]]}" ]] && cmd=$words[$i] && break
+    done
+    [[ -z $cmd ]] && return 1
+    if [[ -z $_cache_virt_admin_cmdopts[$cmd] ]]; then
+      _cache_virt_admin_cmdopts[$cmd]=${(M)${${${${=${(f)"$(_call_program virt-admin virt-admin help $cmd 2>&1)"}}/\[}/\]}/\;}:#-[-0-9A-Za-z]*}
+    fi
+    _values -w options ${=_cache_virt_admin_cmdopts[$cmd]} && ret=0
+  ;;
+
 esac
 
 return ret

Thanks,

-- 
Marko Myllynen