diff options
Diffstat (limited to 'Completion')
-rw-r--r-- | Completion/Commands/_correct_filename | 2 | ||||
-rw-r--r-- | Completion/Commands/_correct_word | 17 | ||||
-rw-r--r-- | Completion/Core/compinit | 111 | ||||
-rw-r--r-- | Completion/Core/compinstall | 415 |
4 files changed, 382 insertions, 163 deletions
diff --git a/Completion/Commands/_correct_filename b/Completion/Commands/_correct_filename index 53ed6d113..baef38edf 100644 --- a/Completion/Commands/_correct_filename +++ b/Completion/Commands/_correct_filename @@ -1,4 +1,4 @@ -#compdef -k complete-word \C-xc +#compdef -k complete-word \C-xC # Function to correct a filename. Can be used as a completion widget, # or as a function in its own right, in which case it will print the diff --git a/Completion/Commands/_correct_word b/Completion/Commands/_correct_word index db3023860..d0abcd4fe 100644 --- a/Completion/Commands/_correct_word +++ b/Completion/Commands/_correct_word @@ -3,10 +3,23 @@ # Simple completion front-end implementing spelling correction. # The maximum number of errors is set quite high, and # the numeric prefix can be used to specify a different value. +# +# If configurations keys with the prefix `correctword_' are +# given they override those starting with `correct_'. local oca="$compconfig[correct_accept]" -compconfig[correct_accept]=6n +local oco="$compconfig[correct_original]" +local ocp="$compconfig[correct_prompt]" +local oci="$compconfig[correct_insert]" + +compconfig[correct_accept]="${compconfig[correctword_accept]-6n}" +compconfig[correct_original]="${compconfig[correctword_original]-$oco}" +compconfig[correct_prompt]="${compconfig[correctword_prompt]-$ocp}" +compconfig[correct_insert]="${compconfig[correctword_insert]}" _main_complete _correct -compconfig[correct_accept]=$oca +compconfig[correct_accept]="$oca" +compconfig[correct_original]="$oco" +compconfig[correct_prompt]="$ocp" +compconfig[correct_insert]="$oci" diff --git a/Completion/Core/compinit b/Completion/Core/compinit index 72d5fde28..77f918502 100644 --- a/Completion/Core/compinit +++ b/Completion/Core/compinit @@ -42,14 +42,32 @@ # If we got the `-d'-flag, we will automatically dump the new state (at # the end). +# `-f dir' is used to pass down the directory where this file was +# found. This is necessary if functionargzero is not set. # If we were given an argument, this will be taken as the name of the # file in which to store the dump. -if [[ "$1" = -d ]]; then - _i_autodump=1 - shift -else - _i_autodump=0 +_i_fdir='' +_i_dumpfile='' +_i_autodump=0 +while [[ $1 = -[df] ]]; do + if [[ "$1" = -d ]]; then + _i_autodump=1 + shift + if [[ -n "$1" && "$1" != -[df] ]]; then + _i_dumpfile="$1" + shift + fi + elif [[ "$1" = -f ]]; then + # Used by compinstall to pass down directory where compinit was found + shift + _i_fdir="$1" + shift + fi +done +# Get the directory if we don't have it already and we can +if [[ -z "$_i_fdir" && -o functionargzero && $0 = */* ]]; then + _i_fdir=${0:h} fi # The associative array containing the definitions for the commands. @@ -63,9 +81,19 @@ _patcomps=() typeset -A compconfig # Standard initialisation for `compconfig'. - -(( $# )) && compconfig[dumpfile]="$1" -[[ -z "$compconfig[dumpfile]" ]] && compconfig[dumpfile]="$0.dump" +if [[ -n $_i_dumpfile ]]; then + # Explicitly supplied dumpfile. + compconfig[dumpfile]="$_i_dumpfile" +elif [[ -o functionargzero ]]; then + # We can deduce it from the name of this script + compconfig[dumpfile]="$0.dump" +elif [[ -n $_i_fdir ]]; then + # We were told what directory to use. + compconfig[dumpfile]="$_i_fdir/compinit.dump" +else + # Now we're stuck, but we'd better do something. + compconfig[dumpfile]="$HOME/.compinit.dump" +fi compconfig[correct_accept]=2n compconfig[correct_prompt]='correct to:' @@ -180,9 +208,15 @@ compdef() { *) # 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 + if [[ -z "$new" ]]; then + for i; do + _comps[$i]="$func" + done + else + for i; do + [[ "${+_comps[$i]}" -eq 0 ]] && _comps[$i]="$func" + done + fi ;; esac else @@ -265,8 +299,24 @@ compconf() { # Now we automatically make the definition files autoloaded. typeset -U _i_files -_i_files=( ${^~fpath}/_(|*[^~])(N:t) ) -_i_initname=$0 +_i_files=( ${^~fpath:/.}/_(|*[^~])(N:t) ) +if [[ $#_i_files -lt 20 ]]; then + # Too few files: we need some more directories + # Assume that we need to add the compinit directory to fpath. + if [[ -n $_i_fdir ]]; then + if [[ $_i_fdir = */Core ]]; then + # Add all the Completion subdirectories + fpath=(${_i_fdir:h}/*(/) $fpath) + elif [[ -d $_i_fdir/Core ]]; then + # Likewise + fpath=(${_i_fdir}/*(/) $fpath) + else + fpath=($_i_fdir $fpath) + fi + _i_files=( ${^~fpath:/.}/_(|*[^~])(N:t) ) + fi +fi + _i_done='' # If we have a dump file, load it. @@ -286,38 +336,45 @@ if [[ -z "$_i_done" ]]; then read -rA _i_line < $_i_file _i_tag=$_i_line[1] shift _i_line - if [[ $_i_tag = '#compdef' ]]; then + case $_i_tag in + (\#compdef) if [[ $_i_line[1] = -[pk] ]]; then compdef ${_i_line[1]}a "${_i_file:t}" "${(@)_i_line[2,-1]}" else compdef -na "${_i_file:t}" "${_i_line[@]}" fi - elif [[ $_i_tag = '#autoload' ]]; then + ;; + (\#autoload) autoload ${_i_file:t} - fi + ;; + esac 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 + case "$_i_line[2]" in + (complete-word) ;& + (delete-char-or-list) ;& + (expand-or-complete) ;& + (expand-or-complete-prefix) ;& + (list-choices) ;& + (menu-complete) ;& + (menu-expand-or-complete) ;& + (reverse-menu-complete) zle -C _complete_$_i_line[2] $_i_line[2] _main_complete bindkey "${_i_line[1][2,-2]}" _complete_$_i_line[2] - fi + ;; + esac done unset _i_dir _i_line _i_file _i_tag # If autodumping was requested, do it now. - (( _i_autodump )) && builtin . ${_i_initname:h}/compdump + if [[ -n ${_i_fdir} && $_i_autodump = 1 ]]; then + builtin . ${_i_fdir}/compdump + fi fi -unset _i_files _i_initname _i_done _i_autodump +unset _i_files _i_initname _i_done _i_autodump _i_fdir _i_dumpfile diff --git a/Completion/Core/compinstall b/Completion/Core/compinstall index d96121cf2..d494ba302 100644 --- a/Completion/Core/compinstall +++ b/Completion/Core/compinstall @@ -1,49 +1,129 @@ -# This script is to be run by a user to setup the new function based +# This script is to be run by a user to set up the new function based # completion system. The functions themselves are assumed to be already # available in some directory; they should have been installed with the # the shell (except we haven't written that yet). # -# Run it as a script under zsh and answer the questions. -# You can run it as `zsh compinstall $FPATH' and it will be able to check -# your function path for the completion functions. +# Source this script (e.g. `. /path/compinstall') and answer the questions. # -# Normally, this will alter ~/.zshrc (or wherever ZDOTDIR puts it), -# but you can make that unwritable and it will leave the lines in a -# temporary file instead. -# -# You can use this script to modify what compinstall previously -# added to ~/.zshrc. +# Normally, this will alter ~/.zshrc (or wherever ZDOTDIR puts it), but you +# can make that unwritable and it will leave the lines in a temporary file +# instead. It doesn't matter if .zshrc didn't exist before. If your +# .zshrc usually exits before the end, then you should take the code added +# by compinstall and put it (including the comment lines at the start and +# end) at the point you want it to be executed. If you run compinstall +# again it will find and replace those lines, so you can use this script to +# modify what compinstall previously added to ~/.zshrc. # # It is safe to abort with ^C any time you are being prompted for # information; your .zshrc will not be altered. # # To do: -# - Maybe this should be sourced, then it can check the user's current -# setup better. But then there is a potentially horrendous option -# setting/resetting problem. (Maybe we need another way of doing that.) # - Should probably offer to set different options for _approximate than # for _complete if both are used. # - Could add code for setting other completers and options. # - Could add keys for context-sensitive help. -# - Probably should allow a set of directories to be added to $fpath, -# like Core, Base, etc. -# In case a startup script changed options +# Save the options. We will need to trap ^C to make sure they get +# restored properly. +typeset -A _ci_options +_ci_options=($(setopt kshoptionprint;setopt)) +[[ -o kshoptionprint ]] || _ci_options[kshoptionprint]=off +[[ -o monitor ]] && _ci_options[monitor]=on +[[ -o zle ]] && _ci_options[zle]=on + emulate zsh -[[ -n $1 ]] && FPATH=$1 +TRAPINT() { + unsetopt ${(k)_ci_options[(R)off]} + setopt ${(k)_ci_options[(R)on]} -for f in $fpath; do - if [[ $f != . && -f $f/compinit && -f $f/compdump ]]; then - fdir=$f - break + unset _ci_options _ci_f _ci_fdir _ci_files _ci_dumpfile _ci_lines + unset _ci_type _ci_completer _ci_accept _ci_cprompt _ci_startline + unset _ci_endline _ci_ifile _ci_tmpf _ci_defaults _ci_compconf _ci_warn + unset _ci_dtype _ci_existing _ci_line + + if (( $1 )); then + print Aborted. + unfunction TRAPINT + return 1 fi -done + return 0 +} + + +# Look for the defaults. +_ci_startline='# The following lines were added by compinstall' +_ci_endline='# End of lines added by compinstall' + +_ci_ifile=${ZDOTDIR:-~}/.zshrc +_ci_lines='' +_ci_existing='' + +typeset -A _ci_defaults +if [[ -f $_ci_ifile ]]; then + # This assumes the lines haven't been altered by the user too much + # after they were added. + _ci_compconf=0 + sed -n "/^$_ci_startline/,/^$_ci_endline/p" $_ci_ifile | + while read -rA _ci_line; do + if (( $_ci_compconf )); then + # parse a compconf component as first argument + if [[ $_ci_line[-1] == \\ ]]; then + _ci_line=(${_ci_line[1,-2]}) + else + _ci_compconf=0 + fi + if [[ $_ci_line[1] = *=* ]]; then + _ci_f="${${_ci_line[*]}#*=}" + if [[ $_ci_f = \'*\' ]]; then + # strip quotes + _ci_f=${_ci_f[2,-2]//\'\\\'\'/\'} + fi + _ci_defaults[${_ci_line[1]%%\=*}]=$_ci_f + fi + _ci_existing="${_ci_existing} $_ci_line +" + elif [[ $_ci_line[1] = . && $_ci_line[2] = */compinit ]]; then + # parse the line sourcing compinit + [[ $_ci_line[3] = -f ]] && _ci_fdir=$_ci_line[4] + [[ $_ci_line[-2] = -d ]] && _ci_dumpfile=$_ci_line[-1] + elif [[ $_ci_line[1] = compconf ]]; then + # parse a compconf component as second argument (should be completer) + [[ $_ci_line[2] = completer=* ]] && + _ci_completer=${_ci_line[2]#completer=} + [[ $_ci_line[-1] == \\ ]] && _ci_compconf=1 + _ci_existing="${_ci_existing}$_ci_line +" + elif [[ $_ci_line[1] != \#* ]]; then + if [[ -z $_ci_warn ]]; then + _ci_warn=1 + print "Warning: existing lines in compinstall setup not understood:" + fi + print - $_ci_line + _ci_existing="${_ci_existing}$_ci_line +" + fi + done +fi + + +# Find out where the completion functions are kept. -if [[ -z $fdir ]]; then +if [[ -z $_ci_fdir || ! -f $_ci_f/compinit || ! -f $_ci_f/compdump ]]; then + for _ci_f in $fpath; do + if [[ $_ci_f != . && -f $_ci_f/compinit && -f $_ci_f/compdump ]]; then + _ci_fdir=$_ci_f + break + fi + done +fi + +if [[ -z $_ci_fdir || ! -d ${~_ci_fdir} ]]; then print "Trying to find where the completion functions are..." - if [[ $0 = */* && -f $0:h/compinit && -f $0:h/compdump ]]; then - fdir=$0:h + if [[ $0 = */* && -o functionargzero && + -f $0:h/compinit && -f $0:h/compdump ]]; then + _ci_fdir=$0:h + print "Using my directory, $_ci_fdir" else # more guesses? print \ @@ -51,22 +131,26 @@ if [[ -z $fdir ]]; then installed. If they are not installed, you will need to find them in the Completion/* directories of the zsh distribution and install them yourself, or insult your system manager for incompetence." - vared -c fdir - while [[ ! -d ${~fdir} || ! -f ${~fdir}/compinit || - ! -f ${~fdir}/compdump ]]; do + vared -c _ci_fdir + while [[ ! -d ${~_ci_fdir} || ! -f ${~_ci_fdir}/compinit || + ! -f ${~_ci_fdir}/compdump ]]; do print "I can't find them in that directory. Try again or abort." - vared fdir + vared _ci_fdir done fi - eval "fpath=($fdir \$fpath)" - fdir=${fdir/#$HOME/\~} - lines="fpath=($fdir \$fpath)\n" else - print "Found completion functions in your fpath, will not alter it." + print "Keeping existing completion directiory $_ci_fdir" fi -files=( ${^~fpath:/.}/_(|*[^~])(N:t) ) -if [[ $#files -lt 20 ]]; then +# Check if this is in fpath already, else put it there (with ~'s expanded). +_ci_f=${~_ci_fdir} +[[ -z ${fpath[(r)$_ci_f]} ]] && fpath=($_ci_f $fpath) +# Contract $HOME to ~ in the parameter to be used for writing. +_ci_fdir=${_ci_fdir/#$HOME/\~} + +# Now check the fpath, ignoring the directory . +_ci_files=( ${^~fpath:/.}/_(|*[^~])(N:t) ) +if [[ $#_ci_files -lt 20 ]]; then print " Hmmm, completion functions seem a bit thin on the ground. There should be lots of files with names beginning with an underscore (_). You should @@ -75,12 +159,20 @@ look and see what's happened to these. read fi -if [[ -w ${~fdir} && ( ! -f ${~fdir}/compinit.dump || - -w ${~fdir}/compinit.dump ) ]] + +# Set up the dumpfile +_ci_dtype=existing +if [[ -z $_ci_dumpfile ]]; then + _ci_dumpfile="${_ci_fdir}/compinit.dump" + _ci_dtype=standard +fi + +if [[ -w ${~_ci_dumpfile:h} && ( ! -f ${~_ci_dumpfile} || + -w ${~_ci_dumpfile} ) ]] then print " -Using standard dumpfile - ${~fdir}/compinit.dump +Using $_ci_dtype dumpfile + ${_ci_dumpfile} to speed up initialisation. [Hit return to continue]" read @@ -88,23 +180,30 @@ else print " I will force completion to dump its status, which will speed up the shell's start-up considerably. However, I can't write the file I'd like to, namely -$fdir/compinit.dump. Please edit a replacement." - dumpfile='~/.compinit.dump' - vared dumpfile - while ! touch ${~dumpfile} >& /dev/null; do +${_ci_dumpfile}. Please edit a replacement." + _ci_dumpfile='~/.compinit.dump' + vared _ci_dumpfile + while ! touch ${~_ci_dumpfile} >& /dev/null; do print "Sorry, I can't write that either. Try again." - vared dumpfile + vared _ci_dumpfile done - [[ -s $dumpfile ]] || rm -f $dumpfile - dumpfile=" $dumpfile" + [[ -s $_ci_dumpfile ]] || rm -f $_ci_dumpfile fi -fdir=${fdir/#$HOME/\~} - -lines="${lines}. $fdir/compinit -d$dumpfile\n" +_ci_lines="${_ci_lines}. $_ci_fdir/compinit -f $_ci_fdir -d" +[[ $_ci_dtype != standard ]] && _ci_lines="${_ci_lines} $_ci_dumpfile" +_ci_lines="${_ci_lines} +" print " +Would you like to set some more advanced options? Otherwise, you +can re-run compinstall later to set these. [n]" + +# The whole of the next part should be indented, but I can't be bothered. +if read -q; then + + print " In addition to completion, zsh can also perform correction of the current word, or approximate completion, i.e. completion where the part of the word typed so far can be corrected; or it can try correction, then @@ -112,105 +211,155 @@ approximate completion if that fails. Would you like: 0: Just ordinary completion C: Correction A: Approximate completion - B: Both? -Please type one of the keys above:" -while read -k type; do - print - case $type in - 0*) completer=_complete - break - ;; - [cC]*) completer=_complete:_correct - break - ;; - [aA]*) completer=_complete:_approximate - break; - ;; - [bB]*) completer=_complete:_correct:_approximate - break - ;; - *) print Try again - ;; - esac -done - -lines="${lines}compconf completer=$completer" - - -if [[ $completer = *(correct|approx)* ]]; then - print " -Correction and approximation will normally allow up to two errors, -and you will be able to use a numeric prefix (e.g. <Esc>4) to allow -more. The standard prompt is \`correct to:'. Do you want to change -any of this? [n]" - if read -q; then - print "Number of errors to accept normally (0 is OK):" - read accept - while [[ $accept != <-> ]]; do - read accept"?Please enter a number: " - done - print \ + B: Both" + if [[ -n $_ci_completer ]]; then + print " Default: use the current completer:\n$_ci_completer" + else + print "Please type one of the keys above." + fi + while read -k _ci_type; do + print + case $_ci_type in + 0*) _ci_completer=_complete + break + ;; + [cC]*) _ci_completer=_complete:_correct + break + ;; + [aA]*) _ci_completer=_complete:_approximate + break; + ;; + [bB]*) _ci_completer=_complete:_correct:_approximate + break + ;; + *) [[ -n $_ci_completer ]] && break + print Try again + ;; + esac + done + + _ci_lines="${_ci_lines}compconf completer=$_ci_completer" + + + if [[ $_ci_completer = *(correct|approx)* ]]; then + _ci_accept=${_ci_defaults[correct_accept]} + _ci_cprompt=${_ci_defaults[correct_prompt]} + print " +Correction and approximation will allow up to ${${_ci_accept:-2}%%[^0-9]*} \ +errors. " + case $_ci_accept in + *n*!*|*!*n) print "A numeric prefix, if not 1, will cause correction \ +not to be done." + ;; + *n*) print "A numeric prefix gives the maximum number of errors which \ +will be accepted." + ;; + *) print "The numeric prefix will not be used." + esac +print "The correction prompt is \`${_ci_cprompt:-correct to:}'. +Do you want to change any of this? [n]" + if read -q; then + print "Number of errors to accept normally (0 is OK):" + _ci_accept=${_ci_accept%%[^0-9]*} + vared _ci_accept + while [[ $_ci_accept != <-> ]]; do + print "Please enter a number:" + vared _ci_accept + done + print \ "How would you like the numeric prefix to be treated: 0: Not used by correction - U: Used to given the number of errors + U: The number gives the largest number of errors which will be + accepted when correcting I: If present, and not 1, do not perform correction? Please type one of the keys above:" - while read -k type; do - print - case $type in - 0*) break - ;; - [uU]*) accept="${accept}n" - break - ;; - [Ii]*) accept="${accept}!n" - break - ;; - *) print Try again - ;; - esac - done - lines="$lines \\\\ - correct_accept='$accept'" - print " + while read -k _ci_type; do + print + case $_ci_type in + 0*) break + ;; + [uU]*) _ci_accept="${_ci_accept}n" + break + ;; + [Ii]*) _ci_accept="${_ci_accept}!n" + break + ;; + *) print Try again + ;; + esac + done + print " Instead of the prompt \`correct to:', you can have no prompt, or a prompt of your choosing which can display the number of errors found by containing the string \`%e'. Do you wish to change the correction prompt? [n]" - if read -q; then - cprompt='' - print "Edit a new prompt (may be empty):" - vared cprompt - lines="$lines \\\\ - correct_prompt='${cprompt//\'/\'\\\'\'}'" + if read -q; then + print "Edit a new prompt (may be empty):" + vared _ci_cprompt + [[ -z $_ci_cprompt ]] && _ci_cprompt=':empty:' + fi + fi + if [[ -n $_ci_accept ]]; then + _ci_lines="$_ci_lines \\ + correct_accept='$_ci_accept'" + unset '_ci_defaults[correct_accept]' + fi + if [[ -n $_ci_cprompt ]]; then + _ci_cprompt=${_ci_cprompt##:empty:} + _ci_lines="$_ci_lines \\ + correct_prompt='${_ci_cprompt//\'/\'\\\'\'}'" + unset '_ci_defaults[correct_prompt]' fi fi -fi -lines="$lines\n" + _ci_warn='' + for _ci_f in ${(k)_ci_defaults}; do + if [[ -z $_ci_warn ]]; then + print " +(Keeping other existing configuration settings...)" + _ci_warn=1 + fi + _ci_lines="$_ci_lines \\ + ${_ci_f}='${_ci_defaults[$_ci_f]//\'/\'\\\'\'}'" + done + _ci_lines="$_ci_lines +" -startline='# The following lines were added by compinstall' -endline='# End of lines added by compinstall' +else -ifile=${ZDOTDIR:-~}/.zshrc -[[ -f $ifile ]] || touch $ifile -tmpf=${TMPPPREFIX:-/tmp/zsh}compinstall$$ + if [[ -n $_ci_existing ]]; then + print -nr " +I will retain the following lines from the existing completion setup: +$_ci_existing" + _ci_lines="$_ci_lines${_ci_existing}" + fi + +fi # End of advanced options -if [[ ! -w $ifile ]]; then - print "\nI can't write to $ifile. I will leave the lines to add in -\`$tmpf' and you must add them by hand." - print "\n$startline\n$lines\n$endline" >$tmpf - return 0 -fi -if grep $endline $ifile >& /dev/null; then - print -- "$startline\n$lines$endline" >$tmpf - sed -e "/^$endline/r $tmpf -/^$startline/,/^$endline/d" $ifile >${tmpf}2 && mv ${tmpf}2 $ifile && - print "\nSuccesfully modified old compinstall lines in $ifile." - rm -f $tmpf ${tmpf}2 +[[ -f $_ci_ifile ]] || touch $_ci_ifile +_ci_tmpf=${TMPPPREFIX:-/tmp/zsh}compinstall$$ + +if [[ ! -w $_ci_ifile ]]; then + print "\nI can't write to $_ci_ifile. I will leave the lines to add in +\`$_ci_tmpf' and you must add them by hand." + print -r - "$_ci_startline +$_ci_lines$_ci_endline" >$_ci_tmpf +elif grep $_ci_endline $_ci_ifile >& /dev/null; then + print -r - "$_ci_startline +$_ci_lines$_ci_endline" >$_ci_tmpf + sed -e "/^$_ci_endline/r $_ci_tmpf +/^$_ci_startline/,/^$_ci_endline/d" $_ci_ifile >${_ci_tmpf}2 && + mv ${_ci_tmpf}2 $_ci_ifile && + print "\nSuccesfully modified old compinstall lines in $_ci_ifile." + rm -f $_ci_tmpf ${_ci_tmpf}2 else - print "\n$startline\n$lines\n$endline" >>$ifile && - print "\nSuccessfully appended lines to $ifile." + print -r - "$_ci_startline +$_ci_lines$_ci_endline" >>$_ci_ifile && + print "\nSuccessfully appended lines to $_ci_ifile." fi + +TRAPINT 0 + +return 0 |