#autoload # This function can be used to separately complete parts of strings # where each part may be one of a set of matches and different parts # have different sets. # Arguments are alternatingly arrays and separator strings. Arrays may # be given by name or literally as words separated by white space in # parentheses, e.g.: # # _comp_parts '(foo bar)' @ hosts # # This will make this function complete the strings in the array # `friends'. If the string on the line contains a `@', the substring # after it will be completed from the array `hosts'. Of course more # arrays may be given, each preceded by another separator string. # # This function understands the `-J group', `-V group', and # `-X explanation' options. # # This function does part of the matching itself and calls the functions # `_match_test' and `_match_pattern' for this. local str arr sep test testarr tmparr prefix suffixes matchers autosuffix local matchflags opt group expl nm=$compstate[nmatches] # Test if we should use this function for the global matcher in use. _match_test _comp_parts || return 1 # Get the options. group=() expl=() while getopts "J:V:X:" opt; do case "$opt" in [JV]) group=("-$opt" "$OPTARG");; X) expl=(-X "$OPTARG");; esac done shift OPTIND-1 # Get the string from the line. str="$PREFIX$SUFFIX" [[ -o globcomplete ]] && str="$str:q" prefix="" # Walk through the arguments to find the longest unambiguous prefix. while [[ $# -gt 1 ]]; do # Get the next array and separator. arr="$1" sep="$2" if [[ "$arr[1]" == '(' ]]; then tmparr=( ${=arr[2,-2]} ) arr=tmparr fi # Is the separator on the line? [[ "$str" != *${sep}* ]] && break # Build a pattern matching the possible matches and get all these # matches in an array. test="${str%%${sep}*}" matchflags="" _match_pattern _comp_parts test matchflags [[ -n "$_comp_correct" ]] && matchflags="$matchflags(#a$_comp_correct)" test="${matchflags}${test}" testarr=( "${(@M)${(@P)arr}:#${~test}*}" ) testarr=( "${(@)testarr:#}" ) # If there are no matches we give up. If there is more than one # match, this is the part we will complete. (( $#testarr )) || return 1 [[ $#testarr -gt 1 ]] && break # Only one match, add it to the prefix and skip over it in `str', # continuing with the next array and separator. prefix="${prefix}${testarr[1]}${sep}" str="${str#*${sep}}" shift 2 done # Get the array to work upon. arr="$1" if [[ "$arr[1]" == '(' ]]; then tmparr=( ${=arr[2,-2]} ) arr=tmparr fi if [[ $# -le 1 || "$str" != *${2}* ]]; then # No more separators, build the matches. matchflags="" test="$str" _match_pattern _comp_parts test matchflags [[ -n "$_comp_correct" ]] && matchflags="$matchflags(#a$_comp_correct)" test="${matchflags}${test}" testarr=( "${(@M)${(@P)arr}:#${~test}*}" ) testarr=( "${(@)testarr:#}" ) fi [[ $#testarr -eq 0 || ${#testarr[1]} -eq 0 ]] && return 1 # Now we build the suffixes to give to the completion code. shift matchers=() suffixes=("") autosuffix=() while [[ $# -gt 0 && "$str" == *${1}* ]]; do # Remove anything up to the the suffix. str="${str#*${1}}" # Again, we get the string from the line up to the next separator # and build a pattern from it. if [[ $# -gt 2 ]]; then test="${str%%${3}*}" else test="$str" fi matchflags="" _match_pattern _comp_parts test matchflags [[ -n "$_comp_correct" ]] && matchflags="$matchflags(#a$_comp_correct)" test="${matchflags}${test}" # We incrementally add suffixes by appending to them the seperators # and the strings from the next array that match the pattern we built. arr="$2" if [[ "$arr[1]" == '(' ]]; then tmparr=( ${=arr[2,-2]} ) arr=tmparr fi tmparr=( "${(@M)${(@P)arr}:#${~test}*}" ) tmparr=( "${(@)testarr:#}" ) suffixes=("${^suffixes[@]}${1}$^tmparr") # We want the completion code to generate the most specific suffix # for us, so we collect matching specifications that allow partial # word matching before the separators on the fly. matchers=("$matchers[@]" "r:|${1}=*") shift 2 done # If we were given at least one more separator we make the completion # code offer it by appending it as a autoremovable suffix. (( $# )) && autosuffix=(-qS "$1") # If we have collected matching specifications, we build an array # from it that can be used as arguments to `compadd'. [[ $#matchers -gt 0 ]] && matchers=(-M "$matchers") # Add the matches for each of the suffixes. for i in "$suffixes[@]"; do compadd -U "$group[@]" "$expl[@]" "$matchers[@]" "$autosuffix[@]" \ -i "$IPREFIX" -p "$prefix" -s "$i" - "$testarr[@]" done # This sets the return value to indicate that we added matches (or not). [[ nm -ne compstate[nmatches] ]]