zsh: rework/simplify prompt
This commit is contained in:
parent
2f1f3527c5
commit
216804c368
1 changed files with 129 additions and 124 deletions
|
@ -1,166 +1,170 @@
|
||||||
black=0 red=1 green=2 yellow=3 blue=4 magenta=5 cyan=6 white=7 gray=8
|
fg_reset='%{%f%}'
|
||||||
|
fg_black='%{%F{black}%}'
|
||||||
|
fg_red='%{%F{red}%}'
|
||||||
|
fg_green='%{%F{green}%}'
|
||||||
|
fg_yellow='%{%F{yellow}%}'
|
||||||
|
fg_blue='%{%F{blue}%}'
|
||||||
|
fg_magenta='%{%F{magenta}%}'
|
||||||
|
fg_cyan='%{%F{cyan}%}'
|
||||||
|
fg_white='%{%F{white}%}'
|
||||||
|
fg_gray='%{%F{8}%}' # %F{...} only supports the 8 basic colors by name
|
||||||
|
|
||||||
PROMPT_SECTIONS=()
|
PROMPT_SECTIONS=()
|
||||||
|
|
||||||
render() { # $1: optional color, $2...: contents
|
|
||||||
if [[ -n $2 ]] {
|
|
||||||
PROMPT_SECTIONS+="%{%F{$1}%}${@[2,-1]}%{%f%}"
|
|
||||||
} else {
|
|
||||||
PROMPT_SECTIONS+="$1"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
render_prompt() {
|
render_prompt() {
|
||||||
setopt localoptions shortloops
|
setopt localoptions shortloops
|
||||||
local separator="%{%F{$gray}%} ❯ %{%f%}"
|
|
||||||
|
|
||||||
PROMPT_SECTIONS=()
|
PROMPT_SECTIONS=()
|
||||||
for s in exit_code user_host pwd git venv jobs exec_time; { render_$s }
|
render_exit_code
|
||||||
|
render_user_host
|
||||||
|
render_pwd
|
||||||
|
render_git
|
||||||
|
render_venv
|
||||||
|
render_jobs
|
||||||
|
render_exec_time
|
||||||
|
|
||||||
echo ${(pj.$separator.)PROMPT_SECTIONS}
|
local separator="${fg_gray} ❯ "
|
||||||
echo -n "%{%F{$gray}%}"
|
echo ${(pj.$separator.)PROMPT_SECTIONS}
|
||||||
printf '❯%.0s' {1..$SHLVL}
|
echo -n "${fg_gray}"
|
||||||
echo -n "%{%f%} "
|
printf '❯%.0s' {1..$SHLVL}
|
||||||
|
echo -n "${fg_reset} "
|
||||||
}
|
}
|
||||||
|
|
||||||
render_exit_code() {
|
render_exit_code() {
|
||||||
if (($PROMPT_EXIT_CODE != 0)) {
|
if ((PROMPT_EXIT_CODE != 0)); then
|
||||||
if ((PROMPT_EXIT_CODE > 128 && PROMPT_EXIT_CODE < 160 )) {
|
if ((PROMPT_EXIT_CODE > 128 && PROMPT_EXIT_CODE < 160)); then
|
||||||
render $white $(kill -l $PROMPT_EXIT_CODE)
|
PROMPT_SECTIONS+="${fg_white}$(kill -l $PROMPT_EXIT_CODE)"
|
||||||
} else {
|
else
|
||||||
render $red "%{%B%}✘ $PROMPT_EXIT_CODE%{%b%}"
|
PROMPT_SECTIONS+="${fg_red}✘ $PROMPT_EXIT_CODE"
|
||||||
}
|
fi
|
||||||
}
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
render_user_host() {
|
render_user_host() {
|
||||||
local sep="%{%F{$gray}%}@%{%f%}"
|
local parts=()
|
||||||
local parts=()
|
|
||||||
|
|
||||||
# username in red if root, yellow if otherwise relevant
|
# username in red if root, yellow if otherwise relevant
|
||||||
if [[ $UID == 0 ]] {
|
if [[ $UID == 0 ]]; then
|
||||||
parts+="%{%F{$red}%}%n%{%f%}"
|
parts+="${fg_red}%n"
|
||||||
} elif [[ $LOGNAME != $USER ]] || [[ -n $SSH_CONNECTION ]] {
|
elif [[ $LOGNAME != $USER ]] || [[ -n $SSH_CONNECTION ]]; then
|
||||||
parts+="%{%F{$yellow}%}%n%{%f%}"
|
parts+="${fg_yellow}%n"
|
||||||
}
|
fi
|
||||||
|
|
||||||
# hostname in yellow if relevant
|
# hostname in yellow if relevant
|
||||||
[[ -n $SSH_CONNECTION ]] && parts+="%{%F{$yellow}%}%m%{%f%}"
|
[[ -n $SSH_CONNECTION ]] && parts+="${fg_yellow}%m"
|
||||||
|
|
||||||
(($#parts)) && render "${(pj:$sep:)parts}"
|
(($#parts)) && {
|
||||||
|
local separator="${fg_gray}@"
|
||||||
|
PROMPT_SECTIONS+="${(pj:$separator:)parts}"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
render_pwd() {
|
render_pwd() {
|
||||||
render $cyan '%~' # TODO add RO if now write permission
|
PROMPT_SECTIONS+="${fg_cyan}%~${fg_reset}"
|
||||||
}
|
}
|
||||||
|
|
||||||
# TODO add stash?
|
|
||||||
# TODO add action?
|
|
||||||
render_git() {
|
render_git() {
|
||||||
local gitstatus # local swallows git's exit code if not on its own line
|
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
|
||||||
|
|
||||||
# Sort through the status of files.
|
# Sort through the status of files.
|
||||||
local untracked=0 dirty=0 staged=0 conflicts=0 branch_line=''
|
local untracked=0 dirty=0 staged=0 conflicts=0 branch_line=''
|
||||||
{
|
{
|
||||||
while IFS='' read -r line || [[ -n $line ]] {
|
while IFS='' read -r line; do
|
||||||
case $line in
|
case $line in
|
||||||
\#\#*) branch_line=$line;;
|
\#\#*) branch_line=$line;;
|
||||||
\?\?*) ((untracked++));;
|
\?\?*) ((untracked++));;
|
||||||
AA*|DD*|U?*|?U*) ((conflicts++));;
|
AA*|DD*|U?*|?U*) ((conflicts++));;
|
||||||
*)
|
*)
|
||||||
[[ ${line:0:1} =~ '[MADRC]' ]] && ((staged++))
|
[[ ${line:0:1} =~ '[MADRC]' ]] && ((staged++))
|
||||||
[[ ${line:1:1} =~ '[MADRC]' ]] && ((dirty++))
|
[[ ${line:1:1} =~ '[MADRC]' ]] && ((dirty++))
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
} <<<$gitstatus
|
done <<<$gitstatus
|
||||||
}
|
}
|
||||||
|
|
||||||
# Find out branch and upstream.
|
# Find out branch and upstream.
|
||||||
local branch='' upstream='' ahead=0 behind=0
|
local branch='' upstream='' ahead=0 behind=0
|
||||||
{
|
{
|
||||||
local fields=(${(s:...:)${branch_line#\#\# }})
|
local fields=(${(s:...:)${branch_line#\#\# }})
|
||||||
branch=$fields[1]
|
branch=$fields[1]
|
||||||
local tracking=$fields[2]
|
local tracking=$fields[2]
|
||||||
|
|
||||||
if [[ $branch == *'Initial commit on'* ]] \
|
if [[ $branch == *'Initial commit on'* ]] \
|
||||||
|| [[ $branch == *'No commits yet on'* ]] {
|
|| [[ $branch == *'No commits yet on'* ]]; then
|
||||||
# Branch name is last word in these possible branch lines:
|
# Branch name is last word in these possible branch lines:
|
||||||
# ## Initial commit on <branch>
|
# ## Initial commit on <branch>
|
||||||
# ## No commits yet on <branch>
|
# ## No commits yet on <branch>
|
||||||
branch=(${${(s: :)branch}[-1]})
|
branch=${${(s: :)branch}[-1]}
|
||||||
|
|
||||||
} elif [[ $branch == *'no branch'* ]] {
|
elif [[ $branch == *'no branch'* ]]; then
|
||||||
# Dettached HEAD (also if a tag is checked out), branch line:
|
# Dettached HEAD (also if a tag is checked out), branch line:
|
||||||
# ## HEAD (no branch)
|
# ## HEAD (no branch)
|
||||||
local tag=$(git describe --tags --exact-match 2>/dev/null)
|
local tag=$(command git describe --tags --exact-match HEAD 2>/dev/null)
|
||||||
if [[ -n $tag ]] {
|
if [[ -n $tag ]]; then
|
||||||
branch="⚑$tag"
|
branch="⚑$tag"
|
||||||
} else {
|
else
|
||||||
branch="#$(git rev-parse --short HEAD)"
|
tag=$(command git describe --tags --long HEAD 2>/dev/null)
|
||||||
}
|
if [[ -n $tag ]]; then
|
||||||
|
branch="$tag"
|
||||||
|
else
|
||||||
|
branch="#$(command git rev-parse --short HEAD 2>/dev/null)"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
} elif (($#fields > 1)) {
|
elif (($#fields > 1)); then
|
||||||
# There is a tracking branch. Possibilites:
|
# There is a tracking branch. Possibilites:
|
||||||
# ## <branch>...<upstream>
|
# ## <branch>...<upstream>
|
||||||
# ## <branch>...<upstream> [ahead 1]
|
# ## <branch>...<upstream> [ahead 1]
|
||||||
# ## <branch>...<upstream> [behind 1]
|
# ## <branch>...<upstream> [behind 1]
|
||||||
# ## <branch>...<upstream> [ahead 1, behind 1]
|
# ## <branch>...<upstream> [ahead 1, behind 1]
|
||||||
tracking=(${(s: [:)${tracking%]}})
|
tracking=(${(s: [:)${tracking%]}})
|
||||||
upstream=$tracking[1]
|
upstream=$tracking[1]
|
||||||
if (($#tracking > 1)) {
|
if (($#tracking > 1)); then
|
||||||
for e in ${(s:, :)tracking[2]}; {
|
for e in ${(s:, :)tracking[2]}; do
|
||||||
if [[ $e == 'ahead '* ]] { ahead=${e:6} }
|
[[ $e == 'ahead '* ]] && ahead=${e:6}
|
||||||
if [[ $e == 'behind '* ]] { behind=${e:7} }
|
[[ $e == 'behind '* ]] && behind=${e:7}
|
||||||
}
|
done
|
||||||
}
|
fi
|
||||||
}
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
local color=$blue trackinfo='' state=''
|
local track_parts=()
|
||||||
{
|
(($ahead > 0 )) && track_parts+="${fg_blue}↑${ahead}"
|
||||||
local attention=()
|
(($behind > 0 )) && track_parts+="${fg_cyan}${behind}↓"
|
||||||
(($conflicts > 0 )) && attention+="%{%B%F{$red}%}$conflicts!%{%b%f%}"
|
|
||||||
(($untracked > 0 )) && attention+="%{%F{$white}%}$untracked?%{%f%}"
|
|
||||||
|
|
||||||
local changes=()
|
local state_parts=()
|
||||||
(($staged > 0)) && changes+="%{%F{$green}%}+$staged%{%f%}"
|
(($staged > 0)) && state_parts+="${fg_green}+${staged}"
|
||||||
(($dirty > 0 )) && changes+="%{%F{$red}%}$dirty✶%{%f%}"
|
(($dirty > 0 )) && state_parts+="${fg_red}${dirty}✶"
|
||||||
|
|
||||||
if (($#attention > 0 || $#changes > 0)) {
|
local separator="${fg_gray}"
|
||||||
local sep="%{%F{$gray}%}|%{%f%}"
|
local gitinfo=("${fg_blue}${branch}")
|
||||||
local state_array=(${(j: :)attention} ${(pj:$sep:)changes})
|
(($#track_parts > 0)) && gitinfo+="${(pj:$separator:)track_parts}"
|
||||||
state=${(j: :)state_array}
|
(($conflicts > 0 )) && gitinfo+="${fg_red}${conflicts} "
|
||||||
}
|
(($untracked > 0 )) && gitinfo+="${fg_white}${untracked}?"
|
||||||
|
(($#state_parts)) && gitinfo+="${(pj:$separator:)state_parts}"
|
||||||
|
|
||||||
local extra=()
|
PROMPT_SECTIONS+="${(j: :)gitinfo}"
|
||||||
(($ahead > 0 )) && extra+="↑$ahead"
|
|
||||||
(($behind > 0 )) && extra+="%{%F{$cyan}%}↓$behind%{%f%}"
|
|
||||||
(($#extra > 0)) && trackinfo="(${(j::)extra}%{%F{$color}%})%{%f%}"
|
|
||||||
}
|
|
||||||
|
|
||||||
local gitinfo=("$branch$trackinfo" $state)
|
|
||||||
render $color ${(j: :)gitinfo}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
render_venv() {
|
render_venv() {
|
||||||
[[ -n $VIRTUAL_ENV ]] && render $green $VIRTUAL_ENV:t # venv if active
|
[[ -n "$VIRTUAL_ENV" ]] && PROMPT_SECTIONS+="${fg_green}${VIRTUAL_ENV:t}"
|
||||||
}
|
}
|
||||||
|
|
||||||
render_jobs() {
|
render_jobs() {
|
||||||
(($PROMPT_JOB_COUNT > 0)) && render $magenta '%j bg' # background jobs if any
|
(($PROMPT_JOB_COUNT > 0)) && PROMPT_SECTIONS+="${fg_magenta}%j bg"
|
||||||
}
|
}
|
||||||
|
|
||||||
render_exec_time() {
|
render_exec_time() {
|
||||||
(($PROMPT_EXEC_TIME > 5)) && { # last command execution time if over 5s
|
(($PROMPT_EXEC_TIME > 3)) && { # last command execution time if over 3s
|
||||||
local components=(
|
local parts=(
|
||||||
"$((PROMPT_EXEC_TIME / 60 / 60 / 24))d" # days
|
"$((PROMPT_EXEC_TIME / 60 / 60 / 24))d" # days
|
||||||
"$((PROMPT_EXEC_TIME / 60 / 60 % 24))h" # hours
|
"$((PROMPT_EXEC_TIME / 60 / 60 % 24))h" # hours
|
||||||
"$((PROMPT_EXEC_TIME / 60 % 60))m" # minutes
|
"$((PROMPT_EXEC_TIME / 60 % 60))m" # minutes
|
||||||
"$((PROMPT_EXEC_TIME % 60))s" # seconds
|
"$((PROMPT_EXEC_TIME % 60))s" # seconds
|
||||||
)
|
)
|
||||||
render $gray ${components:#0*} # only keep non-zero parts
|
PROMPT_SECTIONS+=${fg_gray}${parts:#0*} # only keep non-zero parts
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
# Hook triggered when a command is about to be executed.
|
# Hook triggered when a command is about to be executed.
|
||||||
|
@ -195,3 +199,4 @@ prompt_setup() {
|
||||||
}
|
}
|
||||||
|
|
||||||
prompt_setup
|
prompt_setup
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue