Skip to content

Commit f16dd80

Browse files
committed
feat(__load_completion): check that a completion was actually set
On successful return, at least the full path to the command in question should have a completion set. If the command invoked was given without a path, the pathless one should have one set, too.
1 parent 42b2c02 commit f16dd80

File tree

6 files changed

+42
-4
lines changed

6 files changed

+42
-4
lines changed

bash_completion

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2509,7 +2509,7 @@ __load_completion()
25092509
fi
25102510
25112511
# Try to resolve real path to $cmd, and make it absolute
2512-
local ret realcmd=
2512+
local ret realcmd="" origcmd=$cmd
25132513
if _comp_realcommand "$cmd"; then
25142514
realcmd=$ret
25152515
cmd=${realcmd%/*}/$cmdname # basename could be an alias
@@ -2551,7 +2551,8 @@ __load_completion()
25512551
_comp_split -F : paths "${XDG_DATA_DIRS:-/usr/local/share:/usr/share}"
25522552
dirs+=("${paths[@]/%//bash-completion/completions}")
25532553
2554-
# For loading 3rd party completions wrapped in shopt reset
2554+
# Set up default $IFS in case loaded completions depend on it,
2555+
# as well as for $compspec invocation below.
25552556
local IFS=$' \t\n'
25562557
25572558
# Look up and source
@@ -2573,8 +2574,27 @@ __load_completion()
25732574
[[ $compfile == */.?(.) ]] ||
25742575
echo "bash_completion: $compfile: is a directory" >&2
25752576
elif [[ -e $compfile ]] && . "$compfile" "$cmd" "$@"; then
2576-
[[ $backslash ]] && $(complete -p "$cmdname") "\\$cmdname"
2577-
return 0
2577+
# At least $cmd is expected to have a completion set when
2578+
# we return successfully; see if it already does
2579+
if compspec=$(complete -p "$cmd" 2>/dev/null); then
2580+
local -a extspecs=()
2581+
# $cmd is the case in which we do backslash processing
2582+
[[ $backslash ]] && extspecs+=("$backslash$cmd")
2583+
# If invoked without path, that one should be set, too
2584+
# ...but let's not overwrite an existing one, if any
2585+
[[ $origcmd != */* ]] &&
2586+
! complete -p "$origcmd" &>/dev/null &&
2587+
extspecs+=("$origcmd")
2588+
((${#extspecs[*]} != 0)) && $compspec "${extspecs[@]}"
2589+
return 0
2590+
fi
2591+
# If not, see if we got one for $cmdname
2592+
if compspec=$(complete -p "$cmdname" 2>/dev/null); then
2593+
# Use that for $cmd too, if we have a full path to it
2594+
[[ $cmd == /* ]] && $compspec "$cmd"
2595+
return 0
2596+
fi
2597+
# Nothing expected was set, continue lookup
25782598
fi
25792599
done
25802600
done
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
echo 'cmd1: sourced from prefix1'
2+
complete -C true "$1"
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
echo 'cmd2: sourced from prefix1'
2+
complete -C true "$1"
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
echo 'cmd1: sourced from userdir1'
2+
complete -C true "$1"
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
echo 'cmd2: sourced from userdir2'
2+
complete -C true "$1"

test/t/unit/test_unit_load_completion.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,12 +37,26 @@ def test_PATH_1(self, bash):
3737
bash, "__load_completion cmd2", want_output=True
3838
)
3939
assert output.strip() == "cmd2: sourced from prefix1"
40+
output = assert_bash_exec(
41+
bash, "complete -p cmd2", want_output=True
42+
)
43+
assert " cmd2" in output
44+
output = assert_bash_exec(
45+
bash, 'complete -p "$PWD/prefix1/sbin/cmd2"', want_output=True
46+
)
47+
assert "/prefix1/sbin/cmd2" in output
4048

4149
def test_cmd_path_1(self, bash):
50+
assert_bash_exec(bash, "complete -r cmd1 || :", want_output=None)
4251
output = assert_bash_exec(
4352
bash, "__load_completion prefix1/bin/cmd1", want_output=True
4453
)
4554
assert output.strip() == "cmd1: sourced from prefix1"
55+
output = assert_bash_exec(
56+
bash, 'complete -p "$PWD/prefix1/bin/cmd1"', want_output=True
57+
)
58+
assert "/prefix1/bin/cmd1" in output
59+
assert_bash_exec(bash, "! complete -p cmd1", want_output=None)
4660
output = assert_bash_exec(
4761
bash, "__load_completion prefix1/sbin/cmd2", want_output=True
4862
)

0 commit comments

Comments
 (0)