summary refs log tree commit diff
path: root/Functions/multicomp
diff options
context:
space:
mode:
Diffstat (limited to 'Functions/multicomp')
-rwxr-xr-xFunctions/multicomp80
1 files changed, 80 insertions, 0 deletions
diff --git a/Functions/multicomp b/Functions/multicomp
new file mode 100755
index 000000000..ab206de0d
--- /dev/null
+++ b/Functions/multicomp
@@ -0,0 +1,80 @@
+# multicomp() {
+# Completes all manner of files given prefixes for each path segment.
+# e.g. s/z/s -> src/zsh-2.4/src
+#
+# Usage: e.g.
+# compctl -D -f + -U -Q -S '' -K multicomp
+#
+# Note that exactly matched directories are not expanded, e.g.
+# s/zsh-2.4/s<TAB> will not expand to src/zsh-2.4old/src.
+# Will expand glob patterns already in the word, but use complete-word,
+# not TAB (expand-or-complete), or you will get ordinary glob expansion.
+# Requires the -U option to compctl.
+# Menucompletion is highly recommended for ambiguous matches.
+# Liable to screw up escaped metacharacters royally.
+# $fignore is not used: feel free to add your own bit.
+
+emulate -R zsh				# Requires zsh 3.0-pre4 or later
+local pref head sofar origtop newtop globdir="(-/)" wild
+setopt localoptions nullglob rcexpandparam globdots
+unsetopt markdirs globsubst shwordsplit nounset
+
+pref="${1}$2"
+# Hack to allow programmable completion to select multicomp after a :
+# (e.g.
+# compctl -D -f -x 's[:]' -U -Q -S '' -K multicomp
+# )
+pref="${pref#:}"
+
+sofar=('')
+reply=('')
+
+if [[ "$pref" = \~* ]]; then
+  # If the string started with ~, save the head and what it will become.
+  origtop="${pref%%/*}"
+  eval "newtop=$origtop"
+  # Save the expansion as the bit matched already
+  sofar=($newtop)
+  pref="${pref#$origtop}"
+fi
+
+while [[ -n "$pref" ]]; do
+  [[ "$pref" = /* ]] && sofar=(${sofar}/) && pref="${pref#/}"
+  head="${pref%%/*}"
+  pref="${pref#$head}"
+  if [[ -n "$pref" && -z $sofar[2] && -d "${sofar}$head" ]]; then
+    # Exactly matched directory: don't try to glob
+    reply=("${sofar}$head")
+  else
+    [[ -z "$pref" ]] && globdir=
+    # if path segment contains wildcards, don't add another.
+    if [[ "$head" = *[\[\(\*\?\$\~]* || -z "$head" ]]; then
+      wild=$head
+    else
+      # Simulate case-insensitive globbing for ASCII characters
+      wild="[${(j(][))${(s())head:l}}]*"	# :gs/a/[a]/ etc.
+      # The following could all be one expansion, but for readability:
+      wild=$wild:gs/a/aA/:gs/b/bB/:gs/c/cC/:gs/d/dD/:gs/e/eE/:gs/f/fF/
+      wild=$wild:gs/g/gG/:gs/h/hH/:gs/i/iI/:gs/j/jJ/:gs/k/kK/:gs/l/lL/
+      wild=$wild:gs/m/mM/:gs/n/nN/:gs/o/oO/:gs/p/pP/:gs/q/qQ/:gs/r/rR/
+      wild=$wild:gs/s/sS/:gs/t/tT/:gs/u/uU/:gs/v/vV/:gs/w/wW/:gs/x/xX/
+      wild=$wild:gs/y/yY/:gs/z/zZ/:gs/-/_/:gs/_/-_/:gs/[]//
+
+      # Expand on both sides of '.' (except when leading) as for '/'
+      wild="${${wild:gs/[.]/*.*/}#\*}"
+    fi
+
+    reply=(${sofar}"${wild}${globdir}")
+    reply=(${~reply})
+  fi
+
+  [[ -z $reply[1] ]] && reply=() && break
+  [[ -n $pref ]] && sofar=($reply)
+done
+
+# Restore ~'s in front if there were any.
+# There had better not be anything funny in $newtop.
+[[ -n "$origtop" ]] && reply=("$origtop"${reply#$newtop})
+
+# }
+