From ae73759d82da603e6e88e1a95d91274451defc91 Mon Sep 17 00:00:00 2001 From: Marlon Richert Date: Fri, 16 Apr 2021 23:00:44 +0300 Subject: [PATCH] Reset ZLE hooks when changing prompt themes --- Doc/Zsh/contrib.yo | 10 ++- Functions/Prompts/promptinit | 167 +++++++++++++++-------------------- 2 files changed, 79 insertions(+), 98 deletions(-) diff --git a/Doc/Zsh/contrib.yo b/Doc/Zsh/contrib.yo index 3c4fdded0..6e33cd6f9 100644 --- a/Doc/Zsh/contrib.yo +++ b/Doc/Zsh/contrib.yo @@ -2032,10 +2032,12 @@ 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(Modify hooks)( +Use of tt(add-zsh-hook) and tt(add-zle-hook-widget) is recommended (see +ifzman(the bf(Manipulating Hook Functions) section above)\ +ifnzman(noderef(Manipulating Hook Functions))\ +). All hooks that follow the naming pattern tt(prompt_)var(theme)tt(_)var(hook) +are automatically removed when the prompt theme changes or is disabled. ) item(Declare cleanup)( If your function makes any other changes that should be undone when the diff --git a/Functions/Prompts/promptinit b/Functions/Prompts/promptinit index 37d69f100..a6c9e31dd 100644 --- a/Functions/Prompts/promptinit +++ b/Functions/Prompts/promptinit @@ -14,6 +14,8 @@ prompt_themes=() promptinit () { emulate -L zsh setopt extendedglob + autoload -Uz add-zsh-hook add-zle-hook-widget + local ppath='' name theme local -a match mbegin mend @@ -32,9 +34,6 @@ promptinit () { fi done - # To manipulate precmd and preexec hooks... - autoload -Uz add-zsh-hook - # Variables common to all prompt styles prompt_newline=$'\n%{\r%}' } @@ -47,38 +46,23 @@ prompt_preview_safely() { return fi - # This handles all the stuff from the default :prompt-theme restore - local +h PS1=$PS1 PS2=$PS2 PS3=$PS3 PS4=$PS4 RPS1=$RPS1 RPS2=$RPS2 - local +h PROMPT=$PROMPT RPROMPT=$RPROMPT RPROMPT2=$RPROMPT2 PSVAR=$PSVAR - local -a precmd_functions preexec_functions prompt_preview_restore - local -aLl +h zle_highlight + # Run this in a subshell, so we don't need to clean up afterwards. + ( + # Execute current theme's cleanup sequence, if any. + zstyle -t :prompt-theme cleanup - { - # Save and clear current restore-point if any - zstyle -g prompt_preview_restore :prompt-theme restore - { - zstyle -d :prompt-theme restore - # Execute current cleanup sequence, if any. - zstyle -t :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. + # If we can't find a _preview function, run the _setup function to see if + # it will create one. + typeset +f prompt_${1}_preview >&/dev/null || 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_restore )) && - zstyle -e :prompt-theme restore "${prompt_preview_restore[@]}" - } + # ...then try again. + if typeset +f prompt_${1}_preview >&/dev/null; then + prompt_${1}_preview "$@[2,-1]" + else + prompt_preview_theme "$@" + fi + ) } set_prompt() { @@ -97,21 +81,6 @@ Options: Use prompt -h for help on specific themes.' getopts "chlps:" opt - case "$opt" in - (h|p) - setopt localtraps - if [[ -z "$prompt_theme[1]" ]]; then - # Not using a prompt theme; save settings - local +h PS1=$PS1 PS2=$PS2 PS3=$PS3 PS4=$PS4 RPS1=$RPS1 RPS2=$RPS2 - local +h PROMPT=$PROMPT RPROMPT=$RPROMPT RPROMPT2=$RPROMPT2 PSVAR=$PSVAR - local -a precmd_functions preexec_functions - local theme_reset='' - else - local theme_reset='prompt_${prompt_theme[1]}_setup "${(@)prompt_theme[2,-1]}"' - fi - trap 'zstyle -t :prompt-theme cleanup;'"${theme_reset:+ $theme_reset}" 0 - ;; - esac case "$opt" in c) if [[ -n $prompt_theme ]]; then print -n "Current prompt theme" @@ -123,21 +92,26 @@ Use prompt -h for help on specific themes.' return ;; h) if [[ -n "$2" && -n $prompt_themes[(r)$2] ]]; then - if functions prompt_$2_setup >/dev/null; then + # Run this in a subshell, so we don't need to clean up afterwards. + ( + # Execute current theme's cleanup sequence, if any. zstyle -t :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 _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 - else - print "No help available for $2 theme." - fi - print "\nType \`prompt -p $2' to preview the theme, \`prompt $2'" - print "to try it out, and \`prompt -s $2' to use it in future sessions." + + # If we can't find a _help function, run the _setup function to see + # if it will create one. + typeset +f prompt_$2_help >/dev/null || + prompt_$2_setup + + # ...then try again. + if typeset +f prompt_$2_help >/dev/null; then + print "Help for $2 theme:\n" + prompt_$2_help + else + print "No help available for $2 theme." + fi + print "\nType \`prompt -p $2' to preview the theme, \`prompt $2'" + print "to try it out, and \`prompt -s $2' to use it in future sessions." + ) else print "$usage" fi @@ -178,8 +152,13 @@ Use prompt -h for help on specific themes.' # Reset some commonly altered bits to the default local hook - for hook in chpwd precmd preexec periodic zshaddhistory zshexit; do - add-zsh-hook -D "${hook}" "prompt_*_${hook}" + for hook in chpwd precmd preexec periodic zshaddhistory zshexit \ + zsh_directory_name; do + add-zsh-hook -D "$hook" "prompt_*_$hook" + done + for hook in isearch-exit isearch-update line-pre-redraw line-init \ + line-finish history-line-set keymap-select; do + add-zle-hook-widget -D "$hook" "prompt_*_$hook" done typeset -ga zle_highlight=( ${zle_highlight:#default:*} ) (( ${#zle_highlight} )) || unset zle_highlight @@ -192,11 +171,8 @@ Use prompt -h for help on specific themes.' prompt_cleanup () { local -a cleanup_hooks theme_active - if ! zstyle -g cleanup_hooks :prompt-theme cleanup - then - (( $+prompt_preview_restore == 0 )) && - if ! zstyle -g theme_active :prompt-theme restore - then + if ! zstyle -g cleanup_hooks :prompt-theme cleanup; then + if ! zstyle -g theme_active :prompt-theme restore; then print -u2 "prompt_cleanup: no prompt theme active" return 1 fi @@ -225,22 +201,23 @@ prompt () { [[ -o promptpercent ]] && prompt_opts+=(percent) [[ -o promptsp ]] && prompt_opts+=(sp) [[ -o promptsubst ]] && prompt_opts+=(subst) - zstyle -e :prompt-theme restore \ - 'zstyle -d :prompt-theme restore;' \ - '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)' + zstyle -e :prompt-theme restore " + zstyle -d :prompt-theme restore + 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}} + $( add-zsh-hook -L ) + $( add-zle-hook-widget -L ) + prompt_opts=( $prompt_opts[*] ) + reply=( yes ) + " } set_prompt "$@" @@ -253,12 +230,6 @@ prompt () { prompt_preview_theme () { emulate -L zsh - # Check for proper state handling - (( $+prompt_preview_restore )) || { - prompt_preview_safely "$@" - return - } - # Minimal preview for prompts that don't supply one local -a prompt_opts print -n "$1 theme" @@ -268,12 +239,20 @@ prompt_preview_theme () { prompt_${1}_setup "$@[2,-1]" (( ${#prompt_opts} )) && setopt noprompt{bang,cr,percent,sp,subst} "prompt${^prompt_opts[@]}" + + [[ -n ${chpwd_functions[(r)prompt_${1}_chpwd]} ]] && + prompt_${1}_chpwd [[ -n ${precmd_functions[(r)prompt_${1}_precmd]} ]] && - prompt_${1}_precmd - [[ -o promptcr ]] && print -n $'\r'; : - print -P "${PS1}command arg1 arg2 ... argn" + prompt_${1}_precmd + + # We'd like to call zle-line-init/finish hooks, too, but that's not possible + # while the ZLE is not active. + + [[ -o promptcr ]] && print -n $'\r' + :; print -P -- "${PS1}command arg1 arg2 ... argn" + [[ -n ${preexec_functions[(r)prompt_${1}_preexec]} ]] && - prompt_${1}_preexec + prompt_${1}_preexec } [[ -o kshautoload ]] || promptinit "$@" -- 2.31.1