about summary refs log tree commit diff
path: root/Completion/Core/_multi_parts
blob: 1c02e7e39f574518560d003754e5f416d89791e3 (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
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
#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 match 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+:=match' 'i=imm'

sopts=( "$sopts[@]" "$opts[@]" )
if (( $#match )); then
  match="${match[2]}"
else
  match=''
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:|=* $match" - "$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:|=* $match" - "$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:|=* $match" - "$tmp1[1]"
          else
	    compadd "$group[@]" "$expl[@]" -p "$pref" "$sopts[@]" \
                    -M "r:|${sep}=* r:|=* $match" - "$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:|=* $match" - "$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:|=* $match" - "${i%%${sep}*}" && ret=0
            ;;
	  ${sep}*)
            compadd "$group[@]" "$expl[@]" -S '' "$opts[@]" \
	            -p "$pref" \
                    -M "r:|${sep}=* r:|=* $match" - "$sep" && ret=0
            ;;
	  *${sep}*)
            compadd "$group[@]" "$expl[@]" -r "$sep" -S "$sep" "$opts[@]" \
	            -p "$pref" \
                    -M "r:|${sep}=* r:|=* $match" - "${i%%${sep}*}" && ret=0
            ;;
          *)
            compadd "$group[@]" "$expl[@]" -S '' "$opts[@]" -p "$pref" \
                    -M "r:|${sep}=* r:|=* $match" - "$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:|=* $match" - "${i%%${sep}*}" && ret=0
          else
            compadd "$group[@]" "$expl[@]" -S '' "$opts[@]" -p "$pref" \
                    -M "r:|${sep}=* r:|=* $match" - "$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:|=* $match" - "$pref$pre"
      else
        compadd "$group[@]" "$expl[@]" -S '' "$opts[@]" \
                -M "r:|${sep}=* r:|=* $match" - "$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:|=* $match" - "${${pref%${sep}}##*${sep}}"

      elif [[ "$pref" = *${sep}* ]]; then
        compadd "$group[@]" "$expl[@]" -S '' "$opts[@]" \
                -p "${pref%${sep}*}${sep}" \
                -M "r:|${sep}=* r:|=* $match" - "${pref##*${sep}}"
      else
        compadd "$group[@]" "$expl[@]" -S '' "$opts[@]" \
                -M "r:|${sep}=* r:|=* $match" - "$pref"
      fi
    fi
    return
  fi
done