Skip to content

Commit

Permalink
fix(_comp_count_args): check optarg correctly
Browse files Browse the repository at this point in the history
When the current implementation checks an option argument, it tests
whether the previous word matches $2 (i.e., a pattern of options
taking an option argument).  This implementation has multiple issues:

* When the options taking an option argument does not start with `-`,
  the option is counted as an independent argument.  For example,
  `ssh` completion passes `@(-c|[-+]o)` as $2, but `+o` is counted as
  an argument with the current implementation.

* An option argument that looks like an option taking an option
  argument can prevent the next word counted as an argument.  For
  example, when `cmd -o -o arg` is processed with $2 being `-o`, the
  second `-o` should be treated as an option argument and `arg` should
  be counted as an argument.  However, with the current
  implementation, `arg` is not counted because the previous word `-o`
  matches the pattern.

* When `cmd -o -- -x` is processed with $2 being `-o`, `--` should be
  treated as an option argument so should lose its special meaning,
  and `-x` is still treated as an option.  However, with the current
  implementation, `--` does not lose its special meaning, so `-x` is
  unexpectedly treated as an argument.

* Also, with the current implementation, when $2 is not specified, an
  argument after an empty word is not counted as an argument because
  $2 matches the empty word, (though Readline usually do not store an
  empty word in COMP_WORDS).

This patch fixes those issues by changing how options taking an option
argument are processed.
  • Loading branch information
akinomyoga committed Sep 1, 2023
1 parent 76eea74 commit 874c503
Show file tree
Hide file tree
Showing 2 changed files with 41 additions and 2 deletions.
5 changes: 3 additions & 2 deletions bash_completion
Expand Up @@ -2193,8 +2193,9 @@ _comp_count_args()
ret=1
for ((i = 1; i < cword; i++)); do
# shellcheck disable=SC2053
if [[ (${words[i]} != -?* || ${3-} && ${words[i]} == ${3-}) &&
${words[i - 1]} != ${2-} ]]; then
if [[ ${2-} && ${words[i]} == ${2-} ]]; then
((i++))
elif [[ ${words[i]} != -?* || ${3-} && ${words[i]} == ${3-} ]]; then
((ret++))
elif [[ ${words[i]} == -- ]]; then
((ret += cword - i - 1))
Expand Down
38 changes: 38 additions & 0 deletions test/t/unit/test_unit_count_args.py
Expand Up @@ -111,3 +111,41 @@ def test_12_exclude_optarg_3(self, bash):
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"

0 comments on commit 874c503

Please sign in to comment.