zsh: avoid global array of colors in prompt

This commit is contained in:
Fernando Schauenburg 2024-07-29 19:46:35 +02:00
parent a28fa3a3f8
commit eff5a3d0b1

View file

@ -1,19 +1,6 @@
typeset -gA zfg=(
[reset]='%{%f%}'
[black]='%{%F{black}%}'
[red]='%{%F{red}%}'
[green]='%{%F{green}%}'
[yellow]='%{%F{yellow}%}'
[blue]='%{%F{blue}%}'
[magenta]='%{%F{magenta}%}'
[cyan]='%{%F{cyan}%}'
[white]='%{%F{white}%}'
# %F{...} only supports the 8 basic colors by name
[gray]='%{%F{8}%}'
[bright]='%{%F{15}%}'
[faded]='%{%F{240}%}'
typeset -gA _fs_prompt=(
[is_transient]=true
[transient_color]='%{%F{magenta}%}'
)
typeset -gA icons=(
@ -28,69 +15,101 @@ typeset -gA icons=(
)
fs_prompt_render_full() {
local separator="${zfg[faded]} "
local black='%{%F{0}%}' red='%{%F{1}%}' green='%{%F{2}%}' yellow='%{%F{3}%}' \
blue='%{%F{4}%}' magenta='%{%F{5}%}' cyan='%{%F{6}%}' white='%{%F{7}%}' \
gray='%{%F{8}%}' orange='%{%F{166}%}' faded='%{%F{240}%}' \
reset='%{%f%}'
local sep_color="$faded" signal_color="$white" error_color="$red" reset="$reset"
local sections=(
"$(fs_prompt_exit_code)"
"$(fs_prompt_user_and_hostname)"
"$(fs_prompt_pwd)"
"$(fs_prompt_git)"
"$(fs_prompt_virtualenv)"
"$(fs_prompt_jobs)"
"$(fs_prompt_exec_time)"
"$(fs_prompt_exit_code $error_color $signal_color)"
# root user separator
"$(fs_prompt_user_and_hostname $red $yellow $faded)"
"$(fs_prompt_pwd $cyan)"
# branch ahead behind untracked staged dirty conflict separator
"$(fs_prompt_git $blue $blue $cyan $white $green $orange $red $gray)"
"$(fs_prompt_virtualenv $yellow)"
"$(fs_prompt_jobs $magenta)"
"$(fs_prompt_exec_time $gray)"
)
echo "\n${(@pj.$separator.)sections:#}${zfg[reset]}"
local sep="$sep_color "
echo "\n${(@pj:$sep:)sections:#}$reset"
if ((PROMPT_EXIT_CODE == 0)); then
echo -n "${zfg[faded]}"
echo -n "$sep_color"
elif ((PROMPT_EXIT_CODE > 128 && PROMPT_EXIT_CODE < 160)); then
echo -n "${zfg[white]}"
echo -n "$signal_color"
else
echo -n "${zfg[red]}"
echo -n "$error_color"
fi
(($SHLVL > 1)) && printf '󰅂%.0s' {2..$SHLVL}
echo -n " ${zfg[reset]}"
echo -n " $reset"
}
fs_prompt_render_compact() {
echo -n "${zfg[magenta]} ${zfg[reset]}"
echo -n "${_fs_prompt[transient_color]} %{%f%}"
}
fs_prompt_exit_code() {
((PROMPT_EXIT_CODE == 0)) && return
local error_color="$1" signal_color="$2" bold="%{%B%}" no_bold="%{%b%}"
if ((PROMPT_EXIT_CODE > 128 && PROMPT_EXIT_CODE < 160)); then
print "%{%B%}${zfg[white]}$(kill -l $PROMPT_EXIT_CODE)%{%b%}"
print "${bold}${signal_color}$(kill -l $PROMPT_EXIT_CODE)${no_bold}"
else
print "%{%B%}${zfg[red]}$PROMPT_EXIT_CODE%{%b%}"
print "${bold}${error_color}${PROMPT_EXIT_CODE}${no_bold}"
fi
}
fs_prompt_user_and_hostname() {
local root_color="$1" user_color="$2" at_color="${3:-$2}"
local parts=()
# username in red if root, yellow if otherwise relevant
if [[ $UID == 0 ]]; then
parts+="${zfg[red]}${icons[user]}%n"
parts+="${root_color}${icons[user]}%n"
elif [[ $LOGNAME != $USER ]] || [[ -n $SSH_CONNECTION ]]; then
parts+="${zfg[yellow]}${icons[user]}%n"
parts+="${user_color}${icons[user]}%n"
fi
# hostname in yellow if relevant
[[ -n $SSH_CONNECTION ]] && parts+="${zfg[yellow]}%m"
[[ -n $SSH_CONNECTION ]] && parts+="${user_color}%m"
(($#parts)) && {
local separator="${zfg[gray]}@"
print "${(pj:$separator:)parts}"
local at="${at_color}@"
print "${(pj:$at:)parts}"
}
}
fs_prompt_pwd() {
print "${zfg[cyan]}${icons[folder]}%~"
local color="$1"
print "${color}${icons[folder]}%~"
}
fs_prompt_git() {
local branch_color="$1"
local ahead_color="$2"
local behind_color="$3"
local untracked_color="$4"
local staged_color="$5"
local dirty_color="$6"
local conflict_color="$7"
local sep_color="$8"
local gitstatus # local swallows git's exit code if not on its own line
gitstatus=$(command git status --porcelain -b 2>/dev/null) || return
# gitstatus=$(command git status --porcelain -b 2>/dev/null) || return
gitstatus="## some-longer-name...origin/tracking [ahead 5, behind 12]
M staged_1
M staged_2
M dirty_1
M dirty_2
M dirty_3
DD conflict
?? untracked
"
# Sort through the status of files.
local untracked=0 dirty=0 staged=0 conflicts=0 branch_line=''
@ -150,42 +169,50 @@ fs_prompt_git() {
fi
}
local track_parts=()
(($ahead > 0 )) && track_parts+="${zfg[blue]}${ahead}"
(($behind > 0 )) && track_parts+="${zfg[cyan]}${behind}"
local result=()
{
local sep="${sep_color}"
local tracking=()
(($ahead)) && tracking+="${ahead_color}${ahead}" # ↑
(($behind)) && tracking+="${behind_color}${behind}" # ↓
(($#tracking)) && result+="${(pj:$sep:)tracking} "
local state_parts=()
(($staged > 0)) && state_parts+="${zfg[green]}+${staged}"
(($dirty > 0 )) && state_parts+="${zfg[red]}${dirty}"
(($conflicts)) && result+="${conflict_color} %{%B%}${conflicts}%{%b%}"
local separator="${zfg[gray]}"
local gitinfo=("${zfg[blue]}${branch}")
(($#track_parts > 0)) && gitinfo+="${(pj:$separator:)track_parts}"
(($conflicts > 0 )) && gitinfo+="${zfg[red]}${conflicts}"
(($untracked > 0 )) && gitinfo+="${zfg[white]}${untracked}?"
(($#state_parts)) && gitinfo+="${(pj:$separator:)state_parts}"
local state=()
(($staged)) && state+="${staged_color}+${staged}"
(($dirty)) && state+="${dirty_color}${dirty}"
(($#state)) && result+="${(pj:$sep:)state}"
print "${(j: :)gitinfo}"
(($untracked)) && result+="${untracked_color}${untracked}?"
}
print "${branch_color}${branch} ${(j: :)result}"
}
fs_prompt_virtualenv() {
[[ -n "$VIRTUAL_ENV" ]] && print "${zfg[green]}${icons[python]}${VIRTUAL_ENV:t}"
[[ -n "$VIRTUAL_ENV" ]] || return
local color="$1"
print "${color}${icons[python]}${VIRTUAL_ENV:t}"
}
fs_prompt_jobs() {
(($PROMPT_JOB_COUNT > 0)) && print "${zfg[magenta]}${icons[background]}%j"
(($PROMPT_JOB_COUNT)) || return
local color="$1"
print "${color}${icons[background]}%j"
}
fs_prompt_exec_time() {
(($PROMPT_EXEC_TIME <= 3)) && return # don't print time if under 3s
local color="$1"
local parts=(
"$((PROMPT_EXEC_TIME / 60 / 60 / 24))d" # days
"$((PROMPT_EXEC_TIME / 60 / 60 % 24))h" # hours
"$((PROMPT_EXEC_TIME / 60 % 60))m" # minutes
"$((PROMPT_EXEC_TIME % 60))s" # seconds
)
print ${zfg[gray]}${icons[clock]}${parts:#0*} # only keep non-zero parts
local exec_time="${(@)parts:#0*}" # only keep non-zero parts
[[ -n "$exec_time" ]] && print "${color}${icons[clock]}${exec_time}"
}
# Hook triggered when a command is about to be executed.
@ -224,12 +251,12 @@ fs_setup_prompt() {
add-zsh-hook preexec fs_prompt_preexec
add-zsh-hook precmd fs_prompt_precmd
# Change to false to disable transient prompt.
local use_transient_prompt=true
if $use_transient_prompt; then
if ${_fs_prompt[is_transient]}; then
autoload -Uz add-zle-hook-widget
add-zle-hook-widget line-finish fs_prompt_zle_line_finish
PS2="${zfg[magenta]}%_${zfg[reset]} "
PS2="${_fs_prompt[transient_color]}%_%{%f%} "
else
add-zle-hook-widget -d line-finish fs_prompt_zle_line_finish
fi
}