Skip to content

Commit ff51d67

Browse files
akinomyogascop
andcommitted
refactor(_parse_{help,usage}): renew interface _comp_compgen_help
Co-authored-by: Ville Skyttä <ville.skytta@iki.fi>
1 parent 59a3b49 commit ff51d67

File tree

2 files changed

+167
-86
lines changed

2 files changed

+167
-86
lines changed

bash_completion

Lines changed: 109 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -1190,10 +1190,52 @@ _comp_initialize()
11901190
return 0
11911191
}
11921192

1193-
# Helper function for _parse_help and _parse_usage.
1193+
# Helper function for _comp_compgen_help and _comp_compgen_usage.
1194+
# Obtain the help output based on the arguments.
1195+
# @param $@ args Arguments specified to the caller.
1196+
# @var[out] _lines
1197+
# @return 2 if the usage is wrong, 1 if no output is obtained, or otherwise 0.
1198+
_comp_compgen_help__get_help_lines()
1199+
{
1200+
local -a help_cmd
1201+
case ${1-} in
1202+
-)
1203+
if (($# > 1)); then
1204+
printf 'bash_completion: %s -: extra arguments for -\n' "${FUNCNAME[1]}" >&2
1205+
printf 'usage: %s -\n' "${FUNCNAME[1]}" >&2
1206+
printf 'usage: %s -c cmd args...\n' "${FUNCNAME[1]}" >&2
1207+
printf 'usage: %s [-- args...]\n' "${FUNCNAME[1]}" >&2
1208+
return 2
1209+
fi
1210+
help_cmd=(exec cat)
1211+
;;
1212+
-c)
1213+
if (($# < 2)); then
1214+
printf 'bash_completion: %s -c: no command is specified\n' "${FUNCNAME[1]}" >&2
1215+
printf 'usage: %s -\n' "${FUNCNAME[1]}" >&2
1216+
printf 'usage: %s -c cmd args...\n' "${FUNCNAME[1]}" >&2
1217+
printf 'usage: %s [-- args...]\n' "${FUNCNAME[1]}" >&2
1218+
return 2
1219+
fi
1220+
help_cmd=("${@:2}")
1221+
;;
1222+
--) shift 1 ;&
1223+
*)
1224+
local ret
1225+
_comp_dequote "${comp_args[0]-}" || ret=${comp_args[0]-}
1226+
help_cmd=("${ret:-false}" "$@")
1227+
;;
1228+
esac
1229+
1230+
local ret
1231+
_comp_split -l ret "$(LC_ALL=C "${help_cmd[@]}" 2>&1)" &&
1232+
_lines=("${ret[@]}")
1233+
}
1234+
1235+
# Helper function for _comp_compgen_help and _comp_compgen_usage.
1236+
# @var[in,out] _options Add options
11941237
# @return True (0) if an option was found, False (> 0) otherwise
1195-
# TODO: rename per API conventions, rework to use vars rather than outputting
1196-
__parse_options()
1238+
_comp_compgen_help__parse()
11971239
{
11981240
local option option2 i
11991241

@@ -1219,108 +1261,89 @@ __parse_options()
12191261
if [[ $option =~ (\[((no|dont)-?)\]). ]]; then
12201262
option2=${option/"${BASH_REMATCH[1]}"/}
12211263
option2=${option2%%[<{().[]*}
1222-
printf '%s\n' "${option2/=*/=}"
1264+
_options+=("${option2/=*/=}")
12231265
option=${option/"${BASH_REMATCH[1]}"/"${BASH_REMATCH[2]}"}
12241266
fi
12251267
12261268
option=${option%%[<{().[]*}
12271269
option=${option/=*/=}
12281270
[[ $option ]] || return 1
12291271
1230-
printf '%s\n' "$option"
1272+
_options+=("$option")
12311273
}
12321274
1233-
# Parse GNU style help output of the given command.
1234-
# @param $1 command; if "-", read from stdin and ignore rest of args
1235-
# @param $2 command options (default: --help)
1275+
# Parse GNU style help output of the given command and generate and store
1276+
# completions in an array. The help output is produced in the way depending on
1277+
# the usage:
1278+
# usage: _comp_compgen_help - # read from stdin
1279+
# usage: _comp_compgen_help -c cmd args... # run "cmd args..."
1280+
# usage: _comp_compgen_help [[--] args...] # run "${comp_args[0]} args..."
1281+
# When no arguments are specified, `--help` is assumed.
12361282
#
1237-
# TODO: rename per API conventions, rework to use vars rather than outputting
1238-
_parse_help()
1283+
# @var[in] comp_args[0]
1284+
_comp_compgen_help()
12391285
{
1240-
local IFS=$' \t\n'
1241-
local reset_monitor=$(shopt -po monitor) reset_lastpipe=$(shopt -p lastpipe) reset_noglob=$(shopt -po noglob)
1242-
set +o monitor
1243-
shopt -s lastpipe
1244-
set -o noglob
1286+
(($#)) || set -- -- --help
12451287

1246-
local cmd=$1
1247-
local line rc=1
1248-
(
1249-
case $cmd in
1250-
-) exec cat ;;
1251-
*)
1252-
# shellcheck disable=SC2086
1253-
_comp_dequote "$cmd" && LC_ALL=C "$ret" ${2:---help} 2>&1
1254-
;;
1255-
esac
1256-
) |
1257-
while read -r line; do
1258-
1259-
[[ $line == *([[:blank:]])-* ]] || continue
1260-
# transform "-f FOO, --foo=FOO" to "-f , --foo=FOO" etc
1261-
while [[ $line =~ ((^|[^-])-[A-Za-z0-9?][[:space:]]+)\[?[A-Z0-9]+([,_-]+[A-Z0-9]+)?(\.\.+)?\]? ]]; do
1262-
line=${line/"${BASH_REMATCH[0]}"/"${BASH_REMATCH[1]}"}
1263-
done
1264-
__parse_options "${line// or /, }" && rc=0
1288+
local -a _lines
1289+
_comp_compgen_help__get_help_lines "$@" || return "$?"
12651290

1291+
local -a _options=()
1292+
local _line
1293+
for _line in "${_lines[@]}"; do
1294+
[[ $_line == *([[:blank:]])-* ]] || continue
1295+
# transform "-f FOO, --foo=FOO" to "-f , --foo=FOO" etc
1296+
while [[ $_line =~ ((^|[^-])-[A-Za-z0-9?][[:space:]]+)\[?[A-Z0-9]+([,_-]+[A-Z0-9]+)?(\.\.+)?\]? ]]; do
1297+
_line=${_line/"${BASH_REMATCH[0]}"/"${BASH_REMATCH[1]}"}
12661298
done
1299+
_comp_compgen_help__parse "${_line// or /, }"
1300+
done
1301+
((${#_options[@]})) || return 1
12671302

1268-
$reset_monitor
1269-
$reset_lastpipe
1270-
$reset_noglob
1271-
return $rc
1303+
_comp_compgen -- -W '"${_options[@]}"'
1304+
return 0
12721305
}
12731306

1274-
# Parse BSD style usage output (options in brackets) of the given command.
1275-
# @param $1 command; if "-", read from stdin and ignore rest of args
1276-
# @param $2 command options (default: --usage)
1277-
#
1278-
# TODO: rename per API conventions, rework to use vars rather than outputting
1279-
_parse_usage()
1280-
{
1281-
local IFS=$' \t\n'
1282-
local reset_monitor=$(shopt -po monitor) reset_lastpipe=$(shopt -p lastpipe) reset_noglob=$(shopt -po noglob)
1283-
set +o monitor
1284-
shopt -s lastpipe
1285-
set -o noglob
1286-
1287-
local cmd=$1
1288-
local line match option i char rc=1
1289-
(
1290-
case $cmd in
1291-
-) exec cat ;;
1292-
*)
1293-
# shellcheck disable=SC2086
1294-
_comp_dequote "$cmd" && LC_ALL=C "$ret" ${2:---usage} 2>&1
1295-
;;
1296-
esac
1297-
) |
1298-
while read -r line; do
1299-
1300-
while [[ $line =~ \[[[:space:]]*(-[^]]+)[[:space:]]*\] ]]; do
1301-
match=${BASH_REMATCH[0]}
1302-
option=${BASH_REMATCH[1]}
1303-
case $option in
1304-
-?(\[)+([a-zA-Z0-9?]))
1305-
# Treat as bundled short options
1306-
for ((i = 1; i < ${#option}; i++)); do
1307-
char=${option:i:1}
1308-
[[ $char != '[' ]] && printf '%s\n' -"$char" && rc=0
1309-
done
1310-
;;
1311-
*)
1312-
__parse_options "$option" && rc=0
1313-
;;
1314-
esac
1315-
line=${line#*"$match"}
1316-
done
1317-
1307+
# Parse BSD style usage output (options in brackets) of the given command. The
1308+
# help output is produced in the way depending on the usage:
1309+
# usage: _comp_compgen_usage - # read from stdin
1310+
# usage: _comp_compgen_usage -c cmd args... # run "cmd args..."
1311+
# usage: _comp_compgen_usage [[--] args...] # run "${comp_args[0]} args..."
1312+
# When no arguments are specified, `--usage` is assumed.
1313+
#
1314+
# @var[in] comp_args[0]
1315+
_comp_compgen_usage()
1316+
{
1317+
(($#)) || set -- -- --usage
1318+
1319+
local -a _lines
1320+
_comp_compgen_help__get_help_lines "$@" || return "$?"
1321+
1322+
local -a _options=()
1323+
local _line _match _option _i _char
1324+
for _line in "${_lines[@]}"; do
1325+
while [[ $_line =~ \[[[:space:]]*(-[^]]+)[[:space:]]*\] ]]; do
1326+
_match=${BASH_REMATCH[0]}
1327+
_option=${BASH_REMATCH[1]}
1328+
case $_option in
1329+
-?(\[)+([a-zA-Z0-9?]))
1330+
# Treat as bundled short options
1331+
for ((_i = 1; _i < ${#_option}; _i++)); do
1332+
_char=${_option:_i:1}
1333+
[[ $_char != '[' ]] && _options+=("-$_char")
1334+
done
1335+
;;
1336+
*)
1337+
_comp_compgen_help__parse "$_option"
1338+
;;
1339+
esac
1340+
_line=${_line#*"$_match"}
13181341
done
1342+
done
1343+
((${#_options[@]})) || return 1
13191344

1320-
$reset_monitor
1321-
$reset_lastpipe
1322-
$reset_noglob
1323-
return $rc
1345+
_comp_compgen -- -W '"${_options[@]}"'
1346+
return 0
13241347
}
13251348

13261349
# This function completes on signal names (minus the SIG prefix)

bash_completion.d/000_bash_completion_compat.bash

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -276,4 +276,62 @@ _tilde()
276276
! _comp_compgen -c "$1" tilde
277277
}
278278

279+
# Helper function for _parse_help and _parse_usage.
280+
# @return True (0) if an option was found, False (> 0) otherwise
281+
# @deprecated Use _comp_compgen_help__parse
282+
__parse_options()
283+
{
284+
local -a _options=()
285+
_comp_compgen_help__parse "$1"
286+
printf '%s\n' "${_options[@]}"
287+
}
288+
289+
# Parse GNU style help output of the given command.
290+
# @param $1 command; if "-", read from stdin and ignore rest of args
291+
# @param $2 command options (default: --help)
292+
# @deprecated Use `_comp_compgen_help`. `COMPREPLY=($(compgen -W
293+
# '$(_parse_help "$1" ...)' -- "$cur"))` can be replaced with
294+
# `_comp_compgen_help [-- ...]`. Also, `var=($(_parse_help "$1" ...))` can
295+
# be replaced with `_comp_compgen -Rv var help [-- ...]`.
296+
_parse_help()
297+
{
298+
local -a args
299+
if [[ $1 == - ]]; then
300+
args=(-)
301+
else
302+
local ret opt IFS=$' \t\n'
303+
_comp_dequote "$1"
304+
_comp_split opt "${2:---help}"
305+
args=(-c "$ret" ${opt[@]+"${opt[@]}"})
306+
fi
307+
local -a ret=()
308+
_comp_compgen -Rv ret help "${args[@]}" || return 1
309+
((${#ret[@]})) && printf '%s\n' "${ret[@]}"
310+
return 0
311+
}
312+
313+
# Parse BSD style usage output (options in brackets) of the given command.
314+
# @param $1 command; if "-", read from stdin and ignore rest of args
315+
# @param $2 command options (default: --usage)
316+
# @deprecated Use `_comp_compgen_usage`. `COMPREPLY=($(compgen -W
317+
# '$(_parse_usage "$1" ...)' -- "$cur"))` can be replaced with
318+
# `_comp_compgen_usage [-- ...]`. `var=($(_parse_usage "$1" ...))` can be
319+
# replaced with `_comp_compgen -Rv var usage [-- ...]`.
320+
_parse_usage()
321+
{
322+
local -a args
323+
if [[ $1 == - ]]; then
324+
args=(-)
325+
else
326+
local ret opt IFS=$' \t\n'
327+
_comp_dequote "$1"
328+
_comp_split opt "${2:---usage}"
329+
args=(-c "$ret" ${opt[@]+"${opt[@]}"})
330+
fi
331+
local -a ret=()
332+
_comp_compgen -Rv ret usage "${args[@]}" || return 1
333+
((${#ret[@]})) && printf '%s\n' "${ret[@]}"
334+
return 0
335+
}
336+
279337
# ex: filetype=sh

0 commit comments

Comments
 (0)