Migrate sorin prompt to zsh-async (#1385)

This includes some improvements by @indrajitr in addition to the main migration.

The first step was to avoid PROMPT and RPROMPT modification when possible (which may help resolve some other issues as well relating to zsh crashes with the sorin prompt) then update the displayed git information in a separate variable rather than a command.

We use zsh-async for creating and running background tasks. The sorin prompt uses it to update git info without blocking the prompt from displaying (because of how long it can take). In the future it may be worth moving more tasks and more prompts to using this.

The move to zsh-async does make the git prompt slower in some circumstances (most noticeable in large repos), but this is a worthwhile tradeoff to avoid the cache file which had a number of potential security holes.

We have also switched to adding zsh-async as an external submodule (rather than the version bundled with pure) which may cause some migration headaches, but it will be worth it in the long run.
main
Kaleb Elwert 7 years ago committed by GitHub
parent 9f1a41de90
commit 9bdc1b35d5

3
.gitmodules vendored

@ -22,3 +22,6 @@
[submodule "modules/fasd/external"] [submodule "modules/fasd/external"]
path = modules/fasd/external path = modules/fasd/external
url = https://github.com/clvv/fasd.git url = https://github.com/clvv/fasd.git
[submodule "modules/prompt/external/async"]
path = modules/prompt/external/async
url = https://github.com/mafredri/zsh-async.git

@ -0,0 +1 @@
Subproject commit 28c7a644227a5ad7249193525ef27734781f6a63

@ -1 +1 @@
../external/pure/async.zsh ../external/async/async.zsh

@ -32,49 +32,33 @@
# Load dependencies. # Load dependencies.
pmodload 'helper' pmodload 'helper'
prompt_sorin_git_info() { function prompt_sorin_git_info {
if (( _prompt_sorin_precmd_async_pid > 0 )); then # We can safely split on ':' because it isn't allowed in ref names.
# Append Git status. IFS=':' read _git_target _git_post_target <<<"$3"
if [[ -s "$_prompt_sorin_precmd_async_data" ]]; then
alias typeset='typeset -g' # The target actually contains 3 space separated possibilities, so we need to
source "$_prompt_sorin_precmd_async_data" # make sure we grab the first one.
RPROMPT+='${git_info:+${(e)git_info[status]}}' _git_target=$(coalesce ${(@)${(z)_git_target}})
unalias typeset
if [[ -z "$_git_target" ]]; then
# No git target detected, flush the git fragment and redisplay the prompt.
if [[ -n "$_prompt_sorin_git" ]]; then
_prompt_sorin_git=''
zle && zle reset-prompt
fi fi
else
# Reset PID. # Git target detected, update the git fragment and redisplay the prompt.
_prompt_sorin_precmd_async_pid=0 _prompt_sorin_git="${_git_target}${_git_post_target}"
# Redisplay prompt.
zle && zle reset-prompt zle && zle reset-prompt
fi fi
} }
function prompt_sorin_precmd_async { function prompt_sorin_async_git {
# Get Git repository information. cd -q "$1"
if (( $+functions[git-info] )); then if (( $+functions[git-info] )); then
git-info git-info
### TODO XXX print ${git_info[status]}
# This section exists to patch over vulnerabilities when sourcing the
# file in $_prompt_sorin_precmd_async_data. Without it if a branch is named
# $foo it will expand if we have a $foo variable, and a branch named
# $(IFS=_;cmd=rm_-rf_~;$cmd) could delete the users home directory.
# This is a stopgap to prevent code execution and fix the vulnerability,
# but it eventually needs to be removed in favor of zsh_async and not using
# a file to store the prompt data in.
###
local tmp_prompt_var=$(typeset -p git_info)
# Replace all $ with $\ to escape
tmp_prompt_var=${tmp_prompt_var//\$/\\$}
# Unescape the first \$ as it's our $( )
tmp_prompt_var=${tmp_prompt_var:s/\\$/\$}
# Escape all backticks ` to \`
tmp_prompt_var=${tmp_prompt_var//\`/\\\`}
printf "%s\n" "$tmp_prompt_var" >! "$_prompt_sorin_precmd_async_data"
fi fi
# Signal completion to parent process.
kill -WINCH $$
} }
function prompt_sorin_precmd { function prompt_sorin_precmd {
@ -84,18 +68,21 @@ function prompt_sorin_precmd {
# Format PWD. # Format PWD.
_prompt_sorin_pwd=$(prompt-pwd) _prompt_sorin_pwd=$(prompt-pwd)
# Define prompts.
RPROMPT='${editor_info[overwrite]}%(?:: %F{1}⏎%f)${VIM:+" %B%F{6}V%f%b"}'
# Kill the old process of slow commands if it is still running. # Kill the old process of slow commands if it is still running.
if (( _prompt_sorin_precmd_async_pid > 0 )); then async_flush_jobs async_sorin_git
kill -KILL "$_prompt_sorin_precmd_async_pid" &>/dev/null
# Handle updating git data. We also clear the git prompt data if we're in a
# different git root now.
if (( $+functions[git-dir] )); then
local new_git_root="$(git-dir 2>/dev/null)"
if [[ $new_git_root != $_sorin_cur_git_root ]]; then
_prompt_sorin_git=''
_sorin_cur_git_root=$new_git_root
fi
fi fi
# Compute slow commands in the background. # Compute slow commands in the background.
trap prompt_sorin_git_info WINCH async_job async_sorin_git prompt_sorin_async_git "$PWD"
prompt_sorin_precmd_async &!
_prompt_sorin_precmd_async_pid=$!
} }
function prompt_sorin_setup { function prompt_sorin_setup {
@ -107,6 +94,7 @@ function prompt_sorin_setup {
# Load required functions. # Load required functions.
autoload -Uz add-zsh-hook autoload -Uz add-zsh-hook
autoload -Uz async && async
# Add hook for calling git-info before each command. # Add hook for calling git-info before each command.
add-zsh-hook precmd prompt_sorin_precmd add-zsh-hook precmd prompt_sorin_precmd
@ -133,11 +121,19 @@ function prompt_sorin_setup {
zstyle ':prezto:module:git:info:unmerged' format ' %%B%F{3}═%f%%b' zstyle ':prezto:module:git:info:unmerged' format ' %%B%F{3}═%f%%b'
zstyle ':prezto:module:git:info:untracked' format ' %%B%F{7}◼%f%%b' zstyle ':prezto:module:git:info:untracked' format ' %%B%F{7}◼%f%%b'
zstyle ':prezto:module:git:info:keys' format \ zstyle ':prezto:module:git:info:keys' format \
'status' '$(coalesce "%b" "%p" "%c")%s%A%B%S%a%d%m%r%U%u' 'status' '%b %p %c:%s%A%B%S%a%d%m%r%U%u'
# Get the async worker set up
async_start_worker async_sorin_git -n
async_register_callback async_sorin_git prompt_sorin_git_info
_sorin_cur_git_root=''
_prompt_sorin_git=''
_prompt_sorin_pwd=''
# Define prompts. # Define prompts.
PROMPT='${SSH_TTY:+"%F{9}%n%f%F{7}@%f%F{3}%m%f "}%F{4}${_prompt_sorin_pwd}%(!. %B%F{1}#%f%b.)${editor_info[keymap]} ' PROMPT='${SSH_TTY:+"%F{9}%n%f%F{7}@%f%F{3}%m%f "}%F{4}${_prompt_sorin_pwd}%(!. %B%F{1}#%f%b.)${editor_info[keymap]} '
RPROMPT='' RPROMPT='${editor_info[overwrite]}%(?:: %F{1}⏎%f)${VIM:+" %B%F{6}V%f%b"}${_prompt_sorin_git}'
SPROMPT='zsh: correct %F{1}%R%f to %F{2}%r%f [nyae]? ' SPROMPT='zsh: correct %F{1}%R%f to %F{2}%r%f [nyae]? '
} }

Loading…
Cancel
Save