dotfiles/lib/local/prompt_filthy_setup

312 lines
8.8 KiB
Bash
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env zsh
# Filthy
# by James Dinsdale
# https://github.com/molovo/filthy
# MIT License
# Largely based on Pure by Sindre Sorhus <https://github.com/sindresorhus/pure>
# For my own and others sanity
# git:
# %b => current branch
# %a => current action (rebase/merge)
# prompt:
# %F => color dict
# %f => reset color
# %~ => current path
# %* => time
# %n => username
# %m => shortname host
# %(?..) => prompt conditional - %(condition.true.false)
prompt_filthy_nice_exit_code() {
local exit_status="${1:-$(print -P %?)}";
# nothing to do here
[[ ${FILTHY_SHOW_EXIT_CODE:=0} != 1 || -z $exit_status || $exit_status == 0 ]] && return;
local sig_name;
# is this a signal name (error code = signal + 128) ?
case $exit_status in
129) sig_name=HUP ;;
130) sig_name=INT ;;
131) sig_name=QUIT ;;
132) sig_name=ILL ;;
134) sig_name=ABRT ;;
136) sig_name=FPE ;;
137) sig_name=KILL ;;
139) sig_name=SEGV ;;
141) sig_name=PIPE ;;
143) sig_name=TERM ;;
esac
# usual exit codes
case $exit_status in
-1) sig_name=FATAL ;;
1) sig_name=WARN ;; # Miscellaneous errors, such as "divide by zero"
2) sig_name=BUILTINMISUSE ;; # misuse of shell builtins (pretty rare)
126) sig_name=CCANNOTINVOKE ;; # cannot invoke requested command (ex : source script_with_syntax_error)
127) sig_name=CNOTFOUND ;; # command not found (ex : source script_not_existing)
esac
# assuming we are on an x86 system here
# this MIGHT get annoying since those are in a range of exit codes
# programs sometimes use.... we'll see.
case $exit_status in
19) sig_name=STOP ;;
20) sig_name=TSTP ;;
21) sig_name=TTIN ;;
22) sig_name=TTOU ;;
esac
echo "$ZSH_PROMPT_EXIT_SIGNAL_PREFIX${exit_status}:${sig_name:-$exit_status}$ZSH_PROMPT_EXIT_SIGNAL_SUFFIX ";
}
# turns seconds into human readable time
# 165392 => 1d 21h 56m 32s
prompt_filthy_human_time() {
local tmp=$(( $1 / 1000 ))
local days=$(( tmp / 60 / 60 / 24 ))
local hours=$(( tmp / 60 / 60 % 24 ))
local minutes=$(( tmp / 60 % 60 ))
local seconds=$(( tmp % 60 ))
(( $days > 0 )) && print -n "${days}d "
(( $hours > 0 )) && print -n "${hours}h "
(( $minutes > 0 )) && print -n "${minutes}m "
(( $seconds > 5 )) && print -n "${seconds}s"
(( $tmp <= 5 )) && print -n "${1}ms"
}
# displays the exec time of the last command if set threshold was exceeded
prompt_filthy_cmd_exec_time() {
local stop=$((EPOCHREALTIME*1000))
local start=${cmd_timestamp:-$stop}
integer elapsed=$stop-$start
(($elapsed > ${FILTHY_CMD_MAX_EXEC_TIME:=500})) && prompt_filthy_human_time $elapsed
}
prompt_filthy_preexec() {
cmd_timestamp=$((EPOCHREALTIME*1000))
# shows the current dir and executed command in the title when a process is active
print -Pn "\e]0;"
echo -nE "$PWD:t: $2"
print -Pn "\a"
}
# string length ignoring ansi escapes
prompt_filthy_string_length() {
print ${#${(S%%)1//(\%([KF1]|)\{*\}|\%[Bbkf])}}
}
prompt_filthy_precmd() {
local prompt_filthy_preprompt git_root current_path branch repo_status
# Ensure prompt starts on a new line
prompt_filthy_preprompt="\n"
# Print connection info
prompt_filthy_preprompt+="$(prompt_filthy_connection_info)"
# check if we're in a git repo, and show git info if we are
if command git rev-parse --is-inside-work-tree &>/dev/null; then
# Print the name of the repository
git_root=$(git rev-parse --show-toplevel)
prompt_filthy_preprompt+="%B%F{yellow}$(basename ${git_root})%b%f"
# Print the current_path relative to the git root
current_path=$(git rev-parse --show-prefix)
prompt_filthy_preprompt+=" %F{blue}${${current_path%/}:-"/"}%f"
else
# We're not in a repository, so just print the current path
prompt_filthy_preprompt+="%F{blue}%~%f"
fi
# Print everything so far in the title
# print -Pn '\e]0;${prompt_filthy_preprompt}\a'
# Echo command exec time
prompt_filthy_preprompt+=" %F{yellow}$(prompt_filthy_cmd_exec_time)%f"
if [[ -f "${ZDOTDIR:-$HOME}/.promptmsg" ]]; then
# Echo any stored messages after the pre-prompt
prompt_filthy_preprompt+=" $(cat ${ZDOTDIR:-$HOME}/.promptmsg)"
fi
# We've already added any messages to our prompt, so let's reset them
cat /dev/null >! "${ZDOTDIR:-$HOME}/.promptmsg"
print -P $prompt_filthy_preprompt
# reset value since `preexec` isn't always triggered
unset cmd_timestamp
}
prompt_filthy_rprompt() {
# check if we're in a git repo, and show git info if we are
if command git rev-parse --is-inside-work-tree &>/dev/null; then
# Print the repository status
branch=$(prompt_filthy_git_branch)
repo_status=$(prompt_filthy_git_repo_status)
ci_status=$(prompt_filthy_ci_status)
fi
if builtin type zvm >/dev/null 2>&1; then
zvm_version=" $(zvm current --quiet)"
fi
print "${branch}${repo_status}${ci_status}%F{yellow}${zvm_version}%f"
}
prompt_filthy_ci_status() {
local state git_dir_local state_file
[[ $FILTHY_SHOW_CI_STATUS -eq 0 ]] && return
builtin type hub >/dev/null 2>&1 || return
git_dir_local="$(git rev-parse --git-dir)"
state_file="${git_dir_local}/ci-status"
function _retrieve_ci_status() {
# Delay the asynchronous process, otherwise the status file
# will be empty when we read it
sleep 1
state=$(hub ci-status 2>&1)
cat /dev/null >! "${state_file}" 2>/dev/null
case $state in
success )
print '%F{green}●%f' >> "${state_file}"
;;
pending )
print '%F{yellow}○%f' >> "${state_file}"
;;
failure )
print '%F{red}●%f' >> "${state_file}"
;;
error )
print '%F{red}‼%f' >> "${state_file}"
;;
'no status' )
print '%F{242}○%f' >> "${state_file}"
;;
esac
}
_retrieve_ci_status >/dev/null 2>&1 &!
state=$(cat "${state_file}" 2>/dev/null)
[[ -n $state ]] && print " $state"
}
prompt_filthy_git_repo_status() {
# Do a fetch asynchronously
git fetch > /dev/null 2>&1 &!
local clean
local rtn=""
local count
local up
local down
dirty="$(git diff --ignore-submodules=all HEAD 2>/dev/null)"
[[ $dirty != "" ]] && rtn+=" %F{242}…%f"
staged="$(git diff --staged HEAD 2>/dev/null)"
[[ $staged != "" ]] && rtn+=" %F{242}*%f"
# check if there is an upstream configured for this branch
# exit if there isn't, as we can't check for remote changes
if command git rev-parse --abbrev-ref @'{u}' &>/dev/null; then
# if there is, check git left and right arrow_status
count="$(command git rev-list --left-right --count HEAD...@'{u}' 2>/dev/null)"
# Get the push and pull counts
up="$count[(w)1]"
down="$count[(w)2]"
# Check if either push or pull is needed
[[ $up > 0 || $down > 0 ]] && rtn+=" "
# Push is needed, show up arrow
[[ $up > 0 ]] && rtn+="%F{yellow}⇡%f"
# Pull is needed, show down arrow
[[ $down > 0 ]] && rtn+="%F{yellow}⇣%f"
fi
print $rtn
}
prompt_filthy_git_branch() {
# get the current git status
local branch git_dir_local rtn
branch=$(git status --short --branch -uno --ignore-submodules=all | head -1 | awk '{print $2}' 2>/dev/null)
git_dir_local=$(git rev-parse --git-dir)
# remove reference to any remote tracking branch
branch=${branch%...*}
# check if HEAD is detached
if [[ -d "${git_dir_local}/rebase-merge" ]]; then
branch=$(git status | head -5 | tail -1 | awk '{print $6}')
rtn="%F{red}rebasing interactively%f%F{242} → ${branch//([[:space:]]|\')/}%f"
elif [[ -d "${git_dir_local}/rebase-apply" ]]; then
branch=$(git status | head -2 | tail -1 | awk '{print $6}')
rtn="%F{red}rebasing%f%F{242} → ${branch//([[:space:]]|\')/}%f"
elif [[ -f "${git_dir_local}/MERGE_HEAD" ]]; then
branch=$(git status | head -1 | awk '{print $3}')
rtn="%F{red}merging%f%F{242} → ${branch//([[:space:]]|\')/}%f"
elif [[ "$branch" = "HEAD" ]]; then
commit=$(git status HEAD -uno --ignore-submodules=all | head -1 | awk '{print $4}' 2>/dev/null)
if [[ "$commit" = "on" ]]; then
rtn="%F{yellow}no branch%f"
else
rtn="%F{242}detached@%f"
rtn+="%F{yellow}"
rtn+="$commit"
rtn+="%f"
fi
else
rtn="%F{242}$branch%f"
fi
print "$rtn"
}
prompt_filthy_connection_info() {
# show username@host if logged in through SSH
if [[ "x$SSH_CONNECTION" != "x" ]]; then
echo '%(!.%B%F{red}%n%f%b.%F{242}%n%f)%F{242}@%m%f '
else
echo '%(!.%B%F{red}%n%f%b%F{242}@%m%f .)'
fi
}
prompt_filthy_setup() {
# prevent percentage showing up
# if output doesn't end with a newline
export PROMPT_EOL_MARK=''
prompt_opts=(cr subst percent)
zmodload zsh/datetime
autoload -Uz add-zsh-hook
# autoload -Uz git-info
add-zsh-hook precmd prompt_filthy_precmd
add-zsh-hook preexec prompt_filthy_preexec
# prompt turns red if the previous command didn't exit with 0
PROMPT='%(?.%F{green}.%F{red}$(prompt_filthy_nice_exit_code))%f '
RPROMPT='$(prompt_filthy_rprompt)'
}
prompt_filthy_setup "$@"