about summary refs log tree commit diff
path: root/Completion/Core/_main_complete
blob: 34c5a3d3c7911fd4b2223363aafbd122bdeaaab7 (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
#autoload

# The main loop of the completion code. This is what is called when 
# completion is attempted from the command line.
#
# This code will automatically try to correct the string on the
# line based on the strings generated for the context if the
# parameter `COMPCORRECT' is set and normal completion didn't yield
# any matches. These corrected strings will be shown in a list and
# one can cycle through them as in a menucompletion. To use this 
# feature, `COMPCORRECT' should be set to a number, specifying the
# maximum number of errors that should be accepted. If the string also
# contains a `n' or `N', the code will use the numeric argument as the
# maximum number of errors if a numeric argument was given. If no
# numeric argument was given, the number from the value of
# `COMPCORRECT' will be used. E.g. with `COMPCORRECT=2n' two errors
# will be accepted, but if the user gives another number with the
# numeric argument, this will be prefered. Also, with `COMPCORRECT=0n',
# normally no automatic correction will be tried, but if a numeric
# argument is given, automatic correction will be used. Once the
# number of errors to accept is determined, the code will repeatedly
# try to generate matches by allowing one error, two errors, and so
# on.
# If the parameter `CCORIG' is set (independent of the value), the
# line will first be left unchanged and consecutive TABs cycle through 
# the list.
# When using automatic correction, one can also set the parameter
# `CCPROMPT' to a string that will be shown when multiple
# correction results are displayed and the code starts cycling
# through them (this string is used with the `-X' option and thus may
# contain the control sequences `%n', `%B',...).

local comp name _comp_correct comax

setopt localoptions nullglob rcexpandparam globdots
unsetopt markdirs globsubst shwordsplit nounset ksharrays

# Special completion contexts after `~' and `='.

if [[ -iprefix '=' ]]; then
  compstate[context]=equal
elif [[ "$PREFIX$SUFFIX" != */* && -iprefix '~' ]]; then
  compstate[context]=tilde
fi

# This is not an endless loop.

while true; do

  # An entry for `-first-' is the replacement for `compctl -T'
  # Completion functions may set `_compskip' to any value to make the 
  # main loops stop calling other completion functions.

  comp="$_comps[-first-]"
  if [[ ! -z "$comp" ]]; then
    "$comp"
    if (( $+_compskip )); then
      unset _compskip
      return
    fi
  fi

  # For arguments and command names we use the `_normal' function.

  if [[ "$compstate[context]" = command ]]; then
    _normal
  else
    # Let's see if we have a special completion definition for the other
    # possible contexts.

    comp=''

    case $compstate[context] in
    equal)           comp="$_comps[-equal-]";;
    tilde)           comp="$_comps[-tilde-]";;
    redirect)        comp="$_comps[-redirect-]";;
    math)            comp="$_comps[-math-]";;
    subscript)       comp="$_comps[-subscript-]";;
    value)           comp="$_comps[-value-]";;
    array_value)     comp="$_comps[-array-value-]";;
    condition)       comp="$_comps[-condition-]";;
    parameter)       comp="$_comps[-parameter-]";;
    brace_parameter) comp="$_comps[-brace-parameter-]";;
    esac

    # If not, we use default completion, if any.

    [[ -z "$comp" ]] && comp="$_comps[-default-]"
    [[ -z "$comp" ]] || "$comp"
  fi

  # Use automatic correction?

  if (( $+COMPCORRECT )); then

    # Do we have matches?
    if (( compstate[nmatches] )); then

      # Yes, were they added using correction? (More than one match?)

      if [[ -n "$_comp_correct" && compstate[nmatches] -gt 1 ]]; then

        # If we got more than one string from correction, we add the 
	# original string as a possible match, let it not be shown in
	# the list, and probably display the `CCPROMPT'.

        (( $+CCORIG )) && builtin compadd -nQ - "$PREFIX$SUFFIX"

	# If you always want to see the list of possible corrections,
	# set `compstate[list]=list' here.
      fi
      # Since we have matches, we don't want to try again.
      break
    fi

    # No matches, so let's see if we already tried correction.

    if [[ -n "$_comp_correct" ]]; then

      # Yes, give up if we reached the maximum number of tries,
      # otherwise increment our counter.

      [[ _comp_correct -eq comax ]] && break
      (( _comp_correct++ ))

    elif [[ compstate[matcher] -eq compstate[total_matchers] ]]; then

      # No matches and no correction tried yet, but we just tried the
      # last global match specification, so let's see if we should use
      # correction now. First, get the maximum number of errors.

      if [[ "$COMPCORRECT" = *[nN]* && NUMERIC -ne 1 ]]; then
        # Prefer the numeric argument if that has a sensible value.
        comax="$NUMERIC"
      else
        comax="${COMPCORRECT//[^0-9]}"
      fi
      # If the number of errors to accept is to small, give up.

      [[ "$comax" -lt 1 ]] && break

      # Otherwise temporarily define functions to use instead of
      # the builtins that add matches. This is used to be able
      # to stick the `(#a...)' into the right place (after an
      # ignored prefix).

      compadd() {
        if [[ "$PREFIX" = \~*/* ]]; then
	  PREFIX="${PREFIX%%/*}/(#a${_comp_correct})${PREFIX#*/}"
	else
          PREFIX="(#a${_comp_correct})$PREFIX"
	fi
	if (( $+CCPROMPT )); then
	  builtin compadd -X "$CCPROMPT" -J _correct "$@"
	else
	  builtin compadd -J _correct "$@"
	fi
      }
      compgen() {
        if [[ "$PREFIX" = \~*/* ]]; then
	  PREFIX="${PREFIX%%/*}/(#a${_comp_correct})${PREFIX#*/}"
	else
          PREFIX="(#a${_comp_correct})$PREFIX"
	fi
	if (( $+CCPROMPT )); then
	  builtin compgen "$@" -X "$CCPROMPT" -J _correct
	else
	  builtin compgen "$@" -J _correct
	fi
      }
      # Now initialise our counter. We also set `compstate[matcher]'
      # to `-1'. This allows completion functions to use the simple
      # `[[ compstate[matcher] -gt 1 ]] && return' to avoid being
      # called for multiple global match specs and still be called 
      # again when correction is done. Also, this makes it easy to
      # test if correction is attempted since `compstate[matcher]'
      # will never be set to a negative value by the completion code.

      _comp_correct=1
      compstate[matcher]=-1

      # We also need to set `extendedglob' and to make the completion
      # code behave as if globcomplete were set.

      setopt extendedglob
      compstate[pattern_match]=yes
    else
      # We are still trying global match specifications...
      break
    fi
  else
    # No automatic correction to try, just give up.
    break
  fi
done

# If we added wrapper functions, remove them.

[[ -n "$_comp_correct" ]] && unfunction compadd compgen