Skip to content

Commit 787ad5c

Browse files
authored
Merge pull request #924 from scop/feat/load-completion-improvements
feat: load completion improvements
2 parents 26d8b34 + 01e1ae5 commit 787ad5c

File tree

16 files changed

+175
-83
lines changed

16 files changed

+175
-83
lines changed

bash_completion

Lines changed: 88 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1713,6 +1713,25 @@ _fstypes()
17131713
[[ $fss ]] && _comp_compgen -a COMPREPLY -W "$fss" -- "$cur"
17141714
}
17151715

1716+
# Get absolute path to a file, with rudimentary canonicalization.
1717+
# No symlink resolution or existence checks are done;
1718+
# see `_comp_realcommand` for those.
1719+
# @param $1 The file
1720+
# @var[out] ret The path
1721+
_comp_abspath()
1722+
{
1723+
ret=$1
1724+
case $ret in
1725+
/*) ;;
1726+
../*) ret=$PWD/${ret:3} ;;
1727+
*) ret=$PWD/$ret ;;
1728+
esac
1729+
while [[ $ret == */./* ]]; do
1730+
ret=${ret//\/.\//\/}
1731+
done
1732+
ret=${ret//+(\/)/\/}
1733+
}
1734+
17161735
# Get real command.
17171736
# Command is the filename of command in PATH with possible symlinks resolved
17181737
# (if resolve tooling available), empty string if command not found.
@@ -1731,16 +1750,7 @@ _comp_realcommand()
17311750
elif type -p readlink >/dev/null; then
17321751
ret=$(readlink -f "$file")
17331752
else
1734-
ret=$file
1735-
if [[ $ret == */* ]]; then
1736-
if [[ $ret == ./* ]]; then
1737-
ret=$PWD/${file:2}
1738-
elif [[ $ret == ../* ]]; then
1739-
ret=$PWD/${file:3}
1740-
elif [[ $ret != /* ]]; then
1741-
ret=$PWD/$file
1742-
fi
1743-
fi
1753+
_comp_abspath "$file"
17441754
fi
17451755
}
17461756

@@ -2526,9 +2536,24 @@ complete -F _minimal ''
25262536
25272537
__load_completion()
25282538
{
2529-
local cmd="${1##*/}" dir compfile
2539+
local cmd=$1 cmdname=${1##*/} dir compfile
25302540
local -a paths
2531-
[[ $cmd ]] || return 1
2541+
[[ $cmdname ]] || return 1
2542+
2543+
local backslash=
2544+
if [[ $cmd == \\* ]]; then
2545+
cmd=${cmd:1}
2546+
# If we already have a completion for the "real" command, use it
2547+
$(complete -p "$cmd" 2>/dev/null || echo false) "\\$cmd" && return 0
2548+
backslash=\\
2549+
fi
2550+
2551+
# Resolve absolute path to $cmd
2552+
local ret pathcmd origcmd=$cmd
2553+
if pathcmd=$(type -P "$cmd"); then
2554+
_comp_abspath "$pathcmd"
2555+
cmd=$ret
2556+
fi
25322557
25332558
local -a dirs=()
25342559
@@ -2553,68 +2578,75 @@ __load_completion()
25532578
dirs+=(./completions)
25542579
fi
25552580
2556-
# 3) From bin directories extracted from $(realpath "$cmd") and PATH
2581+
# 3) From bin directories extracted from the specified path to the command,
2582+
# the real path to the command, and $PATH
2583+
paths=()
2584+
[[ $cmd == /* ]] && paths+=("${cmd%/*}")
25572585
local ret
2558-
_comp_realcommand "$1" && paths=("${ret%/*}") || paths=()
2586+
_comp_realcommand "$cmd" && paths+=("${ret%/*}")
25592587
_comp_split -aF : paths "$PATH"
25602588
for dir in "${paths[@]%/}"; do
2561-
if [[ -d $dir && $dir == ?*/@(bin|sbin) ]]; then
2589+
[[ $dir == ?*/@(bin|sbin) ]] &&
25622590
dirs+=("${dir%/*}/share/bash-completion/completions")
2563-
fi
25642591
done
25652592
25662593
# 4) From XDG_DATA_DIRS or system dirs (e.g. /usr/share, /usr/local/share):
25672594
# Completions in the system data dirs.
25682595
_comp_split -F : paths "${XDG_DATA_DIRS:-/usr/local/share:/usr/share}" &&
25692596
dirs+=("${paths[@]/%//bash-completion/completions}")
25702597
2571-
local backslash=
2572-
if [[ $cmd == \\* ]]; then
2573-
cmd=${cmd:1}
2574-
# If we already have a completion for the "real" command, use it
2575-
$(complete -p "$cmd" 2>/dev/null || echo false) "\\$cmd" && return 0
2576-
backslash=\\
2577-
fi
2578-
2579-
# For loading 3rd party completions wrapped in shopt reset
2598+
# Set up default $IFS in case loaded completions depend on it,
2599+
# as well as for $compspec invocation below.
25802600
local IFS=$' \t\n'
25812601
2582-
for dir in "${dirs[@]}"; do
2583-
[[ -d $dir ]] || continue
2584-
for compfile in "$cmd" "$cmd.bash"; do
2585-
compfile="$dir/$compfile"
2586-
# Avoid trying to source dirs as long as we support bash < 4.3
2587-
# to avoid an fd leak; https://bugzilla.redhat.com/903540
2588-
if [[ -d $compfile ]]; then
2589-
# Do not warn with . or .. (especially the former is common)
2590-
[[ $compfile == */.?(.) ]] ||
2591-
echo "bash_completion: $compfile: is a directory" >&2
2592-
elif [[ -e $compfile ]] && . "$compfile"; then
2593-
[[ $backslash ]] && $(complete -p "$cmd") "\\$cmd"
2594-
return 0
2602+
# Look up and source
2603+
shift
2604+
local i prefix compspec
2605+
for prefix in "" _; do # Regular from all dirs first, then fallbacks
2606+
for i in ${!dirs[*]}; do
2607+
dir=${dirs[i]}
2608+
if [[ ! -d $dir ]]; then
2609+
unset -v 'dirs[i]'
2610+
continue
25952611
fi
2612+
for compfile in "$prefix$cmdname" "$prefix$cmdname.bash"; do
2613+
compfile="$dir/$compfile"
2614+
# Avoid trying to source dirs as long as we support bash < 4.3
2615+
# to avoid an fd leak; https://bugzilla.redhat.com/903540
2616+
if [[ -d $compfile ]]; then
2617+
# Do not warn with . or .. (especially the former is common)
2618+
[[ $compfile == */.?(.) ]] ||
2619+
echo "bash_completion: $compfile: is a directory" >&2
2620+
elif [[ -e $compfile ]] && . "$compfile" "$cmd" "$@"; then
2621+
# At least $cmd is expected to have a completion set when
2622+
# we return successfully; see if it already does
2623+
if compspec=$(complete -p "$cmd" 2>/dev/null); then
2624+
local -a extspecs=()
2625+
# $cmd is the case in which we do backslash processing
2626+
[[ $backslash ]] && extspecs+=("$backslash$cmd")
2627+
# If invoked without path, that one should be set, too
2628+
# ...but let's not overwrite an existing one, if any
2629+
[[ $origcmd != */* ]] &&
2630+
! complete -p "$origcmd" &>/dev/null &&
2631+
extspecs+=("$origcmd")
2632+
((${#extspecs[*]} != 0)) && $compspec "${extspecs[@]}"
2633+
return 0
2634+
fi
2635+
# If not, see if we got one for $cmdname
2636+
if [[ $cmdname != "$cmd" ]] && compspec=$(complete -p "$cmdname" 2>/dev/null); then
2637+
# Use that for $cmd too, if we have a full path to it
2638+
[[ $cmd == /* ]] && $compspec "$cmd"
2639+
return 0
2640+
fi
2641+
# Nothing expected was set, continue lookup
2642+
fi
2643+
done
25962644
done
25972645
done
25982646
2599-
# Search fallback completions named "_$cmd"
2600-
for dir in "${dirs[@]}"; do
2601-
[[ -d $dir ]] || continue
2602-
compfile="$dir/_$cmd"
2603-
# Avoid trying to source dirs as long as we support bash < 4.3
2604-
# to avoid an fd leak; https://bugzilla.redhat.com/903540
2605-
if [[ -d $compfile ]]; then
2606-
# Do not warn with . or .. (especially the former is common)
2607-
[[ $compfile == */.?(.) ]] ||
2608-
echo "bash_completion: $compfile: is a directory" >&2
2609-
elif [[ -e $compfile ]] && . "$compfile" "$cmd"; then
2610-
[[ $backslash ]] && $(complete -p "$cmd") "\\$cmd"
2611-
return 0
2612-
fi
2613-
done
2614-
26152647
# Look up simple "xspec" completions
2616-
[[ -v _xspecs[$cmd] ]] &&
2617-
complete -F _filedir_xspec "$cmd" "$backslash$cmd" && return 0
2648+
[[ -v _xspecs[$cmdname] ]] &&
2649+
complete -F _filedir_xspec "$cmdname" "$backslash$cmdname" && return 0
26182650
26192651
return 1
26202652
}

completions/_cargo

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,4 @@
66
local rustup="${1%cargo}rustup" # use rustup from same dir
77
eval -- "$("$rustup" completions bash cargo 2>/dev/null)"
88

9-
{
10-
complete -p "$1" || complete -p "${1##*/}"
11-
} &>/dev/null
12-
139
# ex: filetype=sh

completions/_gh

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,4 @@
55

66
eval -- "$("$1" completion --shell bash 2>/dev/null)"
77

8-
{
9-
complete -p "$1" || complete -p "${1##*/}"
10-
} &>/dev/null
11-
128
# ex: filetype=sh

completions/_golangci-lint

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,4 @@
66

77
eval -- "$("$1" completion bash 2>/dev/null)"
88

9-
{
10-
complete -p "$1" || complete -p "${1##*/}"
11-
} &>/dev/null
12-
139
# ex: filetype=sh

completions/_nox

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,4 @@ eval -- "$(
1010
register-python-argcomplete3 --shell bash "$1" 2>/dev/null
1111
)"
1212

13-
complete -p "$1" &>/dev/null
14-
1513
# ex: filetype=sh

completions/_ruff

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,4 @@
55

66
eval -- "$("$1" generate-shell-completion bash 2>/dev/null)"
77

8-
{
9-
complete -p "$1" || complete -p "${1##*/}"
10-
} &>/dev/null
11-
128
# ex: filetype=sh

completions/_rustup

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,4 @@
44

55
eval -- "$("$1" completions bash rustup 2>/dev/null)"
66

7-
{
8-
complete -p "$1" || complete -p "${1##*/}"
9-
} &>/dev/null
10-
117
# ex: filetype=sh

completions/_vault

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,6 @@
33
#
44
# This serves as a fallback in case the completion is not installed otherwise.
55

6-
type "$1" &>/dev/null && complete -C "\"$1\" 2>/dev/null" "$1" "${1##*/}"
6+
type "$1" &>/dev/null && complete -C "\"$1\" 2>/dev/null" "$1"
77

88
# ex: filetype=sh

completions/_yq

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,4 @@
55

66
eval -- "$("$1" shell-completion bash 2>/dev/null)"
77

8-
{
9-
complete -p "$1" || complete -p "${1##*/}"
10-
} &>/dev/null
11-
128
# ex: filetype=sh
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"

0 commit comments

Comments
 (0)