mirror of
git://git.code.sf.net/p/zsh/code
synced 2025-07-04 14:31:24 +02:00
361 lines
11 KiB
Text
361 lines
11 KiB
Text
# 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. If they have been, the commands `autoload -U compinit; compinit'
|
|
# in the shell startup file should be enough, although you can run
|
|
# compinstall for more configuration choices.
|
|
#
|
|
# Simply run this script as a function and answer the questions.
|
|
# Normally it 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.
|
|
|
|
|
|
emulate -L zsh
|
|
|
|
typeset _ci_options _ci_f _ci_fdir _ci_files _ci_dumpfile _ci_lines
|
|
typeset _ci_type _ci_completer _ci_accept _ci_cprompt _ci_startline
|
|
typeset _ci_endline _ci_ifile _ci_tmpf _ci_compconf _ci_warn
|
|
typeset _ci_dtype _ci_existing _ci_line _ci_end
|
|
|
|
# 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_end=-1
|
|
_ci_compconf=0
|
|
else
|
|
_ci_end=-2
|
|
fi
|
|
if [[ $_ci_line[1] = *=* ]]; then
|
|
_ci_f="${${_ci_line[1,$_ci_end]}#*=}"
|
|
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] = compinit ]]; then
|
|
# parse the line running compinit
|
|
[[ $_ci_line[2] = -f ]] && _ci_fdir=$_ci_line[3]
|
|
[[ $_ci_line[-2] = -d ]] && _ci_dumpfile=$_ci_line[-1]
|
|
elif [[ $_ci_line[1] = _compdir=* ]]; then
|
|
_ci_fdir=${_ci_line[1]##_compdir=}
|
|
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] != \#* && $_ci_line[1] != (autoload|\[\[) ]]; 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_fdir}/compinit ||
|
|
! -f ${~_ci_fdir}/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
|
|
elif [[ $_ci_f != . && -f $_ci_f/Core/compinit &&
|
|
-f $_ci_f/Core/compdump ]]
|
|
then
|
|
_ci_fdir=$_ci_f/Core
|
|
break
|
|
fi
|
|
done
|
|
fi
|
|
|
|
if [[ -z $_ci_fdir || ! -d ${~_ci_fdir} ]]; then
|
|
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) &&
|
|
(! -f ${~_ci_fdir}/Core/compinit || ! -f ${~_ci_fdir}/Core/compdump)) ]]
|
|
do
|
|
print "I can't find them in that directory. Try again or abort."
|
|
vared _ci_fdir
|
|
done
|
|
if [[ -f ${~_ci_fdir}/Core/compinit && ! -f ${~_ci_fdir}/compinit ]]; then
|
|
_ci_fdir=$_ci_fdir/Core
|
|
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=($fpath $_ci_f)
|
|
|
|
# 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="${ZDOTDIR:-$HOME}/.zcompdump"
|
|
_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."
|
|
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}_compdir=$_ci_fdir
|
|
[[ -z \$fpath[(r)\$_compdir] ]] && fpath=(\$fpath \$_compdir)
|
|
autoload -U compinit
|
|
compinit"
|
|
[[ $_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
|
|
|
|
unfunction compinstall
|
|
autoload -U compinstall
|
|
|
|
return 0
|