Shell – Asynchronous RPROMPT

promptshell-scriptzsh

A friend of mine posted this on StackOverflow, and I thought we might be more likely to get an answer here. His post refers to speed, but we now know that the python script we are using to parse git status is slow, and at some point we intend to rewrite it to be faster. however, the question of settign RPROMPT asynchronously is still interesting to me, so I thought I’d quote his question here:

Since I started using git I've had my RPROMPT setup to show the current branch. I've recently been using some of the "fancy" scripts to show un/staged file counts and other useful at a glance things. (https://github.com/olivierverdier/zsh-git-prompt/tree/master)

After using this for a week or two, its performance started to bother me.

Are there faster ways of getting this info or are there maybe ways to asynchronously write the RPROMPT? I don't want to wait to type a command while the RPROMPT is computed and would be perfectly happy with it popping in slightly later then my main PROMPT.

No offense to the aforementioned script; it's great. I'm just impatient.

Best Answer

Here's a solution that uses a background job and signals to asynchronously update the prompt.

The idea is to have your prompt function spawn off a background job that builds the prompt, writes it to a file, then sends a signal to the parent shell that it's done. When the parent shell gets the signal it reads the prompt from the file and redraws the prompt.

In your prompt function, put this:

function async-build-prompt {
    # Simulate a function that takes a couple seconds to build the prompt.
    # Replace this line with your actual function.
    sleep 2 && RPROMPT=$(date)

    # Save the prompt in a temp file so the parent shell can read it.
    printf "%s" $RPROMPT > ${TMPPREFIX}/prompt.$$

    # Signal the parent shell to update the prompt.
    kill --signal USR2 $$
}

# Build the prompt in a background job.
async-build-prompt &!

And in your .zshrc, put this:

function TRAPUSR2 {
    RPROMPT=$(cat "${TMPPREFIX}/prompt.$$")

    # Force zsh to redisplay the prompt.
    zle && zle reset-prompt
}
Related Question