From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 5310 invoked by alias); 28 Jul 2017 02:53:41 -0000 Mailing-List: contact zsh-workers-help@zsh.org; run by ezmlm Precedence: bulk X-No-Archive: yes List-Id: Zsh Workers List List-Post: List-Help: X-Seq: 41472 Received: (qmail 403 invoked from network); 28 Jul 2017 02:53:41 -0000 X-Qmail-Scanner-Diagnostics: from mail-pf0-f177.google.com by f.primenet.com.au (envelope-from , uid 7791) with qmail-scanner-2.11 (clamdscan: 0.99.2/21882. spamassassin: 3.4.1. Clear:RC:0(209.85.192.177):SA:0(-2.8/5.0):. Processed in 1.368721 secs); 28 Jul 2017 02:53:41 -0000 X-Spam-Checker-Version: SpamAssassin 3.4.1 (2015-04-28) on f.primenet.com.au X-Spam-Level: X-Spam-Status: No, score=-2.8 required=5.0 tests=RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H2,SPF_PASS,T_DKIM_INVALID autolearn=unavailable autolearn_force=no version=3.4.1 X-Envelope-From: schaefer@brasslantern.com X-Qmail-Scanner-Mime-Attachments: | X-Qmail-Scanner-Zip-Files: | Received-SPF: pass (ns1.primenet.com.au: SPF record at _netblocks.google.com designates 209.85.192.177 as permitted sender) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=brasslantern-com.20150623.gappssmtp.com; s=20150623; h=from:message-id:date:in-reply-to:comments:references:to:subject :mime-version; bh=9aof0SWAu8y5M5olP367C27DN40Jy/hDbRaqxjO3Spk=; b=P4bfdPNH/iFrn500BM9aRcw8QULBnYyQm4LV3U0ERXth/Tj7L+xAqQ1HFnE3NjTEj2 sTXXnwUzKvQKo8twrO0w0FTslJh8CGoJwXoLJrWP0CK3gQmbS+SJcpTrxNODoKYsrU+8 By1jPNN3Z4fu3yVOFckqxx1uekrfwlPcjVGUR5UdhKx4XuH3YxcmuXlDs/xtD2+wQJIq AyD1IY2GSGpPgPE+6NMzmmff4rXMbolSg0/SdCj7YyF3/r+X2KQc6v4zGjShrswKVL/G oAFk9pIOchRpbak/o7OUeqOFP0LwLs/mmRQi3cB5+5U8tnixo8oR1ngZQHWwRYj3OVKx dn7A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:message-id:date:in-reply-to:comments :references:to:subject:mime-version; bh=9aof0SWAu8y5M5olP367C27DN40Jy/hDbRaqxjO3Spk=; b=fpZgCvtkX7hHbl3J08yt+j5WGa2+lu7x7AAKJGpeNhC+8OSJd5ZlPAMpdhF5khKCa5 9X8Tspk9yypXxuWOe67mdCcZdy0cqErqL/+EkjVxEjhSmQBnleT5qX6iSHQ2LChlzNxT 6wCKOa3IVkZ0/qgnanfRP7ld01hMo6qUKlkgJ7QhVZa3dJE89XmnJtbyxaQTt0s6NnEW uP1Q4wLePYOIGPAzcFpUFEc5aO3xjgXQAn2cFgg1IHy6ndiKspUj91Z+M7BPSs2vku++ aI/w/LiGDqNcnd++PWaMRC/EEqstKemfC2cWtQFM5zyH6zfYuhBd4fONLNWTQvW5DMit 6ThA== X-Gm-Message-State: AIVw113Xgv/yiqsP+utpBD8RdtHuBMFFB+T7EgHt40PCLTVdXNyfb1TJ Tkqjb1KWUMPAdv6W9PM= X-Received: by 10.84.178.101 with SMTP id y92mr3013485plb.118.1501210412300; Thu, 27 Jul 2017 19:53:32 -0700 (PDT) From: Bart Schaefer Message-Id: <170727195331.ZM18653@torch.brasslantern.com> Date: Thu, 27 Jul 2017 19:53:31 -0700 In-Reply-To: Comments: In reply to Daniel Tameling "Re: When RPROMPT != RPS1" (Jun 24, 10:09pm) References: <170621101645.ZM9838@torch.brasslantern.com> <20170622101735.4ebebd11@pwslap01u.europe.root.pri> <170623203737.ZM27250@torch.brasslantern.com> X-Mailer: OpenZMail Classic (0.9.2 24April2005) To: zsh-workers@zsh.org Subject: Re: When RPROMPT != RPS1 MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii I went on vacation for a while immediately after this discussion, so it got left sitting for a bit. Here's a patch which does several things for the prompt theme system: - introduces a standard way for themes to clean up after themselves, the prompt_cleanup callback (aka hook). I've modified "prompt bart" to use this by way of example. - added the "default" and "restore" themes. The "restore" theme is a special case that calls the cleanup hooks to put everything back as it was before you experimented with some other prompt theme. - adds documentation for the above plus a new subsection about how to write a new theme. There are still a few tweaks possible, I expect. There's an awful lot of state to keep track of here. diff --git a/Doc/Zsh/contrib.yo b/Doc/Zsh/contrib.yo index 35ab100..35ce915 100644 --- a/Doc/Zsh/contrib.yo +++ b/Doc/Zsh/contrib.yo @@ -1920,8 +1920,9 @@ subsect(Installation) You should make sure all the functions from the tt(Functions/Prompts) directory of the source distribution are available; they all begin with the string `tt(prompt_)' except for the special function`tt(promptinit)'. -You also need the `tt(colors)' function from tt(Functions/Misc). All of -these functions may already have been installed on your system; if not, +You also need the `tt(colors)' and `tt(add-zsh-hook)' functions from +tt(Functions/Misc). +All these functions may already be installed on your system; if not, you will need to find them and copy them. The directory should appear as one of the elements of the tt(fpath) array (this should already be the case if they were installed), and at least the function tt(promptinit) @@ -1975,6 +1976,75 @@ normally call a theme's setup function directly. ) enditem() +subsect(Utility Themes) + +startitem() +item(tt(prompt off))( +The theme `tt(off)' sets all the prompt variables to minimal values with +no special effects. +) +item(tt(prompt default))( +The theme `tt(default)' sets all prompt variables to the same state as +if an interactive zsh was started with no initialization files. +) +item(tt(prompt restore))( +The special theme `tt(restore)' erases all theme settings and sets prompt +variables to their state before the first time the `tt(prompt)' function +was run, provided each theme has properly defined its cleanup (see below). + +Note that you can undo `tt(prompt off)' and `tt(prompt default)' with +`tt(prompt restore)', but a second restore does not undo the first. +) +enditem() + +subsect(Writing Themes) + +The first step for adding your own theme is to choose a name for it, +and create a file `tt(prompt_var(name)_setup)' in a directory in your +tt(fpath), such as tt(~/myfns) in the example above. The file should +at minimum contain assignments for the prompt variables that your +theme wishes to modify. By convention, themes use tt(PS1), tt(PS2), +tt(RPS1), etc., rather than the longer tt(PROMPT) and tt(RPROMPT). + +The file is autoloaded as a function in the current shell context, so +it may contain any necessary commands to customize your theme, including +defining additional functions. To make some complex tasks easier, your +setup function may also do any of the following: + +startitem() +item(Assign tt(prompt_opts))( +The array tt(prompt_opts) may be assigned any of tt("bang"), tt("cr"), +tt("percent"), tt("sp"), and/or tt("subst") as values. The corresponding +setopts (tt(promptbang), etc.) are turned on, all other prompt-related +options are turned off. The tt(prompt_opts) array preserves setopts even +beyond the scope of tt(localoptions), should your function need that. +) +item(Modify precmd and preexec)( +Use of tt(add-zsh-hook) is recommended. The tt(precmd) and tt(preexec) +hooks are automatically adjusted if the prompt theme changes or is +disabled. +) +item(Declare cleanup)( +If your function makes any other changes that should be undone when the +theme is disabled, your setup function may call +example(prompt_cleanup var(command)) +where var(command) should be suitably quoted. If your theme is ever +disabled or replaced by another, var(command) is executed with tt(eval). +You may declare more than one such cleanup hook. +) +item(Define preview)( +Define or autoload a function tt(prompt_var(name)_preview) to display +a simulated version of your prompt. A simple default previewer is +defined by tt(promptinit) for themes that do not define their own. +This preview function is called by `tt(prompt -p)'. +) +item(Provide help)( +Define or autoload a function tt(prompt_var(name)_help) to display +documentation or help text for your theme. +This help function is called by `tt(prompt -h)'. +) +enditem() + texinode(ZLE Functions)(Exception Handling)(Prompt Themes)(User Contributions) sect(ZLE Functions) diff --git a/Functions/Prompts/prompt_bart_setup b/Functions/Prompts/prompt_bart_setup index cb032de..6de4122 100644 --- a/Functions/Prompts/prompt_bart_setup +++ b/Functions/Prompts/prompt_bart_setup @@ -16,9 +16,13 @@ prompt_bart_help () { blue, and the default foreground) are used if no arguments are given. The defaults look best on a light background. - The "off" token temporarily disables the theme; "on" restores it. No background colors or hardwired cursor motion escapes are used, and it is not necessary to setopt promptsubst. + + The "off" token temporarily disables the theme; "on" restores it. + Note, this does NOT fully reset to the original prompt state, it + only hides/reveals the extra lines above the command line and + removes the supporting hooks. EOF [[ $(read -sek 1 "?${(%):-%S[press return]%s}") == [Qq] ]] && print -nP '\r%E' && return @@ -183,7 +187,7 @@ prompt_bart_setup () { add-zsh-hook -D preexec "prompt_*_preexec" functions[TRAPWINCH]="${functions[TRAPWINCH]//prompt_bart_winch}" [[ $prompt_theme[1] = bart ]] && PS1=${${(f)PS1}[-1]} - return 1 + return 1 # Prevent change of $prompt_theme ;; (on|enable) shift @@ -224,6 +228,8 @@ prompt_bart_setup () { add-zsh-hook precmd prompt_bart_precmd add-zsh-hook preexec prompt_bart_preexec + prompt_cleanup \ + 'functions[TRAPWINCH]="${functions[TRAPWINCH]//prompt_bart_winch}"' functions[TRAPWINCH]="${functions[TRAPWINCH]//prompt_bart_winch} prompt_bart_winch" diff --git a/Functions/Prompts/prompt_default_setup b/Functions/Prompts/prompt_default_setup new file mode 100644 index 0000000..aed74eb --- /dev/null +++ b/Functions/Prompts/prompt_default_setup @@ -0,0 +1,7 @@ +PS1='%m%# ' +PS2='%_> ' +PS3='?# ' +PS4='+%N:%i> ' +unset RPS1 RPS2 RPROMPT RPROMPT2 + +prompt_opts=( cr percent sp ) diff --git a/Functions/Prompts/prompt_off_setup b/Functions/Prompts/prompt_off_setup index f604b47..e6d16bf 100644 --- a/Functions/Prompts/prompt_off_setup +++ b/Functions/Prompts/prompt_off_setup @@ -1,9 +1,10 @@ # Very simple prompt -prompt_off_setup () { - PS1="%# " - PS2="> " - prompt_opts=( cr percent ) -} +prompt_default_setup 2>/dev/null -prompt_off_setup "$@" +PS1="%# " +PS2="> " +PS3='?# ' +PS4='+> ' + +prompt_opts=( cr percent sp ) diff --git a/Functions/Prompts/prompt_restore_setup b/Functions/Prompts/prompt_restore_setup new file mode 100644 index 0000000..54c4adb --- /dev/null +++ b/Functions/Prompts/prompt_restore_setup @@ -0,0 +1,2 @@ +# Damn that was easy +zstyle -t :prompt-theme cleanup diff --git a/Functions/Prompts/promptinit b/Functions/Prompts/promptinit index 5872489..e27b877 100644 --- a/Functions/Prompts/promptinit +++ b/Functions/Prompts/promptinit @@ -47,20 +47,36 @@ prompt_preview_safely() { return fi - local -a psv; psv=($psvar); local -a +h psvar; psvar=($psv) # Ick - local +h PS1=$PS1 PS2=$PS2 PS3=$PS3 PS4=$PS4 RPS1=$RPS1 - local -a precmd_functions preexec_functions - - # The next line is a bit ugly. It (perhaps unnecessarily) - # runs the prompt theme setup function to ensure that if - # the theme has a _preview function that it's been autoloaded. - prompt_${1}_setup - - if typeset +f prompt_${1}_preview >&/dev/null; then - prompt_${1}_preview "$@[2,-1]" - else - prompt_preview_theme "$@" - fi + # This handles all the stuff from the default :prompt-theme cleanup + local +h PS1=$PS1 PS2=$PS2 PS3=$PS3 PS4=$PS4 RPS1=$RPS1 RPS2=$RPS2 + local +h PROMPT=$PROMPT RPROMPT=$RPOMPT RPROMPT2=$RPROMPT2 PSVAR=$PSVAR + local -a precmd_functions preexec_functions prompt_preview_cleanup + local -aLl +h zle_highlight + + { + # Save and clear current restore-point if any + zstyle -g prompt_preview_cleanup :prompt-theme cleanup + { + zstyle -d :prompt-theme cleanup + + # The next line is a bit ugly. It (perhaps unnecessarily) + # runs the prompt theme setup function to ensure that if + # the theme has a _preview function that it's been autoloaded. + prompt_${1}_setup + + if typeset +f prompt_${1}_preview >&/dev/null; then + prompt_${1}_preview "$@[2,-1]" + else + prompt_preview_theme "$@" + fi + } always { + # Run any theme-specific cleanup, then reset restore point + zstyle -t :prompt-theme cleanup + } + } always { + (( $#prompt_preview_cleanup )) && + zstyle -e :prompt-theme cleanup "${prompt_preview_cleanup[@]}" + } } set_prompt() { @@ -84,9 +100,9 @@ Use prompt -h for help on specific themes.' setopt localtraps if [[ -z "$prompt_theme[1]" ]]; then # Not using a prompt theme; save settings - local -a psv; psv=($psvar); local -a +h psvar; psvar=($psv) # Ick - local +h PS1=$PS1 PS2=$PS2 PS3=$PS3 PS4=$PS4 RPS1=$RPS1 - local precmd_functions preexec_functions + local +h PS1=$PS1 PS2=$PS2 PS3=$PS3 PS4=$PS4 RPS1=$RPS1 RPS2=$RPS2 + local +h PROMPT=$PROMPT RPROMPT=$RPOMPT RPROMPT2=$RPROMPT2 PSVAR=$PSVAR + local -a precmd_functions preexec_functions else trap 'prompt_${prompt_theme[1]}_setup "${(@)prompt_theme[2,-1]}"' 0 fi @@ -104,11 +120,11 @@ Use prompt -h for help on specific themes.' ;; h) if [[ -n "$2" && -n $prompt_themes[(r)$2] ]]; then if functions prompt_$2_setup >/dev/null; then - # The next line is a bit ugly. It (perhaps unnecessarily) - # runs the prompt theme setup function to ensure that if - # the theme has a _help function that it's been autoloaded. - prompt_$2_setup - fi + # The next line is a bit ugly. It (perhaps unnecessarily) + # runs the prompt theme setup function to ensure that if + # the theme has a _help function that it's been autoloaded. + prompt_$2_setup + fi if functions prompt_$2_help >/dev/null; then print "Help for $2 theme:\n" prompt_$2_help @@ -168,28 +184,74 @@ Use prompt -h for help on specific themes.' esac } +prompt_cleanup () { + local -a cleanup_hooks + if zstyle -g cleanup_hooks :prompt-theme cleanup + then + cleanup_hooks+=(';' "$@") + zstyle -e :prompt-theme cleanup "${cleanup_hooks[@]}" + elif (( $+prompt_preview_cleanup == 0 )) + then + print -u2 "prompt_cleanup: no prompt theme active" + return 1 + fi +} + prompt () { - local prompt_opts + local -a prompt_opts theme_active + zstyle -g theme_active :prompt-theme cleanup || { + # This is done here rather than in set_prompt so that it + # is safe and sane for set_prompt to setopt localoptions, + # which will be cleared before we arrive back here again. + # This is also why we pass around the prompt_opts array. + [[ -o promptbang ]] && prompt_opts+=(bang) + [[ -o promptcr ]] && prompt_opts+=(cr) + [[ -o promptpercent ]] && prompt_opts+=(percent) + [[ -o promptsp ]] && prompt_opts+=(sp) + [[ -o promptsubst ]] && prompt_opts+=(subst) + zstyle -e :prompt-theme cleanup \ + 'zstyle -d :prompt-theme cleanup;' \ + 'prompt_default_setup;' \ + ${PS1+PS1="${(q)PS1}"} \ + ${PS2+PS2="${(q)PS2}"} \ + ${PS3+PS3="${(q)PS3}"} \ + ${PS4+PS4="${(q)PS4}"} \ + ${RPS1+RPS1="${(q)RPS1}"} \ + ${RPS2+RPS2="${(q)RPS2}"} \ + ${RPROMPT+RPROMPT="${(q)RPROMPT}"} \ + ${RPROMPT2+RPROMPT2="${(q)RPROMPT2}"} \ + ${PSVAR+PSVAR="${(q)PSVAR}"} \ + "precmd_functions=(${(q)precmd_functions[@]})" \ + "preexec_functions=(${(q)preexec_functions[@]})" \ + "prompt_opts=( ${prompt_opts[*]} )" \ + 'reply=(yes)' + } set_prompt "$@" - (( $#prompt_opts )) && - setopt noprompt{bang,cr,percent,subst} "prompt${^prompt_opts[@]}" + (( ${#prompt_opts} )) && + setopt noprompt{bang,cr,percent,sp,subst} "prompt${^prompt_opts[@]}" true } prompt_preview_theme () { emulate -L zsh - local -a psv; psv=($psvar); local -a +h psvar; psvar=($psv) # Ick - local +h PS1=$PS1 PS2=$PS2 PS3=$PS3 PS4=$PS4 RPS1=$RPS1 - local precmd_functions preexec_functions prompt_opts - local -aLl +h zle_highlight + # Check for proper state handling + (( $+prompt_preview_cleanup )) || { + prompt_preview_safely "$@" + return + } + + # Minimal preview for prompts that don't supply one + local -a prompt_opts print -n "$1 theme" (( $#* > 1 )) && print -n " with parameters \`$*[2,-1]'" print ":" prompt_${1}_setup "$@[2,-1]" + (( ${#prompt_opts} )) && + setopt noprompt{bang,cr,percent,sp,subst} "prompt${^prompt_opts[@]}" [[ -n ${precmd_functions[(r)prompt_${1}_precmd]} ]] && prompt_${1}_precmd [[ -o promptcr ]] && print -n $'\r'; :