summaryrefslogtreecommitdiff
path: root/completions/zsh/_podman
diff options
context:
space:
mode:
authorPaul Holzinger <paul.holzinger@web.de>2020-11-08 21:50:51 +0100
committerPaul Holzinger <paul.holzinger@web.de>2020-11-12 11:40:29 +0100
commitae3816614de1c2a0c9ab9cd05afebc5b1dda6429 (patch)
tree11deb2f5b2bb87d1869f7258b8f2e9cc46b57e00 /completions/zsh/_podman
parentb5d1d89a377e38762a75c2042dcc50a370ba6707 (diff)
downloadpodman-ae3816614de1c2a0c9ab9cd05afebc5b1dda6429.tar.gz
podman-ae3816614de1c2a0c9ab9cd05afebc5b1dda6429.tar.bz2
podman-ae3816614de1c2a0c9ab9cd05afebc5b1dda6429.zip
Install the new shell completion logic
Add a new make target (completion) to generate the shell completion scripts. This will generate the scripts for bash, zsh and fish for both podman and podman-remote with `podman completion`. The scripts are put into the completions directory and can be installed system wide with `sudo make install.completions`. This commit replaces the current handwritten scripts for bash and zsh. The `validate.completion` target has been adjusted to make sure nobody edits these scripts directly. Signed-off-by: Paul Holzinger <paul.holzinger@web.de>
Diffstat (limited to 'completions/zsh/_podman')
-rw-r--r--completions/zsh/_podman502
1 files changed, 152 insertions, 350 deletions
diff --git a/completions/zsh/_podman b/completions/zsh/_podman
index b65c3dbb8..b25e9cb08 100644
--- a/completions/zsh/_podman
+++ b/completions/zsh/_podman
@@ -1,379 +1,181 @@
-#compdef podman
+#compdef _podman podman
-# To get zsh to reread this file: unset -f _podman;rm -f ~/.zcompdump;compinit
+# zsh completion for podman -*- shell-script -*-
-# On rereads, reset cache. (Not that the caching works, but some day it might)
-unset -m '_podman_*'
-
-###############################################################################
-# BEGIN 'podman help' parsers -- for options, subcommands, and usage
-
-# Run 'podman XX --help', set _podman_commands to a formatted list of cmds
-_read_podman_commands() {
- local line
-
- # Cache: the intention here is to run each 'podman help' only once.
- # Unfortunately it doesn't seem to actually be working: even though
- # I can see the var_ref in my shell, it's not visible here.
- local _var_ref=_podman_commands_"${*// /_}"
- typeset -ga _podman_commands
- _podman_commands=(${(P)_var_ref})
- (( $#_podman_commands )) && return
-
- _call_program podman podman "$@" --help |\
- sed -n -e '0,/^Available Commands/d' -e '/^[A-Z]/q;p' |\
- sed -e 's/^ \+\([^ ]\+\) \+/\1:/' |\
- egrep . | while read line; do
- _podman_commands+=($line)
- done
-
- eval "typeset -ga $_var_ref"
- eval "$_var_ref=(\$_podman_commands)"
-}
-
-# Run 'podman XX --help', set _podman_option_list to a formatted list
-# of option options for XX
-_read_podman_options() {
- local line
-
- local _var_ref=_podman_options_"${*// /_}"
- eval "typeset -ga ${_var_ref}"
- typeset -ga _podman_option_list
- _podman_option_list=(${(P)_var_ref})
- (( $#_podman_option_list )) && return
-
- # Extract the Options; strip leading whitespace; pack '-f, --foo'
- # as '-f,--foo' (no space); then add '=' to '--foo string'.
- # The result will be, e.g. '-f,--foo=string Description of Option'
- _call_program podman podman "$@" --help |\
- sed -n -e '0,/^Options:/d' -e '/^$/q;p' |\
- grep '^ \+-' |\
- sed -e 's/^ *//' -e 's/^\(-.,\) --/\1--/' |\
- sed -e 's/^\(-[^ ]\+\) \([^ ]\+\) /\1=\2 /' |\
- while read options desc;do
- # options like --foo=string: split into --foo & string
- local -a tmpa
- local optval=
- tmpa=(${(s.=.)options})
- if [ -n "$tmpa[2]" ]; then
- options=$tmpa[1]
- optval=$tmpa[2]
- fi
-
- # 'podman attach --detach-keys' includes ']' in help msg
- desc=${desc//\]/\\]}
-
- for option in ${(s:,:)options}; do
- if [ -n "$optval" ]; then
- _podman_option_list+=("${option}[$desc]:$(_podman_find_helper ${options} ${optval} ${desc})")
- else
- _podman_option_list+=("${option}[$desc]")
- fi
- done
- done
-
- eval "typeset -ga $_var_ref=(\$_podman_option_list)"
-}
-
-# Run 'podman XXX --help', set _podman_usage to the line after "Usage:"
-_read_podman_usage() {
- local _var_ref=_podman_usage_"${*// /_}"
- typeset -ga _podman_usage
- _podman_usage=${(P)_var_ref}
- (( $#_podman_usage )) && return
-
- _podman_usage=$(_call_program podman podman "$@" --help |\
- grep -A1 'Usage:'|\
- tail -1 |\
- sed -e 's/^ *//')
-
- eval "typeset -ga $_var_ref"
- eval "$_var_ref=\$_podman_usage"
-}
-
-# END 'podman help' parsers
-###############################################################################
-# BEGIN custom helpers for individual option arguments
-
-# Find a zsh helper for a given option or command-line option
-_podman_find_helper() {
- local options=$1
- local optval=$2
- local desc=$3
- local helper=
-
- # Yes, this is a lot of hardcoding. IMHO it's still better than
- # hardcoding every possible podman option.
- # FIXME: there are many more options that could use helpers.
- if expr "$desc" : ".*[Dd]irectory" >/dev/null; then
- optval="directory"
- helper="_files -/"
- elif expr "$desc" : ".*[Pp]ath" >/dev/null; then
- optval="path"
- helper=_files
- # For messages like 'restart policy ("always"|"no"|"on-failure")
- elif optlist=$(expr "$desc" : '.*(\(\"[^\\)]\+|[^\\)]\+\"\))' 2>/dev/null); then
- optval=${${options##--}//-/ } # "--log-level" => "log level"
- optlist=${optlist//\"/} # "a"|"b"|"c" => a|b|c
- optlist=${optlist//\|/ } # a|b|c => a b c
- # FIXME: how to present values _in order_, not sorted alphabetically?
- helper="($optlist)"
+__podman_debug()
+{
+ local file="$BASH_COMP_DEBUG_FILE"
+ if [[ -n ${file} ]]; then
+ echo "$*" >> "${file}"
fi
- echo "$optval:$helper"
-}
-
-# END custom helpers for individual option arguments
-###############################################################################
-# BEGIN helpers for command-line args (containers, images)
-
-__podman_helper_generic() {
- local expl line
- local -a results
-
- local foo1=$1; shift
- local name=$2; shift
-
- _call_program $foo1 podman "$@" |\
- while read line; do
- results+=(${=line})
- done
-
- _wanted $foo1 expl $name compadd ${(u)results}
-}
-
-_podman_helper_image() {
- __podman_helper_generic podman-images 'images' \
- images --format '{{.ID}}\ {{.Repository}}:{{.Tag}}'
-}
-
-# FIXME: at some point, distinguish between running & stopped containers
-_podman_helper_container() {
- __podman_helper_generic podman-containers 'containers' \
- ps -a --format '{{.Names}}\ {{.ID}}'
-}
-
-_podman_helper_pod() {
- __podman_helper_generic podman-pods 'pods' pod list --format '{{.Name}}'
}
-_podman_helper_volume() {
- __podman_helper_generic podman-volumes 'volumes' volume ls --format '{{.Name}}'
-}
-
-# Combinations. This one seen in diff & inspect
-_podman_helper_container-or-image() {
- _podman_helper_image
- _podman_helper_container
-}
-
-# Seen in generate-kube
-_podman_helper_container-or-pod() {
- _podman_helper_container
- _podman_helper_pod
-}
-
-# For top and pod-top
-_podman_helper_format-descriptors() {
- __podman_helper_generic top-format-descriptors 'format descriptors' \
- top --list-descriptors
-}
-
-# for push, login/logout, and trust
-# FIXME: some day, use this to define a helper for IMAGE-PATH (in 'pull')
-_podman_helper_registry() {
- local expl
- local -a registries
+_podman()
+{
+ local shellCompDirectiveError=1
+ local shellCompDirectiveNoSpace=2
+ local shellCompDirectiveNoFileComp=4
+ local shellCompDirectiveFilterFileExt=8
+ local shellCompDirectiveFilterDirs=16
+ local shellCompDirectiveLegacyCustomComp=32
+ local shellCompDirectiveLegacyCustomArgsComp=64
+
+ local lastParam lastChar flagPrefix requestComp out directive comp lastComp noSpace
+ local -a completions
+
+ __podman_debug "\n========= starting completion logic =========="
+ __podman_debug "CURRENT: ${CURRENT}, words[*]: ${words[*]}"
+
+ # The user could have moved the cursor backwards on the command-line.
+ # We need to trigger completion from the $CURRENT location, so we need
+ # to truncate the command-line ($words) up to the $CURRENT location.
+ # (We cannot use $CURSOR as its value does not work when a command is an alias.)
+ words=("${=words[1,CURRENT]}")
+ __podman_debug "Truncated words[*]: ${words[*]},"
+
+ lastParam=${words[-1]}
+ lastChar=${lastParam[-1]}
+ __podman_debug "lastParam: ${lastParam}, lastChar: ${lastChar}"
+
+ # For zsh, when completing a flag with an = (e.g., podman -n=<TAB>)
+ # completions must be prefixed with the flag
+ setopt local_options BASH_REMATCH
+ if [[ "${lastParam}" =~ '-.*=' ]]; then
+ # We are dealing with a flag with an =
+ flagPrefix="-P ${BASH_REMATCH}"
+ fi
- # Suggestions for improvement more than welcome.
- python3 -c 'from configparser import ConfigParser;cp=ConfigParser();cp.read("/etc/containers/registries.conf");registries=eval(cp.get("registries.search","registries"));[print(r) for r in registries]' 2>/dev/null | while read line; do
- registries+=($line)
- done
+ # Prepare the command to obtain completions
+ requestComp="${words[1]} __complete ${words[2,-1]}"
+ if [ "${lastChar}" = "" ]; then
+ # If the last parameter is complete (there is a space following it)
+ # We add an extra empty parameter so we can indicate this to the go completion code.
+ __podman_debug "Adding extra empty parameter"
+ requestComp="${requestComp} \"\""
+ fi
- if (( $#registries )); then
- _wanted podman-registry expl "registry" compadd ${(u)registries}
+ __podman_debug "About to call: eval ${requestComp}"
+
+ # Use eval to handle any environment variables and such
+ out=$(eval ${requestComp} 2>/dev/null)
+ __podman_debug "completion output: ${out}"
+
+ # Extract the directive integer following a : from the last line
+ local lastLine
+ while IFS='\n' read -r line; do
+ lastLine=${line}
+ done < <(printf "%s\n" "${out[@]}")
+ __podman_debug "last line: ${lastLine}"
+
+ if [ "${lastLine[1]}" = : ]; then
+ directive=${lastLine[2,-1]}
+ # Remove the directive including the : and the newline
+ local suffix
+ (( suffix=${#lastLine}+2))
+ out=${out[1,-$suffix]}
else
- _hosts
+ # There is no directive specified. Leave $out as is.
+ __podman_debug "No directive found. Setting do default"
+ directive=0
fi
-}
-
-# END helpers for command-line args
-###############################################################################
-# BEGIN figure out completion helpers for a given (sub)command
-# Read Usage string for this subcommand, set up helpers for its subargs
-_set_up_podman_args() {
- _read_podman_usage "$@"
+ __podman_debug "directive: ${directive}"
+ __podman_debug "completions: ${out}"
+ __podman_debug "flagPrefix: ${flagPrefix}"
- typeset -ga _podman_args=()
- # E.g. 'podman exec [options] CONTAINER [...' -> 'CONTAINER [....'
- local usage_rhs=$(expr "$_podman_usage" : ".*\[options\] \+\(.*\)")
-
- # e.g. podman pod ps which takes no further args
- if [ -z "$usage_rhs" ]; then
+ if [ $((directive & shellCompDirectiveError)) -ne 0 ]; then
+ __podman_debug "Completion received error. Ignoring completions."
return
fi
- # podman diff & inspect accept 'CONTAINER | IMAGE'; make into one keyword.
- usage_rhs=${usage_rhs// | /-OR-}
+ while IFS='\n' read -r comp; do
+ if [ -n "$comp" ]; then
+ # If requested, completions are returned with a description.
+ # The description is preceded by a TAB character.
+ # For zsh's _describe, we need to use a : instead of a TAB.
+ # We first need to escape any : as part of the completion itself.
+ comp=${comp//:/\\:}
- # Arg parsing. There are three possibilities in Usage messages:
- #
- # [IMAGE] - optional image arg (zero or one)
- # IMAGE - exactly one image arg
- # IMAGE [IMAGE...] - one or more image args
- # and, theoretically:
- # [IMAGE...] - zero or more? Haven't seen it in practice. Defer.
- #
- # For completion purposes, we only need to provide two options:
- # one, or more than one? That is: continue offering completion
- # suggestions after the first one? For that, we make two passes;
- # in the first, mark an option as either '' (only one) or
+ local tab=$(printf '\t')
+ comp=${comp//$tab/:}
- # Parse each command-line arg seen in usage message
- local word
- local -A _seen=()
- for word in ${=usage_rhs}; do
- local unbracketed=$(expr "$word" : "\[\(.*\)\]")
+ __podman_debug "Adding completion: ${comp}"
+ completions+=${comp}
+ lastComp=$comp
+ fi
+ done < <(printf "%s\n" "${out[@]}")
- if [ -n "$unbracketed" ]; then
- # Remove all dots; assume(!?) that they'll all be at the end
- unbracketed=${unbracketed//./}
+ if [ $((directive & shellCompDirectiveNoSpace)) -ne 0 ]; then
+ __podman_debug "Activating nospace."
+ noSpace="-S ''"
+ fi
- if (( $_seen[$unbracketed] )); then
- # Is this the same word as the previous arg?
- if expr "$_podman_args[-1]" : ":$unbracketed:" >/dev/null; then
- # Yes. Make it '*:...' instead of ':...', indicating >1
- _podman_args[-1]="*$_podman_args[-1]"
- fi
- continue
+ if [ $((directive & shellCompDirectiveFilterFileExt)) -ne 0 ]; then
+ # File extension filtering
+ local filteringCmd
+ filteringCmd='_files'
+ for filter in ${completions[@]}; do
+ if [ ${filter[1]} != '*' ]; then
+ # zsh requires a glob pattern to do file filtering
+ filter="\*.$filter"
fi
-
- word=$unbracketed
+ filteringCmd+=" -g $filter"
+ done
+ filteringCmd+=" ${flagPrefix}"
+
+ __podman_debug "File filtering command: $filteringCmd"
+ _arguments '*:filename:'"$filteringCmd"
+ elif [ $((directive & shellCompDirectiveFilterDirs)) -ne 0 ]; then
+ # File completion for directories only
+ local subDir
+ subdir="${completions[1]}"
+ if [ -n "$subdir" ]; then
+ __podman_debug "Listing directories in $subdir"
+ pushd "${subdir}" >/dev/null 2>&1
+ else
+ __podman_debug "Listing directories in ."
fi
- # As of 2019-03 all such instances are '[COMMAND [ARG...]]' and are,
- # of course, at the end of the line. We can't offer completion for
- # these, because the container will have different commands than
- # the host system... but try anyway.
- if [ "$word" = '[COMMAND' ]; then
- # e.g. podman create, exec, run
- _podman_args+=(
- ":command: _command_names -e"
- "*::arguments: _normal"
- )
- return
+ local result
+ _arguments '*:dirname:_files -/'" ${flagPrefix}"
+ result=$?
+ if [ -n "$subdir" ]; then
+ popd >/dev/null 2>&1
fi
+ return $result
+ else
+ __podman_debug "Calling _describe"
+ if eval _describe "completions" completions $flagPrefix $noSpace; then
+ __podman_debug "_describe found some completions"
- # Look for an existing helper, e.g. IMAGE -> _podman_helper_image
- local helper="_podman_helper_${(L)word}"
- if (( $+functions[$helper] )); then
- :
+ # Return the success of having called _describe
+ return 0
else
- # No defined helper. Reset, but check for known expressions.
- helper=
- case "$word" in
- KUBEFILE) helper='_files -g "*.y(|a)ml(-.)"' ;;
- PATH) helper='_files' ;;
- esac
- fi
-
- # Another special case: 'top' actually takes multiple options
- local multi=
- if [ "$word" = "FORMAT-DESCRIPTORS" ]; then
- multi='*'
+ __podman_debug "_describe did not find completions."
+ __podman_debug "Checking if we should do file completion."
+ if [ $((directive & shellCompDirectiveNoFileComp)) -ne 0 ]; then
+ __podman_debug "deactivating file completion"
+
+ # We must return an error code here to let zsh know that there were no
+ # completions found by _describe; this is what will trigger other
+ # matching algorithms to attempt to find completions.
+ # For example zsh can match letters in the middle of words.
+ return 1
+ else
+ # Perform file completion
+ __podman_debug "Activating file completion"
+
+ # We must return the result of this command, so it must be the
+ # last command, or else we must store its result to return it.
+ _arguments '*:filename:_files'" ${flagPrefix}"
+ fi
fi
- _podman_args+=("$multi:${(L)word}:$helper")
- _seen[$word]=1
- done
-}
-
-# For an endpoint command, i.e. not a subcommand.
-_podman_terminus() {
- typeset -A opt_args
- typeset -ga _podman_option_list
- typeset -ga _podman_args
- integer ret=1
-
- # Find out what args it takes (e.g. image(s), container(s)) and see
- # if we have helpers for them.
- _set_up_podman_args "$@"
- _arguments -C $_podman_option_list $_podman_args && ret=0
-
- return ret
-}
-
-# END figure out completion helpers for a given (sub)command
-################################################################################
-# BEGIN actual entry point
-
-# This is the main entry point; it's also where we (recursively) come in
-# to handle nested subcommands such as 'podman container' or even 3-level
-# ones like 'podman generate kube'. Nesting is complicated, so here's
-# my best understanding as of 2019-03-12:
-#
-# Easy first: when you do "podman <TAB>" zsh calls us, we run 'podman --help',
-# figure out the global options and subcommands, and run the magic _arguments
-# command. That will offer those options/subcommands, and complete, etc.
-#
-# Where it gets harder is when you do "podman container mount <TAB>".
-# zsh first calls us with words=(podman container mount) but we don't
-# want all that full context yet! We want to go a piece at a time,
-# handling 'container' first, then 'mount'; ending up with our
-# final 'podman container mount --help' giving us suitable options
-# and no subcommands; from which we determine that it's a terminus
-# and jump to a function that handles non-subcommand arguments.
-#
-# This is the closest I've yet come to understanding zsh completion;
-# it is still incomplete and may in fact be incorrect. But it works
-# better than anything I've played with so far.
-_podman_subcommand() {
- local curcontext="$curcontext" state line
- typeset -A opt_args
- integer ret=1
-
- # Run 'podman --help' / 'podman system --help' for our context (initially
- # empty, then possibly under subcommands); from those, get a list of
- # options appropriate for this context and, if applicable, subcommands.
- _read_podman_options "$@"
- _read_podman_commands "$@"
-
- # Now, is this a sub-subcommand, or does it have args?
- if (( $#_podman_commands )); then
- # Subcommands required (podman, podman system, etc)
- local cmd=${words// /_}
- _arguments -C $_podman_option_list \
- "(-): :->command" \
- "(-)*:: :->option-or-argument" \
- && ret=0
-
- case $state in
- (command)
- _describe -t command "podman $* command" _podman_commands && ret=0
- ;;
- (option-or-argument)
- # I think this is when we have a _completed_ subcommand.
- # Recurse back into here, offering options for that subcommand.
- curcontext=${curcontext%:*:*}:podman-${words[1]}:
- _podman_subcommand "$@" ${words[1]} && ret=0
- ;;
- esac
- else
- # At a terminus, i.e. podman info, podman history; find out
- # what args it takes.
- _podman_terminus "$@" && ret=0
fi
-
- return ret
}
-_podman() {
- _podman_subcommand
-}
+# don't run the completion function when being source-ed or eval-ed
+if [ "$funcstack[1]" = "_podman" ]; then
+ _podman
+fi
-# Local Variables:
-# mode: shell-script
-# sh-indentation: 4
-# indent-tabs-mode: nil
-# sh-basic-offset: 4
-# End:
-# vim: ft=zsh sw=4 ts=4 et
+# This file is generated with "podman completion"; see: podman-completion(1)