Merge remote-tracking branch 'source/master'
This commit is contained in:
34
.github/issue_template.md
vendored
Normal file
34
.github/issue_template.md
vendored
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
<!-- TIP: Hit 'Preview' for a more readable version of this template -->
|
||||||
|
|
||||||
|
### General information
|
||||||
|
|
||||||
|
- Pure version: 1.x.x
|
||||||
|
- ZSH version: 5.x.x
|
||||||
|
- Terminal program & version: <!-- e.g. Hyper 1.0.0, iTerm 3.0.0, Terminal 2.7.1, xterm 327, other? -->
|
||||||
|
- Operating system: <!-- e.g. macOS Sierra 10.12.1 -->
|
||||||
|
- ZSH framework: <!-- e.g. oh-my-zsh, prezto, antigen, antibody, zplug, other? -->
|
||||||
|
|
||||||
|
I have:
|
||||||
|
- [ ] Tested with another terminal program and can reproduce the issue: <!-- e.g. iTerm, etc. -->
|
||||||
|
- [ ] Followed the [Integration](https://github.com/sindresorhus/pure#integration) instructions for my framework
|
||||||
|
|
||||||
|
### Problem description
|
||||||
|
|
||||||
|
|
||||||
|
### Reproduction steps
|
||||||
|
|
||||||
|
1.
|
||||||
|
2.
|
||||||
|
3.
|
||||||
|
|
||||||
|
### My `.zshrc`:
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Please provide a minimal `.zshrc` that reproduces the issue.
|
||||||
|
Try to remove everything that that does not affect the issue, the fewer lines, the better.
|
||||||
|
-->
|
||||||
|
|
||||||
|
```shell
|
||||||
|
autoload -U promptinit; promptinit
|
||||||
|
prompt pure
|
||||||
|
```
|
||||||
348
async.zsh
348
async.zsh
@@ -3,96 +3,160 @@
|
|||||||
#
|
#
|
||||||
# zsh-async
|
# zsh-async
|
||||||
#
|
#
|
||||||
# version: 1.1.0
|
# version: 1.5.0
|
||||||
# author: Mathias Fredriksson
|
# author: Mathias Fredriksson
|
||||||
# url: https://github.com/mafredri/zsh-async
|
# url: https://github.com/mafredri/zsh-async
|
||||||
#
|
#
|
||||||
|
|
||||||
|
# Produce debug output from zsh-async when set to 1.
|
||||||
|
ASYNC_DEBUG=${ASYNC_DEBUG:-0}
|
||||||
|
|
||||||
# Wrapper for jobs executed by the async worker, gives output in parseable format with execution time
|
# Wrapper for jobs executed by the async worker, gives output in parseable format with execution time
|
||||||
_async_job() {
|
_async_job() {
|
||||||
|
# Disable xtrace as it would mangle the output.
|
||||||
|
setopt localoptions noxtrace
|
||||||
|
|
||||||
# Store start time as double precision (+E disables scientific notation)
|
# Store start time as double precision (+E disables scientific notation)
|
||||||
float -F duration=$EPOCHREALTIME
|
float -F duration=$EPOCHREALTIME
|
||||||
|
|
||||||
# Run the command
|
# Run the command and capture both stdout (`eval`) and stderr (`cat`) in
|
||||||
#
|
# separate subshells. When the command is complete, we grab write lock
|
||||||
# What is happening here is that we are assigning stdout, stderr and ret to
|
# (mutex token) and output everything except stderr inside the command
|
||||||
# variables, and then we are printing out the variable assignment through
|
# block, after the command block has completed, the stdin for `cat` is
|
||||||
# typeset -p. This way when we run eval we get something along the lines of:
|
# closed, causing stderr to be appended with a $'\0' at the end to mark the
|
||||||
# eval "
|
# end of output from this job.
|
||||||
# typeset stdout=' M async.test.sh\n M async.zsh'
|
local stdout stderr ret tok
|
||||||
# typeset ret=0
|
{
|
||||||
# typeset stderr=''
|
stdout=$(eval "$@")
|
||||||
# "
|
ret=$?
|
||||||
unset stdout stderr ret
|
duration=$(( EPOCHREALTIME - duration )) # Calculate duration.
|
||||||
eval "$(
|
|
||||||
{
|
|
||||||
stdout=$(eval "$@")
|
|
||||||
ret=$?
|
|
||||||
typeset -p stdout ret
|
|
||||||
} 2> >(stderr=$(cat); typeset -p stderr)
|
|
||||||
)"
|
|
||||||
|
|
||||||
# Calculate duration
|
# Grab mutex lock, stalls until token is available.
|
||||||
duration=$(( EPOCHREALTIME - duration ))
|
read -r -k 1 -p tok || exit 1
|
||||||
|
|
||||||
# stip all null-characters from stdout and stderr
|
# Return output (<job_name> <return_code> <stdout> <duration> <stderr>).
|
||||||
stdout=${stdout//$'\0'/}
|
print -r -n - ${(q)1} $ret ${(q)stdout} $duration
|
||||||
stderr=${stderr//$'\0'/}
|
} 2> >(stderr=$(cat) && print -r -n - " "${(q)stderr}$'\0')
|
||||||
|
|
||||||
# if ret is missing for some unknown reason, set it to -1 to indicate we
|
# Unlock mutex by inserting a token.
|
||||||
# have run into a bug
|
print -n -p $tok
|
||||||
ret=${ret:--1}
|
|
||||||
|
|
||||||
# Grab mutex lock
|
|
||||||
read -ep >/dev/null
|
|
||||||
|
|
||||||
# return output (<job_name> <return_code> <stdout> <duration> <stderr>)
|
|
||||||
print -r -N -n -- "$1" "$ret" "$stdout" "$duration" "$stderr"$'\0'
|
|
||||||
|
|
||||||
# Unlock mutex
|
|
||||||
print -p "t"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# The background worker manages all tasks and runs them without interfering with other processes
|
# The background worker manages all tasks and runs them without interfering with other processes
|
||||||
_async_worker() {
|
_async_worker() {
|
||||||
|
# Reset all options to defaults inside async worker.
|
||||||
|
emulate -R zsh
|
||||||
|
|
||||||
|
# Make sure monitor is unset to avoid printing the
|
||||||
|
# pids of child processes.
|
||||||
|
unsetopt monitor
|
||||||
|
|
||||||
|
# Redirect stderr to `/dev/null` in case unforseen errors produced by the
|
||||||
|
# worker. For example: `fork failed: resource temporarily unavailable`.
|
||||||
|
# Some older versions of zsh might also print malloc errors (know to happen
|
||||||
|
# on at least zsh 5.0.2 and 5.0.8) likely due to kill signals.
|
||||||
|
exec 2>/dev/null
|
||||||
|
|
||||||
|
# When a zpty is deleted (using -d) all the zpty instances created before
|
||||||
|
# the one being deleted receive a SIGHUP, unless we catch it, the async
|
||||||
|
# worker would simply exit (stop working) even though visible in the list
|
||||||
|
# of zpty's (zpty -L).
|
||||||
|
TRAPHUP() {
|
||||||
|
return 0 # Return 0, indicating signal was handled.
|
||||||
|
}
|
||||||
|
|
||||||
local -A storage
|
local -A storage
|
||||||
local unique=0
|
local unique=0
|
||||||
|
local notify_parent=0
|
||||||
|
local parent_pid=0
|
||||||
|
local coproc_pid=0
|
||||||
|
local processing=0
|
||||||
|
|
||||||
|
local -a zsh_hooks zsh_hook_functions
|
||||||
|
zsh_hooks=(chpwd periodic precmd preexec zshexit zshaddhistory)
|
||||||
|
zsh_hook_functions=(${^zsh_hooks}_functions)
|
||||||
|
unfunction $zsh_hooks &>/dev/null # Deactivate all zsh hooks inside the worker.
|
||||||
|
unset $zsh_hook_functions # And hooks with registered functions.
|
||||||
|
unset zsh_hooks zsh_hook_functions # Cleanup.
|
||||||
|
|
||||||
|
child_exit() {
|
||||||
|
local -a pids
|
||||||
|
pids=(${${(v)jobstates##*:*:}%\=*})
|
||||||
|
|
||||||
|
# If coproc (cat) is the only child running, we close it to avoid
|
||||||
|
# leaving it running indefinitely and cluttering the process tree.
|
||||||
|
if (( ! processing )) && [[ $#pids = 1 ]] && [[ $coproc_pid = $pids[1] ]]; then
|
||||||
|
coproc :
|
||||||
|
coproc_pid=0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# On older version of zsh (pre 5.2) we notify the parent through a
|
||||||
|
# SIGWINCH signal because `zpty` did not return a file descriptor (fd)
|
||||||
|
# prior to that.
|
||||||
|
if (( notify_parent )); then
|
||||||
|
# We use SIGWINCH for compatibility with older versions of zsh
|
||||||
|
# (pre 5.1.1) where other signals (INFO, ALRM, USR1, etc.) could
|
||||||
|
# cause a deadlock in the shell under certain circumstances.
|
||||||
|
kill -WINCH $parent_pid
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Register a SIGCHLD trap to handle the completion of child processes.
|
||||||
|
trap child_exit CHLD
|
||||||
|
|
||||||
# Process option parameters passed to worker
|
# Process option parameters passed to worker
|
||||||
while getopts "np:u" opt; do
|
while getopts "np:u" opt; do
|
||||||
case $opt in
|
case $opt in
|
||||||
# Use SIGWINCH since many others seem to cause zsh to freeze, e.g. ALRM, INFO, etc.
|
n) notify_parent=1;;
|
||||||
n) trap 'kill -WINCH $ASYNC_WORKER_PARENT_PID' CHLD;;
|
p) parent_pid=$OPTARG;;
|
||||||
p) ASYNC_WORKER_PARENT_PID=$OPTARG;;
|
|
||||||
u) unique=1;;
|
u) unique=1;;
|
||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
|
|
||||||
# Create a mutex for writing to the terminal through coproc
|
killjobs() {
|
||||||
coproc cat
|
local tok
|
||||||
# Insert token into coproc
|
local -a pids
|
||||||
print -p "t"
|
pids=(${${(v)jobstates##*:*:}%\=*})
|
||||||
|
|
||||||
while read -r cmd; do
|
# No need to send SIGHUP if no jobs are running.
|
||||||
# Separate on spaces into an array
|
(( $#pids == 0 )) && continue
|
||||||
cmd=(${=cmd})
|
(( $#pids == 1 )) && [[ $coproc_pid = $pids[1] ]] && continue
|
||||||
local job=$cmd[1]
|
|
||||||
|
# Grab lock to prevent half-written output in case a child
|
||||||
|
# process is in the middle of writing to stdin during kill.
|
||||||
|
(( coproc_pid )) && read -r -k 1 -p tok
|
||||||
|
|
||||||
|
kill -HUP -$$ # Send to entire process group.
|
||||||
|
coproc : # Quit coproc.
|
||||||
|
coproc_pid=0 # Reset pid.
|
||||||
|
}
|
||||||
|
|
||||||
|
local request
|
||||||
|
local -a cmd
|
||||||
|
while :; do
|
||||||
|
# Wait for jobs sent by async_job.
|
||||||
|
read -r -d $'\0' request || {
|
||||||
|
# Since we handle SIGHUP above (and thus do not know when `zpty -d`)
|
||||||
|
# occurs, a failure to read probably indicates that stdin has
|
||||||
|
# closed. This is why we propagate the signal to all children and
|
||||||
|
# exit manually.
|
||||||
|
kill -HUP -$$ # Send SIGHUP to all jobs.
|
||||||
|
exit 0
|
||||||
|
}
|
||||||
|
|
||||||
# Check for non-job commands sent to worker
|
# Check for non-job commands sent to worker
|
||||||
case $job in
|
case $request in
|
||||||
_unset_trap)
|
_unset_trap) notify_parent=0; continue;;
|
||||||
trap - CHLD; continue;;
|
_killjobs) killjobs; continue;;
|
||||||
_killjobs)
|
|
||||||
# Do nothing in the worker when receiving the TERM signal
|
|
||||||
trap '' TERM
|
|
||||||
# Send TERM to the entire process group (PID and all children)
|
|
||||||
kill -TERM -$$ &>/dev/null
|
|
||||||
# Reset trap
|
|
||||||
trap - TERM
|
|
||||||
continue
|
|
||||||
;;
|
|
||||||
esac
|
esac
|
||||||
|
|
||||||
|
# Parse the request using shell parsing (z) to allow commands
|
||||||
|
# to be parsed from single strings and multi-args alike.
|
||||||
|
cmd=("${(z)request}")
|
||||||
|
|
||||||
|
# Name of the job (first argument).
|
||||||
|
local job=$cmd[1]
|
||||||
|
|
||||||
# If worker should perform unique jobs
|
# If worker should perform unique jobs
|
||||||
if (( unique )); then
|
if (( unique )); then
|
||||||
# Check if a previous job is still running, if yes, let it finnish
|
# Check if a previous job is still running, if yes, let it finnish
|
||||||
@@ -103,10 +167,25 @@ _async_worker() {
|
|||||||
done
|
done
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Run task in background
|
# Guard against closing coproc from trap before command has started.
|
||||||
|
processing=1
|
||||||
|
|
||||||
|
# Because we close the coproc after the last job has completed, we must
|
||||||
|
# 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_pid="$!"
|
||||||
|
# Insert token into coproc
|
||||||
|
print -n -p "t"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Run job in background, completed jobs are printed to stdout.
|
||||||
_async_job $cmd &
|
_async_job $cmd &
|
||||||
# Store pid because zsh job manager is extremely unflexible (show jobname as non-unique '$job')...
|
# Store pid because zsh job manager is extremely unflexible (show jobname as non-unique '$job')...
|
||||||
storage[$job]=$!
|
storage[$job]="$!"
|
||||||
|
|
||||||
|
processing=0 # Disable guard.
|
||||||
done
|
done
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -127,38 +206,55 @@ _async_worker() {
|
|||||||
async_process_results() {
|
async_process_results() {
|
||||||
setopt localoptions noshwordsplit
|
setopt localoptions noshwordsplit
|
||||||
|
|
||||||
integer count=0
|
|
||||||
local worker=$1
|
local worker=$1
|
||||||
local callback=$2
|
local callback=$2
|
||||||
|
local caller=$3
|
||||||
local -a items
|
local -a items
|
||||||
local IFS=$'\0'
|
local null=$'\0' data
|
||||||
|
integer -l len pos num_processed
|
||||||
|
|
||||||
typeset -gA ASYNC_PROCESS_BUFFER
|
typeset -gA ASYNC_PROCESS_BUFFER
|
||||||
# Read output from zpty and parse it if available
|
|
||||||
while zpty -rt $worker line 2>/dev/null; do
|
|
||||||
# Remove unwanted \r from output
|
|
||||||
ASYNC_PROCESS_BUFFER[$worker]+=${line//$'\r'$'\n'/$'\n'}
|
|
||||||
# Split buffer on null characters, preserve empty elements
|
|
||||||
items=("${(@)=ASYNC_PROCESS_BUFFER[$worker]}")
|
|
||||||
# Remove last element since it's due to the return string separator structure
|
|
||||||
items=("${(@)items[1,${#items}-1]}")
|
|
||||||
|
|
||||||
# Continue until we receive all information
|
# Read output from zpty and parse it if available.
|
||||||
(( ${#items} % 5 )) && continue
|
while zpty -r -t $worker data 2>/dev/null; do
|
||||||
|
ASYNC_PROCESS_BUFFER[$worker]+=$data
|
||||||
|
len=${#ASYNC_PROCESS_BUFFER[$worker]}
|
||||||
|
pos=${ASYNC_PROCESS_BUFFER[$worker][(i)$null]} # Get index of NULL-character (delimiter).
|
||||||
|
|
||||||
# Work through all results
|
# Keep going until we find a NULL-character.
|
||||||
while (( ${#items} > 0 )); do
|
if (( ! len )) || (( pos > len )); then
|
||||||
$callback "${(@)=items[1,5]}"
|
continue
|
||||||
shift 5 items
|
fi
|
||||||
count+=1
|
|
||||||
|
while (( pos <= len )); do
|
||||||
|
# Take the content from the beginning, until the NULL-character and
|
||||||
|
# perform shell parsing (z) and unquoting (Q) as an array (@).
|
||||||
|
items=("${(@Q)${(z)ASYNC_PROCESS_BUFFER[$worker][1,$pos-1]}}")
|
||||||
|
|
||||||
|
# Remove the extracted items from the buffer.
|
||||||
|
ASYNC_PROCESS_BUFFER[$worker]=${ASYNC_PROCESS_BUFFER[$worker][$pos+1,$len]}
|
||||||
|
|
||||||
|
if (( $#items == 5 )); then
|
||||||
|
$callback "${(@)items}" # Send all parsed items to the callback.
|
||||||
|
else
|
||||||
|
# In case of corrupt data, invoke callback with *async* as job
|
||||||
|
# name, non-zero exit status and an error message on stderr.
|
||||||
|
$callback "async" 1 "" 0 "$0:$LINENO: error: bad format, got ${#items} items (${(@q)items})"
|
||||||
|
fi
|
||||||
|
|
||||||
|
(( num_processed++ ))
|
||||||
|
|
||||||
|
len=${#ASYNC_PROCESS_BUFFER[$worker]}
|
||||||
|
if (( len > 1 )); then
|
||||||
|
pos=${ASYNC_PROCESS_BUFFER[$worker][(i)$null]} # Get index of NULL-character (delimiter).
|
||||||
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
# Empty the buffer
|
|
||||||
unset "ASYNC_PROCESS_BUFFER[$worker]"
|
|
||||||
done
|
done
|
||||||
|
|
||||||
# If we processed any results, return success
|
(( num_processed )) && return 0
|
||||||
(( count )) && return 0
|
|
||||||
|
# Avoid printing exit value when `setopt printexitvalue` is active.`
|
||||||
|
[[ $caller = trap || $caller = watcher ]] && return 0
|
||||||
|
|
||||||
# No results were processed
|
# No results were processed
|
||||||
return 1
|
return 1
|
||||||
@@ -172,7 +268,7 @@ _async_zle_watcher() {
|
|||||||
local callback=$ASYNC_CALLBACKS[$worker]
|
local callback=$ASYNC_CALLBACKS[$worker]
|
||||||
|
|
||||||
if [[ -n $callback ]]; then
|
if [[ -n $callback ]]; then
|
||||||
async_process_results $worker $callback
|
async_process_results $worker $callback watcher
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -186,7 +282,14 @@ async_job() {
|
|||||||
setopt localoptions noshwordsplit
|
setopt localoptions noshwordsplit
|
||||||
|
|
||||||
local worker=$1; shift
|
local worker=$1; shift
|
||||||
zpty -w $worker $@
|
|
||||||
|
local -a cmd
|
||||||
|
cmd=("$@")
|
||||||
|
if (( $#cmd > 1 )); then
|
||||||
|
cmd=(${(q)cmd}) # Quote special characters in multi argument commands.
|
||||||
|
fi
|
||||||
|
|
||||||
|
zpty -w $worker $cmd$'\0'
|
||||||
}
|
}
|
||||||
|
|
||||||
# This function traps notification signals and calls all registered callbacks
|
# This function traps notification signals and calls all registered callbacks
|
||||||
@@ -194,7 +297,7 @@ _async_notify_trap() {
|
|||||||
setopt localoptions noshwordsplit
|
setopt localoptions noshwordsplit
|
||||||
|
|
||||||
for k in ${(k)ASYNC_CALLBACKS}; do
|
for k in ${(k)ASYNC_CALLBACKS}; do
|
||||||
async_process_results $k ${ASYNC_CALLBACKS[$k]}
|
async_process_results $k ${ASYNC_CALLBACKS[$k]} trap
|
||||||
done
|
done
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -213,7 +316,9 @@ async_register_callback() {
|
|||||||
|
|
||||||
ASYNC_CALLBACKS[$worker]="$*"
|
ASYNC_CALLBACKS[$worker]="$*"
|
||||||
|
|
||||||
if (( ! ASYNC_USE_ZLE_HANDLER )); then
|
# Enable trap when the ZLE watcher is unavailable, allows
|
||||||
|
# workers to notify (via -n) when a job is done.
|
||||||
|
if [[ ! -o interactive ]] || [[ ! -o zle ]]; then
|
||||||
trap '_async_notify_trap' WINCH
|
trap '_async_notify_trap' WINCH
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
@@ -246,12 +351,19 @@ async_flush_jobs() {
|
|||||||
zpty -t $worker &>/dev/null || return 1
|
zpty -t $worker &>/dev/null || return 1
|
||||||
|
|
||||||
# Send kill command to worker
|
# Send kill command to worker
|
||||||
zpty -w $worker "_killjobs"
|
async_job $worker "_killjobs"
|
||||||
|
|
||||||
# Clear all output buffers
|
# Clear the zpty buffer.
|
||||||
while zpty -r $worker line; do true; done
|
local junk
|
||||||
|
if zpty -r -t $worker junk '*'; then
|
||||||
|
(( ASYNC_DEBUG )) && print -n "async_flush_jobs $worker: ${(V)junk}"
|
||||||
|
while zpty -r -t $worker junk '*'; do
|
||||||
|
(( ASYNC_DEBUG )) && print -n "${(V)junk}"
|
||||||
|
done
|
||||||
|
(( ASYNC_DEBUG )) && print
|
||||||
|
fi
|
||||||
|
|
||||||
# Clear any partial buffers
|
# Finally, clear the process buffer in case of partially parsed responses.
|
||||||
typeset -gA ASYNC_PROCESS_BUFFER
|
typeset -gA ASYNC_PROCESS_BUFFER
|
||||||
unset "ASYNC_PROCESS_BUFFER[$worker]"
|
unset "ASYNC_PROCESS_BUFFER[$worker]"
|
||||||
}
|
}
|
||||||
@@ -276,16 +388,47 @@ async_start_worker() {
|
|||||||
|
|
||||||
typeset -gA ASYNC_PTYS
|
typeset -gA ASYNC_PTYS
|
||||||
typeset -h REPLY
|
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 (( ! ASYNC_ZPTY_RETURNS_FD )) && [[ -o interactive ]] && [[ -o zle ]]; then
|
||||||
|
# When zpty doesn't return a file descriptor (on older versions of zsh)
|
||||||
|
# we try to guess it anyway.
|
||||||
|
integer -l zptyfd
|
||||||
|
exec {zptyfd}>&1 # Open a new file descriptor (above 10).
|
||||||
|
exec {zptyfd}>&- # Close it so it's free to be used by zpty.
|
||||||
|
fi
|
||||||
|
|
||||||
zpty -b $worker _async_worker -p $$ $@ || {
|
zpty -b $worker _async_worker -p $$ $@ || {
|
||||||
async_stop_worker $worker
|
async_stop_worker $worker
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
if (( ASYNC_USE_ZLE_HANDLER )); then
|
# Re-enable it if it was enabled, for debugging.
|
||||||
ASYNC_PTYS[$REPLY]=$worker
|
(( has_xtrace )) && setopt xtrace
|
||||||
zle -F $REPLY _async_zle_watcher
|
|
||||||
|
|
||||||
# If worker was called with -n, disable trap in favor of zle handler
|
if [[ $ZSH_VERSION < 5.0.8 ]]; then
|
||||||
|
# For ZSH versions older than 5.0.8 we delay a bit to give
|
||||||
|
# time for the worker to start before issuing commands,
|
||||||
|
# otherwise it will not be ready to receive them.
|
||||||
|
sleep 0.001
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -o interactive ]] && [[ -o zle ]]; then
|
||||||
|
if (( ! ASYNC_ZPTY_RETURNS_FD )); then
|
||||||
|
REPLY=$zptyfd # Use the guessed value for the file desciptor.
|
||||||
|
fi
|
||||||
|
|
||||||
|
ASYNC_PTYS[$REPLY]=$worker # Map the file desciptor to the worker.
|
||||||
|
zle -F $REPLY _async_zle_watcher # Register the ZLE handler.
|
||||||
|
|
||||||
|
# Disable trap in favor of ZLE handler when notify is enabled (-n).
|
||||||
async_job $worker _unset_trap
|
async_job $worker _unset_trap
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
@@ -310,6 +453,10 @@ async_stop_worker() {
|
|||||||
done
|
done
|
||||||
async_unregister_callback $worker
|
async_unregister_callback $worker
|
||||||
zpty -d $worker 2>/dev/null || ret=$?
|
zpty -d $worker 2>/dev/null || ret=$?
|
||||||
|
|
||||||
|
# Clear any partial buffers.
|
||||||
|
typeset -gA ASYNC_PROCESS_BUFFER
|
||||||
|
unset "ASYNC_PROCESS_BUFFER[$worker]"
|
||||||
done
|
done
|
||||||
|
|
||||||
return $ret
|
return $ret
|
||||||
@@ -328,12 +475,13 @@ async_init() {
|
|||||||
zmodload zsh/zpty
|
zmodload zsh/zpty
|
||||||
zmodload zsh/datetime
|
zmodload zsh/datetime
|
||||||
|
|
||||||
# Check if zsh/zpty returns a file descriptor or not, shell must also be interactive
|
# Check if zsh/zpty returns a file descriptor or not,
|
||||||
ASYNC_USE_ZLE_HANDLER=0
|
# shell must also be interactive with zle enabled.
|
||||||
[[ -o interactive ]] && {
|
ASYNC_ZPTY_RETURNS_FD=0
|
||||||
|
[[ -o interactive ]] && [[ -o zle ]] && {
|
||||||
typeset -h REPLY
|
typeset -h REPLY
|
||||||
zpty _async_test cat
|
zpty _async_test :
|
||||||
(( REPLY )) && ASYNC_USE_ZLE_HANDLER=1
|
(( REPLY )) && ASYNC_ZPTY_RETURNS_FD=1
|
||||||
zpty -d _async_test
|
zpty -d _async_test
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "pure-prompt",
|
"name": "pure-prompt",
|
||||||
"version": "1.3.0",
|
"version": "1.5.0",
|
||||||
"description": "Pretty, minimal and fast ZSH prompt",
|
"description": "Pretty, minimal and fast ZSH prompt",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"repository": "sindresorhus/pure",
|
"repository": "sindresorhus/pure",
|
||||||
@@ -19,7 +19,7 @@
|
|||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"postinstall": "dest=/usr/local/share/zsh/site-functions/; mkdir -p $dest && ln -sf \"$PWD/pure.zsh\" $dest/prompt_pure_setup && ln -sf \"$PWD/async.zsh\" $dest/async || echo 'Could not automagically symlink the prompt. Check out the readme on how to do it manually: https://github.com/sindresorhus/pure#manually'"
|
"postinstall": "dest=/usr/local/share/zsh/site-functions; mkdir -p $dest && ln -sf \"$PWD/pure.zsh\" $dest/prompt_pure_setup && ln -sf \"$PWD/async.zsh\" $dest/async || echo 'Could not automagically symlink the prompt. Check out the readme on how to do it manually: https://github.com/sindresorhus/pure#manually'"
|
||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
"pure.zsh",
|
"pure.zsh",
|
||||||
|
|||||||
156
pure.zsh
156
pure.zsh
@@ -61,30 +61,10 @@ prompt_pure_clear_screen() {
|
|||||||
prompt_pure_preprompt_render precmd
|
prompt_pure_preprompt_render precmd
|
||||||
}
|
}
|
||||||
|
|
||||||
prompt_pure_check_git_arrows() {
|
|
||||||
# reset git arrows
|
|
||||||
prompt_pure_git_arrows=
|
|
||||||
|
|
||||||
# check if there is an upstream configured for this branch
|
|
||||||
command git rev-parse --abbrev-ref @'{u}' &>/dev/null || return
|
|
||||||
|
|
||||||
local arrow_status
|
|
||||||
# check git left and right arrow_status
|
|
||||||
arrow_status="$(command git rev-list --left-right --count HEAD...@'{u}' 2>/dev/null)"
|
|
||||||
# exit if the command failed
|
|
||||||
(( !$? )) || return
|
|
||||||
|
|
||||||
# left and right are tab-separated, split on tab and store as array
|
|
||||||
arrow_status=(${(ps:\t:)arrow_status})
|
|
||||||
local arrows left=${arrow_status[1]} right=${arrow_status[2]}
|
|
||||||
|
|
||||||
(( ${right:-0} > 0 )) && arrows+="${PURE_GIT_DOWN_ARROW:-⇣}"
|
|
||||||
(( ${left:-0} > 0 )) && arrows+="${PURE_GIT_UP_ARROW:-⇡}"
|
|
||||||
|
|
||||||
[[ -n $arrows ]] && prompt_pure_git_arrows=" ${arrows}"
|
|
||||||
}
|
|
||||||
|
|
||||||
prompt_pure_set_title() {
|
prompt_pure_set_title() {
|
||||||
|
# emacs terminal does not support settings the title
|
||||||
|
(( ${+EMACS} )) && return
|
||||||
|
|
||||||
# tell the terminal we are setting the title
|
# tell the terminal we are setting the title
|
||||||
print -n '\e]0;'
|
print -n '\e]0;'
|
||||||
# show hostname if connected through ssh
|
# show hostname if connected through ssh
|
||||||
@@ -120,6 +100,12 @@ prompt_pure_string_length_to_var() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
prompt_pure_preprompt_render() {
|
prompt_pure_preprompt_render() {
|
||||||
|
# store the current prompt_subst setting so that it can be restored later
|
||||||
|
local prompt_subst_status=$options[prompt_subst]
|
||||||
|
|
||||||
|
# make sure prompt_subst is unset to prevent parameter expansion in preprompt
|
||||||
|
setopt local_options no_prompt_subst
|
||||||
|
|
||||||
# check that no command is currently running, the preprompt will otherwise be rendered in the wrong place
|
# check that no command is currently running, the preprompt will otherwise be rendered in the wrong place
|
||||||
[[ -n ${prompt_pure_cmd_timestamp+x} && "$1" != "precmd" ]] && return
|
[[ -n ${prompt_pure_cmd_timestamp+x} && "$1" != "precmd" ]] && return
|
||||||
|
|
||||||
@@ -138,12 +124,15 @@ prompt_pure_preprompt_render() {
|
|||||||
# execution time
|
# execution time
|
||||||
preprompt+="%F{yellow}${prompt_pure_cmd_exec_time}%f"
|
preprompt+="%F{yellow}${prompt_pure_cmd_exec_time}%f"
|
||||||
|
|
||||||
|
# make sure prompt_pure_last_preprompt is a global array
|
||||||
|
typeset -g -a prompt_pure_last_preprompt
|
||||||
|
|
||||||
# if executing through precmd, do not perform fancy terminal editing
|
# if executing through precmd, do not perform fancy terminal editing
|
||||||
if [[ "$1" == "precmd" ]]; then
|
if [[ "$1" == "precmd" ]]; then
|
||||||
print -P "${preprompt}"
|
print -P "${preprompt}"
|
||||||
else
|
else
|
||||||
# only redraw if preprompt has changed
|
# only redraw if the expanded preprompt has changed
|
||||||
[[ "${prompt_pure_last_preprompt}" != "${preprompt}" ]] || return
|
[[ "${prompt_pure_last_preprompt[2]}" != "${(S%%)preprompt}" ]] || return
|
||||||
|
|
||||||
# calculate length of preprompt and store it locally in preprompt_length
|
# calculate length of preprompt and store it locally in preprompt_length
|
||||||
integer preprompt_length lines
|
integer preprompt_length lines
|
||||||
@@ -154,7 +143,7 @@ prompt_pure_preprompt_render() {
|
|||||||
|
|
||||||
# calculate previous preprompt lines to figure out how the new preprompt should behave
|
# calculate previous preprompt lines to figure out how the new preprompt should behave
|
||||||
integer last_preprompt_length last_lines
|
integer last_preprompt_length last_lines
|
||||||
prompt_pure_string_length_to_var "${prompt_pure_last_preprompt}" "last_preprompt_length"
|
prompt_pure_string_length_to_var "${prompt_pure_last_preprompt[1]}" "last_preprompt_length"
|
||||||
(( last_lines = ( last_preprompt_length - 1 ) / COLUMNS + 1 ))
|
(( last_lines = ( last_preprompt_length - 1 ) / COLUMNS + 1 ))
|
||||||
|
|
||||||
# clr_prev_preprompt erases visual artifacts from previous preprompt
|
# clr_prev_preprompt erases visual artifacts from previous preprompt
|
||||||
@@ -174,9 +163,6 @@ prompt_pure_preprompt_render() {
|
|||||||
elif (( last_lines < lines )); then
|
elif (( last_lines < lines )); then
|
||||||
# move cursor using newlines because ansi cursor movement can't push the cursor beyond the last line
|
# move cursor using newlines because ansi cursor movement can't push the cursor beyond the last line
|
||||||
printf $'\n'%.0s {1..$(( lines - last_lines ))}
|
printf $'\n'%.0s {1..$(( lines - last_lines ))}
|
||||||
|
|
||||||
# redraw the prompt since it has been moved by print
|
|
||||||
zle && zle .reset-prompt
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# disable clearing of line if last char of preprompt is last column of terminal
|
# disable clearing of line if last char of preprompt is last column of terminal
|
||||||
@@ -184,11 +170,19 @@ prompt_pure_preprompt_render() {
|
|||||||
(( COLUMNS * lines == preprompt_length )) && clr=
|
(( COLUMNS * lines == preprompt_length )) && clr=
|
||||||
|
|
||||||
# modify previous preprompt
|
# modify previous preprompt
|
||||||
print -Pn "\e7${clr_prev_preprompt}\e[${lines}A\e[1G${preprompt}${clr}\e8"
|
print -Pn "${clr_prev_preprompt}\e[${lines}A\e[${COLUMNS}D${preprompt}${clr}\n"
|
||||||
|
|
||||||
|
if [[ $prompt_subst_status = 'on' ]]; then
|
||||||
|
# re-eanble prompt_subst for expansion on PS1
|
||||||
|
setopt prompt_subst
|
||||||
|
fi
|
||||||
|
|
||||||
|
# redraw prompt (also resets cursor position)
|
||||||
|
zle && zle .reset-prompt
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# store previous preprompt for comparison
|
# store both unexpanded and expanded preprompt for comparison
|
||||||
prompt_pure_last_preprompt=$preprompt
|
prompt_pure_last_preprompt=("$preprompt" "${(S%%)preprompt}")
|
||||||
}
|
}
|
||||||
|
|
||||||
prompt_pure_precmd() {
|
prompt_pure_precmd() {
|
||||||
@@ -199,9 +193,6 @@ prompt_pure_precmd() {
|
|||||||
# with the initial preprompt rendering
|
# with the initial preprompt rendering
|
||||||
prompt_pure_cmd_timestamp=
|
prompt_pure_cmd_timestamp=
|
||||||
|
|
||||||
# check for git arrows
|
|
||||||
prompt_pure_check_git_arrows
|
|
||||||
|
|
||||||
# shows the full path in the title
|
# shows the full path in the title
|
||||||
prompt_pure_set_title 'expand-prompt' '%~'
|
prompt_pure_set_title 'expand-prompt' '%~'
|
||||||
|
|
||||||
@@ -220,29 +211,46 @@ prompt_pure_precmd() {
|
|||||||
|
|
||||||
# fastest possible way to check if repo is dirty
|
# fastest possible way to check if repo is dirty
|
||||||
prompt_pure_async_git_dirty() {
|
prompt_pure_async_git_dirty() {
|
||||||
local untracked_dirty=$1; shift
|
setopt localoptions noshwordsplit
|
||||||
|
local untracked_dirty=$1 dir=$2
|
||||||
|
|
||||||
# use cd -q to avoid side effects of changing directory, e.g. chpwd hooks
|
# use cd -q to avoid side effects of changing directory, e.g. chpwd hooks
|
||||||
cd -q "$*"
|
builtin cd -q $dir
|
||||||
|
|
||||||
if [[ "$untracked_dirty" == "0" ]]; then
|
if [[ $untracked_dirty = 0 ]]; then
|
||||||
command git diff --no-ext-diff --quiet --exit-code
|
command git diff --no-ext-diff --quiet --exit-code
|
||||||
else
|
else
|
||||||
test -z "$(command git status --porcelain --ignore-submodules -unormal)"
|
test -z "$(command git status --porcelain --ignore-submodules -unormal)"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
(( $? )) && echo "%F{red}*%f"
|
return $?
|
||||||
}
|
}
|
||||||
|
|
||||||
prompt_pure_async_git_fetch() {
|
prompt_pure_async_git_fetch() {
|
||||||
|
setopt localoptions noshwordsplit
|
||||||
# use cd -q to avoid side effects of changing directory, e.g. chpwd hooks
|
# use cd -q to avoid side effects of changing directory, e.g. chpwd hooks
|
||||||
cd -q "$*"
|
builtin cd -q $1
|
||||||
|
|
||||||
# set GIT_TERMINAL_PROMPT=0 to disable auth prompting for git fetch (git 2.3+)
|
# set GIT_TERMINAL_PROMPT=0 to disable auth prompting for git fetch (git 2.3+)
|
||||||
GIT_TERMINAL_PROMPT=0 command git -c gc.auto=0 fetch
|
export GIT_TERMINAL_PROMPT=0
|
||||||
|
# set ssh BachMode to disable all interactive ssh password prompting
|
||||||
|
export GIT_SSH_COMMAND=${GIT_SSH_COMMAND:-"ssh -o BatchMode=yes"}
|
||||||
|
|
||||||
|
command git -c gc.auto=0 fetch &>/dev/null || return 1
|
||||||
|
|
||||||
|
# check arrow status after a successful git fetch
|
||||||
|
prompt_pure_async_git_arrows $1
|
||||||
|
}
|
||||||
|
|
||||||
|
prompt_pure_async_git_arrows() {
|
||||||
|
setopt localoptions noshwordsplit
|
||||||
|
builtin cd -q $1
|
||||||
|
command git rev-list --left-right --count HEAD...@'{u}'
|
||||||
}
|
}
|
||||||
|
|
||||||
prompt_pure_async_tasks() {
|
prompt_pure_async_tasks() {
|
||||||
|
setopt localoptions noshwordsplit
|
||||||
|
|
||||||
# initialize async worker
|
# initialize async worker
|
||||||
((!${prompt_pure_async_init:-0})) && {
|
((!${prompt_pure_async_init:-0})) && {
|
||||||
async_start_worker "prompt_pure" -u -n
|
async_start_worker "prompt_pure" -u -n
|
||||||
@@ -261,6 +269,7 @@ prompt_pure_async_tasks() {
|
|||||||
# reset git preprompt variables, switching working tree
|
# reset git preprompt variables, switching working tree
|
||||||
unset prompt_pure_git_dirty
|
unset prompt_pure_git_dirty
|
||||||
unset prompt_pure_git_last_dirty_check_timestamp
|
unset prompt_pure_git_last_dirty_check_timestamp
|
||||||
|
prompt_pure_git_arrows=
|
||||||
|
|
||||||
# set the new working tree and prefix with "x" to prevent the creation of a named path by AUTO_NAME_DIRS
|
# set the new working tree and prefix with "x" to prevent the creation of a named path by AUTO_NAME_DIRS
|
||||||
prompt_pure_current_working_tree="x${working_tree}"
|
prompt_pure_current_working_tree="x${working_tree}"
|
||||||
@@ -269,10 +278,12 @@ prompt_pure_async_tasks() {
|
|||||||
# only perform tasks inside git working tree
|
# only perform tasks inside git working tree
|
||||||
[[ -n $working_tree ]] || return
|
[[ -n $working_tree ]] || return
|
||||||
|
|
||||||
|
async_job "prompt_pure" prompt_pure_async_git_arrows $working_tree
|
||||||
|
|
||||||
# do not preform git fetch if it is disabled or working_tree == HOME
|
# do not preform git fetch if it is disabled or working_tree == HOME
|
||||||
if (( ${PURE_GIT_PULL:-1} )) && [[ $working_tree != $HOME ]]; then
|
if (( ${PURE_GIT_PULL:-1} )) && [[ $working_tree != $HOME ]]; then
|
||||||
# tell worker to do a git fetch
|
# tell worker to do a git fetch
|
||||||
async_job "prompt_pure" prompt_pure_async_git_fetch "${working_tree}"
|
async_job "prompt_pure" prompt_pure_async_git_fetch $working_tree
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# if dirty checking is sufficiently fast, tell worker to check it again, or wait for timeout
|
# if dirty checking is sufficiently fast, tell worker to check it again, or wait for timeout
|
||||||
@@ -280,41 +291,76 @@ prompt_pure_async_tasks() {
|
|||||||
if (( time_since_last_dirty_check > ${PURE_GIT_DELAY_DIRTY_CHECK:-1800} )); then
|
if (( time_since_last_dirty_check > ${PURE_GIT_DELAY_DIRTY_CHECK:-1800} )); then
|
||||||
unset prompt_pure_git_last_dirty_check_timestamp
|
unset prompt_pure_git_last_dirty_check_timestamp
|
||||||
# check check if there is anything to pull
|
# check check if there is anything to pull
|
||||||
async_job "prompt_pure" prompt_pure_async_git_dirty "${PURE_GIT_UNTRACKED_DIRTY:-1}" "${working_tree}"
|
async_job "prompt_pure" prompt_pure_async_git_dirty ${PURE_GIT_UNTRACKED_DIRTY:-1} $working_tree
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
prompt_pure_async_callback() {
|
prompt_pure_check_git_arrows() {
|
||||||
local job=$1
|
setopt localoptions noshwordsplit
|
||||||
local output=$3
|
local arrows left=${1:-0} right=${2:-0}
|
||||||
local exec_time=$4
|
|
||||||
|
|
||||||
case "${job}" in
|
(( right > 0 )) && arrows+=${PURE_GIT_DOWN_ARROW:-⇣}
|
||||||
|
(( left > 0 )) && arrows+=${PURE_GIT_UP_ARROW:-⇡}
|
||||||
|
|
||||||
|
[[ -n $arrows ]] || return
|
||||||
|
typeset -g REPLY=" $arrows"
|
||||||
|
}
|
||||||
|
|
||||||
|
prompt_pure_async_callback() {
|
||||||
|
setopt localoptions noshwordsplit
|
||||||
|
local job=$1 code=$2 output=$3 exec_time=$4
|
||||||
|
|
||||||
|
case $job in
|
||||||
prompt_pure_async_git_dirty)
|
prompt_pure_async_git_dirty)
|
||||||
prompt_pure_git_dirty=$output
|
local prev_dirty=$prompt_pure_git_dirty
|
||||||
prompt_pure_preprompt_render
|
if (( code == 0 )); then
|
||||||
|
prompt_pure_git_dirty=
|
||||||
|
else
|
||||||
|
prompt_pure_git_dirty="%F{red}*%f"
|
||||||
|
fi
|
||||||
|
|
||||||
|
[[ $prev_dirty != $prompt_pure_git_dirty ]] && prompt_pure_preprompt_render
|
||||||
|
|
||||||
# When prompt_pure_git_last_dirty_check_timestamp is set, the git info is displayed in a different color.
|
# When prompt_pure_git_last_dirty_check_timestamp is set, the git info is displayed in a different color.
|
||||||
# To distinguish between a "fresh" and a "cached" result, the preprompt is rendered before setting this
|
# To distinguish between a "fresh" and a "cached" result, the preprompt is rendered before setting this
|
||||||
# variable. Thus, only upon next rendering of the preprompt will the result appear in a different color.
|
# variable. Thus, only upon next rendering of the preprompt will the result appear in a different color.
|
||||||
(( $exec_time > 2 )) && prompt_pure_git_last_dirty_check_timestamp=$EPOCHSECONDS
|
(( $exec_time > 2 )) && prompt_pure_git_last_dirty_check_timestamp=$EPOCHSECONDS
|
||||||
;;
|
;;
|
||||||
prompt_pure_async_git_fetch)
|
prompt_pure_async_git_fetch|prompt_pure_async_git_arrows)
|
||||||
prompt_pure_check_git_arrows
|
# prompt_pure_async_git_fetch executes prompt_pure_async_git_arrows
|
||||||
prompt_pure_preprompt_render
|
# after a successful fetch.
|
||||||
|
if (( code == 0 )); then
|
||||||
|
local REPLY
|
||||||
|
prompt_pure_check_git_arrows ${(ps:\t:)output}
|
||||||
|
if [[ $prompt_pure_git_arrows != $REPLY ]]; then
|
||||||
|
prompt_pure_git_arrows=$REPLY
|
||||||
|
prompt_pure_preprompt_render
|
||||||
|
fi
|
||||||
|
fi
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
}
|
}
|
||||||
|
|
||||||
prompt_pure_setup() {
|
prompt_pure_setup() {
|
||||||
|
local autoload_name=$1; shift
|
||||||
|
|
||||||
# prevent percentage showing up
|
# prevent percentage showing up
|
||||||
# if output doesn't end with a newline
|
# if output doesn't end with a newline
|
||||||
export PROMPT_EOL_MARK=''
|
export PROMPT_EOL_MARK=''
|
||||||
|
|
||||||
prompt_opts=(subst percent)
|
prompt_opts=(subst percent)
|
||||||
|
|
||||||
|
# if autoload_name or eval context differ, pure wasn't autoloaded via
|
||||||
|
# promptinit and we need to take care of setting the options ourselves
|
||||||
|
if [[ $autoload_name != prompt_pure_setup ]] || [[ $zsh_eval_context[-2] != loadautofunc ]]; then
|
||||||
|
# borrowed from `promptinit`, set the pure prompt options
|
||||||
|
setopt noprompt{bang,cr,percent,subst} "prompt${^prompt_opts[@]}"
|
||||||
|
fi
|
||||||
|
|
||||||
zmodload zsh/datetime
|
zmodload zsh/datetime
|
||||||
zmodload zsh/zle
|
zmodload zsh/zle
|
||||||
|
zmodload zsh/parameter
|
||||||
|
|
||||||
autoload -Uz add-zsh-hook
|
autoload -Uz add-zsh-hook
|
||||||
autoload -Uz vcs_info
|
autoload -Uz vcs_info
|
||||||
autoload -Uz async && async
|
autoload -Uz async && async
|
||||||
@@ -345,7 +391,7 @@ prompt_pure_setup() {
|
|||||||
[[ $UID -eq 0 ]] && prompt_pure_username=' %F{white}%n%f%F{242}@%m%f'
|
[[ $UID -eq 0 ]] && prompt_pure_username=' %F{white}%n%f%F{242}@%m%f'
|
||||||
|
|
||||||
# prompt turns red if the previous command didn't exit with 0
|
# prompt turns red if the previous command didn't exit with 0
|
||||||
PROMPT="%(?.%F{magenta}.%F{red})${PURE_PROMPT_SYMBOL:-❯}%f "
|
PROMPT='%(?.%F{magenta}.%F{red})${PURE_PROMPT_SYMBOL:-❯}%f '
|
||||||
}
|
}
|
||||||
|
|
||||||
prompt_pure_setup "$@"
|
prompt_pure_setup "$0" "$@"
|
||||||
|
|||||||
84
readme.md
84
readme.md
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
> Pretty, minimal and fast ZSH prompt
|
> Pretty, minimal and fast ZSH prompt
|
||||||
|
|
||||||

|
<img src="screenshot.png" width="864">
|
||||||
|
|
||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
@@ -14,7 +14,7 @@ Most prompts are cluttered, ugly and slow. I wanted something visually pleasing
|
|||||||
- Comes with the perfect prompt character.
|
- Comes with the perfect prompt character.
|
||||||
Author went through the whole Unicode range to find it.
|
Author went through the whole Unicode range to find it.
|
||||||
- Shows `git` branch and whether it's dirty (with a `*`).
|
- Shows `git` branch and whether it's dirty (with a `*`).
|
||||||
- Indicates when you have unpushed/unpulled `git` commits with up/down arrows.
|
- Indicates when you have unpushed/unpulled `git` commits with up/down arrows. *(Check is done asynchronously!)*
|
||||||
- Prompt character turns red if the last command didn't exit with `0`.
|
- Prompt character turns red if the last command didn't exit with `0`.
|
||||||
- Command execution time will be displayed if it exceeds the set threshold.
|
- Command execution time will be displayed if it exceeds the set threshold.
|
||||||
- Username and host only displayed when in an SSH session.
|
- Username and host only displayed when in an SSH session.
|
||||||
@@ -24,11 +24,11 @@ Most prompts are cluttered, ugly and slow. I wanted something visually pleasing
|
|||||||
|
|
||||||
## Install
|
## Install
|
||||||
|
|
||||||
Can be installed with `npm` or manually. Requires git 2.0.0+ and ZSH 5.0.0+.
|
Can be installed with `npm` or manually. Requires Git 2.0.0+ and ZSH 5.2+. Older versions of ZSH are known to work, but they are **not** recommended.
|
||||||
|
|
||||||
### npm
|
### npm
|
||||||
|
|
||||||
```
|
```console
|
||||||
$ npm install --global pure-prompt
|
$ npm install --global pure-prompt
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -39,7 +39,7 @@ That's it. Skip to [Getting started](#getting-started).
|
|||||||
1. Either…
|
1. Either…
|
||||||
- Clone this repo
|
- Clone this repo
|
||||||
- add it as a submodule, or
|
- add it as a submodule, or
|
||||||
- just download `pure.zsh`
|
- just download `pure.zsh` and `async.zsh`
|
||||||
|
|
||||||
2. Symlink `pure.zsh` to somewhere in [`$fpath`](http://www.refining-linux.org/archives/46/ZSH-Gem-12-Autoloading-functions/) with the name `prompt_pure_setup`.
|
2. Symlink `pure.zsh` to somewhere in [`$fpath`](http://www.refining-linux.org/archives/46/ZSH-Gem-12-Autoloading-functions/) with the name `prompt_pure_setup`.
|
||||||
|
|
||||||
@@ -47,7 +47,7 @@ That's it. Skip to [Getting started](#getting-started).
|
|||||||
|
|
||||||
#### Example
|
#### Example
|
||||||
|
|
||||||
```
|
```console
|
||||||
$ ln -s "$PWD/pure.zsh" /usr/local/share/zsh/site-functions/prompt_pure_setup
|
$ ln -s "$PWD/pure.zsh" /usr/local/share/zsh/site-functions/prompt_pure_setup
|
||||||
$ ln -s "$PWD/async.zsh" /usr/local/share/zsh/site-functions/async
|
$ ln -s "$PWD/async.zsh" /usr/local/share/zsh/site-functions/async
|
||||||
```
|
```
|
||||||
@@ -62,7 +62,7 @@ fpath=( "$HOME/.zfunctions" $fpath )
|
|||||||
|
|
||||||
Then install the theme there:
|
Then install the theme there:
|
||||||
|
|
||||||
```sh
|
```console
|
||||||
$ ln -s "$PWD/pure.zsh" "$HOME/.zfunctions/prompt_pure_setup"
|
$ ln -s "$PWD/pure.zsh" "$HOME/.zfunctions/prompt_pure_setup"
|
||||||
$ ln -s "$PWD/async.zsh" "$HOME/.zfunctions/async"
|
$ ln -s "$PWD/async.zsh" "$HOME/.zfunctions/async"
|
||||||
```
|
```
|
||||||
@@ -74,7 +74,7 @@ Initialize the prompt system (if not so already) and choose `pure`:
|
|||||||
|
|
||||||
```sh
|
```sh
|
||||||
# .zshrc
|
# .zshrc
|
||||||
autoload -U promptinit && promptinit
|
autoload -U promptinit; promptinit
|
||||||
prompt pure
|
prompt pure
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -114,7 +114,7 @@ Defines the git up arrow symbol. The default value is `⇡`.
|
|||||||
```sh
|
```sh
|
||||||
# .zshrc
|
# .zshrc
|
||||||
|
|
||||||
autoload -U promptinit && promptinit
|
autoload -U promptinit; promptinit
|
||||||
|
|
||||||
# optionally define some options
|
# optionally define some options
|
||||||
PURE_CMD_MAX_EXEC_TIME=10
|
PURE_CMD_MAX_EXEC_TIME=10
|
||||||
@@ -125,33 +125,66 @@ prompt pure
|
|||||||
|
|
||||||
## Tips
|
## Tips
|
||||||
|
|
||||||
[Tomorrow Night Eighties](https://github.com/chriskempson/tomorrow-theme) theme with the [Droid Sans Mono](http://www.google.com/webfonts/specimen/Droid+Sans+Mono) font (15pt) is a beautiful combination, as seen in the screenshot above. Just make sure you have anti-aliasing enabled in your Terminal.
|
In the screenshot you see Pure running in [Hyper](https://hyper.is) with the [hyper-snazzy](https://github.com/sindresorhus/hyper-snazzy) theme and Menlo font.
|
||||||
|
|
||||||
To have commands colorized as seen in the screenshot install [zsh-syntax-highlighting](https://github.com/zsh-users/zsh-syntax-highlighting).
|
The [Tomorrow Night Eighties](https://github.com/chriskempson/tomorrow-theme) theme with the [Droid Sans Mono](https://fonts.google.com/specimen/Droid+Sans+Mono) font (15pt) is also a [nice combination](https://github.com/sindresorhus/pure/blob/95ee3e7618c6e2162a1e3cdac2a88a20ac3beb27/screenshot.png).<br>
|
||||||
|
*Just make sure you have anti-aliasing enabled in your terminal.*
|
||||||
|
|
||||||
|
To have commands colorized as seen in the screenshot, install [zsh-syntax-highlighting](https://github.com/zsh-users/zsh-syntax-highlighting).
|
||||||
|
|
||||||
|
|
||||||
## Integration
|
## Integration
|
||||||
|
|
||||||
### [oh-my-zsh](https://github.com/robbyrussell/oh-my-zsh)
|
### [oh-my-zsh](https://github.com/robbyrussell/oh-my-zsh)
|
||||||
|
|
||||||
1. Remove competing theme included in oh-my-zsh `~/.oh-my-zsh/themes/pure.zsh-theme`
|
1. Symlink (or copy) `pure.zsh` to `~/.oh-my-zsh/custom/pure.zsh-theme`.
|
||||||
2. Symlink (or copy) `pure.zsh` to `~/.oh-my-zsh/custom/pure.zsh-theme`
|
2. Symlink (or copy) `async.zsh` to `~/.oh-my-zsh/custom/async.zsh`.
|
||||||
3. Symlink (or copy) `async.zsh` to `~/.oh-my-zsh/custom/async.zsh`
|
3. Set `ZSH_THEME="pure"` in your `.zshrc` file.
|
||||||
4. Add `ZSH_THEME="pure"` to your `.zshrc` file.
|
|
||||||
|
|
||||||
### [prezto](https://github.com/sorin-ionescu/prezto)
|
Or skip the `oh-my-zsh` integration above and simply:
|
||||||
|
|
||||||
|
1. Set `ZSH_THEME=""` in your `.zshrc` to disable oh-my-zsh themes.
|
||||||
|
2. Follow the Pure [Install](#install) instructions.
|
||||||
|
|
||||||
|
### [prezto](https://github.com/zsh-users/prezto)
|
||||||
|
|
||||||
|
Pure is bundled with Prezto. No need to install it.
|
||||||
|
|
||||||
Set `zstyle ':prezto:module:prompt' theme 'pure'` in `~/.zpreztorc`.
|
Set `zstyle ':prezto:module:prompt' theme 'pure'` in `~/.zpreztorc`.
|
||||||
|
|
||||||
|
### [zim](https://github.com/Eriner/zim)
|
||||||
|
|
||||||
|
Pure is bundled with Zim. No need to install it.
|
||||||
|
|
||||||
|
Set `zprompt_theme='pure'` in `~/.zimrc`.
|
||||||
|
|
||||||
### [antigen](https://github.com/zsh-users/antigen)
|
### [antigen](https://github.com/zsh-users/antigen)
|
||||||
|
|
||||||
Update your `.zshrc` file with the following two lines (order matters). Do not use the `antigen theme` function.
|
Update your `.zshrc` file with the following two lines (order matters). Do not use the `antigen theme` function.
|
||||||
|
|
||||||
```
|
```sh
|
||||||
antigen bundle mafredri/zsh-async
|
antigen bundle mafredri/zsh-async
|
||||||
antigen bundle sindresorhus/pure
|
antigen bundle sindresorhus/pure
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### [antibody](https://github.com/getantibody/antibody)
|
||||||
|
|
||||||
|
Update your `.zshrc` file with the following two lines (order matters):
|
||||||
|
|
||||||
|
```sh
|
||||||
|
antibody bundle mafredri/zsh-async
|
||||||
|
antibody bundle sindresorhus/pure
|
||||||
|
```
|
||||||
|
|
||||||
|
### [zplug](https://github.com/zplug/zplug)
|
||||||
|
|
||||||
|
Update your `.zshrc` file with the following two lines:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
zplug mafredri/zsh-async, from:github
|
||||||
|
zplug sindresorhus/pure, use:pure.zsh, from:github, as:theme
|
||||||
|
```
|
||||||
|
|
||||||
## FAQ
|
## FAQ
|
||||||
|
|
||||||
### My preprompt is missing when I clear the screen with Ctrl+L
|
### My preprompt is missing when I clear the screen with Ctrl+L
|
||||||
@@ -171,15 +204,26 @@ Using `git pull` when you get the username prompt should help you to break the l
|
|||||||
|
|
||||||
#### Gentoo
|
#### Gentoo
|
||||||
|
|
||||||
```
|
```console
|
||||||
sudo sh -c "echo 'SANDBOX_WRITE=\"/dev/ptmx\"' > /etc/sandbox.d/10zsh"
|
$ sudo sh -c "echo 'SANDBOX_WRITE=\"/dev/ptmx\"' > /etc/sandbox.d/10zsh"
|
||||||
sudo emerge -1 zsh
|
$ sudo emerge -1 zsh
|
||||||
```
|
```
|
||||||
|
|
||||||
#### FreeBSD 10.1
|
#### FreeBSD 10.1
|
||||||
|
|
||||||
On a default setup, running the command `kldload pty` should do the trick. If you have a custom kernel, you might need to add `device pty` to the configuration file ([example](https://github.com/nbari/freebsd/blob/58646a9c3c4aaabf6f6467ff505f27f09e29dc75/kernels/xen.kernel#L188)).
|
On a default setup, running the command `kldload pty` should do the trick. If you have a custom kernel, you might need to add `device pty` to the configuration file ([example](https://github.com/nbari/freebsd/blob/58646a9c3c4aaabf6f6467ff505f27f09e29dc75/kernels/xen.kernel#L188)).
|
||||||
|
|
||||||
|
## Ports
|
||||||
|
|
||||||
|
* **Bash**
|
||||||
|
* [sapegin/dotfiles](https://github.com/sapegin/dotfiles)’s [prompt](https://github.com/sapegin/dotfiles/blob/dd063f9c30de7d2234e8accdb5272a5cc0a3388b/includes/bash_prompt.bash) and [color theme](https://github.com/sapegin/dotfiles/tree/master/color) for `Terminal.app`.
|
||||||
|
* **Fish**
|
||||||
|
* [brandonweiss/pure.fish](https://github.com/brandonweiss/pure.fish): a Pure-inspired prompt for Fish, not intended to have feature parity.
|
||||||
|
* [rafaelrinaldi/pure](https://github.com/rafaelrinaldi/pure), support for bare Fish and various framework ([Oh-My-Fish](https://github.com//oh-my-fish/oh-my-fish), [Fisherman](https://github.com//fisherman/fisherman) and [Wahoo](https://github.com//bucaran/wahoo)).
|
||||||
|
* **Zsh**
|
||||||
|
* [therealklanni/purity](https://github.com/therealklanni/purity): a more compact current working directory, important details on the main prompt line, and extra Git indicators.
|
||||||
|
* [intelfx/pure](https://github.com/intelfx/pure): Solarized-friendly colors, highly verbose and fully async Git integration
|
||||||
|
|
||||||
## Team
|
## Team
|
||||||
|
|
||||||
[](http://sindresorhus.com) | [](https://github.com/mafredri)
|
[](http://sindresorhus.com) | [](https://github.com/mafredri)
|
||||||
|
|||||||
BIN
screenshot.png
BIN
screenshot.png
Binary file not shown.
|
Before Width: | Height: | Size: 43 KiB After Width: | Height: | Size: 70 KiB |
Reference in New Issue
Block a user