Search code examples
zshexit-codezshrczsh-zle

How can I maintain my last command’s exit-status while resetting my prompt in ZSH?


I’m trying to make an über-simple (single-character) prompt that gives me as much information as possible. Here’s what I have at the moment (had it for ages, can’t remember where I found the original):

# RPS1="['%1v', '%2v', '%3v', '%4v', '%5v', '%6v', '%7v', '%8v', '%9v']" # debug
PS1=" %(?|%2F|%1F)%1(V|%1v|%(#|#|:))%(?|%2f|%1f) "

function zle-line-init {
  zle -K vicmd
}
zle -N   zle-line-init

function zle-keymap-select {
  psvar[1]="${${KEYMAP/(main|viins)/>}/vicmd/}"
  zle reset-prompt
  psvar[1]=""
}
zle -N   zle-keymap-select

This is fairly simple; it initializes the prompt into command mode on rendering, and displays a : prompt when in the vi’s “command” mode, and a > prompt (by changing psvar[1]) when in vi’s “insert” mode. In addition, the : is replaced with # if you are acting as root, and the color of the character exhibits the exit status of the last command.

My problem:

When the keymap changes (that is, when I toggle through the “command” and “insert” modes using, as an example, a and then the escape key, the $? exit-status is overtrodden with a successful 0 status, thus causing the prompt to display in green instead of red (even if the previous command failed). How can I save or set the prompt such that the %(?|…|…) portions of $PS1 will properly display the exit status of the last command sent to the shell?


Solution

  • I got the following answer from Bart Schaefer on the zsh-users mailing list. It works!

    This was fixed in July 2010, so eventually you won't need to do anything:

       * users/15217: Src/Zle/zle_main.c: use top-level status when redrawing prompt.
    

    In the meantime, try this:

    function zle-keymap-select { local Q=$? psvar[1]="${${KEYMAP/(main|viins)/>}/vicmd/}" (return $Q) zle reset-prompt psvar[1]="" }

    If your zsh is recent enough to have anonymous functions, you can replace the subshell (return $Q) with (){ return $Q }.