@@ -381,14 +381,8 @@ _comp_split()
381381 (( _new_size > _old_size))
382382}
383383
384- # Call `compgen` with the specified arguments and store the results in the
385- # specified array.
386- # Usage: _comp_compgen [-alR|-F sep|-v arr|-c cur] -- args...
387- # This function essentially performs arr=($(compgen args...)) but properly
388- # handles shell options, IFS, etc. using _comp_split. This function is
389- # equivalent to `_comp_split [-a] -l arr "$(IFS=sep; compgen args... -- cur)"`,
390- # but this pattern is frequent in the codebase and is good to separate out as a
391- # function for the possible future implementation change.
384+ # Provide a common interface to generate completion candidates in COMPREPLY or
385+ # in a specified array.
392386# OPTIONS
393387# -a Append to the array
394388# -v arr Store the results to the array ARR. The default is `COMPREPLY`.
@@ -404,25 +398,72 @@ _comp_split()
404398# -c cur Set a word used as a prefix to filter the completions. The default
405399# is ${cur-}.
406400# -R The same as -c ''. Use raw outputs without filtering.
407- # @param $1... args Arguments that are passed to compgen
401+ # @var[in,opt] cur Used as the default value of a prefix to filter the
402+ # completions.
403+ #
404+ # Usage #1: _comp_compgen [-alR|-F sep|-v arr|-c cur] -- options...
405+ # Call `compgen` with the specified arguments and store the results in the
406+ # specified array. This function essentially performs arr=($(compgen args...))
407+ # but properly handles shell options, IFS, etc. using _comp_split. This
408+ # function is equivalent to `_comp_split [-a] -l arr "$(IFS=sep; compgen
409+ # args... -- cur)"`, but this pattern is frequent in the codebase and is good
410+ # to separate out as a function for the possible future implementation change.
411+ # @param $1... options Arguments that are passed to compgen (if $1 starts with
412+ # a hyphen `-`).
408413#
409414# Note: References to positional parameters $1, $2, ... (such as -W '$1')
410415# will not work as expected because these reference the arguments of
411416# `_comp_compgen' instead of those of the caller function. When there are
412417# needs to reference them, save the arguments to an array and reference the
413- # array instead. The array option `-V arr` in bash >= 5.3 should be instead
414- # specified as `-v arr` as a part of the `_comp_compgen` options.
415- # @var[in] cur Used as the default value of a prefix to filter the
416- # completions.
418+ # array instead.
419+ #
420+ # Note: The array option `-V arr` in bash >= 5.3 should be instead specified
421+ # as `-v arr` as a part of the `_comp_compgen` options.
422+ #
423+ # Usage #2: _comp_compgen [-alR|-v arr|-c cur] name args...
424+ # Call `_comp_compgen_NAME ARGS...` with the specified options. This provides
425+ # a common interface to call the functions `_comp_compgen_NAME`, which produce
426+ # completion candidates, with custom options [-alR|-v arr|-cur]. The option
427+ # `-F sep` is not used with this usage.
428+ # @param $1... name args Calls the function _comp_compgen_NAME with the
429+ # specified ARGS (if $1 does not start with a hyphen `-`). The options
430+ # [-alR|-v arr|-c cur] are inherited by the child calls of `_comp_compgen`
431+ # inside `_comp_compgen_NAME` unless the child call `_comp_compgen` receives
432+ # overriding options.
433+ # @var[in,opt,internal] _comp_compgen__append
434+ # @var[in,opt,internal] _comp_compgen__var
435+ # @var[in,opt,internal] _comp_compgen__cur
436+ # These variables are internally used to pass the effect of the options
437+ # [-alR|-v arr|-c cur] to the child calls of `_comp_compgen` in
438+ # `_comp_compgen_NAME`.
439+ #
440+ # @remarks Design `_comp_compgen_NAME`: a function that produce completions can
441+ # be defined with the name _comp_compgen_NAME. The function is supposed to
442+ # generate completions by calling `_comp_compgen`. To reflect the options
443+ # specified to the outer calls of `_comp_compgen`, the function should not
444+ # directly modify `COMPREPLY`. To add words, one can call
445+ #
446+ # _comp_compgen -- -W '"${words[@]}"'
447+ #
448+ # To directly add words without filtering by `cur`, one can call
449+ #
450+ # _comp_compgen -R -- -W '"${words[@]}"'
451+ #
452+ # Other nested calls of _comp_compgen can also be used. The function is
453+ # supposed to replace the existing content of the array by default to allow the
454+ # caller control whether to replace or append by the option `-a`.
455+ #
417456_comp_compgen ()
418457{
419- local _append=" " _var=COMPREPLY _cur=${cur-} _ifs=$' \t\n '
420- local -a _split_options=(-l)
458+ local _append=${_comp_compgen__append-}
459+ local _var=${_comp_compgen__var-COMPREPLY}
460+ local _cur=${_comp_compgen__cur-${cur-} }
461+ local _ifs=$' \t\n '
421462
422463 local OPTIND=1 OPTARG=" " OPTERR=0 _opt
423464 while getopts ' :alF:v:Rc:' _opt " $@ " ; do
424465 case $_opt in
425- a) _append=set _split_options+=(-a) ;;
466+ a) _append=set ;;
426467 v)
427468 if [[ $OPTARG == @ (* [^_a-zA-Z0-9]* | [0-9]* | ' ' | _* | IFS| OPTIND| OPTARG| OPTERR) ]]; then
428469 printf ' bash_completion: %s: -v: invalid array name `%s' \' ' .\n' " $FUNCNAME " " $OPTARG " >&2
@@ -445,17 +486,38 @@ _comp_compgen()
445486 printf ' bash_completion: %s: unexpected number of arguments.\n' " $FUNCNAME " >&2
446487 printf ' usage: %s [-alR|-F SEP|-v ARR|-c CUR] -- ARGS...' " $FUNCNAME " >&2
447488 return 2
448- elif
449- # Note: $* in the below checks would be affected by uncontrolled IFS in
450- # bash >= 5.0, so we need to set IFS to the normal value. The behavior
451- # in bash < 5.0, where unquoted $* in conditional command did not honor
452- # IFS, was a bug.
453- local IFS=$' \t\n '
454- # Note: extglob *\$?(\{)[0-9]* can be extremely slow when the string
455- # "${*:2:_nopt}" becomes longer, so we test \$[0-9] and \$\{[0-9]
456- # separately.
457- [[ $* == * \$ [0-9]* || $* == * \$\{ [0-9]* ]]
458- then
489+ fi
490+
491+ if [[ $1 != -* ]]; then
492+ # usage: _comp_compgen [options] NAME args
493+ if ! declare -F " _comp_compgen_$1 " & > /dev/null; then
494+ printf ' bash_completion: %s: unrecognized category `%s' \' ' (function _comp_compgen_%s not found).\n' " $FUNCNAME " " $1 " " $1 " >&2
495+ return 2
496+ fi
497+
498+ local _comp_compgen__append=$_append
499+ local _comp_compgen__var=$_var
500+ local _comp_compgen__cur=$_cur cur=$_cur
501+ # Note: we use $1 as a part of a function name, and we use $2... as
502+ # arguments to the function if any.
503+ # shellcheck disable=SC2145
504+ _comp_compgen_" $@ "
505+ return
506+ fi
507+
508+ # usage: _comp_compgen [options] -- [compgen_options]
509+
510+ # Note: $* in the below checks would be affected by uncontrolled IFS in
511+ # bash >= 5.0, so we need to set IFS to the normal value. The behavior in
512+ # bash < 5.0, where unquoted $* in conditional command did not honor IFS,
513+ # was a bug.
514+ # Note: Also, ${_cur:+-- "$_cur"} and ${_append:+-a} would be affected by
515+ # uncontrolled IFS.
516+ local IFS=$' \t\n '
517+ # Note: extglob *\$?(\{)[0-9]* can be extremely slow when the string
518+ # "${*:2:_nopt}" becomes longer, so we test \$[0-9] and \$\{[0-9]
519+ # separately.
520+ if [[ $* == * \$ [0-9]* || $* == * \$\{ [0-9]* ]]; then
459521 printf ' bash_completion: %s: positional parameter $1, $2, ... do not work inside this function.\n' " $FUNCNAME " >&2
460522 return 2
461523 fi
@@ -475,7 +537,7 @@ _comp_compgen()
475537 return " $_status "
476538 }
477539
478- _comp_split " ${_split_options[@]} " " $_var " " $_result "
540+ _comp_split -l ${_append : +-a} " $_var " " $_result "
479541}
480542
481543# Check if the argument looks like a path.
0 commit comments