Files
oh-my-fish_theme-bobthefish/functions/__bobthefish_git_async_enabled.fish
2026-05-19 22:04:32 -04:00

211 lines
8.4 KiB
Fish

function __bobthefish_git_async_enabled -S -d 'Check whether async git prompt updates should be used'
if [ "$argv[1]" = --init ]
if status is-interactive; and __bobthefish_version_at_least 3.0.0 $version
# The git metadata var must be universal: the detached background fish
# writes it, and a universal variable is the only channel that can deliver
# a value back into this (the parent) shell. It's namespaced by pid so
# concurrent sessions don't clobber each other.
set -g __bobthefish_git_async_var __bobthefish_git_async_$fish_pid
set -U $__bobthefish_git_async_var
# Fires when the background worker publishes new metadata. The pending
# flag tells the next fish_prompt that this render is the async repaint
# (so it must not spawn another worker).
function __bobthefish_git_async_repaint --on-variable $__bobthefish_git_async_var
set -g __bobthefish_git_async_repaint_pending 1
commandline --function repaint
end
function __bobthefish_git_async_cleanup --on-event fish_exit
set -q __bobthefish_git_async_pid
and command kill $__bobthefish_git_async_pid 2>/dev/null
set -q __bobthefish_git_async_trailing_pid
and command kill $__bobthefish_git_async_trailing_pid 2>/dev/null
set -q __bobthefish_git_async_var
and set -e $__bobthefish_git_async_var
end
__bobthefish_git_async_gc
end
# The git metadata var must be universal: the detached background fish
# writes it, and a universal variable is the only channel that can deliver
# a value back into this (the parent) shell. It's namespaced by pid so
# concurrent sessions don't clobber each other.
set -g __bobthefish_git_async_var __bobthefish_git_async_$fish_pid
set -U $__bobthefish_git_async_var
# Fires when the background worker publishes new metadata. The pending
# flag tells the next fish_prompt that this render is the async repaint
# (so it must not spawn another worker).
function __bobthefish_git_async_repaint --on-variable $__bobthefish_git_async_var
set -g __bobthefish_git_async_repaint_pending 1
commandline --function repaint
end
function __bobthefish_git_async_cleanup --on-event fish_exit
set -q __bobthefish_git_async_pid
and command kill $__bobthefish_git_async_pid 2>/dev/null
set -q __bobthefish_git_async_trailing_pid
and command kill $__bobthefish_git_async_trailing_pid 2>/dev/null
set -q __bobthefish_git_async_poll_pid
and command kill $__bobthefish_git_async_poll_pid 2>/dev/null
set -q __bobthefish_git_async_var
and set -e $__bobthefish_git_async_var
end
__bobthefish_git_async_gc
end
function __bobthefish_git_async_enabled -S -d 'Check whether async git prompt updates should be used'
[ "$theme_display_git_async" = no ]
and return 1
[ "$theme_git_worktree_support" = yes ]
and return 1
__bobthefish_version_at_least 3.0.0 $version
and status is-interactive
and set -q __bobthefish_git_async_var
and command -q fish
and __bobthefish_git_async_worker_path >/dev/null
end
function __bobthefish_git_async_worker_path -S -d 'Resolve and cache the async git worker file'
set -q __bobthefish_git_async_worker_path_cache
and echo $__bobthefish_git_async_worker_path_cache
and return
set -l prompt_path (functions --details fish_prompt)
[ -f "$prompt_path" ]
or return
set -l resolved (dirname "$prompt_path")/__bobthefish_git_async_worker.fish
[ -f "$resolved" ]
or return
set -g __bobthefish_git_async_worker_path_cache $resolved
echo $resolved
end
function __bobthefish_git_async_fish -S -d 'Resolve and cache the fish binary used for async git workers'
set -q __bobthefish_git_async_fish_path
or set -g __bobthefish_git_async_fish_path (command -s fish)
echo $__bobthefish_git_async_fish_path
end
function __bobthefish_git_async_gc -S -d 'Remove async git metadata left behind by dead fish sessions'
set -l prefix __bobthefish_git_async_
for var in (set --names --universal | string match "$prefix*")
[ "$var" = "$__bobthefish_git_async_var" ]
and continue
set -l pid (string replace $prefix '' -- $var)
string match -qr '^\d+$' -- "$pid"
or continue
command kill -0 $pid 2>/dev/null
or set -e $var
end
end
function __bobthefish_git_async_refresh_interval -S -d 'Get the minimum seconds between async git workers'
set -l interval 1
string match -qr '^\d+$' -- "$theme_git_async_refresh_interval"
and set interval $theme_git_async_refresh_interval
echo $interval
end
function __bobthefish_git_async_schedule_trailing -S -a git_root_dir -a delay -d 'Schedule a trailing async git refresh'
[ "$delay" -gt 0 ]
or return
set -q __bobthefish_git_async_var
or return
set -l worker_path (__bobthefish_git_async_worker_path)
or return
set -l fish_bin (__bobthefish_git_async_fish)
if set -q __bobthefish_git_async_trailing_pid
command kill -0 $__bobthefish_git_async_trailing_pid 2>/dev/null
and [ "$__bobthefish_git_async_trailing_root_dir" = "$git_root_dir" ]
and return
__bobthefish_git_async_clear_trailing
end
command $fish_bin --private -c '
sleep $argv[1]
source $argv[2]
__bobthefish_git_async_worker $argv[3] $argv[4] $argv[5] $argv[6] $argv[7] $argv[8]
' $delay $worker_path $__bobthefish_git_async_var $git_root_dir "$theme_display_git_dirty" "$theme_display_git_dirty_verbose" "$theme_display_git_untracked" "$theme_display_git_stashed_verbose" &
builtin -q disown
and builtin disown
set -g __bobthefish_git_async_trailing_pid $last_pid
set -g __bobthefish_git_async_trailing_root_dir $git_root_dir
end
function __bobthefish_git_async_start -S -a git_root_dir -d 'Start a background git prompt metadata update'
set -q __bobthefish_git_async_var
or return
# Keep at most one worker active per prompt session. Re-rendering while a
# worker is still collecting git state should not kill the worker that will
# publish the fresh cache.
if set -q __bobthefish_git_async_pid
if command kill -0 $__bobthefish_git_async_pid 2>/dev/null
[ "$__bobthefish_git_async_root_dir" = "$git_root_dir" ]
and return
command kill $__bobthefish_git_async_pid 2>/dev/null
end
set -e __bobthefish_git_async_pid
end
if set -q __bobthefish_git_async_trailing_pid
if [ "$__bobthefish_git_async_trailing_root_dir" != "$git_root_dir" ]
__bobthefish_git_async_clear_trailing
else if not command kill -0 $__bobthefish_git_async_trailing_pid 2>/dev/null
set -e __bobthefish_git_async_trailing_pid
end
end
set -l now (date +%s)
set -l interval (__bobthefish_git_async_refresh_interval)
# Git state can change inside a repo between prompts, so this is a short
# debounce rather than a same-repo cache. Set theme_git_async_refresh_interval
# to 0 to spawn a worker on every eligible prompt render.
if [ "$interval" -gt 0 -a "$__bobthefish_git_async_root_dir" = "$git_root_dir" -a -n "$__bobthefish_git_async_last_start" ]
set -l elapsed (math $now - $__bobthefish_git_async_last_start)
if [ "$elapsed" -lt "$interval" ]
__bobthefish_git_async_schedule_trailing $git_root_dir (math $interval - $elapsed)
return
end
end
__bobthefish_git_async_clear_trailing
set -l worker_path (__bobthefish_git_async_worker_path)
or return
set -l fish_bin (__bobthefish_git_async_fish)
set -l cmd source (string escape -- $worker_path) ';' __bobthefish_git_async_worker \
(string escape -- $__bobthefish_git_async_var) \
(string escape -- $git_root_dir) \
(string escape -- "$theme_display_git_dirty") \
(string escape -- "$theme_display_git_dirty_verbose") \
(string escape -- "$theme_display_git_untracked") \
(string escape -- "$theme_display_git_stashed_verbose")
set -g __bobthefish_git_async_root_dir $git_root_dir
set -g __bobthefish_git_async_last_start $now
command $fish_bin --private --command "$cmd" &
builtin -q disown
and builtin disown
set -g __bobthefish_git_async_pid $last_pid
end