diff --git a/bash_completion b/bash_completion index 00e3b6bf7b1..38578f4bd0d 100644 --- a/bash_completion +++ b/bash_completion @@ -2157,20 +2157,25 @@ _comp_realcommand() } # This function returns the first argument, excluding options -# @param $1 chars Characters out of $COMP_WORDBREAKS which should -# NOT be considered word breaks. See _comp__reassemble_words. -# TODO:API: rename per conventions -_get_first_arg() +# @var[out] ret First argument before current being completed if any, or +# otherwise an empty string +# @return True (0) if any argument is found, False (> 0) otherwise. +# @since 2.12 +_comp_get_first_arg() { local i - arg= - for ((i = 1; i < COMP_CWORD; i++)); do - if [[ ${COMP_WORDS[i]} != -* ]]; then - arg=${COMP_WORDS[i]} + ret= + for ((i = 1; i < cword; i++)); do + if [[ ${words[i]} != -?* ]]; then + ret=${words[i]} + return 0 + elif [[ ${words[i]} == -- ]]; then + ((i + 1 < cword)) && ret=${words[i + 1]} && return 0 break fi done + return 1 } # This function counts the number of args, excluding options @@ -2178,18 +2183,23 @@ _get_first_arg() # NOT be considered word breaks. See _comp__reassemble_words. # @param $2 glob Options whose following argument should not be counted # @param $3 glob Options that should be counted as args -# TODO:API: rename per conventions -_count_args() +# @var[out] ret Return the number of arguments +# @since 2.12 +_comp_count_args() { local i cword words _comp__reassemble_words "${1-}" words cword - args=1 + ret=1 for ((i = 1; i < cword; i++)); do # shellcheck disable=SC2053 - if [[ ${words[i]} != -* && ${words[i - 1]} != ${2-} || - ${words[i]} == ${3-} ]]; then - ((args++)) + if [[ ${2-} && ${words[i]} == ${2-} ]]; then + ((i++)) + elif [[ ${words[i]} != -?* || ${3-} && ${words[i]} == ${3-} ]]; then + ((ret++)) + elif [[ ${words[i]} == -- ]]; then + ((ret += cword - i - 1)) + break fi done } diff --git a/bash_completion.d/000_bash_completion_compat.bash b/bash_completion.d/000_bash_completion_compat.bash index e85f296db74..fb32ae1211f 100644 --- a/bash_completion.d/000_bash_completion_compat.bash +++ b/bash_completion.d/000_bash_completion_compat.bash @@ -398,4 +398,49 @@ _fstypes() _comp_compgen -a fstypes } +# This function returns the first argument, excluding options +# @deprecated 2.12 Use `_comp_get_first_arg`. Note that the new function +# `_comp_get_first_arg` operates on `words` and `cword` instead of `COMP_WORDS` +# and `COMP_CWORD`. The new function considers a command-line argument after +# `--` as an argument. The new function returns the result in variable `ret` +# instead of `arg`. +_get_first_arg() +{ + local i + + arg= + for ((i = 1; i < COMP_CWORD; i++)); do + if [[ ${COMP_WORDS[i]} != -* ]]; then + arg=${COMP_WORDS[i]} + break + fi + done +} + +# This function counts the number of args, excluding options +# @param $1 chars Characters out of $COMP_WORDBREAKS which should +# NOT be considered word breaks. See _comp__reassemble_words. +# @param $2 glob Options whose following argument should not be counted +# @param $3 glob Options that should be counted as args +# @var[out] args Return the number of arguments +# @deprecated 2.12 Use `_comp_count_args`. Note that the new function +# `_comp_count_args` returns the result in variable `ret` instead of `args`. +# In the new function, `-` is also counted as an argument. The new function +# counts all the arguments after `--`. +# shellcheck disable=SC2178 # assignments are not intended for global "args" +_count_args() +{ + local i cword words + _comp__reassemble_words "${1-}" words cword + + args=1 + for ((i = 1; i < cword; i++)); do + # shellcheck disable=SC2053 + if [[ ${words[i]} != -* && ${words[i - 1]} != ${2-} || + ${words[i]} == ${3-} ]]; then + ((args++)) + fi + done +} + # ex: filetype=sh diff --git a/completions/7z b/completions/7z index 368641085cb..87eca50a4aa 100644 --- a/completions/7z +++ b/completions/7z @@ -84,9 +84,9 @@ _comp_cmd_7z() return fi - local args - _count_args "=" - if ((args == 2)); then + local ret + _comp_count_args "=" + if ((ret == 2)); then _filedir_xspec unzip "${@:2}" # TODO: parsing 7z i output? # - how to figure out if the format is input or output? diff --git a/completions/_cal b/completions/_cal index bf6845fb0c5..6ca6520784d 100644 --- a/completions/_cal +++ b/completions/_cal @@ -28,9 +28,9 @@ _comp_cmd_cal() return fi - local args - _count_args - ((args == 1)) && _comp_compgen -- -W '{1..12}' + local ret + _comp_count_args + ((ret == 1)) && _comp_compgen -- -W '{1..12}' } && complete -F _comp_cmd_cal cal ncal diff --git a/completions/_xm b/completions/_xm index 84f29894029..a79c74c1003 100644 --- a/completions/_xm +++ b/completions/_xm @@ -17,7 +17,7 @@ _comp_cmd_xm() # TODO: split longopt - local args command commands options + local ret command commands options commands='console vncviewer create new delete destroy domid domname dump-core list mem-max mem-set migrate pause reboot rename reset @@ -79,16 +79,16 @@ _comp_cmd_xm() mem-max | pause | reboot | rename | shutdown | unpause | \ vcpu-list | vcpu-pin | vcpu-set | block-list | \ network-list | vtpm-list) - _count_args - case $args in + _comp_count_args + case $ret in 2) _comp_cmd_xm__domain_names ;; esac ;; migrate) - _count_args - case $args in + _comp_count_args + case $ret in 2) _comp_cmd_xm__domain_names ;; @@ -101,8 +101,8 @@ _comp_cmd_xm() _comp_compgen_filedir ;; save) - _count_args - case $args in + _comp_count_args + case $ret in 2) _comp_cmd_xm__domain_names ;; @@ -112,8 +112,8 @@ _comp_cmd_xm() esac ;; sysrq) - _count_args - case $args in + _comp_count_args + case $ret in 2) _comp_cmd_xm__domain_names ;; @@ -123,8 +123,8 @@ _comp_cmd_xm() esac ;; block-attach) - _count_args - case $args in + _comp_count_args + case $ret in 2) _comp_cmd_xm__domain_names ;; @@ -140,8 +140,8 @@ _comp_cmd_xm() esac ;; block-detach) - _count_args - case $args in + _comp_count_args + case $ret in 2) _comp_cmd_xm__domain_names ;; @@ -152,8 +152,8 @@ _comp_cmd_xm() esac ;; network-attach) - _count_args - case $args in + _comp_count_args + case $ret in 2) _comp_cmd_xm__domain_names ;; @@ -164,8 +164,8 @@ _comp_cmd_xm() esac ;; network-detach) - _count_args - case $args in + _comp_count_args + case $ret in 2) _comp_cmd_xm__domain_names ;; @@ -201,8 +201,8 @@ _comp_cmd_xm() ;; esac - _count_args - case $args in + _comp_count_args + case $ret in 2) _comp_cmd_xm__domain_names ;; diff --git a/completions/arp b/completions/arp index 7ae609cbb1d..3e653516fd0 100644 --- a/completions/arp +++ b/completions/arp @@ -33,9 +33,9 @@ _comp_cmd_arp() return fi - local args - _count_args "" "@(--device|--protocol|--file|--hw-type|-${noargopts}[iApfHt])" - case $args in + local ret + _comp_count_args "" "@(--device|--protocol|--file|--hw-type|-${noargopts}[iApfHt])" + case $ret in 1) local ips=$("$1" -an | command sed -ne \ 's/.*(\([0-9]\{1,3\}\(\.[0-9]\{1,3\}\)\{3\}\)).*/\1/p') diff --git a/completions/avctrl b/completions/avctrl index f503bd67f22..139bf5961f6 100644 --- a/completions/avctrl +++ b/completions/avctrl @@ -8,9 +8,9 @@ _comp_cmd_avctrl() if [[ $cur == -* ]]; then _comp_compgen -- -W '--help --quiet' else - local args - _count_args - if ((args == 1)); then + local ret + _comp_count_args + if ((ret == 1)); then _comp_compgen -- -W 'discover switch' fi fi diff --git a/completions/chmod b/completions/chmod index cf9a5b169c9..440750f89f5 100644 --- a/completions/chmod +++ b/completions/chmod @@ -27,10 +27,10 @@ _comp_cmd_chmod() return fi - local args - _count_args "" "" "$modearg" + local ret + _comp_count_args "" "" "$modearg" - case $args in + case $ret in 1) ;; # mode *) _comp_compgen_filedir ;; esac diff --git a/completions/chown b/completions/chown index 22fd4bf94ba..986dec79cc4 100644 --- a/completions/chown +++ b/completions/chown @@ -29,12 +29,12 @@ _comp_cmd_chown() --no-dereference --from --silent --quiet --reference --recursive --verbose --help --version $opts' else - local args + local ret # The first argument is a usergroup; the rest are filedir. - _count_args : + _comp_count_args : - if ((args == 1)); then + if ((ret == 1)); then _comp_compgen_usergroup -u else _comp_compgen_filedir diff --git a/completions/cryptsetup b/completions/cryptsetup index 9604b08790d..f7e6675c9fd 100644 --- a/completions/cryptsetup +++ b/completions/cryptsetup @@ -34,21 +34,11 @@ _comp_cmd_cryptsetup() [[ $was_split ]] && return - local arg - _get_first_arg - if [[ ! $arg ]]; then - if [[ $cur == -* ]]; then - _comp_compgen_help - [[ ${COMPREPLY-} == *= ]] && compopt -o nospace - else - _comp_compgen -- -W 'open close resize status benchmark repair - erase luksFormat luksAddKey luksRemoveKey luksChangeKey - luksKillSlot luksUUID isLuks luksDump tcryptDump luksSuspend - luksResume luksHeaderBackup luksHeaderRestore' - fi - else - local args - _count_args "" "-${noargopts}[chslSbopitTdM]" + local ret + if _comp_get_first_arg; then + local arg=$ret + _comp_count_args "" "-${noargopts}[chslSbopitTdM]" + local args=$ret case $arg in open | create | luksOpen | loopaesOpen | tcryptOpen) case $args in @@ -96,6 +86,16 @@ _comp_cmd_cryptsetup() esac ;; esac + else + if [[ $cur == -* ]]; then + _comp_compgen_help + [[ ${COMPREPLY-} == *= ]] && compopt -o nospace + else + _comp_compgen -- -W 'open close resize status benchmark repair + erase luksFormat luksAddKey luksRemoveKey luksChangeKey + luksKillSlot luksUUID isLuks luksDump tcryptDump luksSuspend + luksResume luksHeaderBackup luksHeaderRestore' + fi fi } && diff --git a/completions/gpgv b/completions/gpgv index e83f5f8433c..2d4aba65215 100644 --- a/completions/gpgv +++ b/completions/gpgv @@ -19,8 +19,9 @@ _comp_cmd_gpgv() ;; esac - local args - _count_args "" "--@(weak-digest|*-fd|keyring|homedir)" + local ret + _comp_count_args "" "--@(weak-digest|*-fd|keyring|homedir)" + local args=$ret if [[ $cur == -* && $args -eq 1 ]]; then _comp_compgen_help diff --git a/completions/hcitool b/completions/hcitool index 94b23e10fdd..ce5b35d7261 100644 --- a/completions/hcitool +++ b/completions/hcitool @@ -45,21 +45,12 @@ _comp_cmd_hcitool() [[ $was_split ]] && return - local arg - _get_first_arg - if [[ ! $arg ]]; then - if [[ $cur == -* ]]; then - _comp_compgen_help - else - _comp_compgen -- -W 'dev inq scan name info spinq epinq cmd con cc - dc sr cpt rssi lq tpl afh lst auth enc key clkoff clock' - fi - else - local args - case $arg in + local ret + if _comp_get_first_arg; then + case $ret in name | info | dc | rssi | lq | afh | auth | key | clkoff | lst) - _count_args - if ((args == 2)); then + _comp_count_args + if ((ret == 2)); then _comp_cmd_hcitool__bluetooth_addresses fi ;; @@ -67,37 +58,44 @@ _comp_cmd_hcitool() if [[ $cur == -* ]]; then _comp_compgen -- -W '--role --pkt-type' else - _count_args - if ((args == 2)); then + _comp_count_args + if ((ret == 2)); then _comp_cmd_hcitool__bluetooth_addresses fi fi ;; sr) - _count_args - if ((args == 2)); then + _comp_count_args + if ((ret == 2)); then _comp_cmd_hcitool__bluetooth_addresses else _comp_compgen -- -W 'master slave' fi ;; cpt) - _count_args - if ((args == 2)); then + _comp_count_args + if ((ret == 2)); then _comp_cmd_hcitool__bluetooth_addresses else _comp_cmd_hcitool__bluetooth_packet_types fi ;; tpl | enc | clock) - _count_args - if ((args == 2)); then + _comp_count_args + if ((ret == 2)); then _comp_cmd_hcitool__bluetooth_addresses else _comp_compgen -- -W '0 1' fi ;; esac + else + if [[ $cur == -* ]]; then + _comp_compgen_help + else + _comp_compgen -- -W 'dev inq scan name info spinq epinq cmd con cc + dc sr cpt rssi lq tpl afh lst auth enc key clkoff clock' + fi fi } && complete -F _comp_cmd_hcitool hcitool @@ -116,17 +114,9 @@ _comp_cmd_sdptool() [[ $was_split ]] && return - local arg - _get_first_arg - if [[ ! $arg ]]; then - if [[ $cur == -* ]]; then - _comp_compgen_help - else - _comp_compgen -- -W 'search browse records add del get setattr - setseq' - fi - else - case $arg in + local ret + if _comp_get_first_arg; then + case $ret in search) if [[ $cur == -* ]]; then _comp_compgen -- -W '--bdaddr --tree --raw --xml' @@ -154,6 +144,13 @@ _comp_cmd_sdptool() fi ;; esac + else + if [[ $cur == -* ]]; then + _comp_compgen_help + else + _comp_compgen -- -W 'search browse records add del get setattr + setseq' + fi fi } && complete -F _comp_cmd_sdptool sdptool @@ -198,17 +195,11 @@ _comp_cmd_rfcomm() ;; esac - local arg - _get_first_arg - if [[ ! $arg ]]; then - if [[ $cur == -* ]]; then - _comp_compgen_help - else - _comp_compgen -- -W 'show connect listen watch bind release' - fi - else - local args - _count_args + local ret + if _comp_get_first_arg; then + local arg=$ret + _comp_count_args + local args=$ret if ((args == 2)); then _comp_cmd_hcitool__bluetooth_devices else @@ -220,6 +211,12 @@ _comp_cmd_rfcomm() ;; esac fi + else + if [[ $cur == -* ]]; then + _comp_compgen_help + else + _comp_compgen -- -W 'show connect listen watch bind release' + fi fi } && complete -F _comp_cmd_rfcomm rfcomm @@ -237,24 +234,23 @@ _comp_cmd_ciptool() ;; esac - local arg - _get_first_arg - if [[ ! $arg ]]; then - if [[ $cur == -* ]]; then - _comp_compgen_help - else - _comp_compgen -- -W 'show search connect release loopback' - fi - else - local args - case $arg in + local ret + if _comp_get_first_arg; then + case $ret in connect | release | loopback) - _count_args - if ((args == 2)); then + local ret + _comp_count_args + if ((ret == 2)); then _comp_cmd_hcitool__bluetooth_addresses fi ;; esac + else + if [[ $cur == -* ]]; then + _comp_compgen_help + else + _comp_compgen -- -W 'show search connect release loopback' + fi fi } && complete -F _comp_cmd_ciptool ciptool @@ -274,9 +270,9 @@ _comp_cmd_dfutool() if [[ $cur == -* ]]; then _comp_compgen_help else - local args - _count_args - case $args in + local ret + _comp_count_args + case $ret in 1) _comp_compgen -- -W 'verify modify upgrade archive' ;; @@ -293,40 +289,38 @@ _comp_cmd_hciconfig() local cur prev words cword comp_args _comp_initialize -- "$@" || return - local arg - _get_first_arg - if [[ ! $arg ]]; then - if [[ $cur == -* ]]; then - _comp_compgen -- -W '--help --all' - else - _comp_compgen -- -W 'up down reset rstat auth noauth encrypt - noencrypt secmgr nosecmgr piscan noscan iscan pscan ptype name - class voice iac inqmode inqdata inqtype inqparams pageparms - pageto afhmode aclmtu scomtu putkey delkey commands features - version revision lm' - fi - else - local args - case $arg in + local ret + if _comp_get_first_arg; then + case $ret in putkey | delkey) - _count_args - if ((args == 2)); then + _comp_count_args + if ((ret == 2)); then _comp_cmd_hcitool__bluetooth_addresses fi ;; lm) - _count_args - if ((args == 2)); then + _comp_count_args + if ((ret == 2)); then _comp_compgen -- -W 'MASTER SLAVE NONE ACCEPT' fi ;; ptype) - _count_args - if ((args == 2)); then + _comp_count_args + if ((ret == 2)); then _comp_cmd_hcitool__bluetooth_packet_types fi ;; esac + else + if [[ $cur == -* ]]; then + _comp_compgen -- -W '--help --all' + else + _comp_compgen -- -W 'up down reset rstat auth noauth encrypt + noencrypt secmgr nosecmgr piscan noscan iscan pscan ptype name + class voice iac inqmode inqdata inqtype inqparams pageparms + pageto afhmode aclmtu scomtu putkey delkey commands features + version revision lm' + fi fi } && complete -F _comp_cmd_hciconfig hciconfig @@ -339,9 +333,9 @@ _comp_cmd_hciattach() if [[ $cur == -* ]]; then _comp_compgen -- -W '-n -p -t -b -s -l' else - local args - _count_args - case $args in + local ret + _comp_count_args + case $ret in 1) _comp_expand_glob COMPREPLY '/dev/tty*' ((${#COMPREPLY[@]})) && diff --git a/completions/ifup b/completions/ifup index e9ead9674c3..58c642f0916 100644 --- a/completions/ifup +++ b/completions/ifup @@ -28,10 +28,10 @@ _comp_cmd_ifupdown() return fi - local args - _count_args "" "@(--allow|-i|--interfaces|--state-dir|-X|--exclude|-o)" + local ret + _comp_count_args "" "@(--allow|-i|--interfaces|--state-dir|-X|--exclude|-o)" - if ((args == 1)); then + if ((ret == 1)); then _comp_compgen_configured_interfaces fi } && diff --git a/completions/jq b/completions/jq index f89cde17302..537b8f5a572 100644 --- a/completions/jq +++ b/completions/jq @@ -63,13 +63,13 @@ _comp_cmd_jq() [[ $word != --?(json)args ]] || return done - local args + local ret # TODO: DTRT with args taking 2 options # -f|--from-file are not counted here because they supply the filter - _count_args "" "@(--arg|--arg?(json|file)|--slurpfile|--indent|--run-tests|-${noargopts}L)" + _comp_count_args "" "@(--arg|--arg?(json|file)|--slurpfile|--indent|--run-tests|-${noargopts}L)" # 1st arg is filter - ((args == 1)) && return + ((ret == 1)) && return # 2... are input files _comp_compgen_filedir 'json?(l)' diff --git a/completions/jsonschema b/completions/jsonschema index cd3a8ff7fe5..2b7c49d0e94 100644 --- a/completions/jsonschema +++ b/completions/jsonschema @@ -20,9 +20,9 @@ _comp_cmd_jsonschema() return fi - local args - _count_args "" "-*" - ((args == 1)) || return + local ret + _comp_count_args "" "-*" + ((ret == 1)) || return _comp_compgen_filedir '@(json|schema)' } && complete -F _comp_cmd_jsonschema jsonschema diff --git a/completions/lz4 b/completions/lz4 index b64700472df..65b4a3b370b 100644 --- a/completions/lz4 +++ b/completions/lz4 @@ -19,8 +19,9 @@ _comp_cmd_lz4() return fi - local args word xspec="*.?(t)lz4" - _count_args + local ret word xspec="*.?(t)lz4" + _comp_count_args + local args=$ret ((args > 2)) && return for word in "${words[@]}"; do diff --git a/completions/mkinitrd b/completions/mkinitrd index 5b695559bb5..a81cfbb7299 100644 --- a/completions/mkinitrd +++ b/completions/mkinitrd @@ -30,10 +30,10 @@ _comp_cmd_mkinitrd() --net-dev --fstab --nocompress --dsdt --bootchart' [[ ${COMPREPLY-} == *= ]] && compopt -o nospace else - local args - _count_args + local ret + _comp_count_args - case $args in + case $ret in 1) _comp_compgen_filedir ;; diff --git a/completions/nc b/completions/nc index 104000e8e1b..bb44418b14f 100644 --- a/completions/nc +++ b/completions/nc @@ -38,9 +38,9 @@ _comp_cmd_nc() fi # Complete 1st non-option arg only - local args - _count_args "" "-*[IiMmOPpqsTVWwXx]" - ((args == 1)) || return + local ret + _comp_count_args "" "-*[IiMmOPpqsTVWwXx]" + ((ret == 1)) || return _known_hosts_real -- "$cur" } && diff --git a/completions/nslookup b/completions/nslookup index 76408a254c8..13b029f23d6 100644 --- a/completions/nslookup +++ b/completions/nslookup @@ -52,11 +52,11 @@ _comp_cmd_nslookup() return fi - local args - _count_args "=" - if ((args <= 2)); then + local ret + _comp_count_args "=" + if ((ret <= 2)); then _known_hosts_real -- "$cur" - [[ $args -eq 1 && $cur == @(|-) ]] && COMPREPLY+=(-) + [[ $ret -eq 1 && $cur == @(|-) ]] && COMPREPLY+=(-) fi } && complete -F _comp_cmd_nslookup nslookup @@ -89,11 +89,11 @@ _comp_cmd_host() return fi - local args - _count_args "" "-*[ctmNRW]" - if ((args == 1)); then + local ret + _comp_count_args "" "-*[ctmNRW]" + if ((ret == 1)); then _known_hosts_real -- "$cur" - elif ((args == 2)); then + elif ((ret == 2)); then local ipvx [[ ${words[*]} =~ \ -[^\ ]*([46]) ]] && ipvx=-${BASH_REMATCH[1]} # shellcheck disable=SC2086 diff --git a/completions/patch b/completions/patch index 9f43148bede..8497788fe37 100644 --- a/completions/patch +++ b/completions/patch @@ -55,9 +55,9 @@ _comp_cmd_patch() return fi - local args - _count_args - case $args in + local ret + _comp_count_args + case $ret in 1) _comp_compgen_filedir ;; diff --git a/completions/quota b/completions/quota index 5772d47fa9b..66ff638ff1b 100644 --- a/completions/quota +++ b/completions/quota @@ -83,10 +83,10 @@ _comp_cmd_setquota() if [[ $cur == -* ]]; then _comp_cmd_quota__parse_help "$1" else - local args - _count_args + local ret + _comp_count_args - case $args in + case $ret in 1) _comp_cmd_quota__user_or_group ;; diff --git a/completions/sh b/completions/sh index 75e8350f033..ee680d67b0b 100644 --- a/completions/sh +++ b/completions/sh @@ -25,9 +25,9 @@ _comp_cmd_sh() return fi - local args ext= - _count_args "" "@(-c|[-+]o)" - ((args == 1)) && ext="sh" + local ret ext= + _comp_count_args "" "@(-c|[-+]o)" + ((ret == 1)) && ext="sh" _comp_compgen_filedir $ext } && complete -F _comp_cmd_sh sh diff --git a/completions/ssh b/completions/ssh index c83bf26b12c..d67c35d9f50 100644 --- a/completions/ssh +++ b/completions/ssh @@ -373,10 +373,10 @@ _comp_cmd_ssh() elif [[ $cur == -* ]]; then _comp_compgen_usage else - local args + local ret # Keep glob sort in sync with cases above - _count_args "=" "-*[BbcDeLpRWEFSIiJlmOoQw]" - if ((args > 1)); then + _comp_count_args "=" "-*[BbcDeLpRWEFSIiJlmOoQw]" + if ((ret > 1)); then compopt -o filenames _comp_compgen_commands else diff --git a/completions/xdg-mime b/completions/xdg-mime index b779454468d..acdd09851eb 100644 --- a/completions/xdg-mime +++ b/completions/xdg-mime @@ -25,9 +25,9 @@ _comp_cmd_xdg_mime() local cur prev words cword comp_args _comp_initialize -- "$@" || return - local args - _count_args - + local ret + _comp_count_args + local args=$ret if ((args == 1)); then if [[ $cur == -* ]]; then _comp_compgen -- -W '--help --manual --version' diff --git a/completions/xdg-settings b/completions/xdg-settings index 2e8f41b2292..2a4289d03a4 100644 --- a/completions/xdg-settings +++ b/completions/xdg-settings @@ -16,11 +16,11 @@ _comp_cmd_xdg_settings() return fi - local args - _count_args - if ((args == 1)); then + local ret + _comp_count_args + if ((ret == 1)); then _comp_compgen -- -W "get check set" - elif ((args == 2)); then + elif ((ret == 2)); then _comp_compgen_split -- "$("$1" --list | awk '!/^Known/ { print $1 }')" fi } && diff --git a/completions/zopflipng b/completions/zopflipng index 71bd1180957..0392502e07e 100644 --- a/completions/zopflipng +++ b/completions/zopflipng @@ -27,9 +27,9 @@ _comp_cmd_zopflipng() if [[ ${words[*]} != *\ --prefix=* ]]; then # 2 png args only if --prefix not given - local args - _count_args - ((args < 3)) && _comp_compgen_filedir png + local ret + _comp_count_args + ((ret < 3)) && _comp_compgen_filedir png else # otherwise arbitrary number of png args _comp_compgen_filedir png diff --git a/test/t/unit/Makefile.am b/test/t/unit/Makefile.am index e494396719c..fa14d15647d 100644 --- a/test/t/unit/Makefile.am +++ b/test/t/unit/Makefile.am @@ -12,8 +12,9 @@ EXTRA_DIST = \ test_unit_expand_tilde_by_ref.py \ test_unit_filedir.py \ test_unit_find_unique_completion_pair.py \ - test_unit_get_words.py \ + test_unit_get_first_arg.py \ test_unit_get_cword.py \ + test_unit_get_words.py \ test_unit_initialize.py \ test_unit_ip_addresses.py \ test_unit_known_hosts_real.py \ diff --git a/test/t/unit/test_unit_count_args.py b/test/t/unit/test_unit_count_args.py index 50fdcdd277a..064041bf615 100644 --- a/test/t/unit/test_unit_count_args.py +++ b/test/t/unit/test_unit_count_args.py @@ -4,14 +4,16 @@ @pytest.mark.bashcomp( - cmd=None, ignore_env=r"^[+-](args|COMP_(WORDS|CWORD|LINE|POINT))=" + cmd=None, ignore_env=r"^[+-](ret|COMP_(WORDS|CWORD|LINE|POINT))=" ) class TestUnitCountArgs(TestUnitBase): def _test(self, *args, **kwargs): - return self._test_unit("_count_args %s; echo $args", *args, **kwargs) + return self._test_unit( + '_comp_count_args %s; echo "$ret"', *args, **kwargs + ) def test_1(self, bash): - assert_bash_exec(bash, "COMP_CWORD= _count_args >/dev/null") + assert_bash_exec(bash, "COMP_CWORD= _comp_count_args") def test_2(self, bash): """a b| should set args to 1""" @@ -64,3 +66,86 @@ def test_9(self, bash): bash, "(a -b -c d e)", 4, "a -b -c d e", 11, arg='"" "" "-b"' ) assert output == "3" + + def test_10_single_hyphen_1(self, bash): + """- should be counted as an argument representing stdout/stdin""" + output = self._test(bash, "(a -b - c -d e)", 5, "a -b - c -d e", 12) + assert output == "3" + + def test_10_single_hyphen_2(self, bash): + """- in an option argument should be skipped""" + output = self._test( + bash, "(a -b - c - e)", 5, "a -b - c - e", 11, arg='"" "-b"' + ) + assert output == "3" + + def test_11_double_hyphen_1(self, bash): + """all the words after -- should be counted""" + output = self._test( + bash, "(a -b -- -c -d e)", 5, "a -b -- -c -d e", 14 + ) + assert output == "3" + + def test_11_double_hyphen_2(self, bash): + """all the words after -- should be counted""" + output = self._test(bash, "(a b -- -c -d e)", 5, "a b -- -c -d e", 13) + assert output == "4" + + def test_12_exclude_optarg_1(self, bash): + """an option argument should be skipped even if it matches the argument pattern""" + output = self._test( + bash, "(a -o -x b c)", 4, "a -o -x b c", 10, arg='"" "-o" "-x"' + ) + assert output == "2" + + def test_12_exclude_optarg_2(self, bash): + """an option argument should be skipped even if it matches the argument pattern""" + output = self._test( + bash, "(a -o -x -x c)", 4, "a -o -x -x c", 11, arg='"" "-o" "-x"' + ) + assert output == "2" + + def test_12_exclude_optarg_3(self, bash): + """an option argument should be skipped even if it matches the argument pattern""" + output = self._test( + bash, "(a -o -x -y c)", 4, "a -o -x -y c", 11, arg='"" "-o" "-x"' + ) + assert output == "1" + + def test_13_plus_option_optarg(self, bash): + """When +o is specified to be an option taking an option argument, it should not be counted as an argument""" + output = self._test( + bash, "(a +o b c)", 3, "a +o b c", 7, arg='"" "+o"' + ) + assert output == "1" + + def test_14_no_optarg_chain_1(self, bash): + """an option argument should not take another option argument""" + output = self._test( + bash, "(a -o -o -o -o c)", 5, "a -o -o -o -o c", 14, arg='"" "-o"' + ) + assert output == "1" + + def test_14_no_optarg_chain_2(self, bash): + """an option argument should not take another option argument""" + output = self._test( + bash, + "(a -o -o b -o -o c)", + 6, + "a -o -o b -o -o c", + 16, + arg='"" "-o"', + ) + assert output == "2" + + def test_15_double_hyphen_optarg(self, bash): + """-- should lose its meaning when it is an option argument""" + output = self._test( + bash, "(a -o -- -b -c d)", 5, "a -o -- -b -c d", 14, arg='"" "-o"' + ) + assert output == "1" + + def test_16_empty_word(self, bash): + """An empty word should not take an option argument""" + output = self._test(bash, "(a '' x '' y d)", 5, "a x y d", 8) + assert output == "5" diff --git a/test/t/unit/test_unit_get_first_arg.py b/test/t/unit/test_unit_get_first_arg.py new file mode 100644 index 00000000000..78186c4a6c7 --- /dev/null +++ b/test/t/unit/test_unit_get_first_arg.py @@ -0,0 +1,71 @@ +import pytest + +from conftest import assert_bash_exec + + +@pytest.mark.bashcomp(cmd=None) +class TestUnitGetFirstArg: + @pytest.fixture(scope="class") + def functions(self, bash): + assert_bash_exec( + bash, + '_comp__test_unit() { local -a "words=$1"; local cword=$2 ret=; shift 2; _comp_get_first_arg "$@" && printf "%s\\n" "$ret"; return 0; }', + ) + + def test_1(self, bash, functions): + assert_bash_exec(bash, "_comp__test_unit '()' 0") + + def test_2(self, bash, functions): + output = assert_bash_exec( + bash, '_comp__test_unit "(a b)" 2', want_output=None + ).strip() + assert output == "b" + + def test_3(self, bash, functions): + output = assert_bash_exec( + bash, '_comp__test_unit "(a bc)" 2', want_output=None + ).strip() + assert output == "bc" + + def test_4(self, bash, functions): + output = assert_bash_exec( + bash, '_comp__test_unit "(a b c)" 2', want_output=None + ).strip() + assert output == "b" + + def test_5(self, bash, functions): + """Neither of the current word and the command name should be picked + as the first argument""" + output = assert_bash_exec( + bash, '_comp__test_unit "(a b c)" 1', want_output=None + ).strip() + assert output == "" + + def test_6(self, bash, functions): + """Options starting with - should not be picked as the first + argument""" + output = assert_bash_exec( + bash, '_comp__test_unit "(a -b -c d e)" 4', want_output=None + ).strip() + assert output == "d" + + def test_7_single_hyphen(self, bash, functions): + """- should be counted as an argument representing stdout/stdin""" + output = assert_bash_exec( + bash, '_comp__test_unit "(a -b - c -d e)" 5', want_output=None + ).strip() + assert output == "-" + + def test_8_double_hyphen_1(self, bash, functions): + """any word after -- should be picked""" + output = assert_bash_exec( + bash, '_comp__test_unit "(a -b -- -c -d e)" 5', want_output=None + ).strip() + assert output == "-c" + + def test_8_double_hyphen_2(self, bash, functions): + """any word after -- should be picked only without any preceding argument""" + output = assert_bash_exec( + bash, '_comp__test_unit "(a b -- -c -d e)" 5', want_output=None + ).strip() + assert output == "b"