about summary refs log tree commit diff
path: root/Functions/multicomp
blob: ab206de0d990a9e87095faf5f28a728e074373b3 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
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})

# }