github.com/containers/libpod@v1.9.4-0.20220419124438-4284fd425507/completions/zsh/_podman (about) 1 #compdef podman 2 3 # To get zsh to reread this file: unset -f _podman;rm -f ~/.zcompdump;compinit 4 5 # On rereads, reset cache. (Not that the caching works, but some day it might) 6 unset -m '_podman_*' 7 8 ############################################################################### 9 # BEGIN 'podman help' parsers -- for options, subcommands, and usage 10 11 # Run 'podman XX --help', set _podman_commands to a formatted list of cmds 12 _read_podman_commands() { 13 local line 14 15 # Cache: the intention here is to run each 'podman help' only once. 16 # Unfortunately it doesn't seem to actually be working: even though 17 # I can see the var_ref in my shell, it's not visible here. 18 local _var_ref=_podman_commands_"${*// /_}" 19 typeset -ga _podman_commands 20 _podman_commands=(${(P)_var_ref}) 21 (( $#_podman_commands )) && return 22 23 _call_program podman podman "$@" --help |\ 24 sed -n -e '0,/^Available Commands/d' -e '/^[A-Z]/q;p' |\ 25 sed -e 's/^ \+\([^ ]\+\) \+/\1:/' |\ 26 egrep . | while read line; do 27 _podman_commands+=($line) 28 done 29 30 eval "typeset -ga $_var_ref" 31 eval "$_var_ref=(\$_podman_commands)" 32 } 33 34 # Run 'podman XX --help', set _podman_flag_list to a formatted list 35 # of flag options for XX 36 _read_podman_flags() { 37 local line 38 39 local _var_ref=_podman_flags_"${*// /_}" 40 eval "typeset -ga ${_var_ref}" 41 typeset -ga _podman_flag_list 42 _podman_flag_list=(${(P)_var_ref}) 43 (( $#_podman_flag_list )) && return 44 45 # Extract the Flags; strip leading whitespace; pack '-f, --foo' 46 # as '-f,--foo' (no space); then add '=' to '--foo string'. 47 # The result will be, e.g. '-f,--foo=string Description of Option' 48 _call_program podman podman "$@" --help |\ 49 sed -n -e '0,/^Flags:/d' -e '/^$/q;p' |\ 50 grep '^ \+-' |\ 51 sed -e 's/^ *//' -e 's/^\(-.,\) --/\1--/' |\ 52 sed -e 's/^\(-[^ ]\+\) \([^ ]\+\) /\1=\2 /' |\ 53 while read flags desc;do 54 # flags like --foo=string: split into --foo & string 55 local -a tmpa 56 local optval= 57 tmpa=(${(s.=.)flags}) 58 if [ -n "$tmpa[2]" ]; then 59 flags=$tmpa[1] 60 optval=$tmpa[2] 61 fi 62 63 # 'podman attach --detach-keys' includes ']' in help msg 64 desc=${desc//\]/\\]} 65 66 for flag in ${(s:,:)flags}; do 67 if [ -n "$optval" ]; then 68 _podman_flag_list+=("${flag}[$desc]:$(_podman_find_helper ${flags} ${optval} ${desc})") 69 else 70 _podman_flag_list+=("${flag}[$desc]") 71 fi 72 done 73 done 74 75 eval "typeset -ga $_var_ref=(\$_podman_flag_list)" 76 } 77 78 # Run 'podman XXX --help', set _podman_usage to the line after "Usage:" 79 _read_podman_usage() { 80 local _var_ref=_podman_usage_"${*// /_}" 81 typeset -ga _podman_usage 82 _podman_usage=${(P)_var_ref} 83 (( $#_podman_usage )) && return 84 85 _podman_usage=$(_call_program podman podman "$@" --help |\ 86 grep -A1 'Usage:'|\ 87 tail -1 |\ 88 sed -e 's/^ *//') 89 90 eval "typeset -ga $_var_ref" 91 eval "$_var_ref=\$_podman_usage" 92 } 93 94 # END 'podman help' parsers 95 ############################################################################### 96 # BEGIN custom helpers for individual option arguments 97 98 # Find a zsh helper for a given flag or command-line option 99 _podman_find_helper() { 100 local flags=$1 101 local optval=$2 102 local desc=$3 103 local helper= 104 105 # Yes, this is a lot of hardcoding. IMHO it's still better than 106 # hardcoding every possible podman option. 107 # FIXME: there are many more options that could use helpers. 108 if expr "$desc" : ".*[Dd]irectory" >/dev/null; then 109 optval="directory" 110 helper="_files -/" 111 elif expr "$desc" : ".*[Pp]ath" >/dev/null; then 112 optval="path" 113 helper=_files 114 # For messages like 'restart policy ("always"|"no"|"on-failure") 115 elif optlist=$(expr "$desc" : '.*(\(\"[^\\)]\+|[^\\)]\+\"\))' 2>/dev/null); then 116 optval=${${flags##--}//-/ } # "--log-level" => "log level" 117 optlist=${optlist//\"/} # "a"|"b"|"c" => a|b|c 118 optlist=${optlist//\|/ } # a|b|c => a b c 119 # FIXME: how to present values _in order_, not sorted alphabetically? 120 helper="($optlist)" 121 fi 122 echo "$optval:$helper" 123 } 124 125 # END custom helpers for individual option arguments 126 ############################################################################### 127 # BEGIN helpers for command-line args (containers, images) 128 129 __podman_helper_generic() { 130 local expl line 131 local -a results 132 133 local foo1=$1; shift 134 local name=$2; shift 135 136 _call_program $foo1 podman "$@" |\ 137 while read line; do 138 results+=(${=line}) 139 done 140 141 _wanted $foo1 expl $name compadd ${(u)results} 142 } 143 144 _podman_helper_image() { 145 __podman_helper_generic podman-images 'images' \ 146 images --format '{{.ID}}\ {{.Repository}}:{{.Tag}}' 147 } 148 149 # FIXME: at some point, distinguish between running & stopped containers 150 _podman_helper_container() { 151 __podman_helper_generic podman-containers 'containers' \ 152 ps -a --format '{{.Names}}\ {{.ID}}' 153 } 154 155 _podman_helper_pod() { 156 __podman_helper_generic podman-pods 'pods' pod list --format '{{.Name}}' 157 } 158 159 _podman_helper_volume() { 160 __podman_helper_generic podman-volumes 'volumes' volume ls --format '{{.Name}}' 161 } 162 163 # Combinations. This one seen in diff & inspect 164 _podman_helper_container-or-image() { 165 _podman_helper_image 166 _podman_helper_container 167 } 168 169 # Seen in generate-kube 170 _podman_helper_container-or-pod() { 171 _podman_helper_container 172 _podman_helper_pod 173 } 174 175 # For top and pod-top 176 _podman_helper_format-descriptors() { 177 __podman_helper_generic top-format-descriptors 'format descriptors' \ 178 top --list-descriptors 179 } 180 181 # for push, login/logout, and trust 182 # FIXME: some day, use this to define a helper for IMAGE-PATH (in 'pull') 183 _podman_helper_registry() { 184 local expl 185 local -a registries 186 187 # Suggestions for improvement more than welcome. 188 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 189 registries+=($line) 190 done 191 192 if (( $#registries )); then 193 _wanted podman-registry expl "registry" compadd ${(u)registries} 194 else 195 _hosts 196 fi 197 } 198 199 # END helpers for command-line args 200 ############################################################################### 201 # BEGIN figure out completion helpers for a given (sub)command 202 203 # Read Usage string for this subcommand, set up helpers for its subargs 204 _set_up_podman_args() { 205 _read_podman_usage "$@" 206 207 typeset -ga _podman_args=() 208 # E.g. 'podman exec [flags] CONTAINER [...' -> 'CONTAINER [....' 209 local usage_rhs=$(expr "$_podman_usage" : ".*\[flags\] \+\(.*\)") 210 211 # e.g. podman pod ps which takes no further args 212 if [ -z "$usage_rhs" ]; then 213 return 214 fi 215 216 # podman diff & inspect accept 'CONTAINER | IMAGE'; make into one keyword. 217 usage_rhs=${usage_rhs// | /-OR-} 218 219 # Arg parsing. There are three possibilities in Usage messages: 220 # 221 # [IMAGE] - optional image arg (zero or one) 222 # IMAGE - exactly one image arg 223 # IMAGE [IMAGE...] - one or more image args 224 # and, theoretically: 225 # [IMAGE...] - zero or more? Haven't seen it in practice. Defer. 226 # 227 # For completion purposes, we only need to provide two options: 228 # one, or more than one? That is: continue offering completion 229 # suggestions after the first one? For that, we make two passes; 230 # in the first, mark an option as either '' (only one) or 231 232 # Parse each command-line arg seen in usage message 233 local word 234 local -A _seen=() 235 for word in ${=usage_rhs}; do 236 local unbracketed=$(expr "$word" : "\[\(.*\)\]") 237 238 if [ -n "$unbracketed" ]; then 239 # Remove all dots; assume(!?) that they'll all be at the end 240 unbracketed=${unbracketed//./} 241 242 if (( $_seen[$unbracketed] )); then 243 # Is this the same word as the previous arg? 244 if expr "$_podman_args[-1]" : ":$unbracketed:" >/dev/null; then 245 # Yes. Make it '*:...' instead of ':...', indicating >1 246 _podman_args[-1]="*$_podman_args[-1]" 247 fi 248 continue 249 fi 250 251 word=$unbracketed 252 fi 253 254 # As of 2019-03 all such instances are '[COMMAND [ARG...]]' and are, 255 # of course, at the end of the line. We can't offer completion for 256 # these, because the container will have different commands than 257 # the host system... but try anyway. 258 if [ "$word" = '[COMMAND' ]; then 259 # e.g. podman create, exec, run 260 _podman_args+=( 261 ":command: _command_names -e" 262 "*::arguments: _normal" 263 ) 264 return 265 fi 266 267 # Look for an existing helper, e.g. IMAGE -> _podman_helper_image 268 local helper="_podman_helper_${(L)word}" 269 if (( $+functions[$helper] )); then 270 : 271 else 272 # No defined helper. Reset, but check for known expressions. 273 helper= 274 case "$word" in 275 KUBEFILE) helper='_files -g "*.y(|a)ml(-.)"' ;; 276 PATH) helper='_files' ;; 277 esac 278 fi 279 280 # Another special case: 'top' actually takes multiple options 281 local multi= 282 if [ "$word" = "FORMAT-DESCRIPTORS" ]; then 283 multi='*' 284 fi 285 _podman_args+=("$multi:${(L)word}:$helper") 286 _seen[$word]=1 287 done 288 } 289 290 # For an endpoint command, i.e. not a subcommand. 291 _podman_terminus() { 292 typeset -A opt_args 293 typeset -ga _podman_flag_list 294 typeset -ga _podman_args 295 integer ret=1 296 297 # Find out what args it takes (e.g. image(s), container(s)) and see 298 # if we have helpers for them. 299 _set_up_podman_args "$@" 300 _arguments -C $_podman_flag_list $_podman_args && ret=0 301 302 return ret 303 } 304 305 # END figure out completion helpers for a given (sub)command 306 ################################################################################ 307 # BEGIN actual entry point 308 309 # This is the main entry point; it's also where we (recursively) come in 310 # to handle nested subcommands such as 'podman container' or even 3-level 311 # ones like 'podman generate kube'. Nesting is complicated, so here's 312 # my best understanding as of 2019-03-12: 313 # 314 # Easy first: when you do "podman <TAB>" zsh calls us, we run 'podman --help', 315 # figure out the global options and subcommands, and run the magic _arguments 316 # command. That will offer those options/subcommands, and complete, etc. 317 # 318 # Where it gets harder is when you do "podman container mount <TAB>". 319 # zsh first calls us with words=(podman container mount) but we don't 320 # want all that full context yet! We want to go a piece at a time, 321 # handling 'container' first, then 'mount'; ending up with our 322 # final 'podman container mount --help' giving us suitable flags 323 # and no subcommands; from which we determine that it's a terminus 324 # and jump to a function that handles non-subcommand arguments. 325 # 326 # This is the closest I've yet come to understanding zsh completion; 327 # it is still incomplete and may in fact be incorrect. But it works 328 # better than anything I've played with so far. 329 _podman_subcommand() { 330 local curcontext="$curcontext" state line 331 typeset -A opt_args 332 integer ret=1 333 334 # Run 'podman --help' / 'podman system --help' for our context (initially 335 # empty, then possibly under subcommands); from those, get a list of 336 # flags appropriate for this context and, if applicable, subcommands. 337 _read_podman_flags "$@" 338 _read_podman_commands "$@" 339 340 # Now, is this a sub-subcommand, or does it have args? 341 if (( $#_podman_commands )); then 342 # Subcommands required (podman, podman system, etc) 343 local cmd=${words// /_} 344 _arguments -C $_podman_flag_list \ 345 "(-): :->command" \ 346 "(-)*:: :->option-or-argument" \ 347 && ret=0 348 349 case $state in 350 (command) 351 _describe -t command "podman $* command" _podman_commands && ret=0 352 ;; 353 (option-or-argument) 354 # I think this is when we have a _completed_ subcommand. 355 # Recurse back into here, offering options for that subcommand. 356 curcontext=${curcontext%:*:*}:podman-${words[1]}: 357 _podman_subcommand "$@" ${words[1]} && ret=0 358 ;; 359 esac 360 else 361 # At a terminus, i.e. podman info, podman history; find out 362 # what args it takes. 363 _podman_terminus "$@" && ret=0 364 fi 365 366 return ret 367 } 368 369 _podman() { 370 _podman_subcommand 371 } 372 373 # Local Variables: 374 # mode: shell-script 375 # sh-indentation: 4 376 # indent-tabs-mode: nil 377 # sh-basic-offset: 4 378 # End: 379 # vim: ft=zsh sw=4 ts=4 et