Skip to content

Commit de1ea57

Browse files
committed
feat(bash_completion): add _comp_array_filter
1 parent ba1f944 commit de1ea57

File tree

1 file changed

+118
-0
lines changed

1 file changed

+118
-0
lines changed

bash_completion

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,124 @@ _upvars()
260260
done
261261
}
262262

263+
# Filter the array elements with the specified condition.
264+
# @param $1 Array name (that is not "value" or other internal variable names)
265+
# @param $2 When none of the options -EFG are specified, this is used as the
266+
# command that tests the array element. If this is an existing function
267+
# name, the function is called with the value of the array element.
268+
# Otherwise, this shall be the shell command that tests the array-element
269+
# value stored in the shell variable "value".
270+
#
271+
# Options:
272+
# -E $2 is interpreted as a POSIX extended regular expression.
273+
# This is always the partial matching unless ^, $ is included
274+
# in $2.
275+
# -F $2 is interpreted as a fixed string.
276+
# -G $2 is interpreted as a glob pattern.
277+
#
278+
# -p Combined with -F or -G, it performs the prefix matching.
279+
# -s Combined with -F or -G, it performs the suffix matching.
280+
# -m Combined with -F or -G, it performs the middle matching.
281+
# -r Revert the condition, i.e., remove elements that satisfy
282+
# the original condition.
283+
#
284+
# -C Array compaction is not performed.
285+
#
286+
# @return 2 with a wrong usage, 1 when any elements are removed, 0 when the set
287+
# of array elements are unchanged. [ Note: the compaction will be performed
288+
# (without the option -C) even when the set of array elements are
289+
# unchanged. ]
290+
_comp_array_filter()
291+
{
292+
local __comp_flags='' __comp_pattype='' __comp_anchoring=''
293+
local OPTIND=1 OPTARG='' OPTERR=0 __comp_opt=''
294+
while getopts 'EFGpsmrC' __comp_opt "$@"; do
295+
case $__comp_opt in
296+
[EFG]) __comp_pattype=$__comp_opt ;;
297+
[psm]) __comp_anchoring=$__comp_opt ;;
298+
[rC]) __comp_flags=$__comp_opt$__comp_flags ;;
299+
*)
300+
echo "bash_completion: $FUNCNAME: usage error" >&2
301+
return 2
302+
;;
303+
esac
304+
done
305+
306+
shift $((OPTIND - 1))
307+
if (($# != 2)); then
308+
printf 'bash_completion: %s: %s\n' "$FUNCNAME" "unexpected number of arguments." >&2
309+
printf 'usage: %s %s\n' "$FUNCNAME" "[-EFGpsmrC] ARRAY_NAME CONDITION" >&2
310+
return 2
311+
elif [[ $1 != [a-zA-Z_]*([a-zA-Z_0-9]) ]]; then
312+
printf 'bash_completion: %s: %s\n' "$FUNCNAME" "invalid array name '$1'." >&2
313+
return 2
314+
elif [[ $1 == @(__comp_*|OPTIND|OPTARG|OPTERR) ]]; then
315+
printf 'bash_completion: %s: %s\n' "$FUNCNAME" "array name '$1' is reserved for internal uses." >&2
316+
return 2
317+
elif [[ ! $__comp_pattype && $1 == value ]]; then
318+
printf 'bash_completion: %s: %s\n' "$FUNCNAME" "array name '$1' cannot be used for the predicate." >&2
319+
return 2
320+
fi
321+
# When the array is empty:
322+
eval "((\${#$1[@]}))" || return 0
323+
324+
local __comp_predicate='' __comp_pattern=$2
325+
case $__comp_pattype in
326+
E)
327+
__comp_predicate='[[ $__comp_value == $__comp_pattern ]]'
328+
;;
329+
F)
330+
case $__comp_anchoring in
331+
p) __comp_predicate='[[ $__comp_value == "$__comp_pattern"* ]]' ;;
332+
s) __comp_predicate='[[ $__comp_value == *"$__comp_pattern" ]]' ;;
333+
m) __comp_predicate='[[ $__comp_value == *"$__comp_pattern"* ]]' ;;
334+
*) __comp_predicate='[[ $__comp_value == "$__comp_pattern" ]]' ;;
335+
esac
336+
;;
337+
G)
338+
case $__comp_anchoring in
339+
p) __comp_predicate='[[ $__comp_value == $__comp_pattern* ]]' ;;
340+
s) __comp_predicate='[[ $__comp_value == *$__comp_pattern ]]' ;;
341+
m) __comp_predicate='[[ $__comp_value == *$__comp_pattern* ]]' ;;
342+
*) __comp_predicate='[[ $__comp_value == $__comp_pattern ]]' ;;
343+
esac
344+
;;
345+
*)
346+
if declare -F "$2" &>/dev/null; then
347+
_comp_predicate="$2 \"\$__comp_value\""
348+
else
349+
_comp_predicate="local value=\$__comp_value; $2"
350+
fi
351+
;;
352+
esac
353+
354+
local __comp_unset='' __comp_expected_status=0
355+
[[ $__comp_flags == *r* ]] && __comp_expected_status=1
356+
357+
local __comp_indices __comp_index __comp_value
358+
eval "__comp_indices=(\"\${!$1[@]}\")"
359+
for __comp_index in "${__comp_indices[@]}"; do
360+
eval "__comp_value=\${$1[\$__comp_index]}; $__comp_predicate"
361+
case $? in
362+
"$__comp_expected_status") continue ;;
363+
[01])
364+
unset -v "$1[\$__comp_index]"
365+
__comp_unset=1
366+
;;
367+
*)
368+
printf 'bash_completion: %s: %s\n' "$FUNCNAME" "the filter condition broken." >&2
369+
return 2
370+
;;
371+
esac
372+
done
373+
374+
# Compaction of the sparse array
375+
[[ $__comp_flags == *C* ]] ||
376+
eval -- "((\${#$1[@]})) && $1=(\"\${$1[@]}\")"
377+
378+
[[ ! $__comp_unset ]]
379+
}
380+
263381
# Reassemble command line words, excluding specified characters from the
264382
# list of word completion separators (COMP_WORDBREAKS).
265383
# @param $1 chars Characters out of $COMP_WORDBREAKS which should

0 commit comments

Comments
 (0)