mirror of
				git://git.code.sf.net/p/zsh/code
				synced 2025-10-31 18:10:56 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			267 lines
		
	
	
	
		
			8.1 KiB
		
	
	
	
		
			Text
		
	
	
	
	
	
			
		
		
	
	
			267 lines
		
	
	
	
		
			8.1 KiB
		
	
	
	
		
			Text
		
	
	
	
	
	
| #autoload
 | |
| 
 | |
| # This gets two arguments, a separator (which should be only one
 | |
| # character) and an array. As usual, the array may be given by it's
 | |
| # name or literal as in `(foo bar baz)' (words separated by spaces in
 | |
| # parentheses).
 | |
| # The parts of words from the array that are separated by the
 | |
| # separator character are then completed independently.
 | |
| 
 | |
| local sep matches pref npref i tmp1 group expl menu pre suf opre osuf cpre
 | |
| local opts sopts matcher imm
 | |
| typeset -U tmp2
 | |
| 
 | |
| # Get the options.
 | |
| 
 | |
| zparseopts -D -a sopts \
 | |
|     'J+:=group' 'V+:=group' 'X+:=expl' 'P:=opts' 'F:=opts' \
 | |
|     S: r: R: q 1 2 n f 'M+:=matcher' 'i=imm'
 | |
| 
 | |
| sopts=( "$sopts[@]" "$opts[@]" )
 | |
| if (( $#matcher )); then
 | |
|   matcher="${matcher[2]}"
 | |
| else
 | |
|   matcher=
 | |
| fi
 | |
| 
 | |
| # Get the arguments, first the separator, then the array. The array is 
 | |
| # stored in `matches'. Further on this array will always contain those 
 | |
| # words from the original array that still match everything we have
 | |
| # tried to match while we walk through the string from the line.
 | |
| 
 | |
| sep="$1"
 | |
| if [[ "${2[1]}" = '(' ]]; then
 | |
|   matches=( ${=2[2,-2]} )
 | |
| else
 | |
|   matches=( "${(@P)2}" )
 | |
| fi
 | |
| 
 | |
| # In `pre' and `suf' we will hold the prefix and the suffix from the
 | |
| # line while we walk through them. The original string are used 
 | |
| # temporarily for matching.
 | |
| 
 | |
| pre="$PREFIX"
 | |
| suf="$SUFFIX"
 | |
| opre="$PREFIX"
 | |
| osuf="$SUFFIX"
 | |
| orig="$PREFIX$SUFFIX"
 | |
| 
 | |
| # Special handling for menucompletion?
 | |
| 
 | |
| [[ $compstate[insert] = (*menu|[0-9]*) || -n "$_comp_correct" ||
 | |
|    ( $#compstate[pattern_match] -ne 0 &&
 | |
|      "$orig" != "${orig:q}" ) ]] && menu=yes
 | |
| 
 | |
| # In `pref' we collect the unambiguous prefix path.
 | |
| 
 | |
| pref=''
 | |
| 
 | |
| # If the string from the line matches at least one of the strings,
 | |
| # we use only the matching strings.
 | |
| 
 | |
| compadd -O tmp1 -M "r:|${sep}=* r:|=* $matcher" -a matches
 | |
| 
 | |
| (( $#tmp1 )) && matches=( "$tmp1[@]" )
 | |
| 
 | |
| while true; do
 | |
| 
 | |
|   # Get the prefix and suffix for matching.
 | |
| 
 | |
|   if [[ "$pre" = *${sep}* ]]; then
 | |
|     PREFIX="${pre%%${sep}*}"
 | |
|     SUFFIX=""
 | |
|   else
 | |
|     PREFIX="${pre}"
 | |
|     SUFFIX="${suf%%${sep}*}"
 | |
|   fi
 | |
| 
 | |
|   # Check if the component for some of the possible matches is equal
 | |
|   # to the string from the line. If there are such strings, we directly
 | |
|   # use the stuff from the line. This avoids having `foo' complete to
 | |
|   # both `foo' and `foobar'.
 | |
| 
 | |
|   if [[ -n "$PREFIX$SUFFIX" || "$pre" = ${sep}* ]]; then
 | |
|     tmp1=( "${(@M)matches:#${PREFIX}${SUFFIX}${sep}*}" )
 | |
|   else
 | |
|     tmp1=()
 | |
|   fi
 | |
| 
 | |
|   if (( $#tmp1 )); then
 | |
|     npref="${PREFIX}${SUFFIX}${sep}"
 | |
|   else
 | |
|     # No exact match, see how many strings match what's on the line.
 | |
| 
 | |
|     builtin compadd -O tmp1 - "${(@)matches%%${sep}*}"
 | |
| 
 | |
|     [[ $#tmp1 -eq 0 && -n "$_comp_correct" ]] &&
 | |
|       compadd -O tmp1 - "${(@)matches%%${sep}*}"
 | |
| 
 | |
|     tmp2=( "$tmp1[@]" )
 | |
| 
 | |
|     if [[ $#tmp1 -eq 1 ]]; then
 | |
| 
 | |
|       # Only one match. If there are still separators from the line
 | |
|       # we just accept this component. Otherwise we insert what we 
 | |
|       # have collected, probably giving it a separator character
 | |
|       # as a suffix.
 | |
| 
 | |
|       if [[ "$pre$suf" = *${sep}* ]]; then
 | |
|         npref="${tmp1[1]}${sep}"
 | |
|       else
 | |
|         matches=( "${(@M)matches:#${tmp1[1]}*}" )
 | |
| 
 | |
| 	PREFIX="${cpre}${pre}"
 | |
| 	SUFFIX="$suf"
 | |
| 
 | |
| 	if [[ $#imm -ne 0 && $#matches -eq 1 ]] ||
 | |
|            zstyle -t ":completion:${curcontext}:" expand suffix; then
 | |
| 	  compadd "$group[@]" "$expl[@]" "$opts[@]" \
 | |
|                   -M "r:|${sep}=* r:|=* $matcher" - "$pref$matches[1]"
 | |
|         else
 | |
| 	  tmp2=( "${(@M)matches:#${tmp1[1]}${sep}*}" )
 | |
| 
 | |
| 	  if (( $#tmp2 )); then
 | |
| 	    compadd "$group[@]" "$expl[@]" -p "$pref" -r "$sep" -S "$sep" "$opts[@]" \
 | |
|                     -M "r:|${sep}=* r:|=* $matcher" - "$tmp1[1]"
 | |
|           else
 | |
| 	    compadd "$group[@]" "$expl[@]" -p "$pref" "$sopts[@]" \
 | |
|                     -M "r:|${sep}=* r:|=* $matcher" - "$tmp1[1]"
 | |
|           fi
 | |
|         fi
 | |
| 	return
 | |
|       fi
 | |
|     elif (( $#tmp1 )); then
 | |
|       local ret=1
 | |
| 
 | |
|       # More than one match. First we get all strings that match the
 | |
|       # rest from the line.
 | |
| 
 | |
|       PREFIX="$pre"
 | |
|       SUFFIX="$suf"
 | |
|       compadd -O matches -M "r:|${sep}=* r:|=* $matcher" -a matches
 | |
| 
 | |
|       if [[ "$pre" = *${sep}* ]]; then
 | |
|  	PREFIX="${cpre}${pre%%${sep}*}"
 | |
| 	SUFFIX="${sep}${pre#*${sep}}${suf}"
 | |
|       else
 | |
|         PREFIX="${cpre}${pre}"
 | |
| 	SUFFIX="$suf"
 | |
|       fi
 | |
| 
 | |
|       if ! zstyle -t ":completion:${curcontext}:" expand suffix ||
 | |
|          [[ -n "$menu" || -z "$compstate[insert]" ]]; then
 | |
| 
 | |
|         # With menucompletion we add only the ambiguous component with
 | |
|         # the prefix collected and a spearator for the matches that
 | |
|         # have more components.
 | |
| 
 | |
|         tmp2="$pre$suf"
 | |
|         if [[ "$tmp2" = *${sep}* ]]; then
 | |
|           tmp2=(-s "${sep}${tmp2#*${sep}}")
 | |
|         else
 | |
| 	  tmp2=()
 | |
|         fi
 | |
|         for i in "${(@M)matches:#(${(j:|:)~tmp1})*}"; do
 | |
| 	  case "$i" in
 | |
| 	  *${sep})
 | |
|             compadd "$group[@]" "$expl[@]" -r "$sep" -S "$sep" "$opts[@]" \
 | |
| 	            -p "$pref" \
 | |
|                     -M "r:|${sep}=* r:|=* $matcher" - "${i%%${sep}*}" && ret=0
 | |
|             ;;
 | |
| 	  ${sep}*)
 | |
|             compadd "$group[@]" "$expl[@]" -S '' "$opts[@]" \
 | |
| 	            -p "$pref" \
 | |
|                     -M "r:|${sep}=* r:|=* $matcher" - "$sep" && ret=0
 | |
|             ;;
 | |
| 	  *${sep}*)
 | |
|             compadd "$group[@]" "$expl[@]" -r "$sep" -S "$sep" "$opts[@]" \
 | |
| 	            -p "$pref" \
 | |
|                     -M "r:|${sep}=* r:|=* $matcher" - "${i%%${sep}*}" && ret=0
 | |
|             ;;
 | |
|           *)
 | |
|             compadd "$group[@]" "$expl[@]" -S '' "$opts[@]" -p "$pref" \
 | |
|                     -M "r:|${sep}=* r:|=* $matcher" - "$i" && ret=0
 | |
|             ;;
 | |
|           esac
 | |
|         done
 | |
|       else
 | |
|         # With normal completion we add all matches one-by-one with
 | |
| 	# the unmatched part as a suffix. This will insert the longest
 | |
| 	# unambiguous string for all matching strings.
 | |
| 
 | |
|         for i in "${(@M)matches:#(${(j:|:)~tmp1})*}"; do
 | |
| 	  if [[ "$i" = *${sep}* ]]; then
 | |
|             compadd "$group[@]" "$expl[@]" "$opts[@]" \
 | |
| 	            -p "$pref" -s "${i#*${sep}}" \
 | |
|                     -M "r:|${sep}=* r:|=* $matcher" - "${i%%${sep}*}" && ret=0
 | |
|           else
 | |
|             compadd "$group[@]" "$expl[@]" -S '' "$opts[@]" -p "$pref" \
 | |
|                     -M "r:|${sep}=* r:|=* $matcher" - "$i" && ret=0
 | |
|           fi
 | |
|         done
 | |
|       fi
 | |
|       return ret
 | |
|     else
 | |
|       # We are here if no string matched what's on the line. In this
 | |
|       # case we insert the expanded prefix we collected if it differs
 | |
|       # from the original string from the line.
 | |
| 
 | |
|       { ! zstyle -t ":completion:${curcontext}:" expand prefix ||
 | |
|         [[ "$orig" = "$pref$pre$suf" ]] } && return 1
 | |
| 
 | |
|       PREFIX="${cpre}${pre}"
 | |
|       SUFFIX="$suf"
 | |
| 
 | |
|       if [[ -n "$suf" ]]; then
 | |
|         compadd "$group[@]" "$expl[@]" -s "$suf" "$sopts[@]" \
 | |
|                 -M "r:|${sep}=* r:|=* $matcher" - "$pref$pre"
 | |
|       else
 | |
|         compadd "$group[@]" "$expl[@]" -S '' "$opts[@]" \
 | |
|                 -M "r:|${sep}=* r:|=* $matcher" - "$pref$pre"
 | |
|       fi
 | |
|       return
 | |
|     fi
 | |
|   fi
 | |
| 
 | |
|   # We just accepted and/or expanded a component from the line. We
 | |
|   # remove it from the matches (using only those that have a least
 | |
|   # the skipped string) and ad it the `pref'.
 | |
| 
 | |
|   matches=( "${(@)${(@)${(@M)matches:#${npref}*}#*${sep}}:#}" )
 | |
|   pref="$pref$npref"
 | |
| 
 | |
|   # Now we set `pre' and `suf' to their new values.
 | |
| 
 | |
|   if [[ "$pre" = *${sep}* ]]; then
 | |
|     cpre="${cpre}${pre%%${sep}*}${sep}"
 | |
|     pre="${pre#*${sep}}"
 | |
|   elif [[ "$suf" = *${sep}* ]]; then
 | |
|     cpre="${cpre}${pre}${suf%%${sep}*}${sep}"
 | |
|     pre="${suf#*${sep}}"
 | |
|     suf=""
 | |
|   else
 | |
|     # The string from the line is fully handled. If we collected an
 | |
|     # unambiguous prefix and that differs from the original string,
 | |
|     # we insert it.
 | |
| 
 | |
|     PREFIX="${opre}${osuf}"
 | |
|     SUFFIX=""
 | |
| 
 | |
|     if [[ -n "$pref" && "$orig" != "$pref" ]]; then
 | |
|       if [[ "$pref" = *${sep} ]]; then
 | |
|         compadd "$group[@]" "$expl[@]" "$opts[@]" \
 | |
|                 -p "${pref%${sep}*${sep}}${sep}" -S "$sep" \
 | |
|                 -M "r:|${sep}=* r:|=* $matcher" - "${${pref%${sep}}##*${sep}}"
 | |
| 
 | |
|       elif [[ "$pref" = *${sep}* ]]; then
 | |
|         compadd "$group[@]" "$expl[@]" -S '' "$opts[@]" \
 | |
|                 -p "${pref%${sep}*}${sep}" \
 | |
|                 -M "r:|${sep}=* r:|=* $matcher" - "${pref##*${sep}}"
 | |
|       else
 | |
|         compadd "$group[@]" "$expl[@]" -S '' "$opts[@]" \
 | |
|                 -M "r:|${sep}=* r:|=* $matcher" - "$pref"
 | |
|       fi
 | |
|     fi
 | |
|     return
 | |
|   fi
 | |
| done
 |