Skip to content

Commit

Permalink
perf(_comp_count_args): skip reinitializing cword and words
Browse files Browse the repository at this point in the history
In most of the contexts calling `_comp_count_args` in the current
codebase, `cword` and `words` are already initialized with the same
set of exclude chars (-n chars) by _comp_initialize, so there seems to
be no need to again reassemble `cword` and `words`.  This patch
removes the redundant initialization of `cword` and `words`.

* When `_comp_initialize` is called without `-s` or `-n chars` and
  `_comp_count_args` is called with an empty $1, they are compatible
  so `cword` and `words` outside can be directly used.

* `7z` and `nslookup` specify the same set of exclude chars as
  `_comp_initialize`, so existing `cword` and `words` are compatible.

Nevertheless, we need to reinitialize `cword` and `words` when the
specified exclude chars are different from those specified to
_comp_initialize.

* The `ssh` completion calls `_comp_count_args` with excluding `=`
  while it calls `_comp_initialize` with `-n :`.

* The `chown` completion calls `_comp_count_args` with excluding `:`
  while it calls `_comp_initialize` with `-sn :`.

* The `nc` completions calls `_comp_count_args` without exclude chars
  while it calls `_comp_initialize` with `-n :`.

* The completions for `chmod`, `cryptsetup`, `hcitool`, `mkinitrd`,
  `patch`, `quota`, and `zopflipng` does not specify exclude chars to
  `_comp_count_args` while they specify `-s` to `_comp_initialize`.

It is not clear whether these discrepancies are intended ones are not,
but this patch tries to keep the current behavior by explicitly
specifying the original exclude chars.
  • Loading branch information
akinomyoga committed Sep 2, 2023
1 parent ad4472f commit 29398ef
Show file tree
Hide file tree
Showing 12 changed files with 40 additions and 29 deletions.
6 changes: 4 additions & 2 deletions bash_completion
Expand Up @@ -2239,8 +2239,10 @@ _comp_count_args()
done
shift "$((OPTIND - 1))"

local cword words
_comp__reassemble_words "$exclude<>&" words cword
if [[ $has_exclude ]]; then
local cword words
_comp__reassemble_words "$exclude<>&" words cword
fi

local i
ret=1
Expand Down
2 changes: 1 addition & 1 deletion completions/7z
Expand Up @@ -85,7 +85,7 @@ _comp_cmd_7z()
fi

local ret
_comp_count_args -n "="
_comp_count_args
if ((ret == 2)); then
_filedir_xspec unzip "${@:2}"
# TODO: parsing 7z i output?
Expand Down
2 changes: 1 addition & 1 deletion completions/chmod
Expand Up @@ -28,7 +28,7 @@ _comp_cmd_chmod()
fi

local ret
_comp_count_args -i "$modearg"
_comp_count_args -n "" -i "$modearg"

case $ret in
1) ;; # mode
Expand Down
2 changes: 1 addition & 1 deletion completions/cryptsetup
Expand Up @@ -37,7 +37,7 @@ _comp_cmd_cryptsetup()
local ret
if _comp_get_first_arg; then
local arg=$ret
_comp_count_args -a "-${noargopts}[chslSbopitTdM]"
_comp_count_args -n "" -a "-${noargopts}[chslSbopitTdM]"
local args=$ret
case $arg in
open | create | luksOpen | loopaesOpen | tcryptOpen)
Expand Down
10 changes: 5 additions & 5 deletions completions/hcitool
Expand Up @@ -49,7 +49,7 @@ _comp_cmd_hcitool()
if _comp_get_first_arg; then
case $ret in
name | info | dc | rssi | lq | afh | auth | key | clkoff | lst)
_comp_count_args
_comp_count_args -n ""
if ((ret == 2)); then
_comp_cmd_hcitool__bluetooth_addresses
fi
Expand All @@ -58,30 +58,30 @@ _comp_cmd_hcitool()
if [[ $cur == -* ]]; then
_comp_compgen -- -W '--role --pkt-type'
else
_comp_count_args
_comp_count_args -n ""
if ((ret == 2)); then
_comp_cmd_hcitool__bluetooth_addresses
fi
fi
;;
sr)
_comp_count_args
_comp_count_args -n ""
if ((ret == 2)); then
_comp_cmd_hcitool__bluetooth_addresses
else
_comp_compgen -- -W 'master slave'
fi
;;
cpt)
_comp_count_args
_comp_count_args -n ""
if ((ret == 2)); then
_comp_cmd_hcitool__bluetooth_addresses
else
_comp_cmd_hcitool__bluetooth_packet_types
fi
;;
tpl | enc | clock)
_comp_count_args
_comp_count_args -n ""
if ((ret == 2)); then
_comp_cmd_hcitool__bluetooth_addresses
else
Expand Down
2 changes: 1 addition & 1 deletion completions/mkinitrd
Expand Up @@ -31,7 +31,7 @@ _comp_cmd_mkinitrd()
[[ ${COMPREPLY-} == *= ]] && compopt -o nospace
else
local ret
_comp_count_args
_comp_count_args -n ""

case $ret in
1)
Expand Down
2 changes: 1 addition & 1 deletion completions/nc
Expand Up @@ -39,7 +39,7 @@ _comp_cmd_nc()

# Complete 1st non-option arg only
local ret
_comp_count_args -a "-*[IiMmOPpqsTVWwXx]"
_comp_count_args -n "" -a "-*[IiMmOPpqsTVWwXx]"
((ret == 1)) || return

_known_hosts_real -- "$cur"
Expand Down
2 changes: 1 addition & 1 deletion completions/nslookup
Expand Up @@ -53,7 +53,7 @@ _comp_cmd_nslookup()
fi

local ret
_comp_count_args -n "="
_comp_count_args
if ((ret <= 2)); then
_known_hosts_real -- "$cur"
[[ $ret -eq 1 && $cur == @(|-) ]] && COMPREPLY+=(-)
Expand Down
2 changes: 1 addition & 1 deletion completions/patch
Expand Up @@ -56,7 +56,7 @@ _comp_cmd_patch()
fi

local ret
_comp_count_args
_comp_count_args -n ""
case $ret in
1)
_comp_compgen_filedir
Expand Down
2 changes: 1 addition & 1 deletion completions/quota
Expand Up @@ -84,7 +84,7 @@ _comp_cmd_setquota()
_comp_cmd_quota__parse_help "$1"
else
local ret
_comp_count_args
_comp_count_args -n ""

case $ret in
1)
Expand Down
2 changes: 1 addition & 1 deletion completions/zopflipng
Expand Up @@ -28,7 +28,7 @@ _comp_cmd_zopflipng()
if [[ ${words[*]} != *\ --prefix=* ]]; then
# 2 png args only if --prefix not given
local ret
_comp_count_args
_comp_count_args -n ""
((ret < 3)) && _comp_compgen_filedir png
else
# otherwise arbitrary number of png args
Expand Down
35 changes: 22 additions & 13 deletions test/t/unit/test_unit_count_args.py
Expand Up @@ -4,50 +4,59 @@


@pytest.mark.bashcomp(
cmd=None, ignore_env=r"^[+-](ret|COMP_(WORDS|CWORD|LINE|POINT))="
cmd=None,
ignore_env=r"^[+-](ret|cword|words|COMP_(WORDS|CWORD|LINE|POINT))=",
)
class TestUnitCountArgs(TestUnitBase):
def _test(self, *args, **kwargs):
return self._test_unit(
'_comp_count_args %s; echo "$ret"', *args, **kwargs
@pytest.fixture
def functions(self, bash):
assert_bash_exec(
bash,
'_comp__test_unit() { local -a words=(); local cword ret=""; _comp__reassemble_words "<>&" words cword; _comp_count_args "$@"; echo "$ret"; }',
)

def _test(self, *args, **kwargs):
return self._test_unit("_comp__test_unit %s", *args, **kwargs)

def test_1(self, bash):
assert_bash_exec(bash, "COMP_LINE= COMP_POINT=0 COMP_WORDS=() COMP_CWORD=; _comp_count_args")
assert_bash_exec(
bash,
'COMP_LINE= COMP_POINT=0 COMP_WORDS=() COMP_CWORD=; _comp_count_args -n ""',
)

def test_2(self, bash):
def test_2(self, bash, functions):
"""a b| should set args to 1"""
output = self._test(bash, "(a b)", 1, "a b", 3)
assert output == "1"

def test_3(self, bash):
def test_3(self, bash, functions):
"""a b|c should set args to 1"""
output = self._test(bash, "(a bc)", 1, "a bc", 3)
assert output == "1"

def test_4(self, bash):
def test_4(self, bash, functions):
"""a b c| should set args to 2"""
output = self._test(bash, "(a b c)", 2, "a b c", 4)
assert output == "2"

def test_5(self, bash):
def test_5(self, bash, functions):
"""a b| c should set args to 1"""
output = self._test(bash, "(a b c)", 1, "a b c", 3)
assert output == "1"

def test_6(self, bash):
def test_6(self, bash, functions):
"""a b -c| d should set args to 2"""
output = self._test(bash, "(a b -c d)", 2, "a b -c d", 6)
assert output == "2"

def test_7(self, bash):
def test_7(self, bash, functions):
"""a b -c d e| with -c arg excluded should set args to 2"""
output = self._test(
bash, "(a b -c d e)", 4, "a b -c d e", 10, arg='-a "@(-c|--foo)"'
)
assert output == "2"

def test_8(self, bash):
def test_8(self, bash, functions):
"""a -b -c d e| with -c arg excluded
and -b included should set args to 1"""
output = self._test(
Expand All @@ -60,7 +69,7 @@ def test_8(self, bash):
)
assert output == "2"

def test_9(self, bash):
def test_9(self, bash, functions):
"""a -b -c d e| with -b included should set args to 3"""
output = self._test(
bash, "(a -b -c d e)", 4, "a -b -c d e", 11, arg='-i "-b"'
Expand Down

0 comments on commit 29398ef

Please sign in to comment.