I'm trying to write a ZSH Theme that displays specific characters depending on the Git status (if within a git repo). I've previously implemented this theme in Bash, but am having some issues with moving over to ZSH. This is likely my own fault, but if anyone could help it'd be much appreciated.
Relevant Portion of my theme:
local jobs="%{$terminfo[bold]$fg[cyan]%}[%{$fg[magenta]%}%j%{$terminfo[bold]$fg[cyan]%}]%{$reset_color%}"
local git_branch='$(git_prompt_info)%{$reset_color%}'
local current_dir="%{$terminfo[bold]$fg[orange]%}%~%{$reset_color%}"
if [[ $UID -eq 0 ]]; then
local user_host="%{$terminfo[bold]$fg[red]%}%n@%m%{$reset_color%}"
local user_symbol="#"
else
local user_host="%{$terminfo[bold]$fg[green]%}%n@%{$fg[red]%}%m%{$reset_color%}"
local user_symbol="$"
fi
function git_has_unstaged() {
if ! $(git diff-files --quiet --ignore-submodules --); then
echo -n "%*! ";
fi;
}
function git_has_uncommitted() {
if [ ! $(git diff --quiet --ignore-submodules --cached; echo "${?}") = "0" ]; then
echo -n "%*+ ";
fi;
}
function git_has_untracked() {
if [ -n "$(git ls-files --others --exclude-standard)" ]; then
echo -n "%*? ";
fi;
}
function git_has_stashed() {
if $(git rev-parse --verify refs/stash &>/dev/null); then
echo -n "%*$ ";
fi;
}
function git_status_symbols() {
echo -n "%{$terminfo[bold]$fg[blue]%}[";
if [ $(git rev-parse --is-inside-work-tree &>/dev/null; echo "${?}") = "0" ]; then
#Now is it Git repo
if [ "$(git rev-parse --is-inside-git-dir 2> /dev/null)" = "false" ]; then
# check if the current directory is in .git before running git checks
# Ensure the index is up to date.
git update-index --really-refresh -q &>/dev/null;
git_has_uncommitted;
git_has_unstaged;
git_has_untracked;
git_has_stashed;
fi;
fi;
echo -n "]%{$reset_color%}";
}
PROMPT="╭─${user_host} ${current_dir} ${jobs} ${git_branch} $(git_status_symbols)
╰─%B${user_symbol}%b "
ZSH_THEME_GIT_PROMPT_PREFIX="%{$fg[yellow]%}‹"
ZSH_THEME_GIT_PROMPT_SUFFIX="›%{$reset_color%}"
My issue is that the git_status_symbols value in the prompt doesn't update according to updates in the fs/git. Example:
╭─theonlyjohnny@phoenix ~/configs [0] ‹bored› []
╰─$ git status
On branch bored
Your branch is up to date with 'origin/bored'.
nothing to commit, working tree clean
╭─theonlyjohnny@phoenix ~/configs [0] ‹bored› []
╰─$ touch ./t
╭─theonlyjohnny@phoenix ~/configs [0] ‹bored› []
╰─$ git_has_untracked
%*? % ╭─theonlyjohnny@phoenix ~/configs [0] ‹bored› []
╰─$ zsh
╭─theonlyjohnny@phoenix ~/configs [0] ‹bored› [1:50:37? ]
╰─$
╭─theonlyjohnny@phoenix ~/configs [0] ‹bored› [1:52:14? ]
╰─$ rm t
╭─theonlyjohnny@phoenix ~/configs [0] ‹bored› [1:52:16? ]
╰─$ git_has_untracked
╭─theonlyjohnny@phoenix ~/configs [0] ‹bored› [1:52:22? ]
╰─$ zsh
╭─theonlyjohnny@phoenix ~/configs [0] ‹bored› []
╰─$
As you can see, while my git_has_untracked function is properly echoing a ?, the prompt is not reflecting this until I start a new shell. After removing the untracked file, the git_has_untracked function properly no longer echoes anything. What's interesting here is that the time is still being updated. Upon creation of a new shell the prompt is once again correct.
Any help appreciated! Thanks
This is happening because your PROMPT variable contains the command substitution $(git_status_symbols). This evaluation happens once during assignment and not on every expansion of PROMPT.
The easiest way to fix this is by using a precmd that fills the psvar array and then insert that value into the prompt with a form of %v. For example:
precmd () { psvar[1]=$(git_status_symbols); }
PROMPT="╭─${user_host} ${current_dir} ${jobs} ${git_branch} %1v
╰─%B${user_symbol}%b "
Also, be aware that zsh has its own vcs support (for Git and others) which you may or may not want to leverage instead of writing custom code.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With