# 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