# 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). # # 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. 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: # - 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. # 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 TRAPINT() { unsetopt ${(k)_ci_options[(R)off]} setopt ${(k)_ci_options[(R)on]} 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 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 $_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 = */* && -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 \ "Please edit the name of the directory where the completion functions are 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 _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 _ci_fdir done fi else print "Keeping existing completion directiory $_ci_fdir" fi if [[ ${~_ci_fdir} != /* ]]; then _ci_fdir=$(cd $_ci_fdir;builtin pwd) fi # 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 look and see what's happened to these. [Hit return to continue]" read fi # 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 $_ci_dtype dumpfile ${_ci_dumpfile} to speed up initialisation. [Hit return to continue]" read 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 ${_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 _ci_dumpfile done [[ -s $_ci_dumpfile ]] || rm -f $_ci_dumpfile fi _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 approximate completion if that fails. Would you like: 0: Just ordinary completion C: Correction A: Approximate completion 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: 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 _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 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 _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 " else 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 [[ -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 -r - "$_ci_startline $_ci_lines$_ci_endline" >>$_ci_ifile && print "\nSuccessfully appended lines to $_ci_ifile." fi TRAPINT 0 return 0