diff --git a/.gitmodules b/.gitmodules index 46e6a0e69d7..365b059226a 100644 --- a/.gitmodules +++ b/.gitmodules @@ -22,3 +22,6 @@ [submodule "modules/fasd/external"] path = modules/fasd/external 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 diff --git a/modules/prompt/external/async b/modules/prompt/external/async new file mode 160000 index 00000000000..28c7a644227 --- /dev/null +++ b/modules/prompt/external/async @@ -0,0 +1 @@ +Subproject commit 28c7a644227a5ad7249193525ef27734781f6a63 diff --git a/modules/prompt/functions/async b/modules/prompt/functions/async index d4b591e1e9a..ae0ec558e4e 120000 --- a/modules/prompt/functions/async +++ b/modules/prompt/functions/async @@ -1 +1 @@ -../external/pure/async.zsh \ No newline at end of file +../external/async/async.zsh \ No newline at end of file diff --git a/modules/prompt/functions/prompt_sorin_setup b/modules/prompt/functions/prompt_sorin_setup index b37af7f843d..8eca6f6bb14 100644 --- a/modules/prompt/functions/prompt_sorin_setup +++ b/modules/prompt/functions/prompt_sorin_setup @@ -32,49 +32,33 @@ # Load dependencies. pmodload 'helper' -prompt_sorin_git_info() { - if (( _prompt_sorin_precmd_async_pid > 0 )); then - # Append Git status. - if [[ -s "$_prompt_sorin_precmd_async_data" ]]; then - alias typeset='typeset -g' - source "$_prompt_sorin_precmd_async_data" - RPROMPT+='${git_info:+${(e)git_info[status]}}' - unalias typeset +function prompt_sorin_git_info { + # We can safely split on ':' because it isn't allowed in ref names. + IFS=':' read _git_target _git_post_target <<<"$3" + + # The target actually contains 3 space separated possibilities, so we need to + # make sure we grab the first one. + _git_target=$(coalesce ${(@)${(z)_git_target}}) + + 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 - - # Reset PID. - _prompt_sorin_precmd_async_pid=0 - - # Redisplay prompt. + else + # Git target detected, update the git fragment and redisplay the prompt. + _prompt_sorin_git="${_git_target}${_git_post_target}" zle && zle reset-prompt fi } -function prompt_sorin_precmd_async { - # Get Git repository information. +function prompt_sorin_async_git { + cd -q "$1" if (( $+functions[git-info] )); then git-info - ### TODO XXX - # 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" + print ${git_info[status]} fi - - # Signal completion to parent process. - kill -WINCH $$ } function prompt_sorin_precmd { @@ -84,18 +68,21 @@ function prompt_sorin_precmd { # Format 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. - if (( _prompt_sorin_precmd_async_pid > 0 )); then - kill -KILL "$_prompt_sorin_precmd_async_pid" &>/dev/null + async_flush_jobs async_sorin_git + + # 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 # Compute slow commands in the background. - trap prompt_sorin_git_info WINCH - prompt_sorin_precmd_async &! - _prompt_sorin_precmd_async_pid=$! + async_job async_sorin_git prompt_sorin_async_git "$PWD" } function prompt_sorin_setup { @@ -107,6 +94,7 @@ function prompt_sorin_setup { # Load required functions. autoload -Uz add-zsh-hook + autoload -Uz async && async # Add hook for calling git-info before each command. 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:untracked' format ' %%B%F{7}◼%f%%b' 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. 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]? ' }