about summary refs log tree commit diff
path: root/Completion/Core/compinit
blob: 23bc94cf9fa855cd0a5ad03ce8cc4a2217d83919 (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
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
# Initialisation for new style completion. This mainly contains some helper
# functions and aliases. Everything else is split into different files that
# will automatically be made autoloaded (see the end of this file).
# The names of the files that will be considered for autoloading have to
# start with an underscores (like `_setopt').
# The first line of these files will be read and has to say what should be
# done with its contents:
#
#   `#compdef <names ...>'
#     If the first line looks like this, the file is autoloaded as a
#     function and that function will be called to generate the matches
#     when completing for one of the commands whose <names> are given.
#
#   `#compdef -p <pattern>'
#     This defines a function that should be called to generate matches
#     for commands whose name matches <pattern>. Note that only one pattern
#     may be given.
#
#   `#compdef -k <style> [ <key-sequence> ... ]
#     This is used to bind special completions to all the given
#     <key-sequence>(s). The <style> is the name of one of the built-in
#     completion widgets (complete-word, delete-char-or-list,
#     expand-or-complete, expand-or-complete-prefix, list-choices,
#     menu-complete, menu-expand-or-complete, or reverse-menu-complete).
#     This creates a widget behaving like <style> so that the
#     completions are chosen as given in the the rest of the file,
#     rather than by the context.  The widget has the same name as
#     the autoload file and can be bound using bindkey in the normal way.
#
#   `#autoload'
#     this is for helper functions that are not used to
#     generate matches, but should automatically be loaded
#     when they are called
#
# Note that no white space is allowed between the `#' and the rest of
# the string.
#
# Functions that are used to generate matches should return zero if they
# were able to add matches and non-zero otherwise.
#
# See the file `compdump' for how to speed up initialisation.

# If we got the `-d'-flag, we will automatically dump the new state (at
# the end).
# `-f dir' is used to pass down the directory where this file was
#   found.  This is necessary if functionargzero is not set.
# If we were given an argument, this will be taken as the name of the
# file in which to store the dump.

_i_fdir=''
_i_dumpfile=''
_i_autodump=0
while [[ $# -gt 0 && $1 = -[df] ]]; do
  if [[ "$1" = -d ]]; then
    _i_autodump=1
    shift
    if [[ $# -gt 0 && "$1" != -[df] ]]; then
      _i_dumpfile="$1"
      shift
    fi
  elif [[ "$1" = -f ]]; then
    # Used by compinstall to pass down directory where compinit was found
    shift
    _i_fdir="$1"
    shift
  fi
done
# Get the directory if we don't have it already and we can
if [[ -z "$_i_fdir" && -o functionargzero && $0 = */* ]]; then
  _i_fdir=${0:h}
fi

# The associative array containing the definitions for the commands.
# Definitions for patterns will be stored in the normal array `_patcomps'.

typeset -A _comps
_patcomps=()

# This is the associative array used for configuration.

typeset -A compconfig

# Standard initialisation for `compconfig'.
if [[ -n $_i_dumpfile ]]; then
  # Explicitly supplied dumpfile.
  compconfig[dumpfile]="$_i_dumpfile"
elif [[ -o functionargzero ]]; then
  # We can deduce it from the name of this script
  compconfig[dumpfile]="$0.dump"
elif [[ -n $_i_fdir ]]; then
  # We were told what directory to use.
  compconfig[dumpfile]="$_i_fdir/compinit.dump"
else
  compconfig[dumpfile]=''
fi

if [[ -n $compconfig[dumpfile] ]]; then
  # Check the file is writeable.  If it doesn't exist, the
  # only safe way is to try and create it.
  if [[ -f $compconfig[dumpfile] ]]; then
    [[ -w $compconfig[dumpfile] ]] || compconfig[dumpfile]=''
  elif touch $compconfig[dumpfile] >& /dev/null; then
    rm -f $compconfig[dumpfile]
  else
    compconfig[dumpfile]=''
  fi
fi

if [[ -z $compconfig[dumpfile] ]]; then
  # If no dumpfile given, or it was not writeable, then use
  # user's ZDOTDIR.
  compconfig[dumpfile]="${ZDOTDIR:-$HOME}/.zcompdump"
fi

(( ${+compconfig[correct_accept]} )) || compconfig[correct_accept]=2n
(( ${+compconfig[correct_prompt]} )) ||
  compconfig[correct_prompt]='correct to:'
(( ${+compconfig[completer]} )) || compconfig[completer]=_complete

# This function is used to register or delete completion functions. For
# registering completion functions, it is invoked with the name of the
# function as it's first argument (after the options). The other
# arguments depend on what type of completion function is defined. If
# none of the `-p' and `-k' options is given a function for a command is
# defined. The arguments after the function name are then interpreted as
# the names of the command for which the function generates matches.
# With the `-p' option a function for a name pattern is defined. This 
# function will be invoked when completing for a command whose name 
# matches the pattern given as argument after the function name (in this
# case only one argument is accepted).
# With the `-k' option a function for a special completion keys is 
# defined and immediatly bound to those keys. Here, the extra arguments
# are the name of one of the builtin completion widgets and any number
# of key specifications as accepted by the `bindkey' builtin.
# In any case the `-a' option may be given which makes the function
# whose name is given as the first argument be autoloaded. When defining
# a function for command names the `-n' option may be given and keeps
# the definitions from overriding any previous definitions for the
# commands.
# For deleting definitions, the `-d' option must be given. Without the
# `-p' option, this deletes definitions for functions for the commands
# whose names are given as arguments. If combined with the `-p' option
# it deletes the definitions for the patterns given as argument.
# The `-d' option may not be combined with the `-k' option, i.e.
# definitions for key function can not be removed.
#
# Examples:
#
#  compdef -a foo bar baz
#    make the completion for the commands `bar' and `baz' use the
#    function `foo' and make this function be autoloaded
#
#  compdef -p foo 'c*'
#    make completion for all command whose name begins with a `c'
#    generate matches by calling the function `foo' before generating
#    matches defined for the command itself
#
#  compdef -k foo list-choices '^X^M' '\C-xm'
#    make the function `foo' be invoked when typing `Control-X Control-M'
#    or `Control-X m'; the function should generate matches and will
#    behave like the `list-choices' builtin widget
#
#  compdef -d bar baz
#   delete the definitions for the command names `bar' and `baz'

compdef() {
  local opt autol type func delete new i

  # Get the options.

  while getopts "anpkd" opt; do
    case "$opt" in
    a)    autol=yes;;
    n)    new=yes;;
    [pk]) if [[ -n "$type" ]]; then
            # Error if both `-p' and `-k' are given (or one of them
	    # twice).
            echo "$0: type already set to $type"
	    return 1
	  fi
	  if [[ "$opt" = p ]]; then
	    type=pattern
	  else
	    type=key
	  fi
	  ;;
    d) delete=yes;;
    esac
  done
  shift OPTIND-1

  if [[ -z "$delete" ]]; then
    # Adding definitions, first get the name of the function name
    # and probably do autoloading.

    func="$1"
    [[ -n "$autol" ]] && autoload -U "$func"
    shift

    case "$type" in
    pattern)
      if [[ $# -gt 1 ]]; then
        echo "$0: only one pattern allowed"
	return 1
      fi
      # Patterns are stored in strings like `c* foo', with a space
      # between the pattern and the function name.

      _patcomps=("$_patcomps[@]" "$1 $func")
      ;;
    key)
      if [[ $# -lt 2 ]]; then
        echo "$0: missing keys"
	return 1
      fi

      # Define the widget.
      zle -C "$func" "$1" "$func"
      shift

      # And bind the keys...
      for i; do
        bindkey "$i" "$func"
      done
      ;;
    *)
      # For commands store the function name in the `_comps'
      # associative array, command names as keys.
      if [[ -z "$new" ]]; then
	for i; do
	  _comps[$i]="$func"
	done
      else
        for i; do
          [[ "${+_comps[$i]}" -eq 0 ]] && _comps[$i]="$func"
        done
      fi
      ;;
    esac
  else
    # Handle the `-d' option, deleting.
    case "$type" in
    pattern)
      # Note the space.
      for i; do
        _patcomps=("${(@)patcomps:#$i *}")
      done
      ;;
    key)
      # Oops, cannot do that yet.

      echo "$0: cannot restore key bindings"
      return 1
      ;;
    *)
      # Deleting definitons for command is even simpler.
      for i; do
        unset "_comps[$i]"
      done
    esac
  fi
}

# Functional interface to configuration. This takes its arguments
# and sets the according values in `compconfig'.
# Arguments may be `foo=bar' to set key `foo' to `bar' or `baz' to
# set key `baz' to the empty string.
# If no arguments are given, all configurations keys set are displayed.
# With the option `-l' as the first argument, the other arguments are
# taken to be key names and the values for theses keys are printed, one
# per line.
# When listing is done and the `-L' option is given, the keys and
# values are printed as invocations for this function, usable to be put
# inte a setup script.

compconf() {
  local i opt list

  while getopts "lL" opt; do
    if [[ "$opt" = l ]]; then
      [[ -z "$list" ]] && list=yes
    else
      list=long
    fi
  done
  shift OPTIND-1

  if (( $# )); then
    if [[ -n $list ]]; then
      for i; do
        if [[ $list = long ]]; then
	  (( ${+compconfig[$i]} )) && print "compconf $i='$compconfig[$i]'"
	else
          print $compconfig[$i]
	fi
      done
    else
      for i; do
        if [[ "$i" = *\=* ]]; then
          compconfig[${i%%\=*}]="${i#*\=}"
        else
          compconfig[$i]=''
        fi
      done
    fi
  else
    for i in ${(k)compconfig}; do
      if [[ $list = long ]]; then
	print "compconf $i='$compconfig[$i]'"
      else
        print ${(r:25:)i} $compconfig[$i]
      fi
    done
  fi
}

# Now we automatically make the definition files autoloaded.

typeset -U _i_files
_i_files=( ${^~fpath:/.}/_(|*[^~])(N:t) )
if [[ $#_i_files -lt 20 ]]; then
  # Too few files:  we need some more directories
  # Assume that we need to add the compinit directory to fpath.
  if [[ -n $_i_fdir ]]; then
    if [[ $_i_fdir = */Core ]]; then
      # Add all the Completion subdirectories
      fpath=(${_i_fdir:h}/*(/) $fpath)
    elif [[ -d $_i_fdir/Core ]]; then
      # Likewise
      fpath=(${_i_fdir}/*(/) $fpath)
    else
      fpath=($_i_fdir $fpath)
    fi
    _i_files=( ${^~fpath:/.}/_(|*[^~])(N:t) )
  fi
fi

_i_done=''

# If we have a dump file, load it.

if [[ -f "$compconfig[dumpfile]" ]]; then
  read -rA _i_line < "$compconfig[dumpfile]"
  if [[ _i_autodump -eq 1 && $_i_line[2] -eq $#_i_files ]]; then
    builtin . "$compconfig[dumpfile]"
    _i_done=yes
  fi
  unset _i_line
fi
if [[ -z "$_i_done" ]]; then
  for _i_dir in $fpath; do
    [[ $_i_dir = . ]] && continue
    for _i_file in $_i_dir/_(|*[^~])(N); do
      read -rA _i_line < $_i_file
      _i_tag=$_i_line[1]
      shift _i_line
      case $_i_tag in
      (\#compdef)
	if [[ $_i_line[1] = -[pk] ]]; then
	  compdef ${_i_line[1]}a "${_i_file:t}" "${(@)_i_line[2,-1]}"
	else
	  compdef -na "${_i_file:t}" "${_i_line[@]}"
	fi
	;;
      (\#autoload)
	autoload -U ${_i_file:t}
	;;
      esac
    done
  done

  bindkey |
    while read -rA _i_line; do
      case "$_i_line[2]" in
      (complete-word) ;&
      (delete-char-or-list) ;&
      (expand-or-complete) ;&
      (expand-or-complete-prefix) ;&
      (list-choices) ;&
      (menu-complete) ;&
      (menu-expand-or-complete) ;&
      (reverse-menu-complete)
	zle -C _complete_$_i_line[2] $_i_line[2] _main_complete
	bindkey "${_i_line[1][2,-2]}" _complete_$_i_line[2]
	;;
      esac
    done

  unset _i_dir _i_line _i_file _i_tag

  # If autodumping was requested, do it now.

  if [[ -n ${_i_fdir} && $_i_autodump = 1 ]]; then
    builtin . ${_i_fdir}/compdump
  fi
fi

unset _i_files _i_initname _i_done _i_autodump _i_fdir _i_dumpfile