Skip to content

Commit

Permalink
feat(bash_completion): add _comp_array_filter
Browse files Browse the repository at this point in the history
  • Loading branch information
akinomyoga committed Apr 12, 2022
1 parent 05b4159 commit bc6f87d
Showing 1 changed file with 118 additions and 0 deletions.
118 changes: 118 additions & 0 deletions bash_completion
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,124 @@ _upvars()
done
}

# Filter the array elements with the specified condition.
# @param $1 Array name (that is not "value" or other internal variable names)
# @param $2 When none of the options -EFG are specified, this is used as the
# command that tests the array element. If this is an existing function
# name, the function is called with the value of the array element.
# Otherwise, this shall be the shell command that tests the array-element
# value stored in the shell variable "value".
#
# Options:
# -E $2 is interpreted as a POSIX extended regular expression.
# This is always the partial matching unless ^, $ is included
# in $2.
# -F $2 is interpreted as a fixed string.
# -G $2 is interpreted as a glob pattern.
#
# -p Combined with -F or -G, it performs the prefix matching.
# -s Combined with -F or -G, it performs the suffix matching.
# -m Combined with -F or -G, it performs the middle matching.
# -r Revert the condition, i.e., remove elements that satisfy
# the original condition.
#
# -C Array compaction is not performed.
#
# @return 2 with a wrong usage, 1 when any elements are removed, 0 when the set
# of array elements are unchanged. [ Note: the compaction will be performed
# (without the option -C) even when the set of array elements are
# unchanged. ]
_comp_array_filter()
{
local __comp_flags='' __comp_pattype='' __comp_anchoring=''
local OPTIND=1 OPTARG='' OPTERR=0 __comp_opt=''
while getopts 'EFGpsmrC' __comp_opt "$@"; do
case $__comp_opt in
[EFG]) __comp_pattype=$__comp_opt ;;
[psm]) __comp_anchoring=$__comp_opt ;;
[rC]) __comp_flags=$__comp_opt$__comp_flags ;;
*)
echo "bash_completion: $FUNCNAME: usage error" >&2
return 2
;;
esac
done

shift $((OPTIND - 1))
if (($# != 2)); then
printf 'bash_completion: %s: %s\n' "$FUNCNAME" "unexpected number of arguments." >&2
printf 'usage: %s %s\n' "$FUNCNAME" "[-EFGpsmrC] ARRAY_NAME CONDITION" >&2
return 2
elif [[ $1 != [a-zA-Z_]*([a-zA-Z_0-9]) ]]; then
printf 'bash_completion: %s: %s\n' "$FUNCNAME" "invalid array name '$1'." >&2
return 2
elif [[ $1 == @(__comp_*|OPTIND|OPTARG|OPTERR) ]]; then
printf 'bash_completion: %s: %s\n' "$FUNCNAME" "array name '$1' is reserved for internal uses." >&2
return 2
elif [[ ! $__comp_pattype && $1 == value ]]; then
printf 'bash_completion: %s: %s\n' "$FUNCNAME" "array name '$1' cannot be used for the predicate." >&2
return 2
fi
# When the array is empty:
eval "((\${#$1[@]}))" || return 0

local __comp_predicate='' __comp_pattern=$2
case $__comp_pattype in
E)
__comp_predicate='[[ $__comp_value == $__comp_pattern ]]'
;;
F)
case $__comp_anchoring in
p) __comp_predicate='[[ $__comp_value == "$__comp_pattern"* ]]' ;;
s) __comp_predicate='[[ $__comp_value == *"$__comp_pattern" ]]' ;;
m) __comp_predicate='[[ $__comp_value == *"$__comp_pattern"* ]]' ;;
*) __comp_predicate='[[ $__comp_value == "$__comp_pattern" ]]' ;;
esac
;;
G)
case $__comp_anchoring in
p) __comp_predicate='[[ $__comp_value == $__comp_pattern* ]]' ;;
s) __comp_predicate='[[ $__comp_value == *$__comp_pattern ]]' ;;
m) __comp_predicate='[[ $__comp_value == *$__comp_pattern* ]]' ;;
*) __comp_predicate='[[ $__comp_value == $__comp_pattern ]]' ;;
esac
;;
*)
if declare -F "$2" &>/dev/null; then
_comp_predicate="$2 \"\$__comp_value\""
else
_comp_predicate="local value=\$__comp_value; $2"
fi
;;
esac

local __comp_unset='' __comp_expected_status=0
[[ $__comp_flags == *r* ]] && __comp_expected_status=1

local __comp_indices __comp_index __comp_value
eval "__comp_indices=(\"\${!$1[@]}\")"
for __comp_index in "${__comp_indices[@]}"; do
eval "__comp_value=\${$1[\$__comp_index]}; $__comp_predicate"
case $? in
"$__comp_expected_status") continue ;;
[01])
unset -v "$1[\$__comp_index]"
__comp_unset=1
;;
*)
printf 'bash_completion: %s: %s\n' "$FUNCNAME" "the filter condition broken." >&2
return 2
;;
esac
done

# Compaction of the sparse array
[[ $__comp_flags == *C* ]] ||
eval -- "((\${#$1[@]})) && $1=(\"\${$1[@]}\")"

[[ ! $__comp_unset ]]
}

# Reassemble command line words, excluding specified characters from the
# list of word completion separators (COMP_WORDBREAKS).
# @param $1 chars Characters out of $COMP_WORDBREAKS which should
Expand Down

0 comments on commit bc6f87d

Please sign in to comment.