summaryrefslogtreecommitdiff
path: root/Functions
diff options
context:
space:
mode:
Diffstat (limited to 'Functions')
-rw-r--r--Functions/Newuser/zsh-newuser-install1006
1 files changed, 994 insertions, 12 deletions
diff --git a/Functions/Newuser/zsh-newuser-install b/Functions/Newuser/zsh-newuser-install
index 6c54dc509..e042af08a 100644
--- a/Functions/Newuser/zsh-newuser-install
+++ b/Functions/Newuser/zsh-newuser-install
@@ -1,23 +1,1005 @@
# Function to install startup files for a new user.
-# This dummy version simply creates a new .zshrc with a comment.
-# FIXME: we don't want to distribute a file that does that, it
-# would be preferable to do nothing at all.
+# Currently it only creates or edits .zshrc.
+#
+# It can be run again by giving it the option "-f".
# Sanitize environment.
emulate -L zsh
+setopt extendedglob nonomatch warncreateglobal
+# How the function will be referred to.
+local myname=zsh-newuser-install
+# The directory in which to look for and save .zshrc.
local zd=${ZDOTDIR:-$HOME}
+# The same directory in a user friendly form, i.e. with ~ replacement.
+# (We don't want to use glob_subst since that has other side effects.)
+local zdmsg
+# The message used if an other blank .zshrc is created.
+local msg="# Created by newuser for $ZSH_VERSION"
+# The lines marking the start and end of the section edited.
+local startline="# Lines configured by $myname"
+local endline="# End of lines configured by $myname"
+# Prompts used for reading a key. The initial "?" is required.
+local shortprompt="?--- Type a key --- "
+local longprompt="?--- Type one of the keys in parentheses --- "
+# Prefix for all temporary files. Any files starting with this
+# will be removed at the end of the script.
+local tmpfile=${TMPPREFIX:-/tmp/zsh}-zni-$$
+# Report of the state of settings for the top-level menu.
+local -A install_state
+# Values of all parameters etc. to be saved (including
+# those read in from the existing file.)
+local -A parsed_parameters parsed_options parsed_bindings parsed_keymaps
+# Corresponding state in a user-readable form.
+local -A state_parameters state_options state_bindings state_keymaps
+# Lines read in from between $startline and $endline which were
+# not understood. These are retained but moved out of that section
+# with a message.
+local -a unparsed
+# Lines used in submenus: the setting to output in a form
+# that can be exeucuted (an assignment, setopt or unsetopt), a brief message
+# about the setting, and the state copied from and to state_parameters or
+# state_options. Elements of all three arrays must correspond.
+local -a output_lines display_lines state_lines
+# Variable indicating some of the lines in the above variables
+# have been read in, i.e. the user has already configured the
+# particular set of settings.
+integer lines_read
+# Lines to set up completion. This is special as it is only
+# edited by compinstall, not this function.
+local -a completion_lines
+# Utility variables
+local -a reply match mbegin mend
+# Key read from user, used all over the place.
+local key
+integer save lines_found
-# The zsh/newuser module already tests for the following, so this test only
-# triggers if zsh-newuser-install is run by hand.
-#
-# In future we may want to use this mechanism to update startup files.
-if [[ -e $zd/.zshenv || -e $zd/.zprofile || -e $zd/.zshrc || -e $zs/.zlogin ]]
-then
- print "zsh-newuser-install: startup files exist, aborting" >&2
+install_state[history]=Recommended
+install_state[completion]=Recommended
+
+# Don't save anything if interrupted.
+trap 'save=0' HUP INT QUIT
+
+# Substitute an initial ~ for human consumption.
+if [[ $zd = $HOME ]]; then
+ zdmsg="~"
+else
+ zdmsg=$zd
+fi
+
+# Don't run unless we can talk to the user.
+if [[ ! -t 0 || ! -t 1 ]]; then
+ if [[ $1 = -f ]]; then
+ print -r "$myname: can only be used interactively." >&2
+ fi
+ return 1
+fi
+
+# Don't run unless terminal is sane.
+if (( ${LINES:-0} < 15 || ${COLUMNS:-0} < 72 )); then
return 1
fi
-echo "# Created by newuser for $ZSH_VERSION" >$zd/.zshrc
+if [[ $1 != -f ]]; then
+ # The zsh/newuser module already tests for the following, so this test only
+ # triggers if zsh-newuser-install is run by hand.
+ if [[ -e $zd/.zshenv || -e $zd/.zprofile || \
+ -e $zd/.zshrc || -e $zd/.zlogin ]]; then
+ print -r "$myname: startup files exist, aborting.
+
+Use the argument -f if you want to force the function to be run again." >&2
+ return 1
+ fi
+fi
+
+
+# start of try block for tidy-up in always block
+{
+
+########################################################################
+# Utility functions
+########################################################################
+
+# All internal functions start with __zni_. These will be removed
+# when the main function exits.
+
+# Read existing lines from .zshrc, if any.
+__zni_retrieve_lines() {
+ local line
+
+ reply=()
+
+ lines_found=0
+
+ [[ -f $zd/.zshrc ]] || return 1
+
+ grep "$startline" $zd/.zshrc 1>/dev/null 2>&1 || return 1
+
+ lines_found=1
+
+ sed -n "/^[ ]*$startline/,/^[ ]*$endline/p" $zd/.zshrc |
+ while read -r line; do
+ reply+=($line)
+ done
+
+ return 0
+}
+
+
+# First argument is a state; other arguments are lines
+# to parse. They should either contain single assignments or
+# setopt or unsetopt statements. The state for each parameter
+# or option so parsed is set to the value given by the first argument.
+__zni_parse_lines() {
+ local line opt warned first
+ local -a args
+ local state=$1
+
+ shift
+
+ for line in "$@"; do
+ case $line in
+ ((#b)[[:blank:]]#([[:IDENT:]]##)=(*))
+ parsed_parameters[$match[1]]=$match[2]
+ state_parameters[$match[1]]=$state
+ ;;
+
+ ((#b)[[:blank:]]#(un|)setopt[[:blank:]]##(*))
+ # TBD: handle setopt noX / unsetopt X
+ for opt in ${=match[2]}; do
+ opt=${${opt//(#m)[[:upper:]]/${(L)MATCH}}//_}
+ if [[ $match[1] = un ]]; then
+ parsed_options[$opt]=off
+ else
+ parsed_options[$opt]=on
+ fi
+ state_options[$opt]=$state
+ done
+ ;;
+
+ ((#b)[[:blank:]]#bindkey[[:blank:]]##(*))
+ args=(${(z)match[1]})
+ # store keys unquoted: will need quoting for output.
+ first=${(Q)args[1]}
+ shift args
+ if [[ $first = -[ev] && ${#args} -eq 0 ]]; then
+ case $first in
+ (-e)
+ parsed_keymaps[main]=emacs
+ ;;
+
+ (-v)
+ parsed_keymaps[main]=vi
+ ;;
+ esac
+ state_keymaps[main]=$state
+ else
+ # TODO: handling keymap options
+ parsed_bindings[first]=${args[2,-1]}
+ state_bindings[first]=$state
+ fi
+ ;;
+
+ ([[:blank:]]#($startline|$endline|))
+ ;;
+
+ (*)
+ unparsed+=($line)
+ print -r "WARNING: failed to understand line:
+ $line
+which will be retained but not edited."
+ warned=1
+ ;;
+ esac
+ done
+
+ if [[ -n $warned ]]; then
+ read -k key$shortprompt
+ fi
+}
+
+# Apply defaults. Arguments in the form
+# -p parameter_name default_value description
+# ...
+# -o option_name default=on|off description
+# ...
+# -b bindkey_string default_value description
+# ...
+# -B default_keymap=emacs|vi|none description
+#
+# They're not really defaults (they're not the same as the
+# builtin defaults), so the description output is "not yet saved".
+#
+# All variables to be edited in this section must be mentioned,
+# though defaults can be blank in which case nothing will be
+# saved unless the variable is set by the user. The description
+# is then "no value set".
+#
+# -B is a bit strange: it's simply designed to allow the user to
+# select "bindkey -e" for Emacs or "bindkey -v" for vi. It only
+# takes a single argument. Real key bindings use -b.
+#
+# This operation transfers some subset of settings from the parsed_*
+# and state_* variables to the *_lines variables for editing.
+__zni_apply_defaults() {
+ local un
+
+ # Reset the lines to be edited.
+ state_lines=()
+ display_lines=()
+ output_lines=()
+ lines_read=0
+
+ case $1 in
+ (-p)
+ shift
+ while [[ $# -gt 0 && $1 != -* ]]; do
+ # skip default if it was read in
+ if [[ -z $state_parameters[$1] ]]; then
+ parsed_parameters[$1]=$2
+ if [[ -n $2 ]]; then
+ state_parameters[$1]="not yet saved"
+ else
+ state_parameters[$1]="no value set"
+ fi
+ elif [[ $state_parameters[$1] = saved ]]; then
+ (( lines_read++ ))
+ fi
+ state_lines+=($state_parameters[$1])
+ display_lines+=("$3")
+ output_lines+=("$1=$parsed_parameters[$1]")
+
+ shift 3
+ done
+ ;;
+
+ (-o)
+ shift
+ while [[ $# -gt 0 && $1 != -* ]]; do
+ # skip default if there was a setting
+ if [[ -z $state_options[$1] ]]; then
+ parsed_options[$1]=$2
+ if [[ -n $2 ]]; then
+ state_options[$1]="not yet saved"
+ else
+ state_options[$1]="no value set"
+ fi
+ elif [[ $state_parameters[$1] = saved ]]; then
+ (( lines_read++ ))
+ fi
+ if [[ $parsed_options[$1] = on ]]; then
+ un=
+ else
+ # display as unsetopt even if no value to save yet
+ un=un
+ fi
+ state_lines+=($state_options[$1])
+ display_lines+=("$3")
+ output_lines+=("${un}setopt $1")
+
+ shift 3
+ done
+ ;;
+
+ (-b)
+ shift
+ # this will barf on bindings beginning -; there's no good
+ # reason to rebind that, even in vi command mode, so perhaps
+ # we just add it to the sanity checks when we get around to them.
+ while [[ $# -gt 0 && $1 != -* ]]; do
+ if [[ -z $state_bindings[$1] ]]; then
+ parsed_bindings[$1]=$2
+ if [[ -n $2 ]]; then
+ state_bindings[$1]="not yet saved"
+ else
+ state_bindings[$1]="no value set"
+ fi
+ elif [[ $state_bindings[$1] = saved ]]; then
+ (( lines_read++ ))
+ fi
+ state_lines+=($state_bindings[$1])
+ display_lines+=("$3")
+ output_lines+=("bindkey ${(qq)1}${2:+ $2}")
+
+ shift 3
+ done
+ ;;
+
+ (-B)
+ shift
+ if [[ -z $state_keymaps[main] ]]; then
+ parsed_keymaps[main] = $1
+ if [[ $1 = none ]]; then
+ state_keymaps[main]="no value set"
+ else
+ state_keymaps[main]="not yet saved"
+ fi
+ elif [[ $state_keymaps[main] = saved ]]; then
+ (( lines_read++ ))
+ fi
+ state_lines+=($state_keymaps[main])
+ display_lines+=("$2")
+ # display as -e even if no value to save yet
+ if [[ $parsed_keymaps[main] = vi ]]; then
+ output_lines+=("bindkey -v")
+ else
+ output_lines+=("bindkey -e")
+ fi
+
+ shift 2
+ ;;
+ esac
+}
+
+
+# Display and edit the settings given by the set of *_lines arrays.
+# If requested by the user, apply the settings, updating the
+# parsed_* and state_* variables.
+__zni_display_and_edit() {
+ integer i changes
+ local default edval ldisp rdisp
+ local -a states displays outputs tstval
+
+ states=("${state_lines[@]}")
+ displays=("${display_lines[@]}")
+ outputs=("${output_lines[@]}")
+
+ while true; do
+ clear
+ print -r $1
+ # snicker...
+ print -r ${(l.${#1}..=.):-}
+ print
+ if (( $# > 1 )); then
+ print -rl $argv[2,-1]
+ print
+ fi
+
+ # Output each setting with a description and state.
+ for (( i = 1; i <= ${#output_lines}; i++ )); do
+ default=$states[$i]
+ if [[ $default = ("no value set"|"not to be saved") ]]; then
+ ldisp="# $outputs[$i]"
+ else
+ ldisp=$outputs[$i]
+ fi
+ rdisp=${default:+($default)}
+ print -r "# ($i) $displays[$i]
+$ldisp${(l.$COLUMNS-${#ldisp}-${#rdisp}-1.):-}$rdisp"
+ done
+
+ if (( changes )); then
+ print -r "
+# (0) Remember edits and return to main menu (does not save file yet)
+# (q) Abandon edits and return to main menu
+"
+ else
+ print -r "
+# (0) or (q) Return to main menu (no changes made yet)
+"
+ fi
+ read -k key$longprompt
+ print
+
+ if [[ $key = <-> && $key -ge 1 && $key -le ${#outputs} ]]; then
+ (( i = key ))
+ case $outputs[$i] in
+ ((#b)(|un)setopt' '(*))
+ while true; do
+ clear
+ print "Option $match[2]: $displays[$i]
+The option is currently ${match[1]:+un}set.
+Type:
+ (s) to set it
+ (u) to unset it
+ (n) not to set or unset it (use shell default)
+ (k) to keep the current setting:"
+ read -k key$shortprompt
+ print
+
+ case $key in
+ (s)
+ (( changes++ ))
+ outputs[$i]="setopt $match[2]"
+ states[$i]="set but not saved"
+ ;;
+
+ (s)
+ (( changes++ ))
+ outputs[$i]="unsetopt $match[2]"
+ states[$i]="set but not saved"
+ ;;
+
+ (n)
+ (( changes++ ))
+ outputs[$i]="unsetopt $match[2]"
+ states[$i]="no value set"
+ ;;
+
+ (k)
+ ;;
+
+ (*)
+ continue
+ ;;
+ esac
+ break;
+ done
+ ;;
+
+ ((#b)([^=]##)=(*))
+ print -r "Variable ${match[1]}: $displays[$i]
+Edit a value. If it is left blank, nothing will be saved:"
+ edval=$match[2]
+ if vared -p "$match[1]> " -h edval; then
+ # check this assignment doesn't produce multiple words
+ # e.g. "HISTFILE=never rm -f ~" does produce multiple words...
+ # this isn't perfect, e.g. "(this would get split on assignment)",
+ # but that's fairly benign.
+ tstval=(${=edval})
+ if (( ${#tstval} > 1 )); then
+ print "Error: value isn't a single word.
+Use quotes or backslashes if your value contains spaces.
+Note that you shouldn't quote an initial ~ in file names." >&2
+ read -k key$shortprompt
+ # now check the assignment works...
+ # don't suppress any errors, they may be useful.
+ # this means we need to suppress warncreateglobal.
+ elif ! ( typeset -g $match[1]; eval "$match[1]=$edval" ); then
+ print "Error: bad shell syntax in value.
+The value will be assigned to the variable exactly as you enter it.
+Make sure all quotes are paired." >&2
+ read -k key$shortprompt
+ else
+ outputs[$i]="$match[1]=$edval"
+ if [[ -n $edval ]]; then
+ states[$i]="set but not saved"
+ else
+ states[$i]="no value set"
+ fi
+ (( changes++ ))
+ fi
+ else
+ read -k key'?--- Edit abandoned, type a key --- '
+ fi
+ ;;
+
+ (bindkey' '-[ev])
+ while true; do
+ print -nr "Pick a keymap (set of keys) to use when editing.
+Type:
+ (e) for Emacs keymap (recommended unless you are vi user)
+ (v) for Vi keymap
+ (n) not to set a keymap (allow shell to choose)
+ (k) to keep the current setting, "
+ if [[ $state_lines[$i] = ("no value set"|"not to be saved") ]]
+ then
+ print -r "(n):"
+ elif [[ $output_lines[$i] = *-v ]]; then
+ print -r "(v):"
+ else
+ print -r "(e):"
+ fi
+ read -k key$longprompt
+ case $key in
+ (e)
+ (( changes++ ))
+ outputs[$i]="bindkey -e"
+ states[$i]="set but not saved"
+ ;;
+
+ (v)
+ (( changes++ ))
+ outputs[$i]="bindkey -v"
+ states[$i]="set but not saved"
+ ;;
+
+ (n)
+ (( changes++ ))
+ outputs[$i]="bindkey -e"
+ states[$i]="not to be saved"
+ ;;
+
+ (k)
+ ;;
+
+ (*)
+ continue
+ ;;
+ esac
+ break
+ done
+ ;;
+
+ (bindkey' '*)
+ # TODO: this needs writing. We need to be able to read
+ # keys and translate them, sanity check them, and ideally
+ # handle keymaps, at least vi command and insert.
+ ;;
+
+ (*)
+ print "*** Internal error: bad setting '$outputs[$i]' ***" >&2
+ read -k key'?--- Type a key in forlorn hope --- '
+ ;;
+ esac
+ elif [[ $key = 0 ]]; then
+ # Update the *_lines variables
+ state_lines=("${states[@]}")
+ display_lines=("${displays[@]}")
+ output_lines=("${outputs[@]}")
+
+ # Also save any lines suitably marked to parsed_* and state_*
+ # by rerunning __zni_parse_lines on each such line.
+ for (( i = 1; i <= ${#output_lines}; i++ )); do
+ if [[ $state_lines[$i] = ("set but not saved"|"not to be saved") ]]
+ then
+ __zni_parse_lines $state_lines[$i] $output_lines[$i]
+ fi
+ done
+
+ return $(( changes == 0 ))
+ elif [[ $key = [qQ] ]]; then
+ return 1
+ fi
+ done
+}
+
+
+# Print and despatch a submenu.
+# The first argument is the title. The remaining arguments
+# are pairs of descriptions and functions to execute.
+# There shouldn't be more than 9 entries.
+# The usual entries 0 and q are added automatically.
+__zni_submenu() {
+ local title=$1
+ local desc func
+ local -a descs funcs
+ integer i
+
+ shift
+
+ clear
+ print -r $title
+ print -r ${(l.${#title}..=.):-}
+
+ for desc func; do
+ if [[ -z $func ]]; then
+ print "*** Internal error: bad argument set for __zni_submenu ***" >&2
+ read -k key'?--- Type a key in forlorn hope --- '
+ return 1
+ fi
+
+ descs+=($desc)
+ funcs+=($func)
+ done
+
+ while true; do
+ for (( i = 1; i <= ${#descs}; i++ )); do
+ print -r "
+($i) $descs[$i]"
+ done
+ print -r "
+(0) or (q) Return to previous menu"
+
+ read -k key$longprompt
+
+ if [[ $key = [0qQ] ]]; then
+ return 1
+ elif (( key >= 1 && key <= ${#funcs} )); then
+ $funcs[$key]
+ fi
+ done
+}
+
+
+# Save all values that have been edited to .zshrc.
+__zni_save() {
+ local key optline newline
+ local -a on_opts off_opts lines lines2
+ integer i
+
+ # Record lines containing parameter settings, sorted.
+ for key in ${(ok)parsed_parameters}; do
+ if [[ $state_parameters[$key] != ("no value set"|"not to be saved") ]]
+ then
+ lines+=("$key=$parsed_parameters[$key]")
+ fi
+ done
+
+ # Search through sorted options, make list of those to
+ # be turned on and off. Those marked "no value set" aren't
+ # to be output.
+ for key in ${(ok)parsed_options}; do
+ if [[ $state_options[$key] != ("no value set"|"not to be saved") ]]; then
+ if [[ $parsed_options[$key] = on ]]; then
+ on_opts+=($key)
+ else
+ off_opts+=($key)
+ fi
+ fi
+ done
+
+ # Construct lines of options to turn on, keeping them short.
+ optline="setopt"
+ for (( i = 1; i <= ${#on_opts}; i++ )); do
+ newline="$optline $on_opts[$i]"
+ if [[ ${#newline} -ge 72 ]]; then
+ lines+=($optline)
+ optline="setopt $on_opts[$i]"
+ else
+ optline=$newline
+ fi
+ if (( i == ${#on_opts} )); then
+ lines+=($optline)
+ fi
+ done
+
+ # Construct lines of options to turn off, keeping them short.
+ optline="unsetopt "
+ for (( i = 1; i <= ${#off_opts}; i++ )); do
+ newline="$optline $off_opts[$i]"
+ if [[ ${#newline} -ge 72 ]]; then
+ lines+=($optline)
+ optline="unsetopt $off_opts[$i]"
+ else
+ optline=$newline
+ fi
+ if (( i == ${#off_opts} )); then
+ lines+=($optline)
+ fi
+ done
+
+ # Construct lines of bindkey commands. First the keymap.
+ if [[ $state_keymaps[main] != (|"no value set"|"not to be saved") ]]; then
+ case $parsed_keymaps[main] in
+ (emacs)
+ lines+=("bindkey -e")
+ ;;
+
+ (vi)
+ lines+=("bindkey -v")
+ ;;
+ esac
+ fi
+ # Now bindings.
+ for key in ${(ok)parsed_bindings}; do
+ if [[ $state_bindings[$key] != ("no value set"|"not to be saved") ]]; then
+ lines+=("bindkey ${(qq)key} ${parsed_bindings[$key]}")
+ fi
+ done
+
+ # Save the lines with a start and end marker to a temporary file.
+ print -rl $startline $lines $endline >$tmpfile
+
+ if (( ${#unparsed} )); then
+ print "# The following lines were read by $myname.
+# They were moved here as they could not be understood.
+# $(date)
+${(F)unparsed}
+# End of lines moved by $myname." >>$tmpfile
+ fi
+
+ if grep "$startline" $zd/.zshrc 1>/dev/null 2>&1; then
+ # Found the start line; replace the section.
+ # We could this by reading the lines in zsh, but in case
+ # the .zshrc is huge it's perhaps better to use sed.
+ sed -e "/^[ ]*$endline/r $tmpfile
+/^[ ]*$startline/,/^[ ]*$endline/d" $zd/.zshrc >${tmpfile}.repl &&
+ cp ${tmpfile}.repl $zd/.zshrc
+ else
+ # No current start marker; just append.
+ cat $tmpfile >>$zd/.zshrc
+ fi
+}
+
+
+########################################################################
+# Specific configurations
+########################################################################
+
+__zni_history_config() {
+ __zni_apply_defaults -p \
+ HISTORY 1000 "Number of lines of history kept within shell" \
+ HISTFILE $zdmsg/.histfile "File where history is saved" \
+ SAVEHIST 1000 "Number of lines of history to save to \$HISTFILE"
+
+ if __zni_display_and_edit "History configuration"; then
+ install_state[history]="Unsaved changes"
+ save=1
+ fi
+}
+
+
+__zni_completion_config() {
+ autoload -Uz compinstall
+ if compinstall -d; then
+ print "The completion system has already been activated.
+You can run the configuration tool (compinstall) at any time by typing
+ autoload -Uz compinstall
+ compinstall
+Do you wish to run it now [y/n]?"
+ read -k key$shortprompt
+ if [[ $key = [yY] ]]; then
+ compinstall
+ fi
+ print
+ else
+ while true; do
+ clear
+ print "The new completion system (compsys) allows you to complete
+commands, arguments and special shell syntax such as variables. It provides
+completions for a wide range of commonly used commands in most cases simply
+by typing the TAB key. Documentation is in the zshcompsys manual page.
+If it is not turned on, only a few simple completions such as filenames
+are available but the time to start the shell is slightly shorter.
+
+You can:
+ (1) Turn on completion with the default options.
+
+ (2) Run the configuration tool (compinstall). You can also run
+ this from the command line with the following commands:
+ autoload -Uz compinstall
+ compinstall
+ if you don't want to configure completion now.
+
+ (0) Don't turn on completion.
+"
+ read -k key$longprompt
+ case $key in
+ (1)
+ completion_lines=${(f)"$(compinstall -o)"}
+ install_state[completion]="Unsaved changes"
+ save=1
+ ;;
+
+ (2)
+ compinstall
+ install_state[completion]="Configured"
+ ;;
+
+ (0)
+ completion_lines=()
+ install_state[completion]="Recommended"
+ ;;
+
+ (*)
+ continue
+ ;;
+ esac
+ break
+ done
+ fi
+}
+
+__zni_bindkey_config() {
+ __zni_apply_defaults -B none "Change default editing configuration"
+
+ if __zni_display_and_edit "Default editing configuration" \
+ "The keys in the shell's line editor can be made to behave either" \
+ "like Emacs or like Vi, two common Unix editors. If you have no" \
+ "experience of either, Emacs is recommended. If you don't pick one," \
+ "the shell will try to guess based on the EDITOR environment variable." \
+ "Usually it's better to pick one explicitly."; then
+ install_state[bindkey]="Unsaved changes"
+ save=1
+ fi
+}
+
+__zni_completion_save() {
+ if (( ${#completion_lines} )); then
+ # We don't try to replace existing lines of completion configuration ---
+ # that's up to compinstall. We should already have tested that
+ # there was no existing completion set up.
+ print -rl $completion_lines >>$zd/.zshrc
+ fi
+}
+
+
+__zni_options_config() {
+ # when we have enough, should use:
+ # __zni_submenu "Common shell options"
+
+ # This is deliberately just a tiny selection.
+ # Feel free to extend it, but if you do, consider using __zni_submenu.
+ # The "no" prefix is used to indicate options on by default.
+ __zni_apply_defaults -o autocd '' "Change directory given just path" \
+ extendedglob '' "Use additional pattern matching features" \
+ appendhistory '' "Append new history lines instead of overwriting" \
+ nonomatch '' "Pass unmatched patterns to command instead of error" \
+ nobeep '' "Don't beep on errors" \
+ notify '' "Immediately report changes in background job status"
+
+ if __zni_display_and_edit "Common shell options" \
+ "The following are some of the shell options that are most often used." \
+ "The descriptions are very brief; if you would like more information," \
+ "read the zshoptions manual page (type \"man zshoptions\")."; then
+ install_state[options]="Unsaved changes"
+ save=1
+ fi
+}
+
+
+########################################################################
+# Main function
+########################################################################
+
+# Read and parse any existing lines, in case the function
+# was called again.
+__zni_retrieve_lines &&
+ __zni_parse_lines saved "$reply[@]"
+
+if [[ $state_parameters[HISTORY] = saved ]]; then
+ install_state[history]="Saved"
+fi
+autoload -Uz compinstall
+zstyle :compinstall filename $zd/.zshrc
+if compinstall -d; then
+ install_state[completion]="Saved"
+fi
+
+
+clear
+print -r "This is the Z Shell configuration function for new users, $myname."
+if [[ $1 != -f ]]; then
+ print -r "You are seeing this message because you have no zsh startup files
+(the files .zshenv, .zprofile, .zshrc, .zlogin in the directory
+$zdmsg). This function can help you with a few settings that should
+make your use of the shell easier."
+fi
+
+print -r "
+You can:
+
+(q) Quit and do nothing. The function will be run again next time."
+if [[ ! -f $zd/.zshrc ]]; then
+ print -r "
+(0) Exit, creating the file $zdmsg/.zshrc containing just a comment.
+ That will prevent this function being run again."
+fi
+print -r "
+(1) Continue to main menu.
+"
+
+read -k key$longprompt
+print
+
+case $key in
+ ([qQ])
+ return 0
+ ;;
+
+ (0)
+ print -r $msg >$zd/.zshrc
+ return 0
+ ;;
+
+ (1)
+ ;;
+
+ (*)
+ print -r "Aborting."
+ if [[ $1 != -f ]]; then
+ print "The function will be run again next time. To prevent this, execute:
+ touch $zdmsg/.zshrc"
+ fi
+ return 1
+ ;;
+esac
+
+while true; do
+ clear
+ print -nr "Please pick one of the following options:
+
+(1) Configure settings for history, i.e. command lines remembered
+ and saved by the shell.\
+${install_state[history]:+ ($install_state[history].)}
+
+(2) "
+ if [[ $install_state[completion] = Recommended ]]; then
+ print -nr "Configure"
+ else
+ print -nr "Use"
+ fi
+ print -r " the new completion system.\
+${install_state[completion]:+ ($install_state[completion].)}
+
+(3) Configure how keys behave when editing command lines.
+
+(4) Pick some of the more common shell options. These are simple on
+ or off switches controlling the shell's features. \
+${install_state[options]:+ ($install_state[options].)}
+"
+ print -nr "(0) Exit, "
+ if (( save )); then
+ print -r "saving the new settings. They will take effect immediately."
+ elif [[ -f $zd/.zshrc ]]; then
+ print -r "leaving the existing $zdmsg/.zshrc alone."
+ else
+ print -r "creating a blank $zdmsg/.zshrc file."
+ fi
+ print -r "
+(a) Abort all settings and start from scratch. Note this will overwrite
+ any settings from $myname already in the startup file.
+ It will not alter any of your other settings, however."
+ if [[ $1 = -f ]]; then
+ print -r "
+(q) Quit and do nothing else."
+ else
+ print -r "
+(q) Quit and do nothing else. The function will be run again next time."
+ fi
+
+ read -k key$longprompt
+ print
+
+ case $key in
+ ([qQ])
+ break
+ ;;
+
+ ([aA])
+ parsed_parameters=()
+ state_parameters=()
+ parsed_options=()
+ state_options=()
+ parsed_keymaps=()
+ state_keymaps=()
+ parsed_bindings=()
+ state_bindings=()
+ unparsed=()
+ ;;
+
+ (0)
+ clear
+ if (( save )); then
+ if [[ -f $zd/.zshrc ]]; then
+ cp $zd/.zshrc $zd/.zshrc.zni &&
+ print -r "Copied old '$zdd/.zshrc' to '$zdd/.zshrc.zni'.
+"
+ fi
+
+ __zni_save
+ __zni_completion_save
+ elif [[ ! -f $zd/.zshrc ]]; then
+ print -r $msg >$zd/.zshrc
+ fi
+ if [[ $1 != -f ]]; then
+ print -r "The function will not be run in future, but you can run
+it yourself as follows:
+ autoload $myname
+ $myname -f
+
+The code added to $zdmsg/.zshrc is marked by the lines
+$startline
+$endline
+You should not edit anything between these lines if you intend to
+run $myname again. You may, however, edit any other part
+of the file."
+ fi
+ break
+ ;;
+
+ (1)
+ __zni_history_config
+ ;;
+
+ (2)
+ __zni_completion_config
+ ;;
+
+ (3)
+ __zni_bindkey_config
+ ;;
+
+ (4)
+ __zni_options_config
+ ;;
+ esac
+done
-unfunction zsh-newuser-install
+} always {
+ # Tidy up: always executed unless the shell is stopped dead
+ # in its tracks.
+ unfunction -m $myname __zni_\*
+ rm -f $tmpfile*
+}