about summary refs log tree commit diff
path: root/Functions/Completion/init
blob: a40c5f61b582232e253d4058e90549799f1c038e (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
# Initialisation for new style completion. This mainly contains some helper
# function and aliases. Everything else is split into different files in this
# directory 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 two underscores (like `__setopt).
# The first line of these files will be read and has to say what should be
# done with its contents:
#
#   `#function <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 <name> is given
#
#   `#array <names ...>'
#     with a first line like this, the filename is taken as
#     the name of an array; when trying to generate matches
#     for the command <name>, the file will be sourced and
#     should define this array, the builtin `complist' will
#     then be called with the elements of this array as its
#     arguments; this is intended for simple definitions
#     for which you don't need a shell function
#
#   `#pattern-function <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
#
#   `#pattern-array <pattern>'
#     like `#pattern-function' but defining an array
#
#   `#key-function <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.
#
#   `#key-array <style> [ <key-sequence> ... ]
#     like `#key-function', but defining an array instead
#
#   `#helper'
#     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.


# An associative array for completions definitions. The keys of the entries
# are the names of the command, the values are names of functions or variables
# that are to be used to generate the matches.
# Pattern completions will be stored in an normal array named `patcomps'.
# Completion definitions bound directly to keys are stored in an assoc array
# named `keycomps'.

typeset -A comps
typeset -A keycomps


# This may be used to define completion handlers. The first argument is the
# name of the function or variable containing the definition, the other
# arguments are the command names for which this definition should be used.
# With only one argument the function/variable-name __$1 is used.
# If given the `-a' option, the function is defined as being autoloaded.

defcomp() {
  local name autol=''

  if [[ "$1" = -a ]]; then
    shift
    autol=yes
  fi
  if [[ $# -eq 1 ]]; then
    comps[$1]="__$1"
    [[ -z "$autol" ]] || autoload "__$1"
  else
    name="$1"
    shift
    for i; do
      comps[$i]="$name"
    done
    [[ -z "$autol" ]] || autoload "$name"
  fi
}


# Almost like `defcomp', but this always gets two arguments: the name of a
# variable or function describing what should be completed and the pattern
# that will be compared to the command names for which completion is attempted.

defpatcomp() {
  if [[ "$1" = -a ]]; then
    shift
    autoload "$1"
  fi
  if (( $+patcomps )) then
    patcomps=("$patcomps[@]" "$2 $1" )
  else
    patcomps=( "$2 $1" )
  fi
}


# This is used to define completion handlers directly bound to keys. The
# first argument is as for `defcomp', giving the handler. The second
# argument is the name of one of the built-in completion widgets. Any
# remaining arguments are used as key sequences to bind the widget.
# Typing that key sequence will complete the word the cursor is on
# according to the completion definition given and will behave as if the
# built-in completion widget was used.

defkeycomp() {
  local name

  if [[ "$1" = -a ]]; then
    shift
    autoload "$1"
    name="$1"
  elif [[ "${1[1]}" = ' ' ]]; then
    name="${1:t}"
  else
    name="$1"
  fi
  keycomps[$name]="$1"
  shift
  zle -C "$name" "$1" __main_key_complete
  shift
  while (( $# )); do
    bindkey "$1" "$name"
    shift
  done
}

# These can be used to easily save and restore the state of the special
# variables used by the completion code.

alias compsave='local _oprefix _oiprefix _oargv _ocurrent; \
                _oprefix="$PREFIX"; \
                _oiprefix="$IPREFIX"; \
                _oargv=( "$@" ); \
                _ocurrent="$CURRENT"'
alias compreset='PREFIX="$_oprefix"; \
                 IPREFIX="$_oiprefix"; \
                 argv=( "$_oargv[@]" ); \
		 CURRENT="$_ocur"'


# This is an easy way to get completion for sub-commands.

alias compsub='__normal "$@" || return 1'


# This searches $1 in the array for normal completions and calls the result.

compalso() {
  local tmp

  tmp="$comps[$1]"
  [[ -z "$tmp" ]] || callcomplete comps "$1" "$@"
}


# This generates matches. The first argument is the name of one of the
# arrays containing completion definitions. The second argument is the index
# into this array. The other arguments are the positional parameters to give
# to the completion function (containing the arguments from the command line).

callcomplete() {
  local file def

  # Get the definition from the array.

  eval "def=\$${1}[${2}]"

  # If the definition starts with a space then this means that we should
  # source a file to get the definition for an array.

  if [[ "$def[1]" = ' ' ]]; then
    # The definition starts with a space, so source the file and change
    # the definition.

    file="$def[2,-1]"
    builtin . "$file"
    def="${file:t}"
    eval "${1}[${2}]=$def"
  fi

  # Get rid of the array-name and -index.

  shift 2
  if [[ ${(P)+def} -eq 1 ]]; then
    # It is a parameter name, call complist directly.

    complist "${(@P)def}"
  else
    # Otherwise it's a function name, call this function.

    "$def" "$@"
  fi
}


# Now we make the files automatically autoloaded.

local dir file line func

for dir in $fpath; do
  [[ $dir = . ]] && continue
  for file in $dir/__*~*~(N); do
    read -rA line < $file
    func=$line[1]
    shift line
    if [[ $func = '#function' ]]; then
      defcomp -a ${file:t} "${line[@]}"
    elif [[ $func = '#array' ]]; then
      defcomp " $file" "${line[@]}"
    elif [[ $func = '#pattern-function' ]]; then
      defpatcomp -a ${file:t} "${line[@]}"
    elif [[ $func = '#pattern-array' ]]; then
      defcomp " $file" "${line[@]}"
    elif [[ $func = '#key-function' ]]; then
      defkeycomp -a "${file:t}" "${line[@]}"
    elif [[ $func = '#key-array' ]]; then
      defkeycomp " $file" "${line[@]}"
    elif [[ $func = '#helper' ]]; then
      autoload ${file:t}
    fi
  done
done


# Finally we make all this be called by changing the key bindings.

bindkey | while read -A line; do
            if [[ "$line[2]" = complete-word ||
	    	  "$line[2]" = delete-char-or-list ||
	    	  "$line[2]" = expand-or-complete ||
	    	  "$line[2]" = expand-or-complete-prefix ||
	    	  "$line[2]" = list-choices ||
	    	  "$line[2]" = menu-complete ||
	    	  "$line[2]" = menu-expand-or-complete ||
	    	  "$line[2]" = reverse-menu-complete ]]; then
              zle -C __complete_$line[2] $line[2] __main_complete
              bindkey "${line[1][2,-2]}" __complete_$line[2]
            fi
          done