Folks, I have a command (`nats`) which takes a "context", and where that can be set as an environment variable. I have tab-completion of these working, based upon knowing where the contexts are stored. Each context is a JSON file in a specific directory, but I mostly only need the name. But, I could provide a more detailed description, by parsing the .description field from the JSON file. AFAIK this is going to be external shell-outs to jq right now (unless anyone has a solid reliable pattern for JSON extraction from a simple KV object in zsh?). This is relatively expensive and I don't want to do it by default. I mean, if most people have at most 3 contexts then it might be sane for them, but I have 35 and while I might be an outlier, I can believe there would be people with more. Is there a standard idiom and keybinding for "go ahead and get the expensive data, to provide me with more details to choose between the options", or would I be creating this from scratch? My completion for this command also gets pointed at by the installer, so I really want to make sure that what I end up with is not a baroque Phil Special but instead something which fits better with whatever the plugin ecosystems are doing, etc. I know about use-cache etc and have that in other completions of mine, so if there's no existing idiom for "trigger expensive completion" then I will use a zstyle to let people request "descriptions" for this completion, and use-cache if that's set. I'm asking in case there's a better way I'm not familiar with. Thanks, -Phil
Phil Pennock wrote on Wed, Mar 16, 2022 at 20:26:52 -0400: > Is there a standard idiom and keybinding for "go ahead and get the > expensive data, to provide me with more details to choose between the > options", or would I be creating this from scratch? There's the verbose and extra-verbose styles (the former is default on, the latter is default off). To set that style only for one keybinding, see _generic in the manual. I don't have a recommendation for what key to bind. Cheers, Daniel > My completion for this command also gets pointed at by the installer, > so I really want to make sure that what I end up with is not a baroque > Phil Special but instead something which fits better with whatever the > plugin ecosystems are doing, etc. > > I know about use-cache etc and have that in other completions of mine, > so if there's no existing idiom for "trigger expensive completion" then > I will use a zstyle to let people request "descriptions" for this > completion, and use-cache if that's set. I'm asking in case there's a > better way I'm not familiar with. > > Thanks, > -Phil >
On Wed, Mar 16, 2022 at 5:27 PM Phil Pennock
<zsh-workers+phil.pennock@spodhuis.org> wrote:
>
> But, I could provide a more detailed description, by parsing the
> .description field from the JSON file. AFAIK this is going to be
> external shell-outs to jq right now (unless anyone has a solid reliable
> pattern for JSON extraction from a simple KV object in zsh?).
If it's REALLY simple and both keys and values are properly quoted,
you might be able to get away with
() {
local IFS='{},:'
typeset -gA context=( ${(Q)=1} )
} "$(<json_context_file)"
but that assumes you don't have colons or commas or braces in the keys
or descriptions.
On Wed, Mar 16, 2022 at 6:20 PM Bart Schaefer <schaefer@brasslantern.com> wrote:
>
> typeset -gA context=( ${(Q)=1} )
Even better:
typeset -gA context=( ${(Q)${(z)=1}} )
On 2022-03-17 at 00:50 +0000, Daniel Shahaf wrote:
> There's the verbose and extra-verbose styles (the former is default on,
> the latter is default off). To set that style only for one keybinding,
> see _generic in the manual. I don't have a recommendation for what
> key to bind.
Perfect, extra-verbose is exactly what I was looking for, thank you.
Then folks can set the style on a per-command/value basis, or bind to
set it as they see fit.
Thanks,
-Phil
On 2022-03-16 at 18:20 -0700, Bart Schaefer wrote: > but that assumes you don't have colons or commas or braces in the keys > or descriptions. Alas, one of the fields is almost guaranteed to be a URL, thus containing a colon, and all of those are fair game in the description field. In case anyone is interested: while nats(1) (open-source, part of the CNCF "NATS" project) is changing fast enough that we probably don't want the completion shipping with zsh at this time, here's a copy of the completion I'm using today: https://get-nats.io/zsh.complete.nats The tool is one of those Golang ones which has `--completion-bash` but not `--completion-zsh` and where with a bit of custom zsh logic we can do a "better than stock tool" job of generating a reasonably decent and useful completion. (I know that I'm misusing the per-flag description by listing it twice in this way for the two different location but I haven't yet come up with an easier-to-maintain approach) -Phil
Phil Pennock wrote on Wed, Mar 16, 2022 at 22:21:44 -0400: > On 2022-03-16 at 18:20 -0700, Bart Schaefer wrote: > > but that assumes you don't have colons or commas or braces in the keys > > or descriptions. > > Alas, one of the fields is almost guaranteed to be a URL, thus > containing a colon, and all of those are fair game in the description > field. > > In case anyone is interested: while nats(1) (open-source, part of the > CNCF "NATS" project) is changing fast enough that we probably don't want > the completion shipping with zsh at this time, here's a copy of the > completion I'm using today: > > https://get-nats.io/zsh.complete.nats Review in unidiff form. Meant as suggestions to pick and choose from. I forgot to annotate a few other function definitions with the same "don't unconditionaly redefine" idiom. Cheers, Daniel [[[ --- https://get-nats.io/zsh.complete.nats +++ https://get-nats.io/zsh.complete.nats @@ -23,7 +23,7 @@ # extra-verbose is documented as for features which will slow completion speed. if zstyle -t ":completion:${curcontext}:" extra-verbose; then - if (( $+commands[jq] )); then + if type jq > /dev/null; then nats_everbose=1 else : # should we report this back as an error somehow? @@ -32,15 +32,16 @@ local -r nats_ctxdir="${XDG_CONFIG_HOME:-$HOME/.config}/nats/context" -nats_contexts=( "$nats_ctxdir"/*(:t:r) ) +nats_contexts=( "$nats_ctxdir"/*(N:t:r) ) # TODO: come up with a cache expiration strategy and integrate this with use-cache # or at least a global variable. +(( ${+functions[_nats_context_names]} )) || _nats_context_names() { if (( nats_everbose )); then described_contexts=() for nctx in "${nats_contexts[@]}"; do - described_contexts+=("${nctx//:/\\:}:$(jq -r .description < "$nats_ctxdir/$nctx.json")") + described_contexts+=("${nctx//:/\\:}:$(_call_program jq "jq -r .description < \"${(q)nats_ctxdir}/${(q)nctx}.json\"")") done _describe nats-contexts described_contexts else @@ -55,7 +56,7 @@ ;; esac -nats_decorate_flags_desc=( +nats_decorate_flags_desc=( # this assumes zsh is-at-least 5.5 [context]='NATS user context' [tlscert]='TLS client auth public certificate' [tlskey]='TLS client auth private key' @@ -86,12 +87,12 @@ # We populate a complete set first, so that even if we don't have more details below, # we can still tab-complete it; it will just be missing some data -nats_commands=( $(${words[1]} --completion-bash ${words[1]}) ) +nats_commands=( $(_call_program completion-bash "${words[1]} --completion-bash ${words[1]}") ) # Most flags can appear anywhere; this is not a git-style "top command flags" # vs "subcommand flags", but instead "one pool of flags which gains extra # entries for some subcommands". -nats_flags=( $(${words[1]} --completion-bash ${words[1]} --) ) +nats_flags=( $(${words[1]} --completion-bash ${words[1]} --) ) # TODO add _call_program _nats_decorate_flags() { #zle -M "decorating ..."; zle -R local flag directive @@ -135,7 +136,7 @@ ;; (after-command) curcontext=${curcontext%:*:*}:nats-$words[1]: - nats_sub=( $("$cmdword" --completion-bash "${words[@]}") ) + nats_sub=( $("$cmdword" --completion-bash "${words[@]}") ) # TODO add _call_program if [[ ${#nats_sub} -eq 0 ]]; then # terminal nats_flags=( $("$cmdword" --completion-bash "${words[@]}" --) ) ]]] > (I know that I'm misusing the per-flag description by listing it twice in > this way for the two different location but I haven't yet come up with > an easier-to-maintain approach) > > -Phil >
On Wed, Mar 16, 2022 at 7:21 PM Phil Pennock
<zsh-workers+phil.pennock@spodhuis.org> wrote:
>
> Alas, one of the fields is almost guaranteed to be a URL, thus
> containing a colon, and all of those are fair game in the description
> field.
Are the key-value pairs at least all on one line each? Just choosing
a json file that's lying around on my Ubuntu:
json2hash () {
zmodload zsh/mapfile
setopt localoptions extendedglob
# Might have to undo gmail line wrapping here
typeset -gA $1=(
${(z)${(@)${(f)mapfile[$2]}:#[{}]}/(#b)([^:]#):(*)/${(Q)match[1]}
${(q)${(z)${match[2]%,}}}} )
}
json2hash scopes /usr/share/unity/client-scopes.json
typeset -p scopes
On 2022-03-17 at 13:44 -0700, Bart Schaefer wrote: > Are the key-value pairs at least all on one line each? Just choosing > a json file that's lying around on my Ubuntu: Yes, yes they are. For now. And I'm in a position to chat with the tool maintainer and try to make sure they stay that way. > json2hash () { > zmodload zsh/mapfile > setopt localoptions extendedglob > # Might have to undo gmail line wrapping here > typeset -gA $1=( > ${(z)${(@)${(f)mapfile[$2]}:#[{}]}/(#b)([^:]#):(*)/${(Q)match[1]} > ${(q)${(z)${match[2]%,}}}} ) > } > json2hash scopes /usr/share/unity/client-scopes.json > typeset -p scopes Switching from `jq` to this: described_contexts+=("${nctx//:/\\:}:${${${${(@M)${(f)mapfile[$nats_ctxdir/$nctx.json]}:#[[:space:]]##\"description\":*}#*: \"}%,}%\"}") means that the time to complete, across 35 files, drops from "a few seconds where things appear to have hung" to "faster than I can notice", so I am going to publish a version using this, and most of Daniel's suggestions, in a few minutes. I just have to switch from "extra-verbose" to "verbose" simply because of the difference in overhead now. Thank you! -Phil
Phil Pennock wrote on Thu, 17 Mar 2022 21:38 +00:00:
> I just have to switch from "extra-verbose" to "verbose" simply because
> of the difference in overhead now.
As mentioned, 'verbose' is on by default, so you need s/zstyle -t/zstyle -T/.
Cheers,
Daniel
On 2022-03-18 at 00:28 +0000, Daniel Shahaf wrote:
> Phil Pennock wrote on Thu, 17 Mar 2022 21:38 +00:00:
> > I just have to switch from "extra-verbose" to "verbose" simply because
> > of the difference in overhead now.
>
> As mentioned, 'verbose' is on by default, so you need s/zstyle -t/zstyle -T/.
*sigh* I missed the implication there, will fix now. Tested and
confirmed. Thanks.
Note that a grep over the zsh code-base for `zstyle .* verbose` shows
that things are inconsistent here. Almost 1/3 of checks are using '-t'.
% echo ${(D)PWD}
~/src/zsh/code
% ag 'zstyle -[tT] \S+\s+verbose' | grep -c 'zstyle -T'
22
% ag 'zstyle -[tT] \S+\s+verbose' | grep -c 'zstyle -t'
9
-Phil