summary refs log tree commit diff
path: root/Completion/compinit
blob: 2715c47e8f7f0308a2837ebe09e8a8d082abbb30 (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
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
# Initialisation for new style completion. This mainly contains some helper
# functions and setup. 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 are those that
# begin with an underscores (like `_condition).
#
# The first line of each of these files is read and must indicate 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.
#     The names may also be interspersed with `-T <assoc>' options
#     specifying for which set of functions this should be added.
#
#   `#compdef -[pP] <patterns ...>'
#     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 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.
#
#   `#compdef -K <widget-name> <style> <key-sequence> [ ... ]'
#     This is similar to -k, except it takes any number of sets of
#     three arguments.  In each set, the widget <widget-name> will
#     be defined, which will behave as <style>, as with -k, and will
#     be bound to <key-sequence>, exactly one of which must be defined.
#     <widget-name> must be different for each:  this must begin with an
#     underscore, else one will be added, and should not clash with other
#     completion widgets (names based on the name of the function are the
#     clearest), but is otherwise arbitrary.  It can be tested in the
#     function by the parameter $WIDGET.
#
#   `#autoload [ <options> ]'
#     This is for helper functions that are not used to
#     generate matches, but should automatically be loaded
#     when they are called. The <options> will be given to the
#     autoload builtin when making the function autoloaded. Note
#     that this need not include `-U' and `-z'.
#
# 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).  This takes the dumpfile as an argument.  -d (with the
# default dumpfile) is now the default; to turn off dumping use -D.

# The -C flag bypasses both the check for rebuilding the dump file and the
# usual call to compaudit; the -i flag causes insecure directories found by
# compaudit to be ignored, and the -u flag causes all directories found by
# compaudit to be used (without security checking).  Otherwise the user is
# queried for whether to use or ignore the insecure directories (which
# means compinit should not be called from non-interactive shells).

emulate -L zsh
setopt extendedglob

typeset _i_dumpfile _i_files _i_line _i_done _i_dir _i_autodump=1
typeset _i_tag _i_file _i_addfiles _i_fail=ask _i_check=yes _i_name

while [[ $# -gt 0 && $1 = -[dDiuC] ]]; do
  case "$1" in
  -d)
    _i_autodump=1
    shift
    if [[ $# -gt 0 && "$1" != -[dfQC] ]]; then
      _i_dumpfile="$1"
      shift
    fi
    ;;
  -D)
    _i_autodump=0
    shift
    ;;
  -i)
    _i_fail=ign
    shift
    ;;
  -u)
    _i_fail=use
    shift
    ;;
  -C)
    _i_check=
    shift
    ;;
  esac
done

# The associative arrays containing the definitions for the commands and
# services.

typeset -gHA _comps _services _patcomps _postpatcomps

# `_compautos' contains the names and options for autoloaded functions
# that get options.

typeset -gHA _compautos

# The associative array use to report information about the last
# completion to the outside.

typeset -gHA _lastcomp

# Remember dumpfile.
if [[ -n $_i_dumpfile ]]; then
  # Explicitly supplied dumpfile.
  typeset -g _comp_dumpfile="$_i_dumpfile"
else
  typeset -g _comp_dumpfile="${ZDOTDIR:-$HOME}/.zcompdump"
fi

# The standard options set in completion functions.

typeset -gHa _comp_options
_comp_options=(
       bareglobqual
       extendedglob
       glob
       multibyte
       multifuncdef
       nullglob
       rcexpandparam
       unset
    NO_allexport
    NO_aliases
    NO_cshnullglob
    NO_cshjunkiequotes
    NO_errexit
    NO_errreturn
    NO_globassign
    NO_globsubst
    NO_histsubstpattern
    NO_ignorebraces
    NO_ignoreclosebraces
    NO_kshglob
    NO_ksharrays
    NO_kshtypeset
    NO_markdirs
    NO_octalzeroes
    NO_posixbuiltins
    NO_shwordsplit
    NO_shglob
    NO_warncreateglobal
)

# And this one should be `eval'ed at the beginning of every entry point
# to the completion system.  It sets up what we currently consider a
# sane environment.  That means we set the options above, make sure we
# have a valid stdin descriptor (zle closes it before calling widgets)
# and don't get confused by user's ZERR trap handlers.

typeset -gH _comp_setup='local -A _comp_caller_options;
             _comp_caller_options=(${(kv)options[@]});
             setopt localoptions localtraps localpatterns ${_comp_options[@]};
             local IFS=$'\'\ \\t\\r\\n\\0\'';
             builtin enable -p \| \~ \( \? \* \[ \< \^ \# 2>/dev/null;
             exec </dev/null;
             trap - ZERR;
             local -a reply;
             local REPLY'

# These can hold names of functions that are to be called before/after all
# matches have been generated.

typeset -ga compprefuncs comppostfuncs
compprefuncs=()
comppostfuncs=()

# Loading it now ensures that the `funcstack' parameter is always correct.

: $funcstack

# 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).
# The option `-P' is like `-p', but the function will be called after
# trying to find a function defined for the command on the line if no
# such function could be found.
# With the `-k' option a function for a special completion keys is 
# defined and immediately 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; with `-k', the `-n' option prevents compdef from rebinding
# a key sequence which is already bound.
# 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 eval new i ret=0 cmd svc
  local -a match mbegin mend

  emulate -L zsh
  setopt extendedglob

  # Get the options.

  if (( ! $# )); then
    print -u2 "$0: I need arguments"
    return 1
  fi

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

  if (( ! $# )); then
    print -u2 "$0: I need arguments"
    return 1
  fi

  if [[ -z "$delete" ]]; then
    # If the first word contains an equal sign, all words must contain one
    # and we define which services to use for the commands.

    if [[ -z "$eval" ]] && [[ "$1" = *\=* ]]; then
      while (( $# )); do
        if [[ "$1" = *\=* ]]; then
	  cmd="${1%%\=*}"
	  svc="${1#*\=}"
          func="$_comps[${_services[(r)$svc]:-$svc}]"
          [[ -n ${_services[$svc]} ]] &&
              svc=${_services[$svc]}
	  [[ -z "$func" ]] &&
	      func="${${_patcomps[(K)$svc][1]}:-${_postpatcomps[(K)$svc][1]}}"
          if [[ -n "$func" ]]; then
	    _comps[$cmd]="$func"
	    _services[$cmd]="$svc"
	  else
	    print -u2 "$0: unknown command or service: $svc"
	    ret=1
	  fi
	else
	  print -u2 "$0: invalid argument: $1"
	  ret=1
	fi
        shift
      done

      return ret
    fi

    # Adding definitions, first get the name of the function name
    # and probably do autoloading.

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

    case "$type" in
    widgetkey)
      while [[ -n $1 ]]; do
	if [[ $# -lt 3 ]]; then
	  print -u2 "$0: compdef -K requires <widget> <comp-widget> <key>"
	  return 1
	fi
	[[ $1 = _* ]] || 1="_$1"
	[[ $2 = .* ]] || 2=".$2"
        [[ $2 = .menu-select ]] && zmodload -i zsh/complist
	zle -C "$1" "$2" "$func"
	if [[ -n $new ]]; then
	  bindkey "$3" | IFS=$' \t' read -A opt
	  [[ $opt[-1] = undefined-key ]] && bindkey "$3" "$1"
	else
	  bindkey "$3" "$1"
	fi
	shift 3
      done
      ;;
    key)
      if [[ $# -lt 2 ]]; then
        print -u2 "$0: missing keys"
	return 1
      fi

      # Define the widget.
      if [[ $1 = .* ]]; then
        [[ $1 = .menu-select ]] && zmodload -i zsh/complist
	zle -C "$func" "$1" "$func"
      else
        [[ $1 = menu-select ]] && zmodload -i zsh/complist
	zle -C "$func" ".$1" "$func"
      fi
      shift

      # And bind the keys...
      for i; do
        if [[ -n $new ]]; then
	   bindkey "$i" | IFS=$' \t' read -A opt
	   [[ $opt[-1] = undefined-key ]] || continue
	fi
        bindkey "$i" "$func"
      done
      ;;
    *)
      # For commands store the function name in the
      # associative array, command names as keys.
      while (( $# )); do
        if [[ "$1" = -N ]]; then
          type=normal
        elif [[ "$1" = -p ]]; then
          type=pattern
        elif [[ "$1" = -P ]]; then
          type=postpattern
        else
          case "$type" in
          pattern)
	    if [[ $1 = (#b)(*)=(*) ]]; then
	      _patcomps[$match[1]]="=$match[2]=$func"
	    else
	      _patcomps[$1]="$func"
	    fi
            ;;
          postpattern)
	    if [[ $1 = (#b)(*)=(*) ]]; then
	      _postpatcomps[$match[1]]="=$match[2]=$func"
	    else
	      _postpatcomps[$1]="$func"
	    fi
            ;;
          *)
            if [[ "$1" = *\=* ]]; then
	      cmd="${1%%\=*}"
	      svc=yes
            else
	      cmd="$1"
	      svc=
            fi
            if [[ -z "$new" || -z "${_comps[$1]}" ]]; then
              _comps[$cmd]="$func"
	      [[ -n "$svc" ]] && _services[$cmd]="${1#*\=}"
	    fi
            ;;
          esac
        fi
        shift
      done
      ;;
    esac
  else
    # Handle the `-d' option, deleting.

    case "$type" in
    pattern)
      unset "_patcomps[$^@]"
      ;;
    postpattern)
      unset "_postpatcomps[$^@]"
      ;;
    key)
      # Oops, cannot do that yet.

      print -u2 "$0: cannot restore key bindings"
      return 1
      ;;
    *)
      unset "_comps[$^@]"
    esac
  fi
}

# Now we automatically make the definition files autoloaded.

typeset _i_wdirs _i_wfiles

_i_wdirs=()
_i_wfiles=()

autoload -Uz compaudit
if [[ -n "$_i_check" ]]; then
  typeset _i_q
  if ! eval compaudit; then
    if [[ -n "$_i_q" ]]; then
      if [[ "$_i_fail" = ask ]]; then
        if ! read -q \
"?zsh compinit: insecure $_i_q, run compaudit for list.
Ignore insecure $_i_q and continue [y] or abort compinit [n]? "; then
	  print -u2 "$0: initialization aborted"
          unfunction compinit compdef
          unset _comp_dumpfile _comp_secure compprefuncs comppostfuncs \
                _comps _patcomps _postpatcomps _compautos _lastcomp

          return 1
        fi
        _i_wfiles=()
	_i_wdirs=()
      else
        (( $#_i_wfiles )) && _i_files=( "${(@)_i_files:#(${(j:|:)_i_wfiles%.zwc})}"  )
        (( $#_i_wdirs ))  && _i_files=( "${(@)_i_files:#(${(j:|:)_i_wdirs%.zwc})/*}" )
      fi
    fi
    typeset -g _comp_secure=yes
  fi
fi

# Make sure compdump is available, even if we aren't going to use it.
autoload -Uz compdump compinstall

# If we have a dump file, load it.

_i_done=''

if [[ -f "$_comp_dumpfile" ]]; then
  if [[ -n "$_i_check" ]]; then
    IFS=$' \t' read -rA _i_line < "$_comp_dumpfile"
    if [[ _i_autodump -eq 1 && $_i_line[2] -eq $#_i_files &&
        $ZSH_VERSION = $_i_line[4] ]]
    then
      builtin . "$_comp_dumpfile"
      _i_done=yes
    fi
  else
    builtin . "$_comp_dumpfile"
    _i_done=yes
  fi
fi
if [[ -z "$_i_done" ]]; then
  typeset -A _i_test

  for _i_dir in $fpath; do
    [[ $_i_dir = . ]] && continue
    (( $_i_wdirs[(I)$_i_dir] )) && continue
    for _i_file in $_i_dir/^([^_]*|*~|*.zwc)(N); do
      _i_name="${_i_file:t}"
      (( $+_i_test[$_i_name] + $_i_wfiles[(I)$_i_file] )) && continue
      _i_test[$_i_name]=yes
      IFS=$' \t' read -rA _i_line < $_i_file
      _i_tag=$_i_line[1]
      shift _i_line
      case $_i_tag in
      (\#compdef)
	if [[ $_i_line[1] = -[pPkK](n|) ]]; then
	  compdef ${_i_line[1]}na "${_i_name}" "${(@)_i_line[2,-1]}"
	else
	  compdef -na "${_i_name}" "${_i_line[@]}"
	fi
	;;
      (\#autoload)
	autoload -Uz "$_i_line[@]" ${_i_name}
	[[ "$_i_line" != \ # ]] && _compautos[${_i_name}]="$_i_line"
	;;
      esac
    done
  done

  # If autodumping was requested, do it now.

  if [[ $_i_autodump = 1 ]]; then
    compdump
  fi
fi

# Rebind the standard widgets
for _i_line 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; do
  zle -C $_i_line .$_i_line _main_complete
done
zle -la menu-select && zle -C menu-select .menu-select _main_complete

# If the default completer set includes _expand, and tab is bound
# to expand-or-complete, rebind it to complete-word instead.
bindkey '^i' | IFS=$' \t' read -A _i_line
if [[ ${_i_line[2]} = expand-or-complete ]] &&
  zstyle -a ':completion:' completer _i_line &&
  (( ${_i_line[(i)_expand]} <= ${#_i_line} )); then
  bindkey '^i' complete-word
fi

unfunction compinit compaudit
autoload -Uz compinit compaudit

return 0