diff options
Diffstat (limited to 'Completion/Core')
-rw-r--r-- | Completion/Core/.distfiles | 5 | ||||
-rw-r--r-- | Completion/Core/_approximate | 197 | ||||
-rw-r--r-- | Completion/Core/_comp_parts | 147 | ||||
-rw-r--r-- | Completion/Core/_compalso | 13 | ||||
-rw-r--r-- | Completion/Core/_complete | 52 | ||||
-rw-r--r-- | Completion/Core/_correct | 19 | ||||
-rw-r--r-- | Completion/Core/_expand | 149 | ||||
-rw-r--r-- | Completion/Core/_files | 26 | ||||
-rw-r--r-- | Completion/Core/_list | 61 | ||||
-rw-r--r-- | Completion/Core/_main_complete | 48 | ||||
-rw-r--r-- | Completion/Core/_match | 53 | ||||
-rw-r--r-- | Completion/Core/_multi_parts | 201 | ||||
-rw-r--r-- | Completion/Core/_normal | 54 | ||||
-rw-r--r-- | Completion/Core/_options | 5 | ||||
-rw-r--r-- | Completion/Core/_parameters | 8 | ||||
-rw-r--r-- | Completion/Core/_path_files | 311 | ||||
-rw-r--r-- | Completion/Core/_sep_parts | 171 | ||||
-rw-r--r-- | Completion/Core/_set_options | 7 | ||||
-rw-r--r-- | Completion/Core/_unset_options | 7 | ||||
-rw-r--r-- | Completion/Core/compdump | 89 | ||||
-rw-r--r-- | Completion/Core/compinit | 269 |
21 files changed, 1892 insertions, 0 deletions
diff --git a/Completion/Core/.distfiles b/Completion/Core/.distfiles new file mode 100644 index 000000000..ddf2a707e --- /dev/null +++ b/Completion/Core/.distfiles @@ -0,0 +1,5 @@ +DISTFILES_SRC=' + .distfiles + _compalso _files _main_complete _multi_parts _normal _path_files + _sep_parts compdump compinit +' diff --git a/Completion/Core/_approximate b/Completion/Core/_approximate new file mode 100644 index 000000000..1b40f7cbf --- /dev/null +++ b/Completion/Core/_approximate @@ -0,0 +1,197 @@ +#autoload + +# This code will try to correct the string on the line based on the +# strings generated for the context if `compconfig[correct]' is set. +# These corrected strings will be shown in a list and one can +# cycle through them as in a menucompletion or get the corrected prefix. +# +# Supported configuration keys: +# +# approximate_accept +# This 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 this +# key will be used. E.g. with `compconf approximate_accept=2n' two +# errors will be accepted, but if the user gives another number +# with the numeric argument, this will be prefered. Also, with +# `compconf approximate_accept=0n', normally no correction will be +# tried, but if a numeric argument is given, automatic correction +# will be used. On the other hand, if the string contains an `!' +# and a `n' or `N', correction is not attempted if a numeric +# argument is given. 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. Independent of the +# number of errors the user wants to accept, the code will allow +# only fewer errors than there are characters in the string from +# the line. +# +# approximate_original +# This value is used to determine if the original string should +# be included in the list (and thus be presented to the user when +# cycling through the corrections). If it is set to any non-empty +# value, the original string will be offered. If it contains the +# sub-string `last', the original string will appear as the last +# string when cycling through the corrections, otherwise it will +# appear as the first one (so that the command line does not +# change immediately). Also, if the value contains the sub-string +# `always', the original string will always be included, whereas +# normally it is included only if more than one possible +# correction was generated. +# +# approximate_prompt +# This can be set to a string that should be printed before the +# list of corrected strings when cycling through them. This string +# may contain the control sequences `%n', `%B', etc. known from +# the `-X' option of `compctl'. Also, the sequence `%e' will be +# replaced by the number of errors accepted to generate the +# corrected strings. +# +# approximate_insert +# If this is set to a string starting with `unambig', the code +# will try to insert a usable unambiguous string in the command +# line instead of always cycling through the corrected strings. +# If such a unambiguous string could be found, the original +# string is not used, independent of the setting of +# `approximate_original'. If no sensible string could be found, +# one can cycle through the corrected strings as usual. +# +# If any of these keys is not set, but the the same key with the +# prefix `correct' instead of `approximate' is set, that value will +# be used. + +local _comp_correct _correct_prompt comax +local cfgacc cfgorig cfgps cfgins + +# Only if all global matchers hav been tried. + +[[ compstate[matcher] -ne compstate[total_matchers] ]] && return 1 + +# We don't try correction if the string is too short. + +[[ "${#:-$PREFIX$SUFFIX}" -le 1 ]] && return 1 + +# Get the configuration values, using either the prefix `correct' or +# `approximate'. + +if [[ "$compstate[pattern_match]" = (|\**) ]]; then + cfgacc="${compconfig[approximate_accept]:-$compconfig[correct_accept]}" + cfgorig="${compconfig[approximate_original]:-$compconfig[correct_original]}" + cfgps="${compconfig[approximate_prompt]:-$compconfig[correct_prompt]}" + cfgins="${compconfig[approximate_insert]:-$compconfig[correct_insert]}" +else + cfgacc="$compconfig[correct_accept]" + cfgorig="$compconfig[correct_original]" + cfgps="$compconfig[correct_prompt]" + cfgins="$compconfig[correct_insert]" +fi + +# Get the number of errors to accept. + +if [[ "$cfgacc" = *[nN]* && NUMERIC -ne 1 ]]; then + # Stop if we also have a `!'. + + [[ "$cfgacc" = *\!* ]] && return 1 + + # Prefer the numeric argument if that has a sensible value. + + comax="$NUMERIC" +else + comax="${cfgacc//[^0-9]}" +fi + +# If the number of errors to accept is too small, give up. + +[[ "$comax" -lt 1 ]] && return 1 + +# 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() { + [[ "$*" != *-([a-zA-Z/]#|)U* && + "${#:-$PREFIX$SUFFIX}" -le _comp_correct ]] && return + + if [[ "$PREFIX" = \~*/* ]]; then + PREFIX="${PREFIX%%/*}/(#a${_comp_correct})${PREFIX#*/}" + else + PREFIX="(#a${_comp_correct})$PREFIX" + fi + if [[ -n "$_correct_prompt" ]]; then + builtin compadd -X "$_correct_prompt" -J _correct "$@" + else + builtin compadd -J _correct "$@" + fi +} + +compgen() { + [[ "$*" != *-([a-zA-Z/]#|)U* && + "${#:-$PREFIX$SUFFIX}" -le _comp_correct ]] && return + + if [[ "$PREFIX" = \~*/* ]]; then + PREFIX="${PREFIX%%/*}/(#a${_comp_correct})${PREFIX#*/}" + else + PREFIX="(#a${_comp_correct})$PREFIX" + fi + if [[ -n "$_correct_prompt" ]]; then + builtin compgen "$@" -X "$_correct_prompt" -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 + +_correct_prompt="${cfgps//\%e/1}" + +# We also need to set `extendedglob' and make the completion +# code behave as if globcomplete were set. + +setopt extendedglob + +[[ -z "$compstate[pattern_match]" ]] && compstate[pattern_match]='*' + +while [[ _comp_correct -le comax ]]; do + if _complete; then + if [[ "$cfgins" = unambig* && + "${#compstate[unambiguous]}" -ge "${#:-$PREFIX$SUFFIX}" ]]; then + compstate[pattern_insert]=unambiguous + elif [[ compstate[nmatches] -gt 1 || "$cfgorig" = *always* ]]; then + if [[ "$cfgorig" = *last* ]]; then + builtin compadd -U -V _correct_original -nQ - "$PREFIX$SUFFIX" + elif [[ -n "$cfgorig" ]]; then + builtin compadd -U -nQ - "$PREFIX$SUFFIX" + fi + + # If you always want to see the list of possible corrections, + # set `compstate[list]=list' here. + + compstate[force_list]=list + fi + compstate[matcher]="$compstate[total_matchers]" + unfunction compadd compgen + + return 0 + fi + + [[ "${#:-$PREFIX$SUFFIX}" -le _comp_correct+1 ]] && break + (( _comp_correct++ )) + + _correct_prompt="${cfgps//\%e/$_comp_correct}" +done + +compstate[matcher]="$compstate[total_matchers]" +unfunction compadd compgen + +return 1 diff --git a/Completion/Core/_comp_parts b/Completion/Core/_comp_parts new file mode 100644 index 000000000..7c24fd19d --- /dev/null +++ b/Completion/Core/_comp_parts @@ -0,0 +1,147 @@ +#autoload + +# This function can be used to separately complete parts of strings +# where each part may be one of a set of matches and different parts +# have different sets. +# Arguments are alternatingly arrays and separator strings. Arrays may +# be given by name or literally as words separated by white space in +# parentheses, e.g.: +# +# _comp_parts '(foo bar)' @ hosts +# +# This will make this function complete the strings in the array +# `friends'. If the string on the line contains a `@', the substring +# after it will be completed from the array `hosts'. Of course more +# arrays may be given, each preceded by another separator string. +# +# This function understands the `-J group', `-V group', and +# `-X explanation' options. +# +# This function does part of the matching itself and calls the functions +# `_match_test' and `_match_pattern' for this. + +local str arr sep test testarr tmparr prefix suffixes matchers autosuffix +local matchflags opt group expl + +# Test if we should use this function for the global matcher in use. + +_match_test _comp_parts || return + +# Get the options. + +group=() +expl=() +while getopts "J:V:X:" opt; do + case "$opt" in + [JV]) group=("-$opt" "$OPTARG");; + X) expl=(-X "$OPTARG");; + esac +done +shift OPTIND-1 + +# Get the string from the line. + +str="$PREFIX$SUFFIX" +prefix="" + +# Walk through the arguments to find the longest unambiguous prefix. + +while [[ $# -gt 1 ]]; do + # Get the next array and separator. + arr="$1" + sep="$2" + + if [[ "$arr[1]" == '(' ]]; then + tmparr=( ${=arr[2,-2]} ) + arr=tmparr + fi + # Is the separator on the line? + [[ "$str" != *${sep}* ]] && break + + # Build a pattern matching the possible matches and get all these + # matches in an array. + test="${str%%${sep}*}" + matchflags="" + _match_pattern _comp_parts test matchflags + test="${matchflags}${test}" + testarr=( "${(@M)${(@P)arr}:#${~test}*}" ) + + # If there are no matches we give up. If there is more than one + # match, this is the part we will complete. + (( $#testarr )) || return + [[ $#testarr -gt 1 ]] && break + + # Only one match, add it to the prefix and skip over it in `str', + # continuing with the next array and separator. + prefix="${prefix}${testarr[1]}${sep}" + str="${str#*${sep}}" + shift 2 +done + +# Get the array to work upon. +arr="$1" +if [[ "$arr[1]" == '(' ]]; then + tmparr=( ${=arr[2,-2]} ) + arr=tmparr +fi +if [[ $# -le 1 || "$str" != *${2}* ]]; then + # No more separators, build the matches. + matchflags="" + test="$str" + _match_pattern _comp_parts test matchflags + test="${matchflags}${test}" + testarr=( "${(@M)${(@P)arr}:#${~test}*}" ) +fi + +[[ $#testarr -eq 0 || ${#testarr[1]} -eq 0 ]] && return + +# Now we build the suffixes to give to the completion code. +shift +matchers=() +suffixes=("") +autosuffix=() + +while [[ $# -gt 0 && "$str" == *${1}* ]]; do + # Remove anything up to the the suffix. + str="${str#*${1}}" + + # Again, we get the string from the line up to the next separator + # and build a pattern from it. + if [[ $# -gt 2 ]]; then + test="${str%%${3}*}" + else + test="$str" + fi + matchflags="" + _match_pattern _comp_parts test matchflags + test="${matchflags}${test}" + + # We incrementally add suffixes by appending to them the seperators + # and the strings from the next array that match the pattern we built. + + arr="$2" + if [[ "$arr[1]" == '(' ]]; then + tmparr=( ${=arr[2,-2]} ) + arr=tmparr + fi + suffixes=("${^suffixes[@]}${1}${(@M)^${(@P)arr}:#${~test}*}") + + # We want the completion code to generate the most specific suffix + # for us, so we collect matching specifications that allow partial + # word matching before the separators on the fly. + matchers=("$matchers[@]" "r:|${1}=*") + shift 2 +done + +# If we were given at least one more separator we make the completion +# code offer it by appending it as a autoremovable suffix. +(( $# )) && autosuffix=(-qS "$1") + +# If we have collected matching specifications, we build an array +# from it that can be used as arguments to `compadd'. +[[ $#matchers -gt 0 ]] && matchers=(-M "$matchers") + +# Add the matches for each of the suffixes. +for i in "$suffixes[@]"; do + compadd "$group[@]" "$expl[@]" "$matchers[@]" "$autosuffix[@]" -p "$prefix" -s "$i" - "$testarr[@]" +done diff --git a/Completion/Core/_compalso b/Completion/Core/_compalso new file mode 100644 index 000000000..23a40e2d0 --- /dev/null +++ b/Completion/Core/_compalso @@ -0,0 +1,13 @@ +#autoload + +# This searches $1 in the array for normal completions and calls the result. +# It is used to include completions for another command or special context +# into the list generated by the calling function. +# For example the function for `-subscript-' could call this as in +# `_compalso -math- "$@"' to get the completions that would be generated +# for a mathematical context. + +local tmp + +tmp="$_comps[$1]" +[[ -z "$tmp" ]] || "$tmp" "$@" diff --git a/Completion/Core/_complete b/Completion/Core/_complete new file mode 100644 index 000000000..0f4d5ff4b --- /dev/null +++ b/Completion/Core/_complete @@ -0,0 +1,52 @@ +#autoload + +# Generate all possible completions. Note that this is not intended as +# a normal completion function, but as one possible value for the +# compconfig[completer] parameter. + +local comp name + +# 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 + (( compstate[nmatches] )) + 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 + +(( compstate[nmatches] )) diff --git a/Completion/Core/_correct b/Completion/Core/_correct new file mode 100644 index 000000000..35ab01cf1 --- /dev/null +++ b/Completion/Core/_correct @@ -0,0 +1,19 @@ +#autoload + +# This is mainly a wrapper around the more general `_approximate. +# By setting `compstate[pattern_match]' to something unequal to `*' and +# then calling `_approximate, we get only corrections, not all strings +# with the corrected prefix and something after it. +# +# Supported configuration keys are the same as for `_approximate', only +# starting with `correct'. + +local ret=1 opm="$compstate[pattern_match]" + +compstate[pattern_match]='-' + +_approximate && ret=0 + +compstate[pattern_match]="$opm" + +return ret diff --git a/Completion/Core/_expand b/Completion/Core/_expand new file mode 100644 index 000000000..9172b6cbf --- /dev/null +++ b/Completion/Core/_expand @@ -0,0 +1,149 @@ +#autoload + +# This completer function is intended to be used as the first completer +# function and allows one to say more explicitly when and how the word +# from the line should be expanded than expand-or-complete. +# This function will allow other completer functions to be called if +# the expansions done produce no result or do not change the original +# word from the line. +# +# Configuration keys: +# +# expand_substitute +# If this is unset or set to the empty string, the code will first +# try to expand all substitutions in the string (such as $(...) and +# ${...}). If this is set to an non-empty string it should be +# an expression usable inside a $[...] arithmetical expression. +# In this case, expansion of substitutions will be done if the +# expression evaluates to `1'. For example, with +# +# compconf expand_substitute='NUMERIC != 1' +# +# substitution will be performed only if given an explicit numeric +# argument other than `1', as by typing ESC 2 TAB. +# +# expand_glob +# If this is unset or set to an empty string, globbing will be +# attempted on the word resulting from substitution or the +# original string. The values accepted for this key are the same +# as for expand_substitute. +# +# expand_menu +# If this is unset or set to the empty string, the words resulting +# from expansion (if any) will simply be inserted in the ommand line, +# replacing the original string. However, if this key is set to an +# non-empty string, the user can cycle through the expansion as in +# a menucompletion. Unless the value contains the sub-string `only', +# the user will still be offered all expansions at once as one of +# the strings to insert in the command line. Also, if the value +# contains the sub-string `last', the string with all expansion will +# be offered first, whereas normally it is offered as the last string +# to insert. Finally, if the value contains the sub-string `sort', +# the expansions will be sorted alphabetically, normally they are +# kept in the order the expansion produced them in. +# +# expand_original +# If this is set to an non-empty string, the original string from the +# line will be included in the list of strings the user can cycle +# through as in a menucompletion. If the value contains the sub-string +# `last', the original string will appear as the last string, with +# other values it is inserted as the first one (so that the command +# line does not change immediatly). +# +# expand_prompt +# This may be set to a string that should be displayed before the +# possible expansions. This is given to the -X option and thus may +# contain the control sequences `%n', `%B', etc. Also, the sequence +# `%o' in this string will be replaced by the original string. + +local exp word="$PREFIX$SUFFIX" group=-V + +# Do this only for the first global matcher. + +[[ "$compstate[matcher]" -le 1 ]] || return 1 + +# In exp we will collect the expansion. + +exp=("$word") + +# First try substitution. That weird thing spanning multiple lines +# changes quoted spaces, tabs, and newlines into spaces. + +[[ -z "$compconfig[expand_substitute]" || + "${(e):-\$[$compconfig[expand_substitute]]}" -eq 1 ]] && + exp=( "${(e)exp//\\[ +]/ }" ) + +# If the array is empty, store the original string again. + +[[ -z "$exp" ]] && exp=("$word") + +# Now try globbing. + +[[ -z "$compconfig[expand_glob]" || + "${(e):-\$[$compconfig[expand_glob]]}" -eq 1 ]] && + exp=( ${~exp}(N) ) + +# If we don't have any expansions or only one and that is the same +# as the original string, we let other completers run. + +[[ $#exp -eq 0 || + ( $#exp -eq 1 && "$exp[1]" = "$word" ) ]] && return 1 + +# We have expansions, should we menucomplete them? + +if [[ -z "$compconfig[expand_menu]" ]]; then + + # No, so if the user only wants a list, we add the strings + # separately. Otherwise we add the whole array as one string, + # probably also adding the original string. + + if [[ -z "$compstate[insert]" ]]; then + compadd -U -V _expand -Q - "$exp[@]" + else + [[ -n "$compconfig[expand_original]" && + "$compconfig[expand_original]" != *last* ]] && + compadd -UnQ -V _expand_original - "$word" + + compadd -UQ -V _expand - "$exp" + + [[ -n "$compconfig[expand_original]" && + "$compconfig[expand_original]" = *last* ]] && + compadd -UnQ -V _expand_original - "$word" + + compstate[insert]=menu + fi +else + # Sorting? We just use a different group type then. + + [[ "$compconfig[expand_menu]" = *sort* ]] && group=-J + + # Now add the expansion string, probably also adding the original + # and/or the string containing all expanded string. + + [[ -n "$compconfig[expand_original]" && + "$compconfig[expand_original]" != *last* ]] && + compadd -UnQ -V _expand_original - "$word" + + [[ "$compconfig[expand_menu]" = *last* && + "$compconfig[expand_menu]" != *only* ]] && + compadd -UnQ -V _expand_all - "$exp" + + if [[ -z "$compconfig[expand_prompt]" ]]; then + compadd -UQ $group _expand - "$exp[@]" + else + compadd -UQ -X "${compconfig[expand_prompt]//\%o/$word}" \ + $group _expand - "$exp[@]" + fi + [[ "$compconfig[expand_menu]" != *last* && + "$compconfig[expand_menu]" != *only* ]] && + compadd -UnQ -V _expand_all - "$exp" + + [[ -n "$compconfig[expand_original]" && + "$compconfig[expand_original]" = *last* ]] && + compadd -UnQ -V _expand_original - "$word" + + compstate[insert]=menu +fi + +return 0 diff --git a/Completion/Core/_files b/Completion/Core/_files new file mode 100644 index 000000000..d2cce35e7 --- /dev/null +++ b/Completion/Core/_files @@ -0,0 +1,26 @@ +#autoload + +# Utility function for completing files of a given type or any file. +# In many cases you will want to call this one instead of _path_files(). + +local nm=$NMATCHES + +_path_files "$@" + +if [[ $# -ne 0 && -nmatches nm ]]; then + local opt opts + + # We didn't get any matches for those types of files described by + # the `-g' or `-/' option. Now we try it again accepting all files. + # First we get those options that we have to use even if then. If + # we find out that the `-f' option was given, we already accepted + # all files and give up immediatly. + + opts=() + while getopts "P:S:W:F:J:V:X:f/g:" opt; do + [[ "$opt" = f ]] && return + [[ "$opt" = [PSWFJVX] ]] && opts=("$opts[@]" "-$opt" "$OPTARG") + done + + _path_files "$opts[@]" +fi diff --git a/Completion/Core/_list b/Completion/Core/_list new file mode 100644 index 000000000..099c6bc7b --- /dev/null +++ b/Completion/Core/_list @@ -0,0 +1,61 @@ +#autoload + +# This completer function makes the other completer functions used +# insert possible completions only after once the list has been +# shown. +# +# Configuration keys: +# +# list_condition +# If this key is unset or set to the empty string, this completer +# will delay the insertion of matches unconditionally. However, +# if this value is set, it should be set to an expression usable +# inside a $[...] arithmetical expression. In this case, delaying +# will be done if the expression evaluates to `1'. +# For example, with +# +# compconf list_condition='NUMERIC != 1' +# +# delaying will be done only if given an explicit numeric argument +# other than `1'. +# +# list_word +# To find out if only listing should be done, the code normally +# compares the contents of the line with the contents the line +# had at the time of the last invocation. If this key is set to +# an non-empty string comparison is done using only the current +# word. So if it is set, attempting completion on a word equal +# to the one completion was called on the last time will not +# delay the generation of matches. + +local pre suf + +# Get the strings to compare. + +if [[ -z "$compconfig[list_word]" ]]; then + pre="$HISTNO$LBUFFER" + suf="$RBUFFER" +else + pre="$PREFIX" + suf="$SUFFIX" +fi + +# Should we only show a list now? + +if [[ ( -z "$compconfig[list_condition]" || + "${(e):-\$[$compconfig[expand_glob]]}" -eq 1 ) && + ( "$pre" != "$_list_prefix" || "$suf" != "$_list_suffix" ) ]]; then + + # Yes. Tell the completion code about it and save the new values + # to compare the next time. + + compstate[insert]='' + compstate[list]=list + compstate[force_list]=yes + _list_prefix="$pre" + _list_suffix="$suf" +fi + +# We always return one, because we don't really do any work here. + +return 1 diff --git a/Completion/Core/_main_complete b/Completion/Core/_main_complete new file mode 100644 index 000000000..c7f5a5a96 --- /dev/null +++ b/Completion/Core/_main_complete @@ -0,0 +1,48 @@ +#autoload + +# The main loop of the completion code. This is what is called when +# completion is attempted from the command line. +# The completion code gives us the special variables and the arguments +# from the command line are given as positional parameters. + +local comp name + +setopt localoptions nullglob rcexpandparam globdots +unsetopt markdirs globsubst shwordsplit nounset + +# 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 we use the `_normal function. + +if [[ $CONTEXT == argument || $CONTEXT == command ]]; then + _normal "$@" +else + # Let's see if we have a special completion definition for the other + # possible contexts. + + comp='' + + case $CONTEXT in + redirect) comp="$_comps[-redirect-]";; + math) comp="$_comps[-math-]";; + subscript) comp="$_comps[-subscript-]";; + value) comp="$_comps[-value-]";; + condition) comp="$_comps[-condition-]";; + esac + + # If not, we use default completion, if any. + + [[ -z "$comp" ]] && comp="$_comps[-default-]" + [[ -z "$comp" ]] || "$comp" "$@" +fi diff --git a/Completion/Core/_match b/Completion/Core/_match new file mode 100644 index 000000000..3c639935c --- /dev/null +++ b/Completion/Core/_match @@ -0,0 +1,53 @@ +#autoload + +# This is intended to be used as a completer function after the normal +# completer as in: `compconf completer=_complete:_match'. +# It temporarily switches on pattern matching, allowing you to try +# completion on patterns without having to setopt glob_complete. +# +# Note, however, that this is only really useful if you don't use the +# expand-or-complete function because otherwise the pattern will +# be expanded using globbing. +# +# Configuration key used: +# +# match_original +# If this is set to a `only', pattern matching will only be tried +# with the string from the line. If it is set to any other non-empty +# string, the original pattern will be tried first and if that yields +# no completions, matching will be tried again with a `*' inserted +# at the cursor position. If this key is not set or set to an empty +# string, matching will only be attempted with the `*' inserted. + +local tmp opm="$compstate[pattern_match]" ret=0 + +# Do nothing if we don't have a pattern or there are still global +# match specifications to try. + +tmp="${${:-$PREFIX$SUFFIX}#[~=]}" +[[ "$tmp:q" = "$tmp" || + compstate[matcher] -ne compstate[total_matchers] ]] && return 1 + +# Try completion without inserting a `*'? + +if [[ -n "$compconfig[match_original]" ]]; then + compstate[matcher]=-1 + compstate[pattern_match]='-' + _complete && ret=1 + compstate[pattern_match]="$opm" + compstate[matcher]="$compstate[total_matchers]" + + (( ret )) && return 0 +fi + +# No completion with inserting `*'? + +[[ "$compconfig[match_original]" = only ]] && return 1 + +compstate[matcher]=-1 +compstate[pattern_match]='*' +_complete && ret=1 +compstate[pattern_match]="$opm" +compstate[matcher]="$compstate[total_matchers]" + +return 1-ret diff --git a/Completion/Core/_multi_parts b/Completion/Core/_multi_parts new file mode 100644 index 000000000..1f51d2f6d --- /dev/null +++ b/Completion/Core/_multi_parts @@ -0,0 +1,201 @@ +#autoload + +# This gets two arguments, a separator (which should be only one +# character) and an array. As usual, the array may be given by it's +# name or literal as in `(foo bar baz)' (words separated by spaces in +# parentheses). +# The parts of words from the array that are separated by the +# separator character are then completed independently. + +local sep matches patstr orig matchflags pref i tmp1 tmp2 nm +local group expl + +_match_test _multi_parts || return 1 + +# Save the current number of matches to be able to return if we added +# matches or not. + +nm=$compstate[nmatches] + +# Get the options. + +group=() +expl=() +while getopts "J:V:X:" opt; do + case "$opt" in + [JV]) group=("-$opt" "$OPTARG");; + X) expl=(-X "$OPTARG");; + esac +done +shift OPTIND-1 + +# Get the arguments, first the separator, then the array. The array is +# stored in `matches'. Further on this array will always contain those +# words from the original array that still match everything we have +# tried to match while we walk through the string from the line. + +sep="$1" +if [[ "${2[1]}" = '(' ]]; then + matches=( ${2[2,-2]} ) +else + matches=( "${(@P)2}" ) +fi + +# Now build the pattern from what we have on the line. We also save +# the original string in `orig'. The `eval' is used to replace our +# separator character by `*<sep>'. + +if [[ -o globcomplete ]]; then + patstr="${PREFIX}*${SUFFIX}*" +else + patstr="${PREFIX:q}*${SUFFIX:q}*" +fi +orig="${PREFIX}${SUFFIX}" + +matchflags="" +_match_pattern _path_files patstr matchflags +[[ -n "$_comp_correct" ]] && matchflags="$matchflags(#a$_comp_correct)" + +patstr="${${patstr//$sep/*$sep}//\*##/*}" +#eval patstr\="\$patstr:gs-${sep}-\*${sep}-:gs/\*\*/\*/" + +# First we will skip over those parts of the matches for which we have +# exact substrings on the line. In `pref' we will build the +# unambiguous prefix string. + +pref='' +while [[ "$orig" = *${sep}* ]] do + + # First build the pattern to use, then collect all strings from + # `matches' that match the prefix we have and the exact substring in + # the array `tmp1'. + + pat="${${${patstr#*${sep}}%${sep}*}//\*/[^${sep}]#}${patstr##*${sep}}" + tmp1=( "${(@M)matches:#${~matchflags}${orig%%${sep}*}${sep}${~pat}}" ) + + # If there are no words matching the exact substring, stop. + + (( $#tmp1 )) || break + + # Otherwise add the part to the prefix, remove it from the matches + # (which will also remove all words not matching the string at all), + # and set `patstr' and `orig' to the next component. + + pref="$pref${orig%%${sep}*}${sep}" + matches=( "${(@)${(@)matches#${orig%%${sep}*}${sep}}:#}" ) + orig="${orig#*${sep}}" + patstr="${patstr#*${sep}}" +done + +# Now we get all the words that still match in `tmp1'. + +if [[ "$patstr" = *${sep}* ]]; then + tmp1="${patstr%${sep}*}${sep}" + pat="${tmp1//\*/[^${sep}]#}${patstr##*${sep}}" +else + pat="$patstr" +fi +tmp1=( "${(@M)matches:#${~matchflags}${~pat}}" ) + +if (( $#tmp1 )); then + + # There are words that are matched, put them int `matches' and then + # move all unambiguous components from the beginning into `pref'. + + matches=( "$tmp1[@]" ) + while [[ "$matches[1]" = *${sep}* ]]; do + + # We just take the first component of the first match and see if + # there are other matches with a different prefix (these are + # collected in `tmp2'). If there are any, we give up. + + tmp1="${matches[1]%%${sep}*}${sep}" + tmp2=( "${(@)matches:#${tmp1}*}" ) + (( $#tmp2 )) && break + + # All matches have the same prefix, but it into `pref' and remove + # it from the matches. + + pref="$pref$tmp1" + matches=( "${(@)${(@)matches#$tmp1}:#}" ) + + if [[ "$orig" = *${sep}* ]]; then + orig="${orig#*${sep}}" + else + orig='' + fi + done + + # Now we can tell the completion code about the things we + # found. Strings that have a separator will be added with a suffix. + + if [[ -z "$orig" && "$PREFIX$SUFFIX" != "$pref$orig" ]]; then + compadd -QU "$group[@]" "$expl[@]" -i "$IPREFIX" -S '' - "${pref}${orig}" + elif [[ $compstate[insert] = *menu ]]; then + for i in "$matches[@]" ; do + if [[ "$i" = *${sep}* ]]; then + compadd -U "$group[@]" "$expl[@]" -i "$IPREFIX" \ + -p "$pref" -qS "$sep" - "${i%%${sep}*}" + else + compadd -U "$group[@]" "$expl[@]" -i "$IPREFIX" \ + -p "$pref" - "${i%%${sep}*}" + fi + done + else + for i in "$matches[@]" ; do + if [[ "$i" = *${sep}* ]]; then + compadd -U -i "$IPREFIX" -p "$pref" -s "${sep}${i#*${sep}}" \ + "$group[@]" "$expl[@]" -M "r:|${sep}=*" - "${i%%${sep}*}" + else + compadd -U "$group[@]" "$expl[@]" -i "$IPREFIX" -p "$pref" - "$i" + fi + done + fi +elif [[ "$patstr" = *${sep}* ]]; then + + # We had no words matching the string from the line. But we want to + # be friendly and at least expand the prefix as far as we can. So we + # will loop through the rest of the string from the line and test + # the components one by one. + + while [[ "$patstr" = *${sep}* ]]; do + + # First we get all words matching at least this component in + # `tmp1'. If there are none, we give up. + + tmp1=( "${(@M)matches:#${~matchflags}${~patstr%%${sep}*}${sep}*}" ) + (( $#tmp1 )) || break + + # Then we check if there are words that have a different prefix. + + tmp2=( "${(@)tmp1:#${tmp1[1]%%${sep}*}${sep}*}" ) + if (( $#tmp2 )); then + + # There are words with another prefix, so we have found an + # ambiguous component. So we just give all possible prefixes to + # the completion code together with our prefix and the rest of + # the string from the line as the suffix. + + compadd -U "$group[@]" "$expl[@]" -S '' -i "$IPREFIX" -p "$pref" \ + -s "${sep}${orig#*${sep}}" - "${(@)matches%%${sep}*}" + return 0 + fi + + # All words have the same prefix, so add it to `pref' again and + # try the next component. + + pref="$pref${tmp1[1]%%${sep}*}${sep}" + matches=( "${(@)matches#${tmp1[1]%%${sep}*}${sep}}" ) + orig="${orig#*${sep}}" + patstr="${patstr#*${sep}}" + done + + # Finally, add the unambiguous prefix and the rest of the string + # from the line. + + compadd -U "$group[@]" "$expl[@]" -S '' -i "$IPREFIX" -p "$pref" - "$orig" +fi + +# This sets the return value to indicate that we added matches (or not). + +[[ nm -ne compstate[nmatches] ]] diff --git a/Completion/Core/_normal b/Completion/Core/_normal new file mode 100644 index 000000000..19da6d79b --- /dev/null +++ b/Completion/Core/_normal @@ -0,0 +1,54 @@ +#autoload + +local comp cmd1 cmd2 pat val name + +# Completing in command position? If not we set up `cmd1' and `cmd2' as +# two strings we have search in the completion definition arrays (e.g. +# a path and the last path name component). + +if [[ $CONTEXT == command ]]; then + comp="$_comps[-command-]" + [[ -z "$comp" ]] || "$comp" "$@" + return +elif [[ "$COMMAND[1]" == '=' ]]; then + eval cmd1\=$COMMAND + cmd2="$COMMAND[2,-1]" +elif [[ "$COMMAND" == */* ]]; then + cmd1="$COMMAND" + cmd2="${COMMAND:t}" +else + cmd1="$COMMAND" + eval cmd2=$(whence -p $COMMAND) +fi + +# See if there are any matching pattern completions. + +for i in "$_patcomps[@]"; do + pat="${i% *}" + val="${i#* }" + if [[ "$cmd1" == $~pat || "$cmd2" == $~pat ]]; then + "$val" "$@" + if (( $+_compskip )); then + unset _compskip + return + fi + fi +done + +# Now look up the two names in the normal completion array. + +name="$cmd1" +comp="$_comps[$cmd1]" + +if [[ -z "$comp" ]]; then + name="$cmd2" + comp="$_comps[$cmd2]" +fi + +# And generate the matches, probably using default completion. + +if [[ -z "$comp" ]]; then + name=-default- + comp="$_comps[-default-]" +fi +[[ -z "$comp" ]] || "$comp" "$@" diff --git a/Completion/Core/_options b/Completion/Core/_options new file mode 100644 index 000000000..0a852e6ce --- /dev/null +++ b/Completion/Core/_options @@ -0,0 +1,5 @@ +#autoload + +# This should be used to complete all option names. + +compgen "$@" -M 'L:|[nN][oO]= M:_= M:{A-Z}={a-z}' -o diff --git a/Completion/Core/_parameters b/Completion/Core/_parameters new file mode 100644 index 000000000..0e8c548f7 --- /dev/null +++ b/Completion/Core/_parameters @@ -0,0 +1,8 @@ +#autoload + +# This should be used to complete parameter names if you need some of the +# extra options of compadd. It first tries to complete only non-local +# parameters. All arguments are given to compadd. + +compadd "$@" - "${(@)${(@)${(@)${(@f)$(typeset)}:#*local *\=*}%%\=*}##* }" || + compadd "$@" - "${(@)${(@)${(@f)$(typeset)}%%\=*}##* }" diff --git a/Completion/Core/_path_files b/Completion/Core/_path_files new file mode 100644 index 000000000..83b6e8a09 --- /dev/null +++ b/Completion/Core/_path_files @@ -0,0 +1,311 @@ +#autoload + +# Utility function for in-path completion. +# Supported arguments are: `-f', `-/', `-g <patterns>', `-J <group>', +# `-V <group>', `-W paths', `-X explanation', and `-F <ignore>'. All but +# the last have the same syntax and meaning as for `complist'. The +# `-F <ignore>' option may be used to give a list of suffixes either by +# giving the name of an array or literally by giving them in a string +# surrounded by parentheses. Files with one of the suffixes thus given +# are treated like files with one of the suffixes in the `fignore' array +# in normal completion. +# +# This function uses the helper functions `_match_test' and `_match_pattern'. + +# First see if we should generate matches for the global matcher in use. + +_match_test _path_files || return + +# Yes, so... + +local nm prepaths str linepath realpath donepath patstr prepath testpath rest +local tmp1 collect tmp2 suffixes i ignore matchflags opt group sopt pats gopt +local addpfx addsfx expl + +setopt localoptions nullglob rcexpandparam globdots extendedglob +unsetopt markdirs globsubst shwordsplit nounset + +prepaths=('') +ignore=() +group=() +sopt='-' +gopt='' +pats=() +addpfx=() +addsfx=() +expl=() + +# Get the options. + +while getopts "P:S:W:F:J:V:X:f/g:" opt; do + case "$opt" in + P) addpfx=(-P "$OPTARG") + ;; + S) addsfx=(-S "$OPTARG") + ;; + W) tmp1="$OPTARG" + if [[ "$tmp1[1]" = '(' ]]; then + prepaths=( ${^=tmp1[2,-2]}/ ) + else + prepaths=( ${(P)=${tmp1}} ) + (( ! $#prepaths )) && prepaths=( ${tmp1}/ ) + fi + (( ! $#prepaths )) && prepaths=( '' ) + ;; + F) tmp1="$OPTARG" + if [[ "$tmp1[1]" = '(' ]]; then + ignore=( ${^=tmp1[2,-2]}/ ) + else + ignore=( ${(P)${tmp1}} ) + fi + (( $#ignore )) && ignore=(-F "( $ignore )") + ;; + [JV]) group=("-$opt" "$OPTARG") + ;; + X) expl=(-X "$OPTARG") + ;; + f) sopt="${sopt}f" + pats=("$pats[@]" '*') + ;; + /) sopt="${sopt}/" + pats=("$pats[@]" '*(-/)') + ;; + g) gopt='-g' + pats=("$pats[@]" ${=OPTARG}) + ;; + esac +done + +# If we were given no file selection option, we behave as if we were given +# a `-f'. + +if [[ "$sopt" = - ]]; then + if [[ -z "$gopt" ]]; then + sopt='-f' + pats=('*') + else + unset sopt + fi +fi + +# str holds the whole string from the command line with a `*' between +# the prefix and the suffix. + +str="${PREFIX:q}*${SUFFIX:q}" + +# If the string began with a `~', the quoting turned this into `\~', +# remove the slash. + +[[ "$str" = \\\~* ]] && str="$str[2,-1]" + +# We will first try normal completion called with `complist', but only if we +# weren't given a `-F' option. + +if (( ! $#ignore )); then + # First build an array containing the `-W' option, if there is any and we + # want to use it. We don't want to use it if the string from the command line + # is a absolute path or relative to the current directory. + + if [[ -z "$tmp1[1]" || "$str[1]" = [~/] || "$str" = (.|..)/* ]]; then + tmp1=() + else + tmp1=(-W "( $prepaths )") + fi + + # Now call complist. + + nm=$NMATCHES + if [[ -z "$gopt" ]]; then + complist "$addpfx[@]" "$addsfx[@]" "$group[@]" "$expl[@]" "$tmp1[@]" $sopt + else + complist "$addpfx[@]" "$addsfx[@]" "$group[@]" "$expl[@]" "$tmp1[@]" $sopt -g "$pats" + fi + + # If this generated any matches, we don't want to do in-path completion. + + [[ -nmatches nm ]] || return + + # No `-F' option, so we want to use `fignore'. + + ignore=(-F fignore) +fi + +# Now let's have a closer look at the string to complete. + +if [[ "$str[1]" = \~ ]]; then + # It begins with `~', so remember anything before the first slash to be able + # to report it to the completion code. Also get an expanded version of it + # (in `realpath'), so that we can generate the matches. Then remove that + # prefix from the string to complete, set `donepath' to build the correct + # paths and make sure that the loop below is run only once with an empty + # prefix path by setting `prepaths'. + + linepath="${str%%/*}/" + eval realpath\=$linepath + str="${str#*/}" + donepath='' + prepaths=( '' ) +else + # If the string does not start with a `~' we don't remove a prefix from the + # string. + + liniepath='' + realpath='' + + if [[ "$str[1]" = / ]]; then + # If it is a absolut path name, we remove the first slash and put it in + # `donepath' meaning that we treat it as the path that was already handled. + # Also, we don't use the paths from `-W'. + + str="$str[2,-1]" + donepath='/' + prepaths=( '' ) + else + # The common case, we just use the string as it is, unless it begins with + # `./' or `../' in which case we don't use the paths from `-W'. + + [[ "$str" = (.|..)/* ]] && prepaths=( '' ) + donepath='' + fi +fi + +# First we skip over all pathname components in `str' which really exist in +# the file-system, so that `/usr/lib/l<TAB>' doesn't offer you `lib' and +# `lib5'. Pathname components skipped this way are taken from `str' and added +# to `donepath'. + +while [[ "$str" = */* ]] do + [[ -e "$realpath$donepath${str%%/*}" ]] || break + donepath="$donepath${str%%/*}/" + str="${str#*/}" +done + +# Now build the glob pattern by calling `_match_pattern'. +patstr="$str" +matchflags="" +_match_pattern _path_files patstr matchflags + +# We almost expect the pattern to have changed `..' into `*.*.', `/.' into +# `/*.', and probably to contain two or more consecutive `*'s. Since these +# have special meaning for globbing, we remove them. But before that, we +# add the pattern for matching any characters before a slash. + +patstr="$patstr:gs-/-*/-:gs/*.*.//:gs-/*.-/.-:gs/**/*/" + +# Finally, generate the matches. First we loop over all the paths from `-W'. +# Note that in this loop `str' is used as a modifyable version of `patstr' +# and `testpath' is a modifyable version of `donepath'. + +for prepath in "$prepaths[@]"; do + str="$patstr" + testpath="$donepath" + + # The second loop tests the components of the path in `str' to get the + # possible matches. + + while [[ "$str" = */* ]] do + # `rest' is the pathname after the first slash that is left. In `tmp1' + # we get the globbing matches for the pathname component currently + # handled. + + rest="${str#*/}" + tmp1="${prepath}${realpath}${testpath}${~matchflags}${str%%/*}(-/)" + tmp1=( $~tmp1 ) + + if [[ $#tmp1 -eq 0 ]]; then + # If this didn't produce any matches, we don't need to test this path + # any further, so continue with the next `-W' path, if any. + + continue 2 + elif [[ $#tmp1 -gt 1 ]]; then + # If it produced more than one match, we want to remove those which + # don't have possible following pathname components matching the + # rest of the string we are completing. (The case with only one + # match is handled below.) + # In `collect' we will collect those of the produced pathnames that + # have a matching possible path-suffix. In `suffixes' we build an + # array containing strings build from the rest of the string to + # complete and the glob patterns we were given as arguments. + + collect=() + suffixes=( $rest$^pats ) + suffixes=( "${(@)suffixes:gs.**.*.}" ) + + # In the loop the prefixes from the `tmp1' array produced above and + # the suffixes we just built are used to produce possible matches + # via globbing. + + for i in $tmp1; do + tmp2=( ${~i}/${~matchflags}${~suffixes} ) + [[ $#tmp2 -ne 0 ]] && collect=( $collect $i ) + done + + # If this test showed that none of the matches from the glob in `tmp1' + # has a possible sub-path matching what's on the line, we give up and + # continue with the next `-W' path. + + if [[ $#collect -eq 0 ]]; then + continue 2 + elif [[ $#collect -ne 1 ]]; then + # If we have more than one possible match, this means that the + # pathname component currently handled is ambiguous, so we give + # it to the completion code. + # First we build the full path prefix in `tmp1'. + + tmp1="$prepath$realpath$testpath" + + # Now produce all matching pathnames in `collect'. + + collect=( ${~collect}/${~matchflags}${~suffixes} ) + + # And then remove the common path prefix from all these matches. + + collect=( ${collect#$tmp1} ) + + # Finally, we add all these matches with the common (unexpanded) + # pathprefix (the `-p' option), the path-prefix (the `-W' option) + # to allow the completion code to test file type, and the path- + # suffix (the `-s' option). We also tell the completion code that + # these are file names and that `fignore' should be used as usual + # (the `-f' and `-F' options). + + for i in $collect; do + compadd "$addpfx[@]" "$addsfx[@]" "$group[@]" "$expl[@]" -p "$linepath$testpath" -W "$tmp1" -s "/${i#*/}" -f "$ignore[@]" - "${i%%/*}" + done + + # We have just finished handling all the matches from above, so we + # can continue with the next `-W' path. + + continue 2 + fi + # We reach this point if only one of the path prefixes in `tmp1' + # has a existing path-suffix matching the string from the line. + # In this case we accept this match and continue with the next + # path-name component. + + tmp1=( "$collect[1]" ) + fi + # This is also reached if the first globbing produced only one match + # in this case we just continue with the next pathname component, too. + + tmp1="$tmp1[1]" + testpath="$testpath${tmp1##*/}/" + str="$rest" + done + + # We are here if all pathname components except the last one (which is still + # not tested) are unambiguous. So we add matches with the full path prefix, + # no path suffix, the `-W' we are currently handling, all the matches we + # can produce in this directory, if any. + + tmp1="$prepath$realpath$testpath" + suffixes=( $str$^pats ) + suffixes=( "${(@)suffixes:gs.**.*.}" ) + tmp2=( ${~tmp1}${~matchflags}${~suffixes} ) + if [[ $#tmp2 -eq 0 && "$sopt" = */* ]]; then + [[ "$testpath[-1]" = / ]] && testpath="$testpath[1,-2]" + compadd "$addpfx[@]" "$addsfx[@]" "$group[@]" "$expl[@]" -f - "$linepath$testpath" + else + compadd "$addpfx[@]" "$addsfx[@]" "$group[@]" "$expl[@]" -p "$linepath$testpath" -W "$prepath$realpath$testpath" -f "$ignore[@]" - ${(@)tmp2#$tmp1} + fi +done diff --git a/Completion/Core/_sep_parts b/Completion/Core/_sep_parts new file mode 100644 index 000000000..c1cda2b9a --- /dev/null +++ b/Completion/Core/_sep_parts @@ -0,0 +1,171 @@ +#autoload + +# This function can be used to separately complete parts of strings +# where each part may be one of a set of matches and different parts +# have different sets. +# Arguments are alternatingly arrays and separator strings. Arrays may +# be given by name or literally as words separated by white space in +# parentheses, e.g.: +# +# _sep_parts '(foo bar)' @ hosts +# +# This will make this function complete the strings in the array +# `friends'. If the string on the line contains a `@', the substring +# after it will be completed from the array `hosts'. Of course more +# arrays may be given, each preceded by another separator string. +# +# This function understands the `-J group', `-V group', and +# `-X explanation' options. +# +# This function does part of the matching itself and calls the functions +# `_match_test' and `_match_pattern' for this. + +local str arr sep test testarr tmparr prefix suffixes matchers autosuffix +local matchflags opt group expl nm=$compstate[nmatches] + +# Test if we should use this function for the global matcher in use. + +_match_test _sep_parts || return 1 + +# Get the options. + +group=() +expl=() +while getopts "J:V:X:" opt; do + case "$opt" in + [JV]) group=("-$opt" "$OPTARG");; + X) expl=(-X "$OPTARG");; + esac +done +shift OPTIND-1 + +# Get the string from the line. + +str="$PREFIX$SUFFIX" +[[ $#compstate[pattern_match] -ne 0 ]] || str="$str:q" +prefix="" + +# Walk through the arguments to find the longest unambiguous prefix. + +while [[ $# -gt 1 ]]; do + # Get the next array and separator. + arr="$1" + sep="$2" + + if [[ "$arr[1]" == '(' ]]; then + tmparr=( ${=arr[2,-2]} ) + arr=tmparr + fi + # Is the separator on the line? + [[ "$str" != *${sep}* ]] && break + + # Build a pattern matching the possible matches and get all these + # matches in an array. + + test="${str%%${sep}*}" + [[ -n "$_comp_correct" && $#test -le _comp_correct ]] && return 1 + + matchflags="" + _match_pattern _sep_parts test matchflags + [[ -n "$_comp_correct" ]] && matchflags="$matchflags(#a$_comp_correct)" + + test="${matchflags}${test}" + testarr=( "${(@M)${(@P)arr}:#${~test}*}" ) + testarr=( "${(@)testarr:#}" ) + + # If there are no matches we give up. If there is more than one + # match, this is the part we will complete. + (( $#testarr )) || return 1 + [[ $#testarr -gt 1 ]] && break + + # Only one match, add it to the prefix and skip over it in `str', + # continuing with the next array and separator. + prefix="${prefix}${testarr[1]}${sep}" + str="${str#*${sep}}" + shift 2 +done + +# Get the array to work upon. +arr="$1" +if [[ "$arr[1]" == '(' ]]; then + tmparr=( ${=arr[2,-2]} ) + arr=tmparr +fi +if [[ $# -le 1 || "$str" != *${2}* ]]; then + # No more separators, build the matches. + + test="$str" + [[ -n "$_comp_correct" && $#test -le _comp_correct ]] && return 1 + + matchflags="" + _match_pattern _sep_parts test matchflags + [[ -n "$_comp_correct" ]] && matchflags="$matchflags(#a$_comp_correct)" + + test="${matchflags}${test}" + testarr=( "${(@M)${(@P)arr}:#${~test}*}" ) + testarr=( "${(@)testarr:#}" ) +fi + +[[ $#testarr -eq 0 || ${#testarr[1]} -eq 0 ]] && return 1 + +# Now we build the suffixes to give to the completion code. +shift +matchers=() +suffixes=("") +autosuffix=() + +while [[ $# -gt 0 && "$str" == *${1}* ]]; do + # Remove anything up to the the suffix. + str="${str#*${1}}" + + # Again, we get the string from the line up to the next separator + # and build a pattern from it. + if [[ $# -gt 2 ]]; then + test="${str%%${3}*}" + else + test="$str" + fi + + [[ -n "$_comp_correct" && $#test -le _comp_correct ]] && return 1 + + matchflags="" + _match_pattern _sep_parts test matchflags + [[ -n "$_comp_correct" ]] && matchflags="$matchflags(#a$_comp_correct)" + test="${matchflags}${test}" + + # We incrementally add suffixes by appending to them the seperators + # and the strings from the next array that match the pattern we built. + + arr="$2" + if [[ "$arr[1]" == '(' ]]; then + tmparr=( ${=arr[2,-2]} ) + arr=tmparr + fi + tmparr=( "${(@M)${(@P)arr}:#${~test}*}" ) + tmparr=( "${(@)tmparr:#}" ) + suffixes=("${(@)^suffixes[@]}${1}${(@)^tmparr}") + + # We want the completion code to generate the most specific suffix + # for us, so we collect matching specifications that allow partial + # word matching before the separators on the fly. + matchers=("$matchers[@]" "r:|${1:q}=*") + shift 2 +done + +# If we were given at least one more separator we make the completion +# code offer it by appending it as a autoremovable suffix. +(( $# )) && autosuffix=(-qS "$1") + +# If we have collected matching specifications, we build an array +# from it that can be used as arguments to `compadd'. +[[ $#matchers -gt 0 ]] && matchers=(-M "$matchers") + +# Add the matches for each of the suffixes. +for i in "$suffixes[@]"; do + compadd -U "$group[@]" "$expl[@]" "$matchers[@]" "$autosuffix[@]" \ + -i "$IPREFIX" -p "$prefix" -s "$i" - "$testarr[@]" +done + +# This sets the return value to indicate that we added matches (or not). + +[[ nm -ne compstate[nmatches] ]] diff --git a/Completion/Core/_set_options b/Completion/Core/_set_options new file mode 100644 index 000000000..5f634a2cd --- /dev/null +++ b/Completion/Core/_set_options @@ -0,0 +1,7 @@ +#autoload + +# Complete all set options. This relies on `_main_complete' to store the +# names of the options that were set when it was called in the array +# `_set_options'. + +compadd "$@" -M 'L:|[nN][oO]= M:_= M:{A-Z}={a-z}' - $=_set_options diff --git a/Completion/Core/_unset_options b/Completion/Core/_unset_options new file mode 100644 index 000000000..c5150c2e5 --- /dev/null +++ b/Completion/Core/_unset_options @@ -0,0 +1,7 @@ +#autoload + +# Complete all unset options. This relies on `_main_complete' to store the +# names of the options that were set when it was called in the array +# `_set_options'. + +compadd "$@" -M 'L:|[nN][oO]= M:_= M:{A-Z}={a-z}' - $=_unset_options diff --git a/Completion/Core/compdump b/Completion/Core/compdump new file mode 100644 index 000000000..8be096f50 --- /dev/null +++ b/Completion/Core/compdump @@ -0,0 +1,89 @@ +# This is a file to be sourced to dump the definitions for new-style +# completion defined by 'compinit' in the same directory. The output +# should be directed into the "compinit.dump" in the same directory as +# compinit. If you rename init, just stick .dump onto the end of whatever +# you have called it and put it in the same directory. This is handled +# automatically if you invoke compinit with the option -d. +# +# You will need to update the dump every time you add a new completion. +# To do this, simply remove the .dump file, start a new shell, and +# create the .dump file as before. Again, compinit -d handles this +# automatically. +# +# It relies on KSH_ARRAYS not being set. + +# Print the number of files used for completion. This is used in compinit +# to see if auto-dump should re-dump the dump-file. + +_d_file=${COMPDUMP-${0:h}/compinit.dump} + +typeset -U _d_files +_d_files=( ${^~fpath}/_*~*~(N:t) ) + +print "#files: $#_d_files" > $_d_file + +unset _d_files + +# First dump the arrays _comps and _patcomps. The quoting hieroglyphyics +# ensure that a single quote inside a variable is itself correctly quoted. + +print "_comps=(" >> $_d_file +for _d_f in ${(ok)_comps}; do + print -r - "'${_d_f//\'/'\\''}'" "'${_comps[$_d_f]//\'/'\\''}'" +done >> $_d_file +print ")" >> $_d_file + +print "\n_patcomps=(" >> $_d_file +for _d_f in "$_patcomps[@]"; do + print -r - "'${_d_f//\'/'\\''}'" +done >> $_d_file +print ")" >> $_d_file + +print >> $_d_file + +# Now dump the key bindings. We dump all bindings for zle widgets +# whose names start with a underscore. +# We need both the zle -C's and the bindkey's to recreate. + +_d_bks=() +zle -lL | + while read -rA _d_line; do + if [[ ${_d_line[5]} = _* ]]; then + print -r - ${_d_line} + _d_bks=($_d_bks ${_d_line[3]}) + fi + done >> $_d_file +bindkey | + while read -rA _d_line; do + if [[ ${_d_line[2]} = (${(j.|.)~_d_bks}) ]]; then + print -r "bindkey '${_d_line[1][2,-2]}' ${_d_line[2]}" + fi + done >> $_d_file + +print >> $_d_file + + +# Autoloads: whence -w produces "_d_foo: function", so look for +# all functions beginning with `_'. + +_d_als=($(whence -wm '_*' | sort | +while read -rA _d_line; do + [[ ${_d_line[2]} = function ]] && print -r - ${_d_line[1]%:} +done)) + +# print them out: about five to a line looks neat + +while (( $#_d_als )); do + print -n autoload + for (( _i = 0; _i < 5; _i++ )); do + if (( $#_d_als )); then + print -n " $_d_als[1]" + shift _d_als + fi + done + print +done >> $_d_file + +print >> $_d_file + +unset _d_line _d_zle _d_bks _d_als _d_f _f_file diff --git a/Completion/Core/compinit b/Completion/Core/compinit new file mode 100644 index 000000000..ec5867838 --- /dev/null +++ b/Completion/Core/compinit @@ -0,0 +1,269 @@ +# 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 a underscores (like `_setopt). +# The first line of these files will be read and has to say what should be +# done with its contents: +# +# `#defcomp <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 +# +# `#defpatcomp <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 +# +# `#defkeycomp <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. +# +# See the file `compdump' for how to speed up initialiation. +# +# If you are using global matching specifications with `compctl -M ...' +# have a look at the files `_match_test' and `_match_pattern'. To make +# all the example functions use matching as specified with `-M' these +# need some editing. +# +# If we got the `-d'-flag, we will automatically dump the new state (at +# the end). + +if [[ "$1" = -d ]]; then + _i_autodump=1 +else + _i_autodump=0 +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 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 "$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. + for i; do + [[ -z "$new" || "${+_comps[$i]}" -eq 0 ]] && _comps[$i]="$func" + done + ;; + 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 +} + +# Now we automatically make the definition files autoloaded. + +# First we get the name of a dump file if this will be used. + +: ${COMPDUMP:=$0.dump} + +if [[ ! -o extendedglob ]]; then + _i_noextglob=yes + setopt extendedglob +fi + +typeset -U _i_files +_i_files=( ${^~fpath}/_*~*~(N:t) ) +_i_initname=$0 +_i_done='' + +# If we have a dump file, load it. + +if [[ -f "$COMPDUMP" ]]; then + read -rA _i_line < "$COMPDUMP" + if [[ _i_autodump -eq 1 && $_i_line[2] -eq $#_i_files ]]; then + builtin . "$COMPDUMP" + _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 + if [[ $_i_tag = '#defcomp' ]]; then + compdef -na "${_i_file:t}" "${_i_line[@]}" + elif [[ $_i_tag = '#defpatcomp' ]]; then + compdef -pa "${_i_file:t}" "${_i_line[@]}" + elif [[ $_i_tag = '#defkeycomp' ]]; then + compdef -ka "${_i_file:t}" "${_i_line[@]}" + elif [[ $_i_tag = '#autoload' ]]; then + autoload ${_i_file:t} + fi + done + done + + bindkey | + while read -rA _i_line; do + if [[ "$_i_line[2]" = complete-word || + "$_i_line[2]" = delete-char-or-list || + "$_i_line[2]" = expand-or-complete || + "$_i_line[2]" = expand-or-complete-prefix || + "$_i_line[2]" = list-choices || + "$_i_line[2]" = menu-complete || + "$_i_line[2]" = menu-expand-or-complete || + "$_i_line[2]" = reverse-menu-complete ]]; then + zle -C _complete_$_i_line[2] $_i_line[2] _main_complete + bindkey "${_i_line[1][2,-2]}" _complete_$_i_line[2] + fi + done + + unset _i_dir _i_line _i_file _i_tag + + # If autodumping was requested, do it now. + + (( _i_autodump )) && builtin . ${_i_initname:h}/compdump +fi + +[[ -z "$_i_noextglob" ]] || unsetopt extendedglob + +unset _i_files _i_initname _i_done _i_autodump _i_noextglob |