Skip to content

Commit

Permalink
Merge branch 'develop'
Browse files Browse the repository at this point in the history
  • Loading branch information
agkozak committed Nov 23, 2020
2 parents 880ab69 + 32c0981 commit eb07592
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 37 deletions.
8 changes: 6 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,11 @@ This prompt has been tested on numerous Linux and BSD distributions, as well as
<details>
<summary>Here are the latest features and updates.</summary>

- v3.8 (July 9, 2020)
- v3.8.1 (November 23, 2020)
- WSL2 now uses the `subst-async` method, while WSL1 continues to use `usr1` for reasons of speed.
- The error message `permission denied: /proc/version` is no longer produced in `termux` on Android.
- zsh-async v1.8.5 is included.
- v3.8.0 (July 9, 2020)
- The prompt no longer defaults to `zsh-async` on Solaris and Solaris-derived operating systems, as I have noticed that `zsh-async`'s performance can be quirky on underperforming systems.
- v3.7.3 (May 14, 2020)
- Updated to use zsh-async 1.8.3.
Expand Down Expand Up @@ -274,7 +278,7 @@ This prompt will work perfectly if you use the default ZSH Emacs editing mode; i

The agkozak ZSH Prompt chooses the fastest and most reliable of three different methods for displaying the Git status asynchronously. One asynchronous method that works on all known platforms and with all supported versions of ZSH is [@psprint](https://github.com/psprint)'s `subst-async` technique, which uses process substitution (`<()`) to fork a background process that fetches the Git status and feeds it to a file descriptor. A `zle -F` callback handler then processes the input from the file descriptor and uses it to update the prompt.

`subst-async` works on Windows environments such as Cygwin, MSYS2, and WSL, but it is comparatively slow on these systems. For these platforms, the agkozak ZSH Prompt uses a method described by [Anish Athalye](http://www.anishathalye.com/2015/02/07/an-asynchronous-shell-prompt/). This `usr1` method creates and disowns child processes that calculate the Git status and then kill themselves off, triggering SIGUSR1 in the process. The ZSH `TRAPUSR1` trap function then displays that Git status. Since other scripts or the user could conceivably define `TRAPUSR1` either before or after this prompt is loaded, it regularly checks to see if that is the case and, if so, falls back to the slower but entirely reliable `subst-async` method.
`subst-async` works on Windows environments such as Cygwin, MSYS2, and WSL1, but it is comparatively slow on these systems. For these platforms, the agkozak ZSH Prompt uses a method described by [Anish Athalye](http://www.anishathalye.com/2015/02/07/an-asynchronous-shell-prompt/). This `usr1` method creates and disowns child processes that calculate the Git status and then kill themselves off, triggering SIGUSR1 in the process. The ZSH `TRAPUSR1` trap function then displays that Git status. Since other scripts or the user could conceivably define `TRAPUSR1` either before or after this prompt is loaded, it regularly checks to see if that is the case and, if so, falls back to the slower but entirely reliable `subst-async` method.

This prompt also supplies a `zsh-async` method that relies on the [`zsh-async`](https://github.com/mafredri/zsh-async) library, which uses ZSH's `zsh/zpty` module to spin off pseudo-terminals that can calculate the Git status without blocking the user from continuing to use the terminal. `zsh/zpty` does not work well with Cygwin or MSYS2, however, and it can be quirky on Solaris and related operating systems, so it is no longer used by default, and is only provided for those who want it.

Expand Down
29 changes: 14 additions & 15 deletions agkozak-zsh-prompt.plugin.zsh
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ autoload -Uz is-at-least add-zle-hook-widget
# prevents an unnecessary blank line before the first
# prompt of the session
# AGKOZAK[FUNCTIONS] A list of the prompt's functions
# AGKOZAK[IS_WSL] Whether or not the system is WSL
# AGKOZAK[IS_WSL1] Whether or not the system is WSL1
# AGKOZAK[OLD_PROMPT] The left prompt before this prompt was loaded
# AGKOZAK[OLD RPROMPT] The right prompt before this prompt was loaded
# AGKOZAK[PROMPT] The current state of the left prompt
Expand Down Expand Up @@ -546,12 +546,11 @@ _agkozak_has_usr1() {

############################################################
# If AGKOZAK_FORCE_ASYNC_METHOD is set to a valid value,
# set AGKOZAK[ASYNC_METHOD] to that; otherwise, determine
# the optimal asynchronous method from the environment (usr1
# for MSYS2/Cygwin/WSL, zsh-async for WSL, subst-async for
# everything else), with fallbacks being available. Define
# the necessary asynchronous functions (loading async.zsh
# when necessary).
# use it; otherwise, determine the optimal asynchronous
# method for the environment (usr1 for MSYS2/Cygwin/WSL1,
# subst-async for everything else), with fallbacks being
# available. Define the necessary asynchronous functions
# (loading async.zsh when necessary).
#
# Globals:
# AGKOZAK
Expand All @@ -563,13 +562,13 @@ _agkozak_async_init() {
emulate -L zsh
setopt LOCAL_OPTIONS NO_LOCAL_TRAPS

# Detect the Windows Subsystem for Linux
if (( $+WSL_DISTRO_NAME )) || { [[ $OSTYPE == linux* ]] \
&& [[ "$(< /proc/version)" == *(Microsoft|WSL)* ]]; }; then
# WSL1 should have BG_NICE disabled, since it does not have a Linux kernel
# TODO: Determine what to do for WSL2
# Detect WSL1
if [[ $OSTYPE == linux* &&
-r /proc/version &&
$(< /proc/version) == *Microsoft* ]]; then
# Early versions of WSL1 require BG_NICE to be explicitly disabled
unsetopt BG_NICE
AGKOZAK[IS_WSL]=1 # For later reference
AGKOZAK[IS_WSL1]=1
fi

if [[ $AGKOZAK_FORCE_ASYNC_METHOD == (subst-async|zsh-async|usr1|none) ]]; then
Expand All @@ -579,8 +578,8 @@ _agkozak_async_init() {
# Otherwise, first provide for certain quirky systems
else

# SIGUSR1 method is still much faster on Windows (MSYS2/Cygwin/WSL).
if [[ $OSTYPE == (msys|cygwin) ]] || (( AGKOZAK[IS_WSL] )); then
# SIGUSR1 method is still much faster on Windows (MSYS2/Cygwin/WSL1).
if [[ $OSTYPE == (msys|cygwin) ]] || (( AGKOZAK[IS_WSL1] )); then
if _agkozak_has_usr1; then
AGKOZAK[ASYNC_METHOD]='usr1'
else
Expand Down
51 changes: 31 additions & 20 deletions lib/async.zsh
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@
#
# zsh-async
#
# version: 1.8.3
# version: v1.8.5
# author: Mathias Fredriksson
# url: https://github.com/mafredri/zsh-async
#

typeset -g ASYNC_VERSION=1.8.3
typeset -g ASYNC_VERSION=1.8.5
# Produce debug output from zsh-async when set to 1.
typeset -g ASYNC_DEBUG=${ASYNC_DEBUG:-0}

Expand All @@ -20,7 +20,7 @@ _async_eval() {
# simplicity, this could be improved in the future.
{
eval "$@"
} &> >(ASYNC_JOB_NAME=[async/eval] _async_job 'cat')
} &> >(ASYNC_JOB_NAME=[async/eval] _async_job 'command -p cat')
}

# Wrapper for jobs executed by the async worker, gives output in parseable format with execution time
Expand All @@ -46,7 +46,7 @@ _async_job() {
duration=$(( EPOCHREALTIME - duration )) # Calculate duration.
print -r -n - $'\0'${(q)jobname} $ret ${(q)stdout} $duration
} 2> >(stderr=$(cat) && print -r -n - " "${(q)stderr}$'\0')
} 2> >(stderr=$(command -p cat) && print -r -n - " "${(q)stderr}$'\0')
)"
if [[ $out != $'\0'*$'\0' ]]; then
# Corrupted output (aborted job?), skipping.
Expand Down Expand Up @@ -232,7 +232,7 @@ _async_worker() {
# recreate it when there are no other jobs running.
if (( ! coproc_pid )); then
# Use coproc as a mutex for synchronized output between children.
coproc cat
coproc command -p cat
coproc_pid="$!"
# Insert token into coproc
print -n -p "t"
Expand Down Expand Up @@ -531,7 +531,7 @@ async_flush_jobs() {
# -p pid to notify (defaults to current pid)
#
async_start_worker() {
setopt localoptions noshwordsplit
setopt localoptions noshwordsplit noclobber

local worker=$1; shift
local -a args
Expand All @@ -542,13 +542,6 @@ async_start_worker() {
typeset -h REPLY
typeset has_xtrace=0

# Make sure async worker is started without xtrace
# (the trace output interferes with the worker).
[[ -o xtrace ]] && {
has_xtrace=1
unsetopt xtrace
}

if [[ -o interactive ]] && [[ -o zle ]]; then
# Inform the worker to ignore the notify flag and that we're
# using a ZLE watcher instead.
Expand All @@ -567,17 +560,35 @@ async_start_worker() {
# reassigned to /dev/null by the reassignment done inside the async
# worker.
# See https://github.com/mafredri/zsh-async/issues/35.
integer errfd
exec {errfd}>&2
zpty -b $worker _async_worker -p $$ $args 2>&$errfd || {
exec {errfd}>& -
async_stop_worker $worker
return 1
integer errfd=-1

# Redirect of errfd is broken on zsh 5.0.2.
if is-at-least 5.0.8; then
exec {errfd}>&2
fi

# Make sure async worker is started without xtrace
# (the trace output interferes with the worker).
[[ -o xtrace ]] && {
has_xtrace=1
unsetopt xtrace
}
exec {errfd}>& -

if (( errfd != -1 )); then
zpty -b $worker _async_worker -p $$ $args 2>&$errfd
else
zpty -b $worker _async_worker -p $$ $args
fi
local ret=$?

# Re-enable it if it was enabled, for debugging.
(( has_xtrace )) && setopt xtrace
(( errfd != -1 )) && exec {errfd}>& -

if (( ret )); then
async_stop_worker $worker
return 1
fi

if ! is-at-least 5.0.8; then
# For ZSH versions older than 5.0.8 we delay a bit to give
Expand Down

0 comments on commit eb07592

Please sign in to comment.