Skip to content

Commit ff4c4e3

Browse files
committed
feat(bash_completion): add _comp_array_filter
1 parent 1351139 commit ff4c4e3

File tree

1 file changed

+129
-0
lines changed

1 file changed

+129
-0
lines changed

bash_completion

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -309,6 +309,135 @@ _comp_expand_glob()
309309
return 0
310310
}
311311

312+
# Filter the array elements with the specified condition.
313+
# @param $1 Array name (that is not "value" or other internal variable names)
314+
# @param $2 When none of the options -EFG are specified, this is used as the
315+
# command that tests the array element. The command is supposed to exit with
316+
# status 0 when the element should be preserved, and 1 when the element
317+
# should be removed. The other exit status will cancel the array filtering.
318+
# If this is an existing function name, the function is called with the value
319+
# of the array element. Otherwise, this shall be the shell command that
320+
# tests the array-element value stored in the shell variable "value".
321+
#
322+
# Options:
323+
# -E $2 is interpreted as a POSIX extended regular expression.
324+
# The default anchoring is `-m` (see below).
325+
# -F $2 is interpreted as a fixed string. The default anchoring
326+
# is `-m` (see below).
327+
# -G $2 is interpreted as a glob pattern. The default anchoring
328+
# is `-x` (see below).
329+
#
330+
# -p Combined with -EFG, it performs the prefix matching.
331+
# -s Combined with -EFG, it performs the suffix matching.
332+
# -m Combined with -EFG, it performs the middle matching.
333+
# -x Combined with -EFG, it performs the exact matching.
334+
#
335+
# -r Revert the condition, i.e., remove elements that satisfy
336+
# the original condition.
337+
# -C Array compaction is not performed.
338+
#
339+
# @return 2 with a wrong usage, 1 when any elements are removed, 0 when the set
340+
# of array elements are unchanged. [ Note: the compaction will be performed
341+
# (without the option -C) even when the set of array elements are
342+
# unchanged. ]
343+
_comp_array_filter()
344+
{
345+
local _comp_local_flags='' _comp_local_pattype='' _comp_local_anchoring=''
346+
local OPTIND=1 OPTARG='' OPTERR=0 _comp_local_opt=''
347+
while getopts 'EFGpsmxrC' _comp_local_opt "$@"; do
348+
case $_comp_local_opt in
349+
[EFG]) _comp_local_pattype=$_comp_local_opt ;;
350+
[psmx]) _comp_local_anchoring=$_comp_local_opt ;;
351+
[rC]) _comp_local_flags=$_comp_local_opt$_comp_local_flags ;;
352+
*)
353+
printf 'bash_completion: %s: %s\n' "$FUNCNAME" 'usage error' >&2
354+
printf 'usage: %s %s\n' "$FUNCNAME" "[-EFGpsmxrC] ARRAY_NAME CONDITION" >&2
355+
return 2
356+
;;
357+
esac
358+
done
359+
360+
shift $((OPTIND - 1))
361+
if (($# != 2)); then
362+
printf 'bash_completion: %s: %s\n' "$FUNCNAME" "unexpected number of arguments: $#" >&2
363+
printf 'usage: %s %s\n' "$FUNCNAME" "[-EFGpsmxrC] ARRAY_NAME CONDITION" >&2
364+
return 2
365+
elif [[ $1 != [a-zA-Z_]*([a-zA-Z_0-9]) ]]; then
366+
printf 'bash_completion: %s: %s\n' "$FUNCNAME" "invalid array name '$1'." >&2
367+
return 2
368+
elif [[ $1 == @(_comp_local_*|OPTIND|OPTARG|OPTERR) ]]; then
369+
printf 'bash_completion: %s: %s\n' "$FUNCNAME" "array name '$1' is reserved for internal uses" >&2
370+
return 2
371+
elif [[ ! $_comp_local_pattype && $1 == value ]]; then
372+
printf 'bash_completion: %s: %s\n' "$FUNCNAME" "array name '$1' cannot be used for the predicate" >&2
373+
return 2
374+
fi
375+
# When the array is empty:
376+
eval "((\${#$1[@]}))" || return 0
377+
378+
local _comp_local_predicate='' _comp_local_pattern=$2
379+
case $_comp_local_pattype in
380+
E)
381+
case $_comp_local_anchoring in
382+
p) _comp_local_predicate='[[ $_comp_local_value =~ ^($_comp_local_pattern) ]]' ;;
383+
s) _comp_local_predicate='[[ $_comp_local_value =~ ($_comp_local_pattern)$ ]]' ;;
384+
x) _comp_local_predicate='[[ $_comp_local_value =~ ^($_comp_local_pattern)$ ]]' ;;
385+
*) _comp_local_predicate='[[ $_comp_local_value =~ $_comp_local_pattern ]]' ;;
386+
esac
387+
;;
388+
F)
389+
case $_comp_local_anchoring in
390+
p) _comp_local_predicate='[[ $_comp_local_value == "$_comp_local_pattern"* ]]' ;;
391+
s) _comp_local_predicate='[[ $_comp_local_value == *"$_comp_local_pattern" ]]' ;;
392+
x) _comp_local_predicate='[[ $_comp_local_value == "$_comp_local_pattern" ]]' ;;
393+
*) _comp_local_predicate='[[ $_comp_local_value == *"$_comp_local_pattern"* ]]' ;;
394+
esac
395+
;;
396+
G)
397+
case $_comp_local_anchoring in
398+
p) _comp_local_predicate='[[ $_comp_local_value == $_comp_local_pattern* ]]' ;;
399+
s) _comp_local_predicate='[[ $_comp_local_value == *$_comp_local_pattern ]]' ;;
400+
m) _comp_local_predicate='[[ $_comp_local_value == *$_comp_local_pattern* ]]' ;;
401+
*) _comp_local_predicate='[[ $_comp_local_value == $_comp_local_pattern ]]' ;;
402+
esac
403+
;;
404+
*)
405+
if declare -F "$2" &>/dev/null; then
406+
_comp_local_predicate="$2 \"\$_comp_local_value\""
407+
else
408+
_comp_local_predicate="local value=\$_comp_local_value; $2"
409+
fi
410+
;;
411+
esac
412+
413+
local _comp_local_unset='' _comp_local_expected_status=0
414+
[[ $_comp_local_flags == *r* ]] && _comp_local_expected_status=1
415+
416+
local _comp_local_indices _comp_local_index _comp_local_value
417+
eval "_comp_local_indices=(\"\${!$1[@]}\")"
418+
for _comp_local_index in "${_comp_local_indices[@]}"; do
419+
eval "_comp_local_value=\${$1[\$_comp_local_index]}; $_comp_local_predicate"
420+
case $? in
421+
"$_comp_local_expected_status") continue ;;
422+
[01])
423+
unset -v "$1[\$_comp_local_index]"
424+
_comp_local_unset=1
425+
;;
426+
*)
427+
printf 'bash_completion: %s: %s\n' "$FUNCNAME" \
428+
"the filter condition broken '${_comp_local_pattype:+-$_comp_local_pattype }$2'" >&2
429+
return 2
430+
;;
431+
esac
432+
done
433+
434+
# Compaction of the sparse array
435+
[[ $_comp_local_flags == *C* ]] ||
436+
eval "((\${#$1[@]})) && $1=(\"\${$1[@]}\")"
437+
438+
[[ ! $_comp_local_unset ]]
439+
}
440+
312441
# Reassemble command line words, excluding specified characters from the
313442
# list of word completion separators (COMP_WORDBREAKS).
314443
# @param $1 chars Characters out of $COMP_WORDBREAKS which should

0 commit comments

Comments
 (0)