diff options
Diffstat (limited to 'internal')
-rw-r--r-- | internal/p10k.zsh | 697 | ||||
-rw-r--r-- | internal/worker.zsh | 372 |
2 files changed, 740 insertions, 329 deletions
diff --git a/internal/p10k.zsh b/internal/p10k.zsh index 2fe7fb50..b91f4fca 100644 --- a/internal/p10k.zsh +++ b/internal/p10k.zsh @@ -28,6 +28,7 @@ if ! autoload -Uz is-at-least || ! is-at-least 5.1; then fi source "${__p9k_root_dir}/internal/configure.zsh" +source "${__p9k_root_dir}/internal/worker.zsh" # For compatibility with Powerlevel9k. It's not recommended to use mnemonic color # names in the configuration except for colors 0-7 as these are standard. @@ -206,7 +207,7 @@ function _p9k_prompt_length() { _p9k_ret=$x } -typeset -gr __p9k_byte_suffix=('B' 'K' 'M' 'G' 'T' 'P' 'E' 'Z' 'Y') +typeset -g __p9k_byte_suffix=('B' 'K' 'M' 'G' 'T' 'P' 'E' 'Z' 'Y') # 42 => 42B # 1536 => 1.5K @@ -230,17 +231,6 @@ _p9k_segment_in_use() { $_POWERLEVEL9K_RIGHT_PROMPT_ELEMENTS[(I)$1(|_joined)] )) } -function _p9k_parse_ip() { - local iface_regex="^(${1:-.*})\$" iface ip - for iface ip in "${(@kv)_p9k_iface}"; do - if [[ $iface =~ $iface_regex ]]; then - _p9k_ret=$ip - return 0 - fi - done - return 1 -} - # Caching allows storing array-to-array associations. It should be used like this: # # if ! _p9k_cache_get "$key1" "$key2"; then @@ -995,7 +985,7 @@ prompt_aws() { prompt_aws_eb_env() { (( $+commands[eb] )) || return - local dir=$_p9k_pwd + local dir=$_p9k__pwd while true; do [[ $dir == / ]] && return [[ -d $dir/.elasticbeanstalk ]] && break @@ -1029,24 +1019,59 @@ prompt_background_jobs() { ################################################################ # Segment that indicates usage level of current partition. prompt_disk_usage() { - (( $+commands[df] )) || return - local disk_usage=${${=${(f)"$(df -P . 2>/dev/null)"}[2]}[5]%%%} - local state bg fg - if (( disk_usage >= _POWERLEVEL9K_DISK_USAGE_CRITICAL_LEVEL )); then - state=CRITICAL - bg=red - fg=white - elif (( disk_usage >= _POWERLEVEL9K_DISK_USAGE_WARNING_LEVEL )); then - state=WARNING - bg=yellow - fg=$_p9k_color1 - else - (( _POWERLEVEL9K_DISK_USAGE_ONLY_WARNING )) && return - state=NORMAL - bg=$_p9k_color1 - fg=yellow - fi - _p9k_prompt_segment $0_$state $bg $fg DISK_ICON 0 '' "$disk_usage%%" + _p9k_prompt_segment $0_CRITICAL red white DISK_ICON 1 '$_p9k__disk_usage_critical' '$_p9k__disk_usage_pct%%' + _p9k_prompt_segment $0_WARNING yellow $_p9k_color1 DISK_ICON 1 '$_p9k__disk_usage_warning' '$_p9k__disk_usage_pct%%' + (( _POWERLEVEL9K_DISK_USAGE_ONLY_WARNING )) && return + _p9k_prompt_segment $0_NORMAL $_p9k_color1 yellow DISK_ICON 1 '$_p9k__disk_usage_normal' '$_p9k__disk_usage_pct%%' +} + +_p9k_prompt_disk_usage_init() { + typeset -g _p9k__disk_usage_pct= + typeset -g _p9k__disk_usage_normal= + typeset -g _p9k__disk_usage_warning= + typeset -g _p9k__disk_usage_critical= + + _p9k_worker_send_params \ + _POWERLEVEL9K_DISK_USAGE_CRITICAL_LEVEL \ + _POWERLEVEL9K_DISK_USAGE_WARNING_LEVEL \ + _POWERLEVEL9K_DISK_USAGE_ONLY_WARNING \ + _p9k__disk_usage_pct \ + _p9k__disk_usage_normal \ + _p9k__disk_usage_warning \ + _p9k__disk_usage_critical +} + +_p9k_prompt_disk_usage_compute() { + _p9k_worker_invoke \ + disk_usage \ + _p9k_prompt_disk_usage_cond \ + "_p9k_prompt_disk_usage_async ${(q)_p9k__pwd_a}" \ + _p9k_prompt_disk_usage_sync +} + +_p9k_prompt_disk_usage_cond() { + (( $+commands[df] )) +} + +_p9k_prompt_disk_usage_async() { + local pct=${${=${(f)"$(df -P $1 2>/dev/null)"}[2]}[5]%%%} + [[ $pct == <0-100> && pct != $_p9k__disk_usage_pct ]] || return + _p9k__disk_usage_pct=$pct + _p9k__disk_usage_normal= + _p9k__disk_usage_warning= + _p9k__disk_usage_critical= + if (( _p9k__disk_usage_pct >= _POWERLEVEL9K_DISK_USAGE_CRITICAL_LEVEL )); then + _p9k__disk_usage_critical=1 + elif (( _p9k__disk_usage_pct >= _POWERLEVEL9K_DISK_USAGE_WARNING_LEVEL )); then + _p9k__disk_usage_warning=1 + elif (( ! _POWERLEVEL9K_DISK_USAGE_ONLY_WARNING )); then + _p9k__disk_usage_normal=1 + fi + typeset -p \ + _p9k__disk_usage_pct \ + _p9k__disk_usage_normal \ + _p9k__disk_usage_warning \ + _p9k__disk_usage_critical } function _p9k_read_file() { @@ -1057,7 +1082,7 @@ function _p9k_read_file() { prompt_fvm() { (( $+commands[fvm] )) || return - local dir=$_p9k_pwd_a + local dir=$_p9k__pwd_a while [[ $dir != / ]]; do local link=$dir/fvm if [[ -L $link ]]; then @@ -1193,13 +1218,75 @@ prompt_battery() { ################################################################ # Public IP segment prompt_public_ip() { - local icon='PUBLIC_IP_ICON' + local ip='${_p9k__public_ip:-$_POWERLEVEL9K_PUBLIC_IP_NONE}' if [[ -n $_POWERLEVEL9K_PUBLIC_IP_VPN_INTERFACE ]]; then - _p9k_parse_ip $_POWERLEVEL9K_PUBLIC_IP_VPN_INTERFACE && icon='VPN_ICON' + _p9k_prompt_segment "$0" "$_p9k_color1" "$_p9k_color2" PUBLIC_IP_ICON 1 '${_p9k__public_ip_not_vpn:+'$ip'}' $ip + _p9k_prompt_segment "$0" "$_p9k_color1" "$_p9k_color2" VPN_ICON 1 '${_p9k__public_ip_vpn:+'$ip'}' $ip + else + _p9k_prompt_segment "$0" "$_p9k_color1" "$_p9k_color2" PUBLIC_IP_ICON 1 $ip $ip fi +} - local ip='${_p9k__public_ip:-$_POWERLEVEL9K_PUBLIC_IP_NONE}' - _p9k_prompt_segment "$0" "$_p9k_color1" "$_p9k_color2" "$icon" 1 $ip $ip +_p9k_prompt_public_ip_init() { + typeset -g _p9k__public_ip= + typeset -gF _p9k__public_ip_next_time=0 + + _p9k_worker_send_params \ + _POWERLEVEL9K_PUBLIC_IP_METHODS \ + _POWERLEVEL9K_PUBLIC_IP_TIMEOUT \ + _POWERLEVEL9K_PUBLIC_IP_HOST \ + _p9k__public_ip \ + _p9k__public_ip_next_time +} + +_p9k_prompt_public_ip_cond() { + (( EPOCHREALTIME >= _p9k__public_ip_next_time )) +} + +_p9k_prompt_public_ip_async() { + local ip method + local -F start=EPOCHREALTIME + local -F next='start + 5' + for method in $_POWERLEVEL9K_PUBLIC_IP_METHODS $_POWERLEVEL9K_PUBLIC_IP_METHODS; do + case $method in + dig) + if (( $+commands[dig] )); then + ip="$(dig +tries=1 +short -4 A myip.opendns.com @resolver1.opendns.com 2>/dev/null)" + [[ $ip == ';'* ]] && ip= + if [[ -z $ip ]]; then + ip="$(dig +tries=1 +short -6 AAAA myip.opendns.com @resolver1.opendns.com 2>/dev/null)" + [[ $ip == ';'* ]] && ip= + fi + fi + ;; + curl) + if (( $+commands[curl] )); then + ip="$(curl --max-time 5 -w '\n' "$ip_url" 2>/dev/null)" + fi + ;; + wget) + if (( $+commands[wget] )); then + ip="$(wget -T 5 -qO- "$ip_url" 2>/dev/null)" + fi + ;; + esac + [[ $ip =~ '^[0-9a-f.:]+$' ]] || ip='' + if [[ -n $ip ]]; then + next=$((start + _POWERLEVEL9K_PUBLIC_IP_TIMEOUT)) + break + fi + done + _p9k__public_ip=$ip + _p9k__public_ip_next_time=$next + typeset -p _p9k__public_ip _p9k__public_ip_next_time +} + +_p9k_prompt_public_ip_sync() { + [[ $prev == $cur ]] && return + local prev_ip=$_p9k__public_ip + eval $cur + [[ $_p9k__public_ip == $prev_ip ]] && return + _p9k_worker_reply $cur } ################################################################ @@ -1353,10 +1440,10 @@ function _p9k_shorten_delim_len() { # Dir: current working directory prompt_dir() { if (( _POWERLEVEL9K_DIR_PATH_ABSOLUTE )); then - local p=$_p9k_pwd + local p=$_p9k__pwd local -a parts=("${(s:/:)p}") elif [[ -o auto_name_dirs ]]; then - local p=${_p9k_pwd/#(#b)$HOME(|\/*)/'~'$match[1]} + local p=${_p9k__pwd/#(#b)$HOME(|\/*)/'~'$match[1]} local -a parts=("${(s:/:)p}") else local p=${(%):-%~} @@ -1369,7 +1456,7 @@ prompt_dir() { local func='' local -a parts=() reply=() for func in zsh_directory_name $zsh_directory_name_functions; do - if (( $+functions[$func] )) && $func d $_p9k_pwd && [[ $p == '~['$reply[1]']'* ]]; then + if (( $+functions[$func] )) && $func d $_p9k__pwd && [[ $p == '~['$reply[1]']'* ]]; then parts+='~['$reply[1]']' break fi @@ -1377,7 +1464,7 @@ prompt_dir() { if (( $#parts )); then parts+=(${(s:/:)${p#$parts[1]}}) else - p=$_p9k_pwd + p=$_p9k__pwd parts=("${(s:/:)p}") fi else @@ -1418,7 +1505,7 @@ prompt_dir() { $+commands[jq] == 1 && $#_POWERLEVEL9K_DIR_PACKAGE_FILES > 0 ]] || return local pats="(${(j:|:)_POWERLEVEL9K_DIR_PACKAGE_FILES})" local -i i=$#parts - local dir=$_p9k_pwd + local dir=$_p9k__pwd for (( ; i > 0; --i )); do local markers=($dir/${~pats}(N)) if (( $#markers )); then @@ -1479,7 +1566,7 @@ prompt_dir() { elif [[ $p[1] == / ]]; then (( ++i )) fi - local parent="${_p9k_pwd%/${(pj./.)parts[i,-1]}}" + local parent="${_p9k__pwd%/${(pj./.)parts[i,-1]}}" if (( i <= e )); then local MATCH= mtimes=() zstat -A mtimes +mtime -- ${(@)${:-{$i..$e}}/(#m)*/$parent/${(pj./.)parts[i,$MATCH]}} 2>/dev/null || mtimes=() @@ -1487,7 +1574,7 @@ prompt_dir() { else local key='good' fi - if ! _p9k_cache_ephemeral_get $0 $e $i $_p9k_pwd || [[ $key != $_p9k_cache_val[1] ]] ; then + if ! _p9k_cache_ephemeral_get $0 $e $i $_p9k__pwd || [[ $key != $_p9k_cache_val[1] ]] ; then _p9k_prompt_length $delim local -i real_delim_len=_p9k_ret [[ -n $parts[i-1] ]] && parts[i-1]="\${(Q)\${:-${(qqq)${(q)parts[i-1]}}}}"$'\2' @@ -1562,7 +1649,7 @@ prompt_dir() { ;; truncate_with_folder_marker) if [[ -n $_POWERLEVEL9K_SHORTEN_FOLDER_MARKER ]]; then - local dir=$_p9k_pwd + local dir=$_p9k__pwd local -a m=() local -i i=$(($#parts - 1)) for (( ; i > 1; --i )); do @@ -1586,9 +1673,9 @@ prompt_dir() { ;; esac - [[ $_POWERLEVEL9K_DIR_SHOW_WRITABLE == 1 && ! -w $_p9k_pwd ]] + [[ $_POWERLEVEL9K_DIR_SHOW_WRITABLE == 1 && ! -w $_p9k__pwd ]] local w=$? - if ! _p9k_cache_ephemeral_get $0 $_p9k_pwd $p $w $fake_first "${parts[@]}"; then + if ! _p9k_cache_ephemeral_get $0 $_p9k__pwd $p $w $fake_first "${parts[@]}"; then local state=$0 local icon='' if (( ! w )); then @@ -1597,7 +1684,7 @@ prompt_dir() { else local a='' b='' c='' for a b c in "${_POWERLEVEL9K_DIR_CLASSES[@]}"; do - if [[ $_p9k_pwd == ${~a} ]]; then + if [[ $_p9k__pwd == ${~a} ]]; then [[ -n $b ]] && state+=_${(U)b} icon=$'\1'$c break @@ -1685,7 +1772,7 @@ prompt_dir() { local content="${(pj.$sep.)parts}" if (( _POWERLEVEL9K_DIR_HYPERLINK )); then - local header=$'%{\e]8;;file://'${${_p9k_pwd//\%/%%25}//'#'/%%23}$'\a%}' + local header=$'%{\e]8;;file://'${${_p9k__pwd//\%/%%25}//'#'/%%23}$'\a%}' local footer=$'%{\e]8;;\a%}' if (( expand )); then _p9k_escape $header @@ -1738,8 +1825,8 @@ prompt_go_version() { p="$(go env GOPATH 2>/dev/null)" && [[ -n $p ]] || return fi fi - if [[ $_p9k_pwd/ != $p/* && $_p9k_pwd_a/ != $p/* ]]; then - local dir=$_p9k_pwd_a + if [[ $_p9k__pwd/ != $p/* && $_p9k__pwd_a/ != $p/* ]]; then + local dir=$_p9k__pwd_a while true; do [[ $dir == / ]] && return [[ -e $dir/go.mod ]] && break @@ -1772,15 +1859,13 @@ prompt_detect_virt() { ################################################################ # Segment to display the current IP address prompt_ip() { - _p9k_parse_ip $_POWERLEVEL9K_IP_INTERFACE || return - _p9k_prompt_segment "$0" "cyan" "$_p9k_color1" 'NETWORK_ICON' 0 '' "${_p9k_ret//\%/%%}" + _p9k_prompt_segment "$0" "cyan" "$_p9k_color1" 'NETWORK_ICON' 1 '$_p9k__ip_ip' '$_p9k__ip_ip' } ################################################################ # Segment to display if VPN is active prompt_vpn_ip() { - _p9k_parse_ip $_POWERLEVEL9K_VPN_IP_INTERFACE || return - _p9k_prompt_segment "$0" "cyan" "$_p9k_color1" 'VPN_ICON' 0 '' "${_p9k_ret//\%/%%}" + _p9k_prompt_segment "$0" "cyan" "$_p9k_color1" 'VPN_ICON' 1 '$_p9k__vpn_ip_ip' '$_p9k__vpn_ip_ip' } ################################################################ @@ -1797,38 +1882,66 @@ prompt_laravel_version() { ################################################################ # Segment to display load prompt_load() { - local bucket=2 - case $_POWERLEVEL9K_LOAD_WHICH in - 1) bucket=1;; - 5) bucket=2;; - 15) bucket=3;; - esac - - local load - case $_p9k_os in - OSX|BSD) - (( $+commands[sysctl] )) || return - load="$(sysctl -n vm.loadavg 2>/dev/null)" || return - load=${${(A)=load}[bucket+1]//,/.} - ;; - *) - [[ -r /proc/loadavg ]] || return - _p9k_read_file /proc/loadavg || return - load=${${(A)=_p9k_ret}[bucket]//,/.} - ;; - esac - - (( _p9k_num_cpus )) || return - - if (( load > 0.7 * _p9k_num_cpus )); then - local state=CRITICAL bg=red - elif (( load > 0.5 * _p9k_num_cpus )); then - local state=WARNING bg=yellow + if [[ $_p9k_os == (OSX|BSD) ]]; then + _p9k_prompt_segment $0_CRITICAL red "$_p9k_color1" LOAD_ICON 1 '$_p9k__load_critical' '$_p9k__load_value' + _p9k_prompt_segment $0_WARNING yellow "$_p9k_color1" LOAD_ICON 1 '$_p9k__load_warning' '$_p9k__load_value' + _p9k_prompt_segment $0_NORMAL green "$_p9k_color1" LOAD_ICON 1 '$_p9k__load_normal' '$_p9k__load_value' + return + fi + + [[ -r /proc/loadavg ]] || return + _p9k_read_file /proc/loadavg || return + local load=${${(A)=_p9k_ret}[_POWERLEVEL9K_LOAD_WHICH]//,/.} + local -F pct='100. * load / _p9k_num_cpus' + if (( pct > 70 )); then + _p9k_prompt_segment $0_CRITICAL red "$_p9k_color1" LOAD_ICON 0 '' $load + elif (( pct > 50 )); then + _p9k_prompt_segment $0_WARNING yellow "$_p9k_color1" LOAD_ICON 0 '' $load else - local state=NORMAL bg=green + _p9k_prompt_segment $0_NORMAL green "$_p9k_color1" LOAD_ICON 0 '' $load + fi +} + +_p9k_prompt_load_init() { + [[ $_p9k_os == (OSX|BSD) ]] || return + typeset -g _p9k__load_value= + typeset -g _p9k__load_normal= + typeset -g _p9k__load_warning= + typeset -g _p9k__load_critical= + _p9k_worker_send_params \ + $_POWERLEVEL9K_LOAD_WHICH \ + _p9k_num_cpus \ + _p9k__load_value \ + _p9k__load_normal \ + _p9k__load_warning \ + _p9k__load_critical +} + +_p9k_prompt_load_cond() { + (( $+commands[sysctl] )) +} + +_p9k_prompt_load_async() { + local load="$(sysctl -n vm.loadavg 2>/dev/null)" || return + load=${${(A)=load}[_POWERLEVEL9K_LOAD_WHICH+1]//,/.} + [[ $load == <->(|.<->) && $load != $_p9k__load_value ]] || return + _p9k__load_value=$load + _p9k__load_normal= + _p9k__load_warning= + _p9k__load_critical= + local -F pct='100. * _p9k__load_value / _p9k_num_cpus' + if (( pct > 70 )); then + _p9k__load_critical=1 + elif (( pct > 50 )); then + _p9k__load_warning=1 + else + _p9k__load_normal=1 fi - - _p9k_prompt_segment $0_$state $bg "$_p9k_color1" LOAD_ICON 0 '' $load + typeset -p \ + _p9k__load_value \ + _p9k__load_normal \ + _p9k__load_warning \ + _p9k__load_critical } function _p9k_cached_cmd_stdout() { @@ -1863,7 +1976,7 @@ prompt_node_version() { (( $+commands[node] )) || return if (( _POWERLEVEL9K_NODE_VERSION_PROJECT_ONLY )); then - local dir=$_p9k_pwd + local dir=$_p9k__pwd while true; do [[ $dir == / ]] && return [[ -e $dir/package.json ]] && break @@ -2022,7 +2135,7 @@ prompt_nodenv() { (( $+commands[nodenv] || $+functions[nodenv] )) || return _p9k_ret=$NODENV_VERSION if [[ -z $_p9k_ret ]]; then - [[ $NODENV_DIR == /* ]] && local dir=$NODENV_DIR || local dir="$_p9k_pwd_a/$NODENV_DIR" + [[ $NODENV_DIR == /* ]] && local dir=$NODENV_DIR || local dir="$_p9k__pwd_a/$NODENV_DIR" while [[ $dir != //[^/]# ]]; do _p9k_read_nodenv_version_file $dir/.node-version && break [[ $dir == / ]] && break @@ -2049,15 +2162,15 @@ prompt_dotnet_version() { (( $+commands[dotnet] )) || return if (( _POWERLEVEL9K_DOTNET_VERSION_PROJECT_ONLY )); then - case $_p9k_pwd in + case $_p9k__pwd in ~|/) return 0;; ~/*) local parent=~/ - local parts=(${(s./.)_p9k_pwd#$parent}) + local parts=(${(s./.)_p9k__pwd#$parent}) ;; *) local parent=/ - local parts=(${(s./.)_p9k_pwd}) + local parts=(${(s./.)_p9k__pwd}) ;; esac local MATCH @@ -2065,7 +2178,7 @@ prompt_dotnet_version() { local mtimes=() zstat -A mtimes +mtime -- $dirs 2>/dev/null || mtimes=() local key="${(pj.:.)mtimes}" - if ! _p9k_cache_ephemeral_get $0 $_p9k_pwd || [[ $key != $_p9k_cache_val[1] ]] ; then + if ! _p9k_cache_ephemeral_get $0 $_p9k__pwd || [[ $key != $_p9k_cache_val[1] ]] ; then local -i i found for i in {1..$#dirs}; do local dir=$dirs[i] mtime=$mtimes[i] @@ -2110,6 +2223,16 @@ prompt_php_version() { ################################################################ # Segment to display free RAM and used Swap prompt_ram() { + _p9k_prompt_segment $0 yellow "$_p9k_color1" RAM_ICON 1 '$_p9k__ram_free' '$_p9k__ram_free' +} + +function _p9k_prompt_ram_init() { + typeset -g _p9k__ram_free= + _p9k_worker_send_params _p9k_os _p9k__ram_free __p9k_byte_suffix + _p9k_worker_send_functions _p9k_human_readable_bytes +} + +_p9k_prompt_ram_async() { local -F free_bytes case $_p9k_os in @@ -2117,9 +2240,9 @@ prompt_ram() { (( $+commands[vm_stat] )) || return local stat && stat="$(vm_stat 2>/dev/null)" || return [[ $stat =~ 'Pages free:[[:space:]]+([0-9]+)' ]] || return - (( free_bytes+=match[1] )) + (( free_bytes += match[1] )) [[ $stat =~ 'Pages inactive:[[:space:]]+([0-9]+)' ]] || return - (( free_bytes+=match[1] )) + (( free_bytes += match[1] )) (( free_bytes *= 4096 )) ;; BSD) @@ -2129,13 +2252,15 @@ prompt_ram() { *) [[ -r /proc/meminfo ]] || return local stat && stat="$(</proc/meminfo)" || return - [[ $stat == (#b)*'MemAvailable:'[[:space:]]#(<->)* ]] || return - free_bytes=$(( $match[1] * 1024 )) + [[ $stat == (#b)*(MemAvailable:|MemFree:)[[:space:]]#(<->)* ]] || return + free_bytes=$(( $match[2] * 1024 )) ;; esac _p9k_human_readable_bytes $free_bytes - _p9k_prompt_segment $0 yellow "$_p9k_color1" RAM_ICON 0 '' $_p9k_ret + [[ $_p9k_ret != $_p9k__ram_free ]] || return + _p9k__ram_free=$_p9k_ret + typeset -p _p9k__ram_free } function _p9k_read_rbenv_version_file() { @@ -2159,7 +2284,7 @@ prompt_rbenv() { local v=$RBENV_VERSION else (( ${_POWERLEVEL9K_RBENV_SOURCES[(I)local|global]} )) || return - [[ $RBENV_DIR == /* ]] && local dir=$RBENV_DIR || local dir="$_p9k_pwd_a/$RBENV_DIR" + [[ $RBENV_DIR == /* ]] && local dir=$RBENV_DIR || local dir="$_p9k__pwd_a/$RBENV_DIR" while true; do if _p9k_read_rbenv_version_file $dir/.ruby-version; then (( ${_POWERLEVEL9K_RBENV_SOURCES[(I)local]} )) || return @@ -2206,7 +2331,7 @@ prompt_luaenv() { local v=$LUAENV_VERSION else (( ${_POWERLEVEL9K_LUAENV_SOURCES[(I)local|global]} )) || return - [[ $LUAENV_DIR == /* ]] && local dir=$LUAENV_DIR || local dir="$_p9k_pwd_a/$LUAENV_DIR" + [[ $LUAENV_DIR == /* ]] && local dir=$LUAENV_DIR || local dir="$_p9k__pwd_a/$LUAENV_DIR" while true; do if _p9k_read_luaenv_version_file $dir/.lua-version; then (( ${_POWERLEVEL9K_LUAENV_SOURCES[(I)local]} )) || return @@ -2264,7 +2389,7 @@ prompt_plenv() { local v=$PLENV_VERSION else (( ${_POWERLEVEL9K_PLENV_SOURCES[(I)local|global]} )) || return - [[ $PLENV_DIR == /* ]] && local dir=$PLENV_DIR || local dir="$_p9k_pwd_a/$PLENV_DIR" + [[ $PLENV_DIR == /* ]] && local dir=$PLENV_DIR || local dir="$_p9k__pwd_a/$PLENV_DIR" while true; do if _p9k_read_plenv_version_file $dir/.perl-version; then (( ${_POWERLEVEL9K_PLENV_SOURCES[(I)local]} )) || return @@ -2300,7 +2425,7 @@ prompt_jenv() { local v=$JENV_VERSION else (( ${_POWERLEVEL9K_JENV_SOURCES[(I)local|global]} )) || return - [[ $JENV_DIR == /* ]] && local dir=$JENV_DIR || local dir="$_p9k_pwd_a/$JENV_DIR" + [[ $JENV_DIR == /* ]] && local dir=$JENV_DIR || local dir="$_p9k__pwd_a/$JENV_DIR" while true; do if _p9k_read_jenv_version_file $dir/.java-version; then (( ${_POWERLEVEL9K_JENV_SOURCES[(I)local]} )) || return @@ -2351,7 +2476,7 @@ prompt_rust_version() { unset P9K_RUST_VERSION (( $+commands[rustc] )) || return if (( _POWERLEVEL9K_RUST_VERSION_PROJECT_ONLY )); then - local dir=$_p9k_pwd_a + local dir=$_p9k__pwd_a while true; do [[ $dir == / ]] && return [[ -e $dir/Cargo.toml ]] && break @@ -2384,7 +2509,7 @@ prompt_rust_version() { _p9k_cache_stat_set ${keys:^vals} fi local -A overrides=($_p9k_cache_val) - local dir=$_p9k_pwd_a + local dir=$_p9k__pwd_a while true; do if (( $+overrides[$dir] )); then toolchain=$overrides[$dir] @@ -2631,6 +2756,20 @@ instant_prompt_time() { _p9k_prompt_segment prompt_time "$_p9k_color2" "$_p9k_color1" "TIME_ICON" 1 '' $stash$_p9k_ret } +_p9k_prompt_time_init() { + (( _POWERLEVEL9K_EXPERIMENTAL_TIME_REALTIME )) || return + _p9k_worker_send_functions _p9k_prompt_time_sync + _p9k__async_segments_compute+=_p9k_prompt_time_compute +} + +_p9k_prompt_time_async() { + sleep 1 || true +} + +_p9k_prompt_time_sync() { + _p9k_worker_reply _p9k_prompt_time_compute +} + ################################################################ # System date prompt_date() { @@ -2930,7 +3069,7 @@ function _p9k_vcs_status_for_dir() { _p9k_ret=$_p9k__gitstatus_last[GIT_DIR:$GIT_DIR] [[ -n $_p9k_ret ]] else - local dir=$_p9k_pwd_a + local dir=$_p9k__pwd_a while true; do _p9k_ret=$_p9k__gitstatus_last[$dir] [[ -n $_p9k_ret ]] && return 0 @@ -3170,8 +3309,8 @@ function _p9k_vcs_resume() { if [[ -z $_p9k__gitstatus_next_dir ]]; then unset _p9k__gitstatus_next_dir case $VCS_STATUS_RESULT in - norepo-async) (( $1 )) && _p9k_vcs_status_purge $_p9k_pwd_a;; - ok-async) (( $1 )) || _p9k__gitstatus_next_dir=$_p9k_pwd_a;; + norepo-async) (( $1 )) && _p9k_vcs_status_purge $_p9k__pwd_a;; + ok-async) (( $1 )) || _p9k__gitstatus_next_dir=$_p9k__pwd_a;; esac fi @@ -3199,12 +3338,12 @@ function _p9k_vcs_resume() { function _p9k_vcs_gitstatus() { if [[ $_p9k_refresh_reason == precmd ]]; then if (( $+_p9k__gitstatus_next_dir )); then - _p9k__gitstatus_next_dir=$_p9k_pwd_a + _p9k__gitstatus_next_dir=$_p9k__pwd_a else local -F timeout=_POWERLEVEL9K_VCS_MAX_SYNC_LATENCY_SECONDS if ! _p9k_vcs_status_for_dir; then _p9k__git_dir=$GIT_DIR - gitstatus_query -d $_p9k_pwd_a -t $timeout -p -c '_p9k_vcs_resume 0' POWERLEVEL9K || return 1 + gitstatus_query -d $_p9k__pwd_a -t $timeout -p -c '_p9k_vcs_resume 0' POWERLEVEL9K || return 1 _p9k_maybe_ignore_git_repo case $VCS_STATUS_RESULT in tout) _p9k__gitstatus_next_dir=''; _p9k__gitstatus_start_time=$EPOCHREALTIME; return 0;; @@ -3215,7 +3354,7 @@ function _p9k_vcs_gitstatus() { if [[ -n $GIT_DIR ]]; then [[ $_p9k_git_slow[GIT_DIR:$GIT_DIR] == 1 ]] && timeout=0 else - local dir=$_p9k_pwd_a + local dir=$_p9k__pwd_a while true; do case $_p9k_git_slow[$dir] in "") [[ $dir == / ]] && break; dir=${dir:h};; @@ -3227,14 +3366,14 @@ function _p9k_vcs_gitstatus() { fi (( _p9k__prompt_idx == 1 )) && timeout=0 _p9k__git_dir=$GIT_DIR - if ! gitstatus_query -d $_p9k_pwd_a -t $timeout -c '_p9k_vcs_resume 1' POWERLEVEL9K; then + if ! gitstatus_query -d $_p9k__pwd_a -t $timeout -c '_p9k_vcs_resume 1' POWERLEVEL9K; then unset VCS_STATUS_RESULT return 1 fi _p9k_maybe_ignore_git_repo case $VCS_STATUS_RESULT in tout) _p9k__gitstatus_next_dir=''; _p9k__gitstatus_start_time=$EPOCHREALTIME;; - norepo-sync) _p9k_vcs_status_purge $_p9k_pwd_a;; + norepo-sync) _p9k_vcs_status_purge $_p9k__pwd_a;; ok-sync) _p9k_vcs_status_save;; esac fi @@ -3361,7 +3500,7 @@ prompt_pyenv() { (( ${_POWERLEVEL9K_PYENV_SOURCES[(I)shell]} )) || return else (( ${_POWERLEVEL9K_PYENV_SOURCES[(I)local|global]} )) || return - [[ $PYENV_DIR == /* ]] && local dir=$PYENV_DIR || local dir="$_p9k_pwd_a/$PYENV_DIR" + [[ $PYENV_DIR == /* ]] && local dir=$PYENV_DIR || local dir="$_p9k__pwd_a/$PYENV_DIR" while true; do if _p9k_read_pyenv_version_file $dir/.python-version; then (( ${_POWERLEVEL9K_PYENV_SOURCES[(I)local]} )) || return @@ -3404,7 +3543,7 @@ prompt_goenv() { (( $+commands[goenv] || $+functions[goenv] )) || return local v=${(j.:.)${(s.:.)GOENV_VERSION}} if [[ -z $v ]]; then - [[ $GOENV_DIR == /* ]] && local dir=$GOENV_DIR || local dir="$_p9k_pwd_a/$GOENV_DIR" + [[ $GOENV_DIR == /* ]] && local dir=$GOENV_DIR || local dir="$_p9k__pwd_a/$GOENV_DIR" while true; do if _p9k_read_goenv_version_file $dir/.go-version; then v=$_p9k_ret @@ -3451,7 +3590,7 @@ prompt_swift_version() { ################################################################ # dir_writable: Display information about the user's permission to write in the current directory prompt_dir_writable() { - if [[ ! -w "$_p9k_pwd" ]]; then + if [[ ! -w "$_p9k__pwd" ]]; then _p9k_prompt_segment "$0_FORBIDDEN" "red" "yellow1" 'LOCK_ICON' 0 '' '' fi } @@ -3818,16 +3957,37 @@ _p9k_preexec2() { _p9k__timer_start=EPOCHREALTIME } -function _p9k_set_iface() { - _p9k_iface=() +function _p9k_prompt_net_iface_init() { + typeset -g _p9k__public_ip_vpn= + typeset -g _p9k__public_ip_not_vpn= + typeset -g _p9k__ip_ip= + typeset -g _p9k__vpn_ip_ip= + [[ -z $_POWERLEVEL9K_PUBLIC_IP_VPN_INTERFACE ]] && _p9k__public_ip_not_vpn=1 + _p9k_worker_send_params \ + _POWERLEVEL9K_PUBLIC_IP_VPN_INTERFACE \ + _POWERLEVEL9K_IP_INTERFACE \ + _POWERLEVEL9K_VPN_IP_INTERFACE + _p9k_worker_send_functions _p9k_prompt_net_iface_match +} + +# reads `iface2ip` and sets `ip` +function _p9k_prompt_net_iface_match() { + local iface_regex="^($1)\$" iface + for iface ip in "${(@kv)iface2ip}"; do + [[ $iface =~ $iface_regex ]] && return + done + return 1 +} + +function _p9k_prompt_net_iface_async() { + local iface ip line var + typeset -A iface2ip if [[ -x /sbin/ifconfig ]]; then - local line - local iface for line in ${(f)"$(/sbin/ifconfig 2>/dev/null)"}; do if [[ $line == (#b)([^[:space:]]##):[[:space:]]##flags=(<->)'<'* ]]; then [[ $match[2] == *[13579] ]] && iface=$match[1] || iface= elif [[ -n $iface && $line == (#b)[[:space:]]##inet[[:space:]]##([0-9.]##)* ]]; then - _p9k_iface[$iface]=$match[1] + iface2ip[$iface]=$match[1] iface= fi done @@ -3838,17 +3998,36 @@ function _p9k_set_iface() { if [[ $line == (#b)<->:[[:space:]]##([^:]##):[[:space:]]##\<([^\>]#)\>* ]]; then [[ ,$match[2], == *,UP,* ]] && iface=$match[1] || iface= elif [[ -n $iface && $line == (#b)[[:space:]]##inet[[:space:]]##([0-9.]##)* ]]; then - _p9k_iface[$iface]=$match[1] + iface2ip[$iface]=$match[1] iface= fi done fi + + if _p9k_prompt_net_iface_match $_POWERLEVEL9K_PUBLIC_IP_VPN_INTERFACE; then + _p9k__public_ip_vpn=1 + _p9k__public_ip_not_vpn= + else + _p9k__public_ip_vpn= + _p9k__public_ip_not_vpn=1 + fi + if _p9k_prompt_net_iface_match $_POWERLEVEL9K_IP_INTERFACE; then + _p9k__ip_ip=$ip + else + _p9k__ip_ip= + fi + if _p9k_prompt_net_iface_match $_POWERLEVEL9K_VPN_IP_INTERFACE; then + _p9k__vpn_ip_ip=$ip + else + _p9k__vpn_ip_ip= + fi + typeset -p _p9k__public_ip_vpn _p9k__public_ip_not_vpn _p9k__ip_ip _p9k__vpn_ip_ip } function _p9k_build_segment() { _p9k_segment_name=${_p9k_segment_name%_joined} local disabled=_POWERLEVEL9K_${(U)_p9k_segment_name}_DISABLED_DIR_PATTERN - [[ $_p9k_pwd == ${(P)~disabled} ]] && return + [[ $_p9k__pwd == ${(P)~disabled} ]] && return if [[ $_p9k_segment_name == custom_* ]]; then _p9k_custom_prompt $_p9k_segment_name[8,-1] elif (( $+functions[prompt_$_p9k_segment_name] )); then @@ -3860,7 +4039,7 @@ function _p9k_build_segment() { function _p9k_build_instant_segment() { _p9k_segment_name=${_p9k_segment_name%_joined} local disabled=_POWERLEVEL9K_${(U)_p9k_segment_name}_DISABLED_DIR_PATTERN - [[ $_p9k_pwd == ${(P)~disabled} ]] && return + [[ $_p9k__pwd == ${(P)~disabled} ]] && return if (( $+functions[instant_prompt_$_p9k_segment_name] )); then local -i len=$#_p9k__prompt _p9k_non_hermetic_expansion=0 @@ -3875,15 +4054,19 @@ function _p9k_build_instant_segment() { function _p9k_set_prompt() { local ifs=$IFS IFS=$' \t\n\0' - _p9k_pwd=${(%):-%/} - _p9k_pwd_a=${_p9k_pwd:A} + + if [[ $_p9k_refresh_reason == precmd ]]; then + local f_compute + for f_compute in "${_p9k__async_segments_compute[@]}"; do + $f_compute + done + fi + PROMPT= RPROMPT= [[ $1 == instant_ ]] || PROMPT+='${$((_p9k_on_expand()))+}' PROMPT+=$_p9k_prompt_prefix_left - (( _p9k_fetch_iface )) && _p9k_set_iface - local -i left_idx=1 right_idx=1 num_lines=$#_p9k_line_segments_left for _p9k_line_index in {1..$num_lines}; do local right= @@ -3974,7 +4157,7 @@ _p9k_dump_instant_prompt() { local root_dir=${__p9k_dump_file:h} local prompt_dir=${root_dir}/p10k-$user local root_file=$root_dir/p10k-instant-prompt-$user.zsh - local prompt_file=$prompt_dir/prompt-${#_p9k_pwd} + local prompt_file=$prompt_dir/prompt-${#_p9k__pwd} [[ -d $prompt_dir ]] || mkdir -p $prompt_dir || return [[ -w $root_dir && -w $prompt_dir ]] || return @@ -4275,17 +4458,6 @@ _p9k_dump_instant_prompt() { zf_mv -f $tmp $prompt_file 2>/dev/null || return } -powerlevel9k_refresh_prompt_inplace() { - emulate -L zsh - setopt no_hist_expand extended_glob no_prompt_bang prompt_{percent,subst} no_aliases - (( __p9k_enabled )) || return - _p9k_refresh_reason=precmd - _p9k_set_prompt - _p9k_refresh_reason='' -} - -p9k_refresh_prompt_inplace() { powerlevel9k_refresh_prompt_inplace } - typeset -gi __p9k_sh_glob typeset -gi __p9k_ksh_arrays typeset -gi __p9k_new_status @@ -4494,7 +4666,7 @@ function _p9k_clear_instant_prompt() { function _p9k_maybe_dump() { (( __p9k_dumps_enabled )) || return 0 - _p9k__instant_prompt_sig=$_p9k_pwd:$P9K_SSH:${(%):-%#} + _p9k__instant_prompt_sig=$_p9k__pwd:$P9K_SSH:${(%):-%#} if (( ! _p9k__dump_pid )) || ! kill -0 $_p9k__dump_pid 2>/dev/null; then _p9k__dump_pid=0 @@ -4534,7 +4706,6 @@ function _p9k_maybe_dump() { } function _p9k_on_expand() { - _p9k__last_prompt_update_time=EPOCHREALTIME (( _p9k__expanded && ! $+__p9k_instant_prompt_active )) && return () { @@ -4699,6 +4870,9 @@ _p9k_precmd_impl() { (( ++_p9k__prompt_idx )) fi + _p9k__pwd=${(%):-%/} + _p9k__pwd_a=${_p9k__pwd:A} + _p9k_refresh_reason=precmd _p9k_set_prompt _p9k_refresh_reason='' @@ -4753,169 +4927,6 @@ function _p9k_reset_prompt() { fi } -_p9k_deinit_async_pump() { - if (( _p9k__async_pump_lock_fd )); then - zsystem flock -u $_p9k__async_pump_lock_fd - _p9k__async_pump_lock_fd=0 - fi - if (( _p9k__async_pump_fd )); then - zle -F $_p9k__async_pump_fd - exec {_p9k__async_pump_fd}>&- - _p9k__async_pump_fd=0 - fi - if (( _p9k__async_pump_pid )); then - kill -- -$_p9k__async_pump_pid &>/dev/null - _p9k__async_pump_pid=0 - fi - if [[ -n $_p9k__async_pump_fifo ]]; then - rm -f $_p9k__async_pump_fifo - _p9k__async_pump_fifo='' - fi - if [[ -n $_p9k__async_pump_lock ]]; then - rm -f $_p9k__async_pump_lock - _p9k__async_pump_lock='' - fi - _p9k__async_pump_subshell=-1 - _p9k__async_pump_shell_pid=-1 - add-zsh-hook -D zshexit _p9k_kill_async_pump -} - -function _p9k_on_async_message() { - emulate -L zsh - setopt no_hist_expand extended_glob no_prompt_bang prompt_{percent,subst} no_aliases - if (( ARGC != 1 )); then - _p9k_deinit_async_pump - return 0 - fi - local msg='' IFS='' - while read -r -t -u $1 msg; do - [[ $__p9k_enabled == 1 && $1 == $_p9k__async_pump_fd ]] && eval $_p9k__async_pump_line$msg - _p9k__async_pump_line= - msg= - done - _p9k__async_pump_line+=$msg - [[ $__p9k_enabled == 1 && $1 == $_p9k__async_pump_fd ]] && _p9k_reset_prompt -} - -function _p9k_async_pump() { - emulate -L zsh || return - setopt no_aliases no_hist_expand extended_glob || return - setopt no_prompt_bang prompt_{percent,subst} || return - zmodload zsh/system zsh/datetime || return - echo $$ || return - - local ip last_ip - local -F next_ip_time - while ! zsystem flock -t 0 $lock 2>/dev/null && kill -0 $parent_pid; do - if (( time_realtime )); then - echo || break - # SIGWINCH is a workaround for a bug in zsh. After a background job completes, callbacks - # registered with `zle -F` stop firing until the user presses any key or the process - # receives a signal (any signal at all). - # Fix: https://github.com/zsh-users/zsh/commit/5e11082349bf72897f93f3a4493a97a2caf15984. - kill -WINCH $parent_pid - fi - if (( public_ip && EPOCHREALTIME >= next_ip_time )); then - ip= - local method='' - local -F start=EPOCHREALTIME - next_ip_time=$((start + 5)) - for method in $ip_methods $ip_methods; do - case $method in - dig) - if (( $+commands[dig] )); then - ip="$(dig +tries=1 +short -4 A myip.opendns.com @resolver1.opendns.com 2>/dev/null)" - [[ $ip == ';'* ]] && ip= - if [[ -z $ip ]]; then - ip="$(dig +tries=1 +short -6 AAAA myip.opendns.com @resolver1.opendns.com 2>/dev/null)" - [[ $ip == ';'* ]] && ip= - fi - fi - ;; - curl) - if (( $+commands[curl] )); then - ip="$(curl --max-time 5 -w '\n' "$ip_url" 2>/dev/null)" - fi - ;; - wget) - if (( $+commands[wget] )); then - ip="$(wget -T 5 -qO- "$ip_url" 2>/dev/null)" - fi - ;; - esac - [[ $ip =~ '^[0-9a-f.:]+$' ]] || ip='' - if [[ -n $ip ]]; then - next_ip_time=$((start + tout)) - break - fi - done - if [[ $ip != $last_ip ]]; then - last_ip=$ip - echo _p9k__public_ip=${(q)${${ip//\%/%%}//$'\n'}} || break - kill -WINCH $parent_pid - fi - fi - sleep 1 - done - rm -f $lock $fifo -} - -function _p9k_kill_async_pump() { - emulate -L zsh - setopt no_hist_expand extended_glob no_prompt_bang prompt_{percent,subst} no_aliases - if [[ $ZSH_SUBSHELL == $_p9k__async_pump_subshell && $$ == $_p9k__async_pump_shell_pid ]]; then - _p9k_deinit_async_pump - fi -} - -_p9k_init_async_pump() { - local -i public_ip time_realtime - _p9k_segment_in_use public_ip && public_ip=1 - (( _POWERLEVEL9K_EXPERIMENTAL_TIME_REALTIME )) && time_realtime=1 - (( public_ip || time_realtime )) || return - - _p9k_start_async_pump() { - setopt err_return no_bg_nice - - _p9k__async_pump_lock=${TMPDIR:-/tmp}/p9k-$$-async-pump-lock.$EPOCHREALTIME.$RANDOM - _p9k__async_pump_fifo=${TMPDIR:-/tmp}/p9k-$$-async-pump-fifo.$EPOCHREALTIME.$RANDOM - echo -n >$_p9k__async_pump_lock - mkfifo $_p9k__async_pump_fifo - sysopen -rw -o cloexec,sync -u _p9k__async_pump_fd $_p9k__async_pump_fifo - zle -F $_p9k__async_pump_fd _p9k_on_async_message - zsystem flock -f _p9k__async_pump_lock_fd $_p9k__async_pump_lock - - local cmd=" - local -i public_ip=$public_ip time_realtime=$time_realtime parent_pid=$$ - local -a ip_methods=($_POWERLEVEL9K_PUBLIC_IP_METHODS) - local -F tout=$_POWERLEVEL9K_PUBLIC_IP_TIMEOUT - local ip_url=$_POWERLEVEL9K_PUBLIC_IP_HOST - local lock=$_p9k__async_pump_lock - local fifo=$_p9k__async_pump_fifo - $functions[_p9k_async_pump]" - - local setsid=${commands[setsid]:-/usr/local/opt/util-linux/bin/setsid} - [[ -f $setsid ]] && setsid=${(q)setsid} || setsid= - local zsh=${${:-/proc/self/exe}:A} - [[ -x $zsh ]] || zsh=zsh - cmd="$setsid ${(q)zsh} -dfc ${(q)cmd} &!" - $zsh --nobgnice -dfmc $cmd </dev/null >&$_p9k__async_pump_fd 2>/dev/null &! - - IFS='' read -t 5 -r -u $_p9k__async_pump_fd _p9k__async_pump_pid && (( _p9k__async_pump_pid )) - - _p9k__async_pump_subshell=$ZSH_SUBSHELL - _p9k__async_pump_shell_pid=$$ - add-zsh-hook zshexit _p9k_kill_async_pump - } - - if ! _p9k_start_async_pump ; then - >&2 print -rP -- "%F{red}[ERROR]%f Powerlevel10k failed to start async worker. The following segments may malfunction: " - (( public_ip )) && >&2 print -rP -- " - %F{green}public_ip%f" - (( time_realtime )) && >&2 print -rP -- " - %F{green}time%f" - _p9k_deinit_async_pump - fi -} - # Does ZSH have a certain off-by-one bug that triggers when PROMPT overflows to a new line? # # Bug: https://github.com/zsh-users/zsh/commit/d8d9fee137a5aa2cf9bf8314b06895bfc2a05518. @@ -4936,7 +4947,6 @@ typeset -g _p9k__param_pat typeset -g _p9k__param_sig _p9k_init_vars() { - typeset -gF _p9k__last_prompt_update_time typeset -ga _p9k_show_on_command typeset -g _p9k__last_buffer typeset -ga _p9k__last_commands @@ -4992,14 +5002,6 @@ _p9k_init_vars() { typeset -gi _p9k_line_index typeset -g _p9k_refresh_reason typeset -gi _p9k__region_active - typeset -g _p9k__async_pump_line - typeset -g _p9k__async_pump_fifo - typeset -g _p9k__async_pump_lock - typeset -gi _p9k__async_pump_lock_fd - typeset -gi _p9k__async_pump_fd - typeset -gi _p9k__async_pump_pid - typeset -gi _p9k__async_pump_subshell - typeset -gi _p9k__async_pump_shell_pid typeset -ga _p9k_line_segments_left typeset -ga _p9k_line_segments_right typeset -ga _p9k_line_prefix_left @@ -5041,10 +5043,8 @@ _p9k_init_vars() { typeset -g _p9k_w typeset -gi _p9k_dir_len typeset -gi _p9k_num_cpus - typeset -g _p9k_pwd - typeset -g _p9k_pwd_a - typeset -gA _p9k_iface - typeset -gi _p9k_fetch_iface + typeset -g _p9k__pwd + typeset -g _p9k__pwd_a typeset -g _p9k__keymap typeset -g _p9k__zle_state typeset -g _p9k_uname @@ -5054,6 +5054,8 @@ _p9k_init_vars() { typeset -g _p9k__last_prompt_pwd typeset -gA _p9k_display_k typeset -ga _p9k__display_v + typeset -ga _p9k_async_segments_init + typeset -ga _p9k__async_segments_compute typeset -gA _p9k__dotnet_stat_cache typeset -gA _p9k__dir_stat_cache @@ -5084,6 +5086,7 @@ _p9k_init_params() { _p9k_declare -s POWERLEVEL9K_TRANSIENT_PROMPT off [[ $_POWERLEVEL9K_TRANSIENT_PROMPT == (off|always|same-dir) ]] || _POWERLEVEL9K_TRANSIENT_PROMPT=off + _p9k_declare -s POWERLEVEL9K_WORKER_LOG_LEVEL _p9k_declare -i POWERLEVEL9K_COMMANDS_MAX_TOKEN_COUNT 64 _p9k_declare -a POWERLEVEL9K_HOOK_WIDGETS -- _p9k_declare -b POWERLEVEL9K_TODO_HIDE_ZERO_TOTAL 0 @@ -5140,6 +5143,7 @@ _p9k_init_params() { _p9k_declare -e POWERLEVEL9K_PUBLIC_IP_NONE "" _p9k_declare -s POWERLEVEL9K_PUBLIC_IP_HOST "http://ident.me" _p9k_declare -s POWERLEVEL9K_PUBLIC_IP_VPN_INTERFACE "" + _p9k_segment_in_use public_ip || _POWERLEVEL9K_PUBLIC_IP_VPN_INTERFACE= _p9k_declare -b POWERLEVEL9K_ALWAYS_SHOW_CONTEXT 0 _p9k_declare -b POWERLEVEL9K_ALWAYS_SHOW_USER 0 _p9k_declare -e POWERLEVEL9K_CONTEXT_TEMPLATE "%n@%m" @@ -5232,8 +5236,17 @@ _p9k_init_params() { [[ -z $_POWERLEVEL9K_SHORTEN_FOLDER_MARKER ]] && _POWERLEVEL9K_DIR_TRUNCATE_BEFORE_MARKER=0 _p9k_declare -i POWERLEVEL9K_SHORTEN_DIR_LENGTH _p9k_declare -s POWERLEVEL9K_IP_INTERFACE "" + : ${_POWERLEVEL9K_IP_INTERFACE:='.*'} + _p9k_segment_in_use ip || _POWERLEVEL9K_IP_INTERFACE= _p9k_declare -s POWERLEVEL9K_VPN_IP_INTERFACE "(wg|(.*tun))[0-9]*" + : ${_POWERLEVEL9K_VPN_IP_INTERFACE:='.*'} + _p9k_segment_in_use vpn_ip || _POWERLEVEL9K_VPN_IP_INTERFACE= _p9k_declare -i POWERLEVEL9K_LOAD_WHICH 5 + case $_POWERLEVEL9K_LOAD_WHICH in + 1) _POWERLEVEL9K_LOAD_WHICH=1;; + 15) _POWERLEVEL9K_LOAD_WHICH=3;; + *) _POWERLEVEL9K_LOAD_WHICH=2;; + esac _p9k_declare -b POWERLEVEL9K_NODENV_PROMPT_ALWAYS_SHOW 0 _p9k_declare -b POWERLEVEL9K_NODE_VERSION_PROJECT_ONLY 0 _p9k_declare -b POWERLEVEL9K_DOTNET_VERSION_PROJECT_ONLY 1 @@ -5345,7 +5358,6 @@ _p9k_init_params() { _p9k_declare -b POWERLEVEL9K_TIME_UPDATE_ON_COMMAND 0 # If set to true, time will update every second. _p9k_declare -b POWERLEVEL9K_EXPERIMENTAL_TIME_REALTIME 0 - _p9k_segment_in_use time || _POWERLEVEL9K_EXPERIMENTAL_TIME_REALTIME=0 local -i i=1 while (( i <= $#_POWERLEVEL9K_LEFT_PROMPT_ELEMENTS )); do @@ -5786,12 +5798,12 @@ function _p9k_on_widget_zle-line-finish() { (( $+functions[p10k-on-post-prompt] )) && p10k-on-post-prompt if [[ -n $_p9k_transient_prompt ]]; then - if [[ $_POWERLEVEL9K_TRANSIENT_PROMPT == always || $_p9k_pwd == $_p9k__last_prompt_pwd ]]; then + if [[ $_POWERLEVEL9K_TRANSIENT_PROMPT == always || $_p9k__pwd == $_p9k__last_prompt_pwd ]]; then RPROMPT= PROMPT=$_p9k_transient_prompt __p9k_reset_state=2 else - _p9k__last_prompt_pwd=$_p9k_pwd + _p9k__last_prompt_pwd=$_p9k__pwd fi fi @@ -6122,10 +6134,10 @@ _p9k_init_display() { $i/left $((n+=2)) $j/left $n $i/right $((n+=2)) $j/right $n $i/gap $((n+=2)) $j/gap $n) - for name in ${(@0)_p9k_line_segments_left[i]}; do + for name in ${${(@0)_p9k_line_segments_left[i]}%_joined}; do _p9k_display_k+=($i/left/$name $((n+=2)) $j/left/$name $n) done - for name in ${(@0)_p9k_line_segments_right[i]}; do + for name in ${${(@0)_p9k_line_segments_right[i]}%_joined}; do _p9k_display_k+=($i/right/$name $((n+=2)) $j/right/$name $n) done done @@ -6297,11 +6309,11 @@ function _p9k_init_cacheable() { _p9k_init_prompt _p9k_init_display - local elem + local elem func local -i i=0 for i in {1..$#_p9k_line_segments_left}; do - for elem in ${(@0)_p9k_line_segments_left[i]}; do + for elem in ${${(@0)_p9k_line_segments_left[i]}%_joined}; do local var=POWERLEVEL9K_${(U)elem}_SHOW_ON_COMMAND (( $+parameters[$var] )) || continue _p9k_show_on_command+=( @@ -6309,7 +6321,7 @@ function _p9k_init_cacheable() { $((1+_p9k_display_k[$i/left/$elem])) _p9k__${i}l$elem) done - for elem in ${(@0)_p9k_line_segments_right[i]}; do + for elem in ${${(@0)_p9k_line_segments_right[i]}%_joined}; do local var=POWERLEVEL9K_${(U)elem}_SHOW_ON_COMMAND (( $+parameters[$var] )) || continue local cmds=(${(P)var}) @@ -6320,6 +6332,19 @@ function _p9k_init_cacheable() { done done + local -U segments=( + ${_POWERLEVEL9K_LEFT_PROMPT_ELEMENTS%_joined} + ${_POWERLEVEL9K_RIGHT_PROMPT_ELEMENTS%_joined}) + if [[ -n $_POWERLEVEL9K_PUBLIC_IP_VPN_INTERFACE || + -n $_POWERLEVEL9K_IP_INTERFACE || + -n $_POWERLEVEL9K_VPN_IP_INTERFACE ]]; then + segments+=net_iface + fi + for elem in $segments; do + local f_init=_p9k_prompt_${elem}_init + (( $+functions[$f_init] )) && _p9k_async_segments_init+=$f_init + done + if [[ $_POWERLEVEL9K_TRANSIENT_PROMPT != off ]]; then _p9k_transient_prompt='%b%k%s%u%F{%(?.' _p9k_color prompt_prompt_char_OK_VIINS FOREGROUND 76 @@ -6429,6 +6454,7 @@ function _p9k_init_cacheable() { BSD) (( $+commands[sysctl] )) && _p9k_num_cpus="$(sysctl -n hw.ncpu 2>/dev/null)";; *) (( $+commands[nproc] )) && _p9k_num_cpus="$(nproc 2>/dev/null)";; esac + (( _p9k_num_cpus )) || _p9k_num_cpus=1 if _p9k_segment_in_use dir; then if (( $+_POWERLEVEL9K_DIR_CLASSES )); then @@ -6461,11 +6487,6 @@ function _p9k_init_cacheable() { done fi fi - - if [[ -n $_POWERLEVEL9K_PUBLIC_IP_VPN_INTERFACE ]] && _p9k_segment_in_use public_ip || - _p9k_segment_in_use ip || _p9k_segment_in_use vpn_ip; then - _p9k_fetch_iface=1 - fi } _p9k_init_vcs() { @@ -6565,6 +6586,25 @@ _p9k_init() { _p9k_init_vars _p9k_restore_state || _p9k_init_cacheable + local f_init + for f_init in $_p9k_async_segments_init; do + $f_init || continue + local f=${f_init%init} + if (( ! $+functions[${f}sync] )); then + functions[${f}sync]='[[ $cur == $prev ]] && return; eval $cur; _p9k_worker_reply $cur' + fi + if (( ! $+functions[${f}compute] )); then + local body="_p9k_worker_invoke $f " + (( $+functions[${f}cond] )) && body+="${f}cond " || body+="'' " + (( $+functions[${f}async] )) && body+="${f}async " || body+="'' " + functions[${f}compute]="$body ${f}sync" + fi + _p9k_worker_send_functions ${f}{cond,sync,async} + _p9k__async_segments_compute+=${f}compute + done + + (( $#_p9k__async_segments_compute )) && _p9k_worker_start + local k v for k v in ${(kv)_p9k_display_k}; do [[ $k == -* ]] && continue @@ -6609,7 +6649,6 @@ _p9k_init() { print -rP -- 'Either install %F{green}jq%f or change the value of %BPOWERLEVEL9K_SHORTEN_STRATEGY%b.' fi - _p9k_init_async_pump _p9k_init_vcs if (( _POWERLEVEL9K_DISABLE_INSTANT_PROMPT )); then @@ -6652,7 +6691,7 @@ _p9k_init() { _p9k_deinit() { (( $+functions[_p9k_preinit] )) && unfunction _p9k_preinit (( $+functions[gitstatus_stop] )) && gitstatus_stop POWERLEVEL9K - _p9k_deinit_async_pump + _p9k_worker_stop (( _p9k__dump_pid )) && wait $_p9k__dump_pid 2>/dev/null (( $+_p9k__iterm2_precmd )) && functions[iterm2_precmd]=$_p9k__iterm2_precmd (( $+_p9k__iterm2_decorate_prompt )) && functions[iterm2_decorate_prompt]=$_p9k__iterm2_decorate_prompt diff --git a/internal/worker.zsh b/internal/worker.zsh new file mode 100644 index 00000000..a0217ac3 --- /dev/null +++ b/internal/worker.zsh @@ -0,0 +1,372 @@ +# invoked in worker: _p9k_worker_main <timeout> +function _p9k_worker_main() { + emulate -L zsh + setopt no_hist_expand extended_glob no_prompt_bang prompt_percent prompt_subst no_aliases no_bgnice + + zmodload zsh/system || return + zmodload zsh/zselect || return + ! { zselect -t0 || (( $? != 1 )) } || return + + function _p9k_worker_reply_begin() { print -nr -- e } + function _p9k_worker_reply_end() { print -nr -- $'\x1e' } + function _p9k_worker_reply() { print -nr -- e${(pj:\n:)@}$'\x1e' } + + typeset -g IFS=$' \t\n\0' + + local req fd + local -a ready + local -A inflight # fd => id$'\x1f'sync + local -A last_async + local -ri _p9k_worker_runs_me=1 + + { + while zselect -a ready 0 ${(k)inflight}; do + [[ $ready[1] == -r ]] || return + for fd in ${ready:1}; do + if [[ $fd == 0 ]]; then + local buf= + while true; do + sysread -t 0 'buf[$#buf+1]' && continue + (( $? == 4 )) || return + [[ $buf[-1] == (|$'\x1e') ]] && break + sysread 'buf[$#buf+1]' || return + done + for req in ${(ps:\x1e:)buf}; do + local parts=("${(@ps:\x1f:)req}") # id cond async sync + if () { eval $parts[2] }; then + if [[ -n $parts[3] ]]; then + sysopen -r -o cloexec -u fd <( + () { eval $parts[3]; } && print -n '\x1e') || return + inflight[$fd]=$parts[1]$'\x1f'$parts[4] + continue + fi + () { eval $parts[4] } + fi + if [[ -n $parts[1] ]]; then + print -rn -- d$parts[1]$'\x1e' || return + fi + done + else + local cur= + while true; do + sysread -i $fd 'cur[$#cur+1]' && continue + (( $? == 5 )) || return + break + done + local parts=("${(@ps:\x1f:)inflight[$fd]}") # id sync + if [[ $cur == *$'\x1e' ]]; then + cur[-1]="" + () { + local prev + if [[ -n $parts[1] ]]; then + prev=$last_async[$parts[1]] + last_async[$parts[1]]=$cur + fi + eval $parts[2] + } + fi + if [[ -n $parts[1] ]]; then + print -rn -- d$parts[1]$'\x1e' || return + fi + unset "inflight[$fd]" + exec {fd}>&- + fi + done + done + } always { + kill -- -$sysparams[pid] + } +} + +typeset -g _p9k__worker_pid +typeset -g _p9k__worker_req_fd +typeset -g _p9k__worker_resp_fd +typeset -g _p9k__worker_shell_pid +typeset -g _p9k__worker_file_prefix +typeset -gaU _p9k__worker_params +typeset -gaU _p9k__worker_functions +typeset -gA _p9k__worker_request_map +typeset -ga _p9k__worker_request_queue + +function _p9k_worker_print_params() { + local names=(${@:/(#m)*/${${${+parameters[$MATCH]}:#0}:+$MATCH}}) + (( ! $#names )) && return + print -n -- '\x1f' && typeset -p -- $names && print -n -- '\x1f\x1f\x1e' +} + +function _p9k_worker_print_functions() { + local names=(${@:/(#m)*/${${${+functions[$MATCH]}:#0}:+$MATCH}}) + (( ! $#names )) && return + print -n -- '\x1f' && functions -- $names && print -n -- '\x1f\x1f\x1e' +} + +# invoked in master: _p9k_worker_send_params [param]... +function _p9k_worker_send_params() { + if [[ -n $_p9k__worker_req_fd ]]; then + _p9k_worker_print_params ${(u)@} >&$_p9k__worker_req_fd && return + _p9k_worker_stop + return 1 + else + _p9k__worker_params+=($@) + fi +} + +# invoked in master: _p9k_worker_send_functions [function-name]... +function _p9k_worker_send_functions() { + if [[ -n $_p9k__worker_req_fd ]]; then + _p9k_worker_print_functions ${(u)@} >&$_p9k__worker_req_fd && return + _p9k_worker_stop + return 1 + else + _p9k__worker_functions+=($@) + fi +} + +# invoked in master: _p9k_worker_invoke <request-id> <cond> <async> <sync> +function _p9k_worker_invoke() { + if [[ -n $_p9k__worker_resp_fd ]]; then + local req=$1$'\x1f'$2$'\x1f'$3$'\x1f'$4$'\x1e' + if [[ -n $_p9k__worker_req_fd && $+_p9k__worker_request_map[$1] == 0 ]]; then + [[ -n $1 ]] && _p9k__worker_request_map[$1]= + print -rnu $_p9k__worker_req_fd -- $req + return + fi + if [[ -n $1 ]]; then + (( $+_p9k__worker_request_map[$1] )) || _p9k__worker_request_queue+=$1 + _p9k__worker_request_map[$1]=$req + else + _p9k__worker_request_queue+=$req + fi + else + if () { eval $2 }; then + local REPLY= + () { eval $3 } + () { eval $4 } + fi + fi +} + +function _p9k_worker_cleanup() { + emulate -L zsh + setopt no_hist_expand extended_glob no_prompt_bang prompt_{percent,subst} no_aliases + [[ $_p9k__worker_shell_pid == $sysparams[pid] ]] && _p9k_worker_stop + return 0 +} + +function _p9k_worker_stop() { + emulate -L zsh + setopt no_hist_expand extended_glob no_prompt_bang prompt_{percent,subst} no_aliases + add-zsh-hook -D zshexit _p9k_worker_cleanup + [[ -n $_p9k__worker_resp_fd ]] && zle -F $_p9k__worker_resp_fd + [[ -n $_p9k__worker_resp_fd ]] && exec {_p9k__worker_resp_fd}>&- + [[ -n $_p9k__worker_req_fd ]] && exec {_p9k__worker_req_fd}>&- + [[ -n $_p9k__worker_pid ]] && kill -- -$_p9k__worker_pid 2>/dev/null + _p9k__worker_pid= + _p9k__worker_req_fd= + _p9k__worker_resp_fd= + _p9k__worker_shell_pid= + _p9k__worker_params=() + _p9k__worker_functions=() + _p9k__worker_request_map=() + _p9k__worker_request_queue=() + return 0 +} + +function _p9k_worker_receive() { + emulate -L zsh + setopt no_hist_expand extended_glob no_prompt_bang prompt_{percent,subst} no_aliases + + { + (( $# <= 1 )) || return + + local buf resp reset + while true; do + sysread -t 0 -i $_p9k__worker_resp_fd 'buf[$#buf+1]' && continue + (( $? == 4 )) || return + [[ $buf[-1] == (|$'\x1e') ]] && break + sysread -i $_p9k__worker_resp_fd 'buf[$#buf+1]' || return + done + + for resp in ${(ps:\x1e:)buf}; do + local arg=$resp[2,-1] + case $resp[1] in + d) + local req=$_p9k__worker_request_map[$arg] + if [[ -n $req ]]; then + _p9k__worker_request_map[$arg]= + print -rnu $_p9k__worker_req_fd -- $req || return + else + unset "_p9k__worker_request_map[$arg]" + fi + ;; + e) + if (( start_time )); then + local -F end_time=EPOCHREALTIME + local -F3 latency=$((1000*(end_time-start_time))) + echo "latency: $latency ms" >>/tmp/log + start_time=0 + fi + reset=1 + () { eval $arg } + ;; + s) + [[ -z $_p9k__worker_pid ]] || return + [[ $arg == <1-> ]] || return + _p9k__worker_pid=$arg + sysopen -w -o cloexec -u _p9k__worker_req_fd $_p9k__worker_file_prefix.fifo || return + { + local init=" + zmodload zsh/datetime + zmodload zsh/mathfunc + zmodload zsh/parameter + zmodload zsh/system + zmodload zsh/termcap + zmodload zsh/terminfo + zmodload zsh/zleparameter + zmodload -F zsh/stat b:zstat + zmodload -F zsh/net/socket b:zsocket + zmodload -F zsh/files b:zf_mv b:zf_rm + autoload -Uz is-at-least + () { $functions[_p9k_worker_main] }" + print -r -- ${init//$'\n'/$'\x1e'} || return + _p9k_worker_print_params $_p9k__worker_params || return + _p9k_worker_print_functions $_p9k__worker_functions || return + _p9k__worker_params=() + _p9k__worker_functions() + local req= + for req in $_p9k__worker_request_queue; do + if [[ $req != *$'\x1e' ]]; then + local id=$req + req=$_p9k__worker_request_map[$id] + _p9k__worker_request_map[$id]= + fi + print -rnu $_p9k__worker_req_fd -- $req || return + done + _p9k__worker_request_queue=() + } >&$_p9k__worker_req_fd + ;; + *) + return 1 + ;; + esac + done + + (( reset )) && _p9k_reset_prompt + return 0 + } always { + (( $? )) && _p9k_worker_stop + } +} + +function _p9k_worker_start() { + setopt no_bgnice + { + [[ -n $_p9k__worker_resp_fd ]] && return + _p9k__worker_file_prefix=${TMPDIR:-/tmp}/p10k.worker.$EUID.$$.$EPOCHSECONDS + + if [[ -n $_POWERLEVEL9K_WORKER_LOG_LEVEL ]]; then + local trace=x + local log_file=$file_prefix.log + else + local trace= + local log_file=/dev/null + fi + + log_file=/tmp/log # todo: remove + trace=x + + local fifo=$_p9k__worker_file_prefix.fifo + local zsh=${${:-/proc/self/exe}:A} + [[ -x $zsh ]] || zsh=zsh + local bootstrap=' + "emulate" "-L" "zsh" "-o" "no_aliases" "-o" "no_bgnice" + { + local fifo='${(q)fifo}' + { + zmodload zsh/system && + mkfifo $fifo && + exec >&4 && + echo -n "s$sysparams[pid]\x1e" && + exec 0<$fifo || exit + } always { rm -f -- $fifo } + IFS= read -r && eval ${REPLY//$'"'\x1e'"'/$'"'\n'"'} + } &! + exec true' + sysopen -r -o cloexec -u _p9k__worker_resp_fd <( + _p9k_worker_bootstrap=${bootstrap// ##} </dev/null 4>&1 &>>$log_file \ + exec $zsh -${trace}dfmc '"eval" "$_p9k_worker_bootstrap"') || return + zle -F $_p9k__worker_resp_fd _p9k_worker_receive + _p9k__worker_shell_pid=$sysparams[pid] + add-zsh-hook zshexit _p9k_worker_cleanup + } always { + (( $? )) && _p9k_worker_stop + } +} + +# todo: remove + +return + +function _p9k_reset_prompt() { + zle && zle reset-prompt && zle -R +} + +emulate -L zsh -o prompt_subst # -o xtrace + +POWERLEVEL9K_WORKER_LOG_LEVEL=DEBUG + +zmodload zsh/datetime +zmodload zsh/system +autoload -Uz add-zsh-hook + +typeset -F start_time=EPOCHREALTIME +_p9k_worker_start +echo -E - $((1000*(EPOCHREALTIME-start_time))) + +function foo_cond() { + typeset -gi foo_counter + typeset -g foo="[$bar] cond $1 $((foo_counter++))" +} + +function foo_async() { + sleep 1 + REPLY="$foo / async $1" +} + +function foo_sync() { + REPLY+=" / sync $1" + _p9k_worker_reply "typeset -g foo=${(q)REPLY}" +} + +() { + typeset -g RPROMPT='$foo %*' + typeset -g bar='lol' + _p9k_worker_send_params bar + + local f + for f in foo_{cond,async,sync}; do + _p9k_worker_invoke "" "function $f() { $functions[$f] }" "" "" + done + + () { + local -i i + for i in {1..10}; do + _p9k_worker_invoke foo$i "foo_cond c$i\$\{" "foo_async a$i\$\{" "foo_sync s$i\$\{" + done + } +} + +function in_worker() { + _p9k_worker_reply 'echo roundtrip: $((1000*(EPOCHREALTIME-'$1'))) >>/tmp/log' +} + +_p9k_worker_invoke "" "function in_worker() { $functions[in_worker] }" "" "" +_p9k_worker_invoke w "in_worker $EPOCHREALTIME" "" "" +# for i in {1..100}; do _p9k_worker_invoke w$i "in_worker $EPOCHREALTIME"; done + +# TODO: +# +# - Segment API: _p9k_prompt_foo_worker_{params,cond,async,sync}. +# - _p9k_worker_request -- cacheable variable that contains full request to worker. +# - _p9k_set_prompt sends stuff to worker or evals it. +# - _p9k_on_expand has _REALTIME check at the top and sends keep-alive to worker. |