Skip to content

Commit 9564031

Browse files
committed
zsh-async v1.7.0
1 parent 7c9568f commit 9564031

File tree

1 file changed

+59
-13
lines changed

1 file changed

+59
-13
lines changed

lib/async.zsh

Lines changed: 59 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,26 @@
33
#
44
# zsh-async
55
#
6-
# version: 1.6.2
6+
# version: 1.7.0
77
# author: Mathias Fredriksson
88
# url: https://github.com/mafredri/zsh-async
99
#
1010

11-
typeset -g ASYNC_VERSION=1.6.2
11+
typeset -g ASYNC_VERSION=1.7.0
1212
# Produce debug output from zsh-async when set to 1.
1313
typeset -g ASYNC_DEBUG=${ASYNC_DEBUG:-0}
1414

15+
# Execute commands that can manipulate the environment inside the async worker. Return output via callback.
16+
_async_eval() {
17+
local ASYNC_JOB_NAME
18+
# Rename job to _async_eval and redirect all eval output to cat running
19+
# in _async_job. Here, stdout and stderr are not separated for
20+
# simplicity, this could be improved in the future.
21+
{
22+
eval "$@"
23+
} &> >(ASYNC_JOB_NAME=[async/eval] _async_job 'cat')
24+
}
25+
1526
# Wrapper for jobs executed by the async worker, gives output in parseable format with execution time
1627
_async_job() {
1728
# Disable xtrace as it would mangle the output.
@@ -26,6 +37,7 @@ _async_job() {
2637
# block, after the command block has completed, the stdin for `cat` is
2738
# closed, causing stderr to be appended with a $'\0' at the end to mark the
2839
# end of output from this job.
40+
local jobname=${ASYNC_JOB_NAME:-$1}
2941
local stdout stderr ret tok
3042
{
3143
stdout=$(eval "$@")
@@ -36,7 +48,7 @@ _async_job() {
3648
read -r -k 1 -p tok || exit 1
3749

3850
# Return output (<job_name> <return_code> <stdout> <duration> <stderr>).
39-
print -r -n - $'\0'${(q)1} $ret ${(q)stdout} $duration
51+
print -r -n - $'\0'${(q)jobname} $ret ${(q)stdout} $duration
4052
} 2> >(stderr=$(cat) && print -r -n - " "${(q)stderr}$'\0')
4153

4254
# Unlock mutex by inserting a token.
@@ -132,7 +144,7 @@ _async_worker() {
132144
coproc_pid=0 # Reset pid.
133145
}
134146

135-
local request
147+
local request do_eval=0
136148
local -a cmd
137149
while :; do
138150
# Wait for jobs sent by async_job.
@@ -147,8 +159,9 @@ _async_worker() {
147159

148160
# Check for non-job commands sent to worker
149161
case $request in
150-
_unset_trap) notify_parent=0; continue;;
151-
_killjobs) killjobs; continue;;
162+
_unset_trap) notify_parent=0; continue;;
163+
_killjobs) killjobs; continue;;
164+
_async_eval*) do_eval=1;;
152165
esac
153166

154167
# Parse the request using shell parsing (z) to allow commands
@@ -181,18 +194,27 @@ _async_worker() {
181194
print -n -p "t"
182195
fi
183196

184-
# Run job in background, completed jobs are printed to stdout.
185-
_async_job $cmd &
186-
# Store pid because zsh job manager is extremely unflexible (show jobname as non-unique '$job')...
187-
storage[$job]="$!"
197+
if (( do_eval )); then
198+
shift cmd # Strip _async_eval from cmd.
199+
_async_eval $cmd
200+
do_eval=0
201+
else
202+
# Run job in background, completed jobs are printed to stdout.
203+
_async_job $cmd &
204+
# Store pid because zsh job manager is extremely unflexible (show jobname as non-unique '$job')...
205+
storage[$job]="$!"
206+
fi
188207

189208
processing=0 # Disable guard.
190209
done
191210
}
192211

193212
#
194-
# Get results from finnished jobs and pass it to the to callback function. This is the only way to reliably return the
195-
# job name, return code, output and execution time and with minimal effort.
213+
# Get results from finished jobs and pass it to the to callback function. This is the only way to reliably return the
214+
# job name, return code, output and execution time and with minimal effort.
215+
#
216+
# If the async process buffer becomes corrupt, the callback will be invoked with the first argument being `[async]` (job
217+
# name), non-zero return code and fifth argument describing the error (stderr).
196218
#
197219
# usage:
198220
# async_process_results <worker_name> <callback_function>
@@ -252,7 +274,7 @@ async_process_results() {
252274
else
253275
# In case of corrupt data, invoke callback with *async* as job
254276
# name, non-zero exit status and an error message on stderr.
255-
$callback "async" 1 "" 0 "$0:$LINENO: error: bad format, got ${#items} items (${(q)items})" $has_next
277+
$callback "[async]" 1 "" 0 "$0:$LINENO: error: bad format, got ${#items} items (${(q)items})" $has_next
256278
fi
257279
done
258280
done
@@ -299,6 +321,30 @@ async_job() {
299321
zpty -w $worker "$cmd"$'\0'
300322
}
301323

324+
#
325+
# Evaluate a command (like async_job) inside the async worker, then worker environment can be manipulated. For example,
326+
# issuing a cd command will change the PWD of the worker which will then be inherited by all future async jobs.
327+
#
328+
# Output will be returned via callback, job name will be [async/eval].
329+
#
330+
# usage:
331+
# async_worker_eval <worker_name> <my_function> [<function_params>]
332+
#
333+
async_worker_eval() {
334+
setopt localoptions noshwordsplit noksharrays noposixidentifiers noposixstrings
335+
336+
local worker=$1; shift
337+
338+
local -a cmd
339+
cmd=("$@")
340+
if (( $#cmd > 1 )); then
341+
cmd=(${(q)cmd}) # Quote special characters in multi argument commands.
342+
fi
343+
344+
# Quote the cmd in case RC_EXPAND_PARAM is set.
345+
zpty -w $worker "_async_eval $cmd"$'\0'
346+
}
347+
302348
# This function traps notification signals and calls all registered callbacks
303349
_async_notify_trap() {
304350
setopt localoptions noshwordsplit

0 commit comments

Comments
 (0)