Zsh Mailing List Archive
Messages sorted by:
Reverse Date,
Date,
Thread,
Author
Re: OpenStack CLI completion
Hey,
> I have a vague recollection that some (sub)command(s) had a slightly
> inconsistent output with colons somewhere which had to be removed for
> completions. Could have been just one or two such commands but
> unfortunately I don't remember exactly anymore.
@Marko, I tried all clients I could find easily and could not find any
suggestions including colons. I cannot handle an error I don't know how to
throw. Please let me know if you remember an example.
> > * Why is there a check for not prefix-needed?
> >
> > Some comments from the original author would be quite helpful if he
> > still remembers why it was done a certain way :)
>
> Hmm, this one I don't remember, I guess this might be a common
> convention or something like that, probably not specific to _openstack.
I read up on it and I think it has something to do with how people configure
their completion preferences with `zstyle`. If you are a nice completion writer
you honor the setting. I hope I did it justice.
> I haven't dealt with OpenStack recently so I can't actually test
> anything around it anymore but if you could fix the issue that would be
> great.
I gave it a try. I ignored any colon issues for now. Would be great if someone
could.
So @list what do I need to do to get this merged?
From ecb7cbe49bce946bb4d6e919794af72f3d43014d Mon Sep 17 00:00:00 2001
From: Syphdias <syphdias+git@xxxxxxxxx>
Date: Thu, 18 Mar 2021 23:23:16 +0100
Subject: [PATCH] Fix _openstack completion for new style clients
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
I have rewritten the completion function for new style clients. It no longer
differentiates between command and sub command since this lead to a limited
depth for completion lookups. This gets rid of the extra global caching variable
`_cache_openstack_clnt_cmds`. I tried to stay true to the prefix-needed
setting.
First I process the words left of the cursor. `words` did not provide this
granularity I needed so I opted to parse `LBUFFER` also for saving a partial
input/match for later. I remove `help` since everything after it is normal (sub)
command, so matches are identical to proper commands. Also I filter out every
flag/option.
To find the proper completion options I try one level at a time.
* $service
* $service $some_command
* $service $some_command $some_command_for_some_command
* etc.
You could probably do this in reverse to save time but I don't think it is worth
the effort.
I add the global options if `-` is used as a prefix at the current position.
Caveats:
* I know there are options like `--file`. The new implementation does not handle
this – neither did the old one. For this level, I'd suggest the OpenStack team
to provide official zsh completions
* Ignores everything right of the cursor so you can end up with command
suggestion that are already on the right
* `openstack complete` gets completed now, old implementation ignored it
---
Completion/Unix/Command/_openstack | 119 +++++++++++++++--------------
1 file changed, 63 insertions(+), 56 deletions(-)
diff --git a/Completion/Unix/Command/_openstack b/Completion/Unix/Command/_openstack
index fcb704ac8..c12f25985 100644
--- a/Completion/Unix/Command/_openstack
+++ b/Completion/Unix/Command/_openstack
@@ -34,8 +34,6 @@ if (( ! $+_cache_openstack_clnt_opts )); then
typeset -gA _cache_openstack_clnt_opts
typeset -gA _cache_openstack_clnt_cmds
typeset -gA _cache_openstack_clnt_cmds_opts
- typeset -gA _cache_openstack_clnt_cmds_subcmds
- typeset -gA _cache_openstack_clnt_cmds_subcmd_opts
fi
local -a conn_opts
@@ -61,65 +59,74 @@ if [[ -n ${clnts_compl_new[(r)$service]} ]]; then
# Populate caches - clnt_outputs is command raw output used later
_cache_openstack_clnt_outputs[$service]=${:-"$($service ${(Q)conn_opts} complete 2>/dev/null)"}
_cache_openstack_clnt_opts[$service]=${${${${(M)${${${${=${(f)"$($service help 2>/dev/null)"}}/\[}/\]}/\;}:#-[-0-9A-Za-z]*}/,}/\.}%--os-}
- _cache_openstack_clnt_cmds[$service]=${${${${_cache_openstack_clnt_outputs[$service]}/* cmds=\'}/\'*}/complete}
fi
- local cmd subcmd
- # Determine the command
- for word in ${words:1}; do
- local s=${_cache_openstack_clnt_cmds[$service]}
- [[ $s[(wI)$word] -gt 0 ]] && cmd=$word && break
+
+ # get worlds left of the curser into an array
+ local -a left_words
+ left_words=(${=LBUFFER})
+
+ # if curser is directly at a word (no space at the end),
+ # exclude the last word to offer right matches
+ # the last word could be a partial match that is later checked (prefix-needed)
+ local partial=""
+ if [[ "${LBUFFER[-1]}" != " " ]]; then
+ partial=${(@)left_words[-1]}
+ left_words=(${(@)left_words[1,$#left_words-1]})
+ fi
+ # remove $service
+ left_words=(${left_words:1})
+
+ # filter out "help"
+ if [[ $left_words[1] == help ]]; then
+ left_words=(${(@)left_words[2,$#left_words]})
+ fi
+
+ # filter out options (-*)
+ left_words=(${left_words//-*})
+
+ local -a subcmd_array cmds_array cache_key_array cache_values
+ subcmd_array=()
+ cmds_array=(cmds)
+ cache_key_array=(${service})
+ cache_values=()
+ local cache_key cmds
+ cache_key=""
+ cmds=""
+
+ # Check for matches one level at a time
+ # example: "" server create
+ for word in "" ${(@)left_words}; do # first loop second loop third loop
+ subcmd_array=(${(@)subcmd_array} ${word}) # () (server) (server create)
+ cmds_array=(${(@)cmds_array} ${word}) # (cmds) (cmds server) (cmds server create)
+ cmds=${${(j:_:)cmds_array}/-/_} # cmds cmds_openstack cmds_server_create
+ cache_key_array=(${(@)cache_key_array} ${word}) # (openstack) (openstack server) (openstack server create)
+ cache_key=${${(j:_:)cache_key_array}/-/_} # openstack openstack_server openstack_server_create
+
+ # lookup if current word is in cache_values of last elements
+ if [[ ${cache_values[(wI)${word}]} -gt 0 || $word == "" ]]; then
+ _cache_openstack_clnt_cmds[${cache_key}]=${${${_cache_openstack_clnt_outputs[${service}]}/* ${cmds}=\'}/\'*}
+ fi
+ # set cache_values for next loop
+ cache_values=${_cache_openstack_clnt_cmds[${cache_key}]}
done
- # Populate the subcommand cache
- if [[ -n $cmd && -z $_cache_openstack_clnt_cmds_subcmds[$service$cmd] ]]; then
- local t=cmds_${cmd//-/_}
- _cache_openstack_clnt_cmds_subcmds[$service$cmd]=${${${_cache_openstack_clnt_outputs[$service]}/* $t=\'}/\'*}
- fi
- # Determine the subcommand
- if [[ -n $cmd ]]; then
- for word in ${words:2}; do
- local s=${_cache_openstack_clnt_cmds_subcmds[$service$cmd]}
- [[ $s[(wI)$word] -gt 0 ]] && subcmd=$word && break
- done
- # Populate subcommand option cache
- if [[ -n $subcmd && -z $_cache_openstack_clnt_cmds_subcmd_opts[$service${cmd}--$subcmd] ]]; then
- local t=cmds_${cmd//-/_}_${subcmd//-/_}
- _cache_openstack_clnt_cmds_subcmd_opts[$service${cmd}--$subcmd]=${${${_cache_openstack_clnt_outputs[$service]}/* $t=\'}/\'*}
- fi
- fi
- # Special treatment for the help command
- if [[ $cmd == help ]]; then
- if [[ $words[CURRENT-1] == $cmd && $words[CURRENT] != -* ]]; then
- # Offer commands
- [[ -n $_cache_openstack_clnt_cmds[$service] ]] && _values -w option ${(u)=_cache_openstack_clnt_cmds[$service]} && ret=0
- elif [[ $words[CURRENT-2] == $cmd && $words[CURRENT-1] != -* && $words[CURRENT] != -* ]]; then
- # Offer subcommands
- local cmd=$words[CURRENT-1]
- local t=cmds_${cmd//-/_}
- [[ -z $_cache_openstack_clnt_cmds_subcmds[$service$cmd] ]] && _cache_openstack_clnt_cmds_subcmds[$service$cmd]=${${${_cache_openstack_clnt_outputs[$service]}/* $t=\'}/\'*}
- [[ -n $_cache_openstack_clnt_cmds_subcmds[$service$cmd] ]] && _values -w option ${(u)=_cache_openstack_clnt_cmds_subcmds[$service$cmd]} && ret=0
- else
- # Handle help<TAB> properly
- _values -w option help && ret=0
- fi
- # Client options
- elif [[ -z $cmd && $words[CURRENT] == -* ]]; then
- _values -w option ${(u)=_cache_openstack_clnt_opts[$service]} && ret=0
- # Commands
- elif [[ -z $cmd ]]; then
- if [[ -z $_cache_openstack_clnt_cmds[$service] ]]; then
- _message "missing authentication options"
- else
- _values -w option ${(u)=_cache_openstack_clnt_cmds[$service]} && ret=0
- fi
- # Subcommands
- elif [[ -z $subcmd ]]; then
- [[ -n $_cache_openstack_clnt_cmds_subcmds[$service$cmd] ]] && _values -w option ${(u)=_cache_openstack_clnt_cmds_subcmds[$service$cmd]} && ret=0
- # Subcommand options
+
+ # Populate the command cache
+ if [[ -z $_cache_openstack_clnt_cmds[${cache_key}] ]]; then
+ _message "missing authentication options"
else
- { ! zstyle -T ":completion:${curcontext}:options" prefix-needed || [[ -prefix - ]] } && \
- [[ -n $_cache_openstack_clnt_cmds_subcmd_opts[$service${cmd}--$subcmd] ]] && _values -w option ${(u)=_cache_openstack_clnt_cmds_subcmd_opts[$service${cmd}--$subcmd]//\:/\\\:} && ret=0
+ # add global options to completion list if current word start with -*
+ local extra_opts
+ if [[ $words[CURRENT] == -* ]]; then;
+ extra_opts=${_cache_openstack_clnt_opts[$service]}
+ fi
+
+ { ! zstyle -T ":completion:${curcontext}:options" prefix-needed \
+ || [[ -n "${partial}" && ${${_cache_openstack_clnt_cmds[${cache_key}]}[(Iw)${partial}*]} -gt 0 || -prefix - ]] } \
+ && _values -w option ${(u)=_cache_openstack_clnt_cmds[${cache_key}]} ${(u)=extra_opts} \
+ && ret=0
fi
+
# Old style clients
elif [[ -n ${clnts_compl_old[(r)$service]} ]]; then
if [[ -z $_cache_openstack_clnt_cmds[$service] ]]; then
--
2.30.1
Messages sorted by:
Reverse Date,
Date,
Thread,
Author