about summary refs log tree commit diff
path: root/Completion/Core/_multi_parts
diff options
context:
space:
mode:
Diffstat (limited to 'Completion/Core/_multi_parts')
-rw-r--r--Completion/Core/_multi_parts287
1 files changed, 123 insertions, 164 deletions
diff --git a/Completion/Core/_multi_parts b/Completion/Core/_multi_parts
index ab9438494..b49c41e22 100644
--- a/Completion/Core/_multi_parts
+++ b/Completion/Core/_multi_parts
@@ -7,15 +7,8 @@
 # The parts of words from the array that are separated by the
 # separator character are then completed independently.
 
-local sep matches patstr orig matchflags pref i tmp1 tmp2 nm
-local group expl menu origflags mflags
-
-_match_test _multi_parts || return 1
-
-# Save the current number of matches to be able to return if we added
-# matches or not.
-
-nm=$compstate[nmatches]
+local sep matches pref npref i tmp1 group expl menu pre suf
+typeset -U tmp2
 
 # Get the options.
 
@@ -41,193 +34,159 @@ else
   matches=( "${(@P)2}" )
 fi
 
-# Now build the pattern from what we have on the line. We also save
-# the original string in `orig'.
+# 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.
 
-if [[ $#compstate[pattern_match] -ne 0 ]]; then
-  if [[ "${compstate[pattern_match]-*}" = \** ]]; then
-    str="${PREFIX}*${SUFFIX}*"
-  else
-    str="${PREFIX}${SUFFIX}"
-  fi
-else
-  patstr="${PREFIX:q}*${SUFFIX:q}*"
-fi
-orig="${PREFIX}${SUFFIX}"
+pre="$PREFIX"
+suf="$SUFFIX"
+orig="$PREFIX$SUFFIX"
 
-[[ $compstate[insert] = *menu || -n "$_comp_correct" ||
+# Special handling for menucompletion?
+
+[[ $compstate[insert] = (*menu|[0-9]*) || -n "$_comp_correct" ||
    ( $#compstate[pattern_match] -ne 0 &&
      "$orig" != "${orig:q}" ) ]] && menu=yes
 
-matchflags=""
-_match_pattern _path_files patstr matchflags
-origflags="$matchflags"
-[[ -n "$_comp_correct" ]] && matchflags="$matchflags(#a$_comp_correct)"
-
-patstr="${${patstr//$sep/*$sep}//\*##/*}"
-
-# First we will skip over those parts of the matches for which we have 
-# exact substrings on the line. In `pref' we will build the
-# unambiguous prefix string.
+# In `pref' we collect the unambiguous prefix path.
 
 pref=''
-while [[ "$orig" = *${sep}* ]] do
 
-  # First build the pattern to use, then collect all strings from
-  # `matches' that match the prefix we have and the exact substring in 
-  # the array `tmp1'.
+# If the string from the line matches at least one of the strings,
+# we use only the matching strings.
 
-  if [[ -n "$_comp_correct" && "${#orig%%${sep}*}" -le _comp_correct ]]; then
-    mflags="$origflags"
-  else
-    mflags="$matchflags"
-  fi
-
-  pat="${${${patstr#*${sep}}%${sep}*}//\*/[^${sep}]#}"
-  tmp1=( "${(@M)matches:#${~mflags}${orig%%${sep}*}${sep}${~pat}}" )
+compadd -O tmp1 -M "r:|${sep}=* r:|=*" - "$matches[@]"
 
-  # If there are no words matching the exact substring, stop.
+(( $#tmp1 )) && matches=( "$tmp1[@]" )
 
-  (( $#tmp1 )) || break
+while true; do
 
-  # Otherwise add the part to the prefix, remove it from the matches
-  # (and also remove all words not matching the string at all), and
-  # set `patstr' and `orig' to the next component.
+  # Get the prefix and suffix for matching.
 
-  tmp1="${orig%%${sep}*}${sep}"
-  pref="$pref$tmp1"
-  matches=("${(@)${(@)${(@M)matches:#${tmp1}*}#$tmp1}:#}")
-  orig="${orig#*${sep}}"
-  patstr="${patstr#*${sep}}"
-done
-
-# Now we get all the words that still match in `tmp1'.
-
-if [[ "$patstr" = *${sep}* ]]; then
-  tmp1="${patstr%${sep}*}${sep}"
-  pat="${tmp1//\*/[^${sep}]#}${patstr##*${sep}}"
-else
-  pat="$patstr"
-fi
-if [[ -n "$_comp_correct" && "${#orig%%${sep}*}" -le _comp_correct ]]; then
-  mflags="$origflags"
-else
-  mflags="$matchflags"
-fi
-tmp1=( "${(@M)matches:#${~mflags}${~pat}}" )
-
-if (( $#tmp1 )); then
-
-  # There are words that are matched, put them into `matches' and then
-  # move all unambiguous components from the beginning into `pref'.
+  if [[ "$pre" = *${sep}* ]]; then
+    PREFIX="${pre%%${sep}*}"
+    SUFFIX=""
+  else
+    PREFIX="${pre}"
+    SUFFIX="${suf%%${sep}*}"
+  fi
 
-  matches=( "$tmp1[@]" )
-  while [[ "$matches[1]" = *${sep}* ]]; do
+  # 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'.
 
-    # We just take the first component of the first match and see if
-    # there are other matches with a different prefix (these are
-    # collected in `tmp2'). If there are any, we give up.
+  tmp1=( "${(@M)matches:#${PREFIX}${SUFFIX}${sep}*}" )
 
-    tmp1="${matches[1]%%${sep}*}${sep}"
-    tmp2=( "${(@)matches:#${tmp1}*}" )
-    (( $#tmp2 )) && break
+  if (( $#tmp1 )); then
+    npref="${PREFIX}${SUFFIX}${sep}"
+  else
+    # No exact match, see how many strings match what's on the line.
 
-    # All matches have the same prefix, put it into `pref' and remove
-    # it from the matches.
+    tmp2=( "${(@)matches%%${sep}*}" )
+    compadd -O tmp1 - "$tmp2[@]"
 
-    pref="$pref$tmp1"
-    matches=( "${(@)${(@)matches#$tmp1}:#}" )
+    if [[ $#tmp1 -eq 1 ]]; then
 
-    if [[ "$orig" = *${sep}* ]]; then
-      orig="${orig#*${sep}}"
-    else
-      orig=''
-    fi
-  done
+      # 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.
 
-  # Now we can tell the completion code about the things we
-  # found. Strings that have a separator will be added with a suffix.
-
-  if [[ -z "$orig" && "$PREFIX$SUFFIX" != "$pref$orig" ]]; then
-    compadd -QU  "$group[@]" "$expl[@]" -i "$IPREFIX" -I "$ISUFFIX" -S '' - \
-            "${pref}${orig}"
-  elif [[ -n "$menu" ]]; then
-    if [[ "$orig" = *${sep}* ]]; then
-      orig="${sep}${orig#*${sep}}"
-    else
-      orig=''
-    fi
-    for i in "$matches[@]" ; do
-      if [[ "$i" = *${sep}* ]]; then
-        compadd -U "$group[@]" "$expl[@]" -i "$IPREFIX" -I "$ISUFFIX" \
-	        -p "$pref" -s "$orig" - "${i%%${sep}*}${sep}"
+      if [[ "$pre$suf" = *${sep}* ]]; then
+        npref="${tmp1[1]}${sep}"
       else
-        compadd -U "$group[@]" "$expl[@]" -i "$IPREFIX" -I "$ISUFFIX" \
-	        -p "$pref" -s "$orig" - "${i%%${sep}*}"
+        matches=( "${(@M)matches:#${tmp1[1]}*}" )
+	tmp2=( "${(@M)matches:#${tmp1[1]}${sep}*}" )
+
+	if (( $#tmp2 )); then
+	  compadd -U "$group[@]" "$expl[@]" -i "$IPREFIX" -I "$ISUFFIX" \
+	          -p "$pref" -qS "$sep" - "$tmp1[1]"
+        else
+	  compadd -U "$group[@]" "$expl[@]" -i "$IPREFIX" -I "$ISUFFIX" \
+	          -p "$pref" - "$tmp1[1]"
+        fi
+	return 1
       fi
-    done
-  else
-    for i in "$matches[@]" ; do
-      if [[ "$i" = *${sep}* ]]; then
-        compadd -U -i "$IPREFIX" -I "$ISUFFIX" -p "$pref" -s "${i#*${sep}}" \
-	        "$group[@]" "$expl[@]" -M "r:|${sep:q}=*" - "${i%%${sep}*}${sep}"
+    elif (( $#tmp1 )); then
+
+      # 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:|=*" - "$matches[@]"
+
+      if [[ -n "$menu" ]]; then
+        # With menucompletion we just add matches for the matching
+        # components with the prefix we collected and the rest from the
+        # line as a suffix.
+
+        tmp2="$pre$suf"
+        if [[ "$tmp2" = *${sep}* ]]; then
+          compadd -U "$group[@]" "$expl[@]" -i "$IPREFIX" -I "$ISUFFIX" \
+	          -p "$pref" -s "${sep}${tmp2#*${sep}}" - "$tmp1[@]"
+        else
+          compadd -U "$group[@]" "$expl[@]" -i "$IPREFIX" -I "$ISUFFIX" \
+	          -p "$pref" - "$tmp1[@]"
+        fi
       else
-        compadd -U "$group[@]" "$expl[@]" -i "$IPREFIX" -I "$ISUFFIX" \
-                -p "$pref" - "$i"
+        # 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 -U "$group[@]" "$expl[@]" -i "$IPREFIX" -I "$ISUFFIX" \
+	            -S '' -p "$pref" -s "${i#*${sep}}" - "${i%%${sep}*}${sep}"
+          else
+            compadd -U "$group[@]" "$expl[@]" -i "$IPREFIX" -I "$ISUFFIX" \
+	            -S '' -p "$pref" - "$i"
+          fi
+        done
       fi
-    done
-  fi
-elif [[ "$patstr" = *${sep}* ]]; then
-
-  # We had no words matching the string from the line. But we want to
-  # be friendly and at least expand the prefix as far as we can. So we 
-  # will loop through the rest of the string from the line and test
-  # the components one by one.
-
-  while [[ "$patstr" = *${sep}* ]]; do
-
-    # First we get all words matching at least this component in
-    # `tmp1'. If there are none, we give up.
-
-    if [[ -n "$_comp_correct" && "${#orig%%${sep}*}" -le _comp_correct ]]; then
-      mflags="$origflags"
+      return 0
     else
-      mflags="$matchflags"
-    fi
-    tmp1=( "${(@M)matches:#${~mflags}${~patstr%%${sep}*}${sep}*}" )
-    (( $#tmp1 )) || break
-
-    # Then we check if there are words that have a different prefix.
+      # 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.
 
-    tmp2=( "${(@)tmp1:#${tmp1[1]%%${sep}*}${sep}*}" )
-    if (( $#tmp2 )); then
+      [[ "$orig" = "$pref$pre$suf" ]] && return 1
 
-      # There are words with another prefix, so we have found an
-      # ambiguous component. So we just give all possible prefixes to
-      # the completion code together with our prefix and the rest of
-      # the string from the line as the suffix.
-
-      compadd -U "$group[@]" "$expl[@]" -S '' -i "$IPREFIX" -I "$ISUFFIX" \
-              -p "$pref" -s "${sep}${orig#*${sep}}" - "${(@)matches%%${sep}*}"
+      if [[ -n "$suf" ]]; then
+        compadd -U "$group[@]" "$expl[@]" -i "$IPREFIX" -I "$ISUFFIX" \
+	        -s "$suf" - "$pref$pre"
+      else
+        compadd -U "$group[@]" "$expl[@]" -i "$IPREFIX" -I "$ISUFFIX" \
+	        -S '' - "$pref$pre$suf"
+      fi
       return 0
     fi
+  fi
 
-    # All words have the same prefix, so add it to `pref' again and
-    # try the next component.
+  # 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'.
 
-    pref="$pref${tmp1[1]%%${sep}*}${sep}"
-    matches=( "${(@)matches#${tmp1[1]%%${sep}*}${sep}}" )
-    orig="${orig#*${sep}}"
-    patstr="${patstr#*${sep}}"
-  done
+  matches=( "${(@)${(@)${(@M)matches:#${npref}*}#*${sep}}:#}" )
+  pref="$pref$npref"
 
-  # Finally, add the unambiguous prefix and the rest of the string
-  # from the line.
+  # Now we set `pre' and `suf' to their new values.
 
-  compadd -U "$group[@]" "$expl[@]" -S '' -i "$IPREFIX" -I "$ISUFFIX" \
-          -p "$pref" - "$orig"
-fi
+  if [[ "$pre" = *${sep}* ]]; then
+    pre="${pre#*${sep}}"
+  elif [[ "$suf" = *${sep}* ]]; then
+    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.
 
-# This sets the return value to indicate that we added matches (or not).
+    [[ -n "$pref" && "$orig" != "$pref" ]] &&
+        compadd -U "$group[@]" "$expl[@]" -i "$IPREFIX" -I "$ISUFFIX" \
+	        -S '' - "$pref"
 
-[[ nm -ne compstate[nmatches] ]]
+    return
+  fi
+done