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_parts201
1 files changed, 201 insertions, 0 deletions
diff --git a/Completion/Core/_multi_parts b/Completion/Core/_multi_parts
new file mode 100644
index 000000000..1f51d2f6d
--- /dev/null
+++ b/Completion/Core/_multi_parts
@@ -0,0 +1,201 @@
+#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 patstr orig matchflags pref i tmp1 tmp2 nm
+local group expl
+
+_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]
+
+# 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 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
+
+# Now build the pattern from what we have on the line. We also save
+# the original string in `orig'. The `eval' is used to replace our
+# separator character by `*<sep>'.
+
+if [[ -o globcomplete ]]; then
+  patstr="${PREFIX}*${SUFFIX}*"
+else
+  patstr="${PREFIX:q}*${SUFFIX:q}*"
+fi
+orig="${PREFIX}${SUFFIX}"
+
+matchflags=""
+_match_pattern _path_files patstr matchflags
+[[ -n "$_comp_correct" ]] && matchflags="$matchflags(#a$_comp_correct)"
+
+patstr="${${patstr//$sep/*$sep}//\*##/*}"
+#eval patstr\="\$patstr:gs-${sep}-\*${sep}-:gs/\*\*/\*/"
+
+# 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.
+
+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'.
+
+  pat="${${${patstr#*${sep}}%${sep}*}//\*/[^${sep}]#}${patstr##*${sep}}"
+  tmp1=( "${(@M)matches:#${~matchflags}${orig%%${sep}*}${sep}${~pat}}" )
+
+  # If there are no words matching the exact substring, stop.
+
+  (( $#tmp1 )) || break
+
+  # Otherwise add the part to the prefix, remove it from the matches
+  # (which will also remove all words not matching the string at all), 
+  # and set `patstr' and `orig' to the next component.
+
+  pref="$pref${orig%%${sep}*}${sep}"
+  matches=( "${(@)${(@)matches#${orig%%${sep}*}${sep}}:#}" )
+  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
+tmp1=( "${(@M)matches:#${~matchflags}${~pat}}" )
+
+if (( $#tmp1 )); then
+
+  # There are words that are matched, put them int `matches' and then
+  # move all unambiguous components from the beginning into `pref'.
+
+  matches=( "$tmp1[@]" )
+  while [[ "$matches[1]" = *${sep}* ]]; do
+
+    # 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="${matches[1]%%${sep}*}${sep}"
+    tmp2=( "${(@)matches:#${tmp1}*}" )
+    (( $#tmp2 )) && break
+
+    # All matches have the same prefix, but it into `pref' and remove
+    # it from the matches.
+
+    pref="$pref$tmp1"
+    matches=( "${(@)${(@)matches#$tmp1}:#}" )
+
+    if [[ "$orig" = *${sep}* ]]; then
+      orig="${orig#*${sep}}"
+    else
+      orig=''
+    fi
+  done
+
+  # 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" -S '' - "${pref}${orig}"
+  elif [[ $compstate[insert] = *menu ]]; then
+    for i in "$matches[@]" ; do
+      if [[ "$i" = *${sep}* ]]; then
+        compadd -U "$group[@]" "$expl[@]" -i "$IPREFIX" \
+	        -p "$pref" -qS "$sep" - "${i%%${sep}*}"
+      else
+        compadd -U "$group[@]" "$expl[@]" -i "$IPREFIX" \
+	        -p "$pref" - "${i%%${sep}*}"
+      fi
+    done
+  else
+    for i in "$matches[@]" ; do
+      if [[ "$i" = *${sep}* ]]; then
+        compadd -U -i "$IPREFIX" -p "$pref" -s "${sep}${i#*${sep}}" \
+	        "$group[@]" "$expl[@]" -M "r:|${sep}=*" - "${i%%${sep}*}"
+      else
+        compadd -U "$group[@]" "$expl[@]" -i "$IPREFIX" -p "$pref" - "$i"
+      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.
+
+    tmp1=( "${(@M)matches:#${~matchflags}${~patstr%%${sep}*}${sep}*}" )
+    (( $#tmp1 )) || break
+
+    # Then we check if there are words that have a different prefix.
+
+    tmp2=( "${(@)tmp1:#${tmp1[1]%%${sep}*}${sep}*}" )
+    if (( $#tmp2 )); then
+
+      # 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" -p "$pref" \
+              -s "${sep}${orig#*${sep}}" - "${(@)matches%%${sep}*}"
+      return 0
+    fi
+
+    # All words have the same prefix, so add it to `pref' again and
+    # try the next component.
+
+    pref="$pref${tmp1[1]%%${sep}*}${sep}"
+    matches=( "${(@)matches#${tmp1[1]%%${sep}*}${sep}}" )
+    orig="${orig#*${sep}}"
+    patstr="${patstr#*${sep}}"
+  done
+
+  # Finally, add the unambiguous prefix and the rest of the string
+  # from the line.
+
+  compadd -U "$group[@]" "$expl[@]" -S '' -i "$IPREFIX" -p "$pref" - "$orig"
+fi
+
+# This sets the return value to indicate that we added matches (or not).
+
+[[ nm -ne compstate[nmatches] ]]