Skip to content

refactor: API/naming of functions and a variable in bash_completion #1032

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Sep 7, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
171 changes: 93 additions & 78 deletions bash_completion
Original file line number Diff line number Diff line change
Expand Up @@ -2071,7 +2071,7 @@ _comp_compgen_allowed_groups()
}

# @since 2.12
_comp_selinux_users()
_comp_compgen_selinux_users()
{
_comp_compgen_split -- "$(semanage user -nl 2>/dev/null |
awk '{ print $1 }')"
Expand Down Expand Up @@ -2308,8 +2308,7 @@ _known_hosts()
# Helper function to locate ssh included files in configs
# This function looks for the "Include" keyword in ssh config files and
# includes them recursively, adding each result to the config variable.
# TODO:API: rename per conventions
_included_ssh_config_files()
_comp__included_ssh_config_files()
{
(($# < 1)) &&
echo "bash_completion: $FUNCNAME: missing mandatory argument CONFIG" >&2
Expand Down Expand Up @@ -2358,7 +2357,7 @@ _included_ssh_config_files()
fi
done
done
} # _included_ssh_config_files()
} # _comp__included_ssh_config_files()

# Helper function for completing _known_hosts.
# This function performs host completion based on ssh's config and known_hosts
Expand Down Expand Up @@ -2435,7 +2434,7 @@ _known_hosts_real()
# "Include" keyword in ssh config files
if ((${#config[@]} > 0)); then
for i in "${config[@]}"; do
_included_ssh_config_files "$i"
_comp__included_ssh_config_files "$i"
done
fi

Expand Down Expand Up @@ -2659,7 +2658,7 @@ _comp_command_offset()
# If still nothing, just load it for the basename
if [[ ! $cspec ]]; then
compcmd=${cmd##*/}
_completion_loader "$compcmd"
_comp_load -D -- "$compcmd"
cspec=$(complete -p "$compcmd" 2>/dev/null)
fi

Expand Down Expand Up @@ -2838,7 +2837,8 @@ complete -F _comp_longopt \
sed seq shar sort split strip sum tac tail tee \
texindex touch tr uname unexpand uniq units vdir wc who

declare -Ag _xspecs
# @since 2.12
declare -Ag _comp_xspecs

# TODO:API: rename per conventions
_filedir_xspec()
Expand All @@ -2852,7 +2852,7 @@ _filedir_xspec()
_comp_quote_compgen "$cur"
local quoted=$ret

local xspec=${_xspecs[${1##*/}]}
local xspec=${_comp_xspecs[${1##*/}]-${_xspecs[${1##*/}]-}}
local -a toks
_comp_compgen -v toks -c "$quoted" -- -d

Expand Down Expand Up @@ -2880,75 +2880,74 @@ _filedir_xspec()
fi
}

# TODO:API: rename per conventions
_install_xspec()
_comp__init_install_xspec()
{
local xspec=$1 cmd
shift
for cmd in "$@"; do
_xspecs[$cmd]=$xspec
_comp_xspecs[$cmd]=$xspec
done
}
# bzcmp, bzdiff, bz*grep, bzless, bzmore intentionally not here, see Debian: #455510
_install_xspec '!*.?(t)bz?(2)' bunzip2 bzcat pbunzip2 pbzcat lbunzip2 lbzcat
_install_xspec '!*.@(zip|[aegjkswx]ar|exe|pk3|wsz|zargo|xpi|s[tx][cdiw]|sx[gm]|o[dt][tspgfc]|od[bm]|oxt|?(o)xps|epub|cbz|apk|aab|ipa|do[ct][xm]|p[op]t[mx]|xl[st][xm]|pyz|whl|[Ff][Cc][Ss]td)' unzip zipinfo
_install_xspec '*.Z' compress znew
_comp__init_install_xspec '!*.?(t)bz?(2)' bunzip2 bzcat pbunzip2 pbzcat lbunzip2 lbzcat
_comp__init_install_xspec '!*.@(zip|[aegjkswx]ar|exe|pk3|wsz|zargo|xpi|s[tx][cdiw]|sx[gm]|o[dt][tspgfc]|od[bm]|oxt|?(o)xps|epub|cbz|apk|aab|ipa|do[ct][xm]|p[op]t[mx]|xl[st][xm]|pyz|whl|[Ff][Cc][Ss]td)' unzip zipinfo
_comp__init_install_xspec '*.Z' compress znew
# zcmp, zdiff, z*grep, zless, zmore intentionally not here, see Debian: #455510
_install_xspec '!*.@(Z|[gGd]z|t[ag]z)' gunzip zcat
_install_xspec '!*.@(Z|[gGdz]z|t[ag]z)' unpigz
_install_xspec '!*.Z' uncompress
_comp__init_install_xspec '!*.@(Z|[gGd]z|t[ag]z)' gunzip zcat
_comp__init_install_xspec '!*.@(Z|[gGdz]z|t[ag]z)' unpigz
_comp__init_install_xspec '!*.Z' uncompress
# lzcmp, lzdiff intentionally not here, see Debian: #455510
_install_xspec '!*.@(tlz|lzma)' lzcat lzegrep lzfgrep lzgrep lzless lzmore unlzma
_install_xspec '!*.@(?(t)xz|tlz|lzma)' unxz xzcat
_install_xspec '!*.lrz' lrunzip
_install_xspec '!*.@(gif|jp?(e)g|miff|tif?(f)|pn[gm]|p[bgp]m|bmp|xpm|ico|xwd|tga|pcx)' ee
_install_xspec '!*.@(gif|jp?(e)g|tif?(f)|png|p[bgp]m|bmp|x[bp]m|rle|rgb|pcx|fits|pm|svg)' qiv
_install_xspec '!*.@(gif|jp?(e)g?(2)|j2[ck]|jp[2f]|tif?(f)|png|p[bgp]m|bmp|x[bp]m|rle|rgb|pcx|fits|pm|?(e)ps)' xv
_install_xspec '!*.@(@(?(e)ps|?(E)PS|pdf|PDF)?(.gz|.GZ|.bz2|.BZ2|.Z))' gv ggv kghostview
_install_xspec '!*.@(dvi|DVI)?(.@(gz|Z|bz2))' xdvi kdvi
_install_xspec '!*.dvi' dvips dviselect dvitype dvipdf advi dvipdfm dvipdfmx
_install_xspec '!*.[pf]df' acroread gpdf xpdf
_install_xspec '!*.@(?(e)ps|pdf)' kpdf
_install_xspec '!*.@(okular|@(?(e|x)ps|?(E|X)PS|[pf]df|[PF]DF|dvi|DVI|cb[rz]|CB[RZ]|djv?(u)|DJV?(U)|dvi|DVI|gif|jp?(e)g|miff|tif?(f)|pn[gm]|p[bgp]m|bmp|xpm|ico|xwd|tga|pcx|GIF|JP?(E)G|MIFF|TIF?(F)|PN[GM]|P[BGP]M|BMP|XPM|ICO|XWD|TGA|PCX|epub|EPUB|odt|ODT|fb?(2)|FB?(2)|mobi|MOBI|g3|G3|chm|CHM|md|markdown)?(.?(gz|GZ|bz2|BZ2|xz|XZ)))' okular
_install_xspec '!*.pdf' epdfview pdfunite
_install_xspec '!*.@(cb[rz7t]|djv?(u)|?(e)ps|pdf)' zathura
_install_xspec '!*.@(?(e)ps|pdf)' ps2pdf ps2pdf12 ps2pdf13 ps2pdf14 ps2pdfwr
_install_xspec '!*.texi*' makeinfo texi2html
_install_xspec '!*.@(?(la)tex|texi|dtx|ins|ltx|dbj)' tex latex slitex jadetex pdfjadetex pdftex pdflatex texi2dvi xetex xelatex luatex lualatex
_install_xspec '!*.mp3' mpg123 mpg321 madplay
_install_xspec '!*@(.@(mp?(e)g|MP?(E)G|wm[av]|WM[AV]|avi|AVI|asf|vob|VOB|bin|dat|divx|DIVX|vcd|ps|pes|fli|flv|FLV|fxm|FXM|viv|rm|ram|yuv|mov|MOV|qt|QT|web[am]|WEB[AM]|mp[234]|MP[234]|m?(p)4[av]|M?(P)4[AV]|mkv|MKV|og[agmv]|OG[AGMV]|t[ps]|T[PS]|m2t?(s)|M2T?(S)|mts|MTS|wav|WAV|flac|FLAC|asx|ASX|mng|MNG|srt|m[eo]d|M[EO]D|s[3t]m|S[3T]M|it|IT|xm|XM)|+([0-9]).@(vdr|VDR))?(.@(crdownload|part))' xine aaxine cacaxine fbxine
_install_xspec '!*@(.@(mp?(e)g|MP?(E)G|wm[av]|WM[AV]|avi|AVI|asf|vob|VOB|bin|dat|divx|DIVX|vcd|ps|pes|fli|flv|FLV|fxm|FXM|viv|rm|ram|yuv|mov|MOV|qt|QT|web[am]|WEB[AM]|mp[234]|MP[234]|m?(p)4[av]|M?(P)4[AV]|mkv|MKV|og[agmv]|OG[AGMV]|opus|OPUS|t[ps]|T[PS]|m2t?(s)|M2T?(S)|mts|MTS|wav|WAV|flac|FLAC|asx|ASX|mng|MNG|srt|m[eo]d|M[EO]D|s[3t]m|S[3T]M|it|IT|xm|XM|iso|ISO)|+([0-9]).@(vdr|VDR))?(.@(crdownload|part))' kaffeine dragon totem
_install_xspec '!*.@(avi|asf|wmv)' aviplay
_install_xspec '!*.@(rm?(j)|ra?(m)|smi?(l))' realplay
_install_xspec '!*.@(mpg|mpeg|avi|mov|qt)' xanim
_install_xspec '!*.@(og[ag]|m3u|flac|spx)' ogg123
_install_xspec '!*.@(mp3|ogg|pls|m3u)' gqmpeg freeamp
_install_xspec '!*.fig' xfig
_install_xspec '!*.@(mid?(i)|cmf)' playmidi
_install_xspec '!*.@(mid?(i)|rmi|rcp|[gr]36|g18|mod|xm|it|x3m|s[3t]m|kar)' timidity
_install_xspec '!*.@(669|abc|am[fs]|d[bs]m|dmf|far|it|mdl|m[eo]d|mid?(i)|mt[2m]|oct|okt?(a)|p[st]m|s[3t]m|ult|umx|wav|xm)' modplugplay modplug123
_install_xspec '*.@([ao]|so|so.!(conf|*/*)|[rs]pm|gif|jp?(e)g|mp3|mp?(e)g|avi|asf|ogg|class)' vi vim gvim rvim view rview rgvim rgview gview emacs xemacs sxemacs kate kwrite
_install_xspec '!*.@(zip|z|gz|tgz)' bzme
_comp__init_install_xspec '!*.@(tlz|lzma)' lzcat lzegrep lzfgrep lzgrep lzless lzmore unlzma
_comp__init_install_xspec '!*.@(?(t)xz|tlz|lzma)' unxz xzcat
_comp__init_install_xspec '!*.lrz' lrunzip
_comp__init_install_xspec '!*.@(gif|jp?(e)g|miff|tif?(f)|pn[gm]|p[bgp]m|bmp|xpm|ico|xwd|tga|pcx)' ee
_comp__init_install_xspec '!*.@(gif|jp?(e)g|tif?(f)|png|p[bgp]m|bmp|x[bp]m|rle|rgb|pcx|fits|pm|svg)' qiv
_comp__init_install_xspec '!*.@(gif|jp?(e)g?(2)|j2[ck]|jp[2f]|tif?(f)|png|p[bgp]m|bmp|x[bp]m|rle|rgb|pcx|fits|pm|?(e)ps)' xv
_comp__init_install_xspec '!*.@(@(?(e)ps|?(E)PS|pdf|PDF)?(.gz|.GZ|.bz2|.BZ2|.Z))' gv ggv kghostview
_comp__init_install_xspec '!*.@(dvi|DVI)?(.@(gz|Z|bz2))' xdvi kdvi
_comp__init_install_xspec '!*.dvi' dvips dviselect dvitype dvipdf advi dvipdfm dvipdfmx
_comp__init_install_xspec '!*.[pf]df' acroread gpdf xpdf
_comp__init_install_xspec '!*.@(?(e)ps|pdf)' kpdf
_comp__init_install_xspec '!*.@(okular|@(?(e|x)ps|?(E|X)PS|[pf]df|[PF]DF|dvi|DVI|cb[rz]|CB[RZ]|djv?(u)|DJV?(U)|dvi|DVI|gif|jp?(e)g|miff|tif?(f)|pn[gm]|p[bgp]m|bmp|xpm|ico|xwd|tga|pcx|GIF|JP?(E)G|MIFF|TIF?(F)|PN[GM]|P[BGP]M|BMP|XPM|ICO|XWD|TGA|PCX|epub|EPUB|odt|ODT|fb?(2)|FB?(2)|mobi|MOBI|g3|G3|chm|CHM|md|markdown)?(.?(gz|GZ|bz2|BZ2|xz|XZ)))' okular
_comp__init_install_xspec '!*.pdf' epdfview pdfunite
_comp__init_install_xspec '!*.@(cb[rz7t]|djv?(u)|?(e)ps|pdf)' zathura
_comp__init_install_xspec '!*.@(?(e)ps|pdf)' ps2pdf ps2pdf12 ps2pdf13 ps2pdf14 ps2pdfwr
_comp__init_install_xspec '!*.texi*' makeinfo texi2html
_comp__init_install_xspec '!*.@(?(la)tex|texi|dtx|ins|ltx|dbj)' tex latex slitex jadetex pdfjadetex pdftex pdflatex texi2dvi xetex xelatex luatex lualatex
_comp__init_install_xspec '!*.mp3' mpg123 mpg321 madplay
_comp__init_install_xspec '!*@(.@(mp?(e)g|MP?(E)G|wm[av]|WM[AV]|avi|AVI|asf|vob|VOB|bin|dat|divx|DIVX|vcd|ps|pes|fli|flv|FLV|fxm|FXM|viv|rm|ram|yuv|mov|MOV|qt|QT|web[am]|WEB[AM]|mp[234]|MP[234]|m?(p)4[av]|M?(P)4[AV]|mkv|MKV|og[agmv]|OG[AGMV]|t[ps]|T[PS]|m2t?(s)|M2T?(S)|mts|MTS|wav|WAV|flac|FLAC|asx|ASX|mng|MNG|srt|m[eo]d|M[EO]D|s[3t]m|S[3T]M|it|IT|xm|XM)|+([0-9]).@(vdr|VDR))?(.@(crdownload|part))' xine aaxine cacaxine fbxine
_comp__init_install_xspec '!*@(.@(mp?(e)g|MP?(E)G|wm[av]|WM[AV]|avi|AVI|asf|vob|VOB|bin|dat|divx|DIVX|vcd|ps|pes|fli|flv|FLV|fxm|FXM|viv|rm|ram|yuv|mov|MOV|qt|QT|web[am]|WEB[AM]|mp[234]|MP[234]|m?(p)4[av]|M?(P)4[AV]|mkv|MKV|og[agmv]|OG[AGMV]|opus|OPUS|t[ps]|T[PS]|m2t?(s)|M2T?(S)|mts|MTS|wav|WAV|flac|FLAC|asx|ASX|mng|MNG|srt|m[eo]d|M[EO]D|s[3t]m|S[3T]M|it|IT|xm|XM|iso|ISO)|+([0-9]).@(vdr|VDR))?(.@(crdownload|part))' kaffeine dragon totem
_comp__init_install_xspec '!*.@(avi|asf|wmv)' aviplay
_comp__init_install_xspec '!*.@(rm?(j)|ra?(m)|smi?(l))' realplay
_comp__init_install_xspec '!*.@(mpg|mpeg|avi|mov|qt)' xanim
_comp__init_install_xspec '!*.@(og[ag]|m3u|flac|spx)' ogg123
_comp__init_install_xspec '!*.@(mp3|ogg|pls|m3u)' gqmpeg freeamp
_comp__init_install_xspec '!*.fig' xfig
_comp__init_install_xspec '!*.@(mid?(i)|cmf)' playmidi
_comp__init_install_xspec '!*.@(mid?(i)|rmi|rcp|[gr]36|g18|mod|xm|it|x3m|s[3t]m|kar)' timidity
_comp__init_install_xspec '!*.@(669|abc|am[fs]|d[bs]m|dmf|far|it|mdl|m[eo]d|mid?(i)|mt[2m]|oct|okt?(a)|p[st]m|s[3t]m|ult|umx|wav|xm)' modplugplay modplug123
_comp__init_install_xspec '*.@([ao]|so|so.!(conf|*/*)|[rs]pm|gif|jp?(e)g|mp3|mp?(e)g|avi|asf|ogg|class)' vi vim gvim rvim view rview rgvim rgview gview emacs xemacs sxemacs kate kwrite
_comp__init_install_xspec '!*.@(zip|z|gz|tgz)' bzme
# konqueror not here on purpose, it's more than a web/html browser
_install_xspec '!*.@(?([xX]|[sS])[hH][tT][mM]?([lL]))' netscape mozilla lynx galeon dillo elinks amaya epiphany
_install_xspec '!*.@(sxw|stw|sxg|sgl|doc?([mx])|dot?([mx])|rtf|txt|htm|html|?(f)odt|ott|odm|pdf)' oowriter lowriter
_install_xspec '!*.@(sxi|sti|pps?(x)|ppt?([mx])|pot?([mx])|?(f)odp|otp)' ooimpress loimpress
_install_xspec '!*.@(sxc|stc|xls?([bmx])|xlw|xlt?([mx])|[ct]sv|?(f)ods|ots)' oocalc localc
_install_xspec '!*.@(sxd|std|sda|sdd|?(f)odg|otg)' oodraw lodraw
_install_xspec '!*.@(sxm|smf|mml|odf)' oomath lomath
_install_xspec '!*.odb' oobase lobase
_install_xspec '!*.[rs]pm' rpm2cpio
_install_xspec '!*.aux' bibtex
_install_xspec '!*.po' poedit gtranslator kbabel lokalize
_install_xspec '!*.@([Pp][Rr][Gg]|[Cc][Ll][Pp])' harbour gharbour hbpp
_install_xspec '!*.[Hh][Rr][Bb]' hbrun
_install_xspec '!*.ly' lilypond ly2dvi
_install_xspec '!*.@(dif?(f)|?(d)patch)?(.@([gx]z|bz2|lzma))' cdiff
_install_xspec '!@(*.@(ks|jks|jceks|p12|pfx|bks|ubr|gkr|cer|crt|cert|p7b|pkipath|pem|p10|csr|crl)|cacerts)' portecle
_install_xspec '!*.@(mp[234c]|og[ag]|@(fl|a)ac|m4[abp]|spx|tta|w?(a)v|wma|aif?(f)|asf|ape)' kid3 kid3-qt
unset -f _install_xspec

# Minimal completion to use as fallback in _completion_loader.
_comp__init_install_xspec '!*.@(?([xX]|[sS])[hH][tT][mM]?([lL]))' netscape mozilla lynx galeon dillo elinks amaya epiphany
_comp__init_install_xspec '!*.@(sxw|stw|sxg|sgl|doc?([mx])|dot?([mx])|rtf|txt|htm|html|?(f)odt|ott|odm|pdf)' oowriter lowriter
_comp__init_install_xspec '!*.@(sxi|sti|pps?(x)|ppt?([mx])|pot?([mx])|?(f)odp|otp)' ooimpress loimpress
_comp__init_install_xspec '!*.@(sxc|stc|xls?([bmx])|xlw|xlt?([mx])|[ct]sv|?(f)ods|ots)' oocalc localc
_comp__init_install_xspec '!*.@(sxd|std|sda|sdd|?(f)odg|otg)' oodraw lodraw
_comp__init_install_xspec '!*.@(sxm|smf|mml|odf)' oomath lomath
_comp__init_install_xspec '!*.odb' oobase lobase
_comp__init_install_xspec '!*.[rs]pm' rpm2cpio
_comp__init_install_xspec '!*.aux' bibtex
_comp__init_install_xspec '!*.po' poedit gtranslator kbabel lokalize
_comp__init_install_xspec '!*.@([Pp][Rr][Gg]|[Cc][Ll][Pp])' harbour gharbour hbpp
_comp__init_install_xspec '!*.[Hh][Rr][Bb]' hbrun
_comp__init_install_xspec '!*.ly' lilypond ly2dvi
_comp__init_install_xspec '!*.@(dif?(f)|?(d)patch)?(.@([gx]z|bz2|lzma))' cdiff
_comp__init_install_xspec '!@(*.@(ks|jks|jceks|p12|pfx|bks|ubr|gkr|cer|crt|cert|p7b|pkipath|pem|p10|csr|crl)|cacerts)' portecle
_comp__init_install_xspec '!*.@(mp[234c]|og[ag]|@(fl|a)ac|m4[abp]|spx|tta|w?(a)v|wma|aif?(f)|asf|ape)' kid3 kid3-qt
unset -f _comp__init_install_xspec

# Minimal completion to use as fallback in _comp_complete_load.
# TODO:API: rename per conventions
_minimal()
{
Expand All @@ -2960,9 +2959,22 @@ _minimal()
# https://lists.gnu.org/archive/html/bug-bash/2012-01/msg00045.html
complete -F _minimal ''

# TODO:API: rename per conventions
__load_completion()
# @since 2.12
_comp_load()
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm wondering if this is something we want/need to expose as a public function? External completions shouldn't need to use it I think.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you.

Hmm, ble.sh actually wants to use it. _completion_loader (which is also used by git-completion.bash from the Git project) sets _minimal up when none is found, but ble.sh wants to fall back into its own completion if none is found. Another option is to make _completion_loader accept an option that suppresses the fallback to _minimal.


I now noticed that I overlooked _completion_loader in renaming functions. I think _completion_loader should be renamed to _comp_load instead of __load_completion. I'll fix it.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another option is to make _completion_loader accept an option that suppresses the fallback to _minimal.

I suggested this, but _completion_loader is used as a completion function for complete -F, so I realized it causes a problem when a user inputs a command name starting with - in the command line. So we should prepare two separate functions for complete -F and for a feature to load the completion from a script.

Also, as far as I search on GitHub, __load_completion is directly used by some users (regardless of whether it was originally intended by us).


The function _completion_loader currently seems to be intended for two different roles. One is as a function that is specified to complete -F. For example, returning status 124 on successful loading is a part of this use case. The other is as a function that can be called by a user to load a completion for a specific command. Here, I suggest splitting _completion_loader into two: 1) _comp_xxx "$cmd" that can be used to load a completion for the specified command, and 2) _comp_yyy that can be specified to complete -F.

Then, I would also like to have an option that does not define the fallback completion _minimal when the specific completion is not found. I use it for ble.sh. There are also other scripts that use __load_completion (though I'm not sure if they recognize _completion_loader). There are two ways:

  • Opt-out _minimal -- _comp_xxx -- "$cmd" defines the fallback setting _minimal by default, and _comp_xxx -R "$cmd" does not.
  • Opt-in _minimal -- _comp_xxx -- "$cmd" does not define _minimal, and _comp_xxx -D "$cmd" defines _minimal.

The opt-in behavior seems natural to me as defining _minimal is an additional behavior on top of just loading a specific completion. Actually, the opt-in behavior is mostly the same as the current implementation of __load_completion, so my current suggestion is to merge the feature _comp_xxx (feature 1 of _completion_loader) into __load_completion as an option -D (defining the default setting _minimal) and to make it a public interface under the name _comp_load.

The feature _comp_yyy is named as a function _comp_complete_load in accordance with the naming convention suggested in #1037. I can adjust the naming.

I've pushed those adjustments in commit 77497bc.

{
local flag_fallback_default="" IFS=$' \t\n'
local OPTIND=1 OPTARG="" OPTERR=0 opt
while getopts ':D' opt "$@"; do
case $opt in
D) flag_fallback_default=set ;;
*)
echo "bash_completion: $FUNCNAME: usage error" >&2
return 2
;;
esac
done
shift "$((OPTIND - 1))"

local cmd=$1 cmdname=${1##*/} dir compfile
local -a paths
[[ $cmdname ]] || return 1
Expand Down Expand Up @@ -3069,25 +3081,28 @@ __load_completion()
done

# Look up simple "xspec" completions
[[ -v _xspecs[$cmdname] ]] &&
[[ -v _comp_xspecs[$cmdname] || -v _xspecs[$cmdname] ]] &&
complete -F _filedir_xspec "$cmdname" "$backslash$cmdname" && return 0

if [[ $flag_fallback_default ]]; then
complete -F _minimal -- "$origcmd" && return 0
fi

return 1
}

# set up dynamic completion loading
# TODO:API: rename per conventions
_completion_loader()
# @since 2.12
_comp_complete_load()
{
# $1=_EmptycmD_ already for empty cmds in bash 4.3, set to it for earlier
local cmd=${1:-_EmptycmD_}

__load_completion "$cmd" && return 124

# Need to define *something*, otherwise there will be no completion at all.
complete -F _minimal -- "$cmd" && return 124
# Pass -D to define *something*, or otherwise there will be no completion
# at all.
_comp_load -D -- "$cmd" && return 124
} &&
complete -D -F _completion_loader
complete -D -F _comp_complete_load

# Function for loading and calling functions from dynamically loaded
# completion files that may not have been sourced yet.
Expand All @@ -3102,7 +3117,7 @@ _comp_xfunc()
local xfunc_name=$2
[[ $xfunc_name == _* ]] ||
xfunc_name=_comp_xfunc_${1//[^a-zA-Z0-9_]/_}_$xfunc_name
declare -F "$xfunc_name" &>/dev/null || __load_completion "$1"
declare -F "$xfunc_name" &>/dev/null || _comp_load "$1"
"$xfunc_name" "${@:3}"
}

Expand Down
14 changes: 14 additions & 0 deletions bash_completion.d/000_bash_completion_compat.bash
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ _comp_deprecate_func 2.12 _modules _comp_compgen_kernel_modules
_comp_deprecate_func 2.12 _installed_modules _comp_compgen_inserted_kernel_modules
_comp_deprecate_func 2.12 _usergroup _comp_compgen_usergroup
_comp_deprecate_func 2.12 _complete_as_root _comp_as_root
_comp_deprecate_func 2.12 __load_completion _comp_load

# @deprecated 2.12 Use `_comp_xspecs`
declare -Ag _xspecs

# Backwards compatibility for compat completions that use have().
# @deprecated 1.90 should no longer be used; generally not needed with
Expand Down Expand Up @@ -443,4 +447,14 @@ _count_args()
done
}

# @deprecated 2.12 Use `_comp_load -D -- CommandName` to load the completion,
# or use `_comp_complete_load` as a completion function specified to `complete
# -F`.
_completion_loader()
{
# We call `_comp_complete_load` instead of `_comp_load -D` in case that
# `_completion_loader` is used without an argument or `_completion_loader`
# is specified to `complete -F` by a user.
_comp_complete_load "$@"
}
# ex: filetype=sh
2 changes: 1 addition & 1 deletion completions/_cargo
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
#
# This serves as a fallback in case the completion is not installed otherwise.

# shellcheck disable=SC2168 # "local" is ok, assume sourced by __load_completion
# shellcheck disable=SC2168 # "local" is ok, assume sourced by _comp_load
local rustup="${1%cargo}rustup" # use rustup from same dir
eval -- "$("$rustup" completions bash cargo 2>/dev/null)"

Expand Down
2 changes: 1 addition & 1 deletion completions/mussh
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ _comp_cmd_mussh()
;;
-c)
compopt -o filenames
_comp_compgen -a commands
_comp_compgen_commands
return
;;
esac
Expand Down
2 changes: 1 addition & 1 deletion completions/useradd
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ _comp_cmd_useradd()
return
;;
--selinux-user | -${noargopts}Z)
_comp_selinux_users
_comp_compgen_selinux_users
return
;;
--shell | -${noargopts}s)
Expand Down
2 changes: 1 addition & 1 deletion completions/usermod
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ _comp_cmd_usermod()
return
;;
--selinux-user | -${noargopts}Z)
_comp_selinux_users
_comp_compgen_selinux_users
return
;;
--shell | -${noargopts}s)
Expand Down
4 changes: 2 additions & 2 deletions test/t/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -379,9 +379,9 @@ def is_bash_type(bash: pexpect.spawn, cmd: Optional[str]) -> bool:

def load_completion_for(bash: pexpect.spawn, cmd: str) -> bool:
try:
# Allow __load_completion to fail so we can test completions
# Allow _comp_load to fail so we can test completions
# that are directly loaded in bash_completion without a separate file.
assert_bash_exec(bash, "__load_completion %s || :" % cmd)
assert_bash_exec(bash, "_comp_load %s || :" % cmd)
assert_bash_exec(bash, "complete -p %s &>/dev/null" % cmd)
except AssertionError:
return False
Expand Down
6 changes: 3 additions & 3 deletions test/t/test_dict.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,11 @@ def test_1(self, completion):
)
@pytest.mark.complete("dict --database ", require_cmd=True)
def test_database(self, completion):
# Ensure the directory name "__load_completion/" not generated because
# Ensure the directory name "_comp_load/" not generated because
# filenames in the current dictory (i.e., test/fixtures) are generated
# by "-o default" when "_comp_cmd_dict" fails to generate any
# completions.
assert completion and "__load_completion/" not in completion
assert completion and "_comp_load/" not in completion

@pytest.mark.xfail(
os.environ.get("NETWORK") == "none",
Expand All @@ -28,4 +28,4 @@ def test_database(self, completion):
"dict -h dict.org --database ", require_cmd=True, env=dict(IFS="")
)
def test_database_IFS(self, completion):
assert completion and "__load_completion/" not in completion
assert completion and "_comp_load/" not in completion
Loading