Skip to content

Instantly share code, notes, and snippets.

@jonjitsu
Created November 3, 2023 14:32
Show Gist options
  • Save jonjitsu/23c7c57f18832c3f80a888952b2f67ed to your computer and use it in GitHub Desktop.
Save jonjitsu/23c7c57f18832c3f80a888952b2f67ed to your computer and use it in GitHub Desktop.
Bash prompt
PROMPT_BOLD="$COLOR_BOLD"
USER_COLOR="$COLOR_BOLD_BLUE"
PREPOSITION_COLOR="$COLOR_BOLD_WHITE"
DEVICE_COLOR="$COLOR_BOLD_CYAN"
DIR_COLOR="$COLOR_BOLD_GREEN"
COLOR_YELLOW="$COLOR_BOLD_YELLOW"
GIT_PROGRESS_COLOR="$COLOR_BOLD_RED"
SYMBOL_COLOR="$PROMPT_BOLD" # BOLD
# Define the default prompt terminator character '$'
if [[ "$UID" == 0 ]]; then
jonjitsu_bash_prompt_symbol="#"
else
jonjitsu_bash_prompt_symbol="\$"
fi
# Apply any color overrides that have been set in the environment
if [[ -n "$PROMPT_USER_COLOR" ]]; then USER_COLOR="$PROMPT_USER_COLOR"; fi
if [[ -n "$PROMPT_PREPOSITION_COLOR" ]]; then PREPOSITION_COLOR="$PROMPT_PREPOSITION_COLOR"; fi
if [[ -n "$PROMPT_DEVICE_COLOR" ]]; then DEVICE_COLOR="$PROMPT_DEVICE_COLOR"; fi
if [[ -n "$PROMPT_DIR_COLOR" ]]; then DIR_COLOR="$PROMPT_DIR_COLOR"; fi
if [[ -n "$PROMPT_GIT_STATUS_COLOR" ]]; then COLOR_YELLOW="$PROMPT_GIT_STATUS_COLOR"; fi
if [[ -n "$PROMPT_GIT_PROGRESS_COLOR" ]]; then GIT_PROGRESS_COLOR="$PROMPT_GIT_PROGRESS_COLOR"; fi
if [[ -n "$PROMPT_SYMBOL" ]]; then jonjitsu_bash_prompt_symbol="$PROMPT_SYMBOL"; fi
if [[ -n "$PROMPT_SYMBOL_COLOR" ]]; then SYMBOL_COLOR="$PROMPT_SYMBOL_COLOR"; fi
# Set up symbols
jonjitsu_bash_prompt_synced_symbol=""
jonjitsu_bash_prompt_dirty_synced_symbol="*"
jonjitsu_bash_prompt_unpushed_symbol="△"
jonjitsu_bash_prompt_dirty_unpushed_symbol="▲"
jonjitsu_bash_prompt_unpulled_symbol="▽"
jonjitsu_bash_prompt_dirty_unpulled_symbol="▼"
jonjitsu_bash_prompt_unpushed_unpulled_symbol="⬡"
jonjitsu_bash_prompt_dirty_unpushed_unpulled_symbol="⬢"
# Apply symbol overrides that have been set in the environment
# DEV: Working unicode symbols can be determined via the following gist
# **WARNING: The following gist has 64k lines and may freeze your browser**
# https://gist.github.com/twolfson/9cc7968eb6ee8b9ad877
if [[ -n "$PROMPT_SYNCED_SYMBOL" ]]; then jonjitsu_bash_prompt_synced_symbol="$PROMPT_SYNCED_SYMBOL"; fi
if [[ -n "$PROMPT_DIRTY_SYNCED_SYMBOL" ]]; then jonjitsu_bash_prompt_dirty_synced_symbol="$PROMPT_DIRTY_SYNCED_SYMBOL"; fi
if [[ -n "$PROMPT_UNPUSHED_SYMBOL" ]]; then jonjitsu_bash_prompt_unpushed_symbol="$PROMPT_UNPUSHED_SYMBOL"; fi
if [[ -n "$PROMPT_DIRTY_UNPUSHED_SYMBOL" ]]; then jonjitsu_bash_prompt_dirty_unpushed_symbol="$PROMPT_DIRTY_UNPUSHED_SYMBOL"; fi
if [[ -n "$PROMPT_UNPULLED_SYMBOL" ]]; then jonjitsu_bash_prompt_unpulled_symbol="$PROMPT_UNPULLED_SYMBOL"; fi
if [[ -n "$PROMPT_DIRTY_UNPULLED_SYMBOL" ]]; then jonjitsu_bash_prompt_dirty_unpulled_symbol="$PROMPT_DIRTY_UNPULLED_SYMBOL"; fi
if [[ -n "$PROMPT_UNPUSHED_UNPULLED_SYMBOL" ]]; then jonjitsu_bash_prompt_unpushed_unpulled_symbol="$PROMPT_UNPUSHED_UNPULLED_SYMBOL"; fi
if [[ -n "$PROMPT_DIRTY_UNPUSHED_UNPULLED_SYMBOL" ]]; then jonjitsu_bash_prompt_dirty_unpushed_unpulled_symbol="$PROMPT_DIRTY_UNPUSHED_UNPULLED_SYMBOL"; fi
function jonjitsu_bash_prompt_get_git_branch() {
# On branches, this will return the branch name
# On non-branches, (no branch)
ref="$(git symbolic-ref HEAD 2> /dev/null | sed -e 's/refs\/heads\///')"
if [[ "$ref" != "" ]]; then
echo "$ref"
else
echo "(no branch)"
fi
}
function jonjitsu_bash_prompt_get_git_progress() {
# Detect in-progress actions (e.g. merge, rebase)
# https://github.com/git/git/blob/v1.9-rc2/wt-status.c#L1199-L1241
git_dir="$(git rev-parse --git-dir)"
# git merge
if [[ -f "$git_dir/MERGE_HEAD" ]]; then
echo " [merge]"
elif [[ -d "$git_dir/rebase-apply" ]]; then
# git am
if [[ -f "$git_dir/rebase-apply/applying" ]]; then
echo " [am]"
# git rebase
else
echo " [rebase]"
fi
elif [[ -d "$git_dir/rebase-merge" ]]; then
# git rebase --interactive/--merge
echo " [rebase]"
elif [[ -f "$git_dir/CHERRY_PICK_HEAD" ]]; then
# git cherry-pick
echo " [cherry-pick]"
fi
if [[ -f "$git_dir/BISECT_LOG" ]]; then
# git bisect
echo " [bisect]"
fi
if [[ -f "$git_dir/REVERT_HEAD" ]]; then
# git revert --no-commit
echo " [revert]"
fi
}
jonjitsu_bash_prompt_is_branch1_behind_branch2 () {
# $ git log origin/master..master -1
# commit 4a633f715caf26f6e9495198f89bba20f3402a32
# Author: Todd Wolfson <todd@twolfson.com>
# Date: Sun Jul 7 22:12:17 2013 -0700
#
# Unsynced commit
# Find the first log (if any) that is in branch1 but not branch2
first_log="$(git log $1..$2 -1 2> /dev/null)"
# Exit with 0 if there is a first log, 1 if there is not
[[ -n "$first_log" ]]
}
jonjitsu_bash_prompt_branch_exists () {
# List remote branches | # Find our branch and exit with 0 or 1 if found/not found
git branch --remote 2> /dev/null | grep --quiet "$1"
}
jonjitsu_bash_prompt_parse_git_ahead () {
# Grab the local and remote branch
branch="$(jonjitsu_bash_prompt_get_git_branch)"
remote_branch="origin/$branch"
# $ git log origin/master..master
# commit 4a633f715caf26f6e9495198f89bba20f3402a32
# Author: Todd Wolfson <todd@twolfson.com>
# Date: Sun Jul 7 22:12:17 2013 -0700
#
# Unsynced commit
# If the remote branch is behind the local branch
# or it has not been merged into origin (remote branch doesn't exist)
if (jonjitsu_bash_prompt_is_branch1_behind_branch2 "$remote_branch" "$branch" ||
! jonjitsu_bash_prompt_branch_exists "$remote_branch"); then
# echo our character
echo 1
fi
}
prompt.parse_git_behind () {
# Grab the branch
branch="$(jonjitsu_bash_prompt_get_git_branch)"
remote_branch="origin/$branch"
# $ git log master..origin/master
# commit 4a633f715caf26f6e9495198f89bba20f3402a32
# Author: Todd Wolfson <todd@twolfson.com>
# Date: Sun Jul 7 22:12:17 2013 -0700
#
# Unsynced commit
# If the local branch is behind the remote branch
if jonjitsu_bash_prompt_is_branch1_behind_branch2 "$branch" "$remote_branch"; then
# echo our character
echo 1
fi
}
function jonjitsu_bash_prompt_parse_git_dirty() {
# If the git status has *any* changes (e.g. dirty), echo our character
if [[ -n "$(git status --porcelain 2> /dev/null)" ]]; then
echo 1
fi
}
function jonjitsu_bash_prompt_is_on_git() {
git rev-parse 2> /dev/null
}
function jonjitsu_bash_prompt_get_git_status() {
# Grab the git dirty and git behind
dirty_branch="$(jonjitsu_bash_prompt_parse_git_dirty)"
branch_ahead="$(jonjitsu_bash_prompt_parse_git_ahead)"
branch_behind="$(prompt.parse_git_behind)"
# Iterate through all the cases and if it matches, then echo
if [[ "$dirty_branch" == 1 && "$branch_ahead" == 1 && "$branch_behind" == 1 ]]; then
echo "$jonjitsu_bash_prompt_dirty_unpushed_unpulled_symbol"
elif [[ "$branch_ahead" == 1 && "$branch_behind" == 1 ]]; then
echo "$jonjitsu_bash_prompt_unpushed_unpulled_symbol"
elif [[ "$dirty_branch" == 1 && "$branch_ahead" == 1 ]]; then
echo "$jonjitsu_bash_prompt_dirty_unpushed_symbol"
elif [[ "$branch_ahead" == 1 ]]; then
echo "$jonjitsu_bash_prompt_unpushed_symbol"
elif [[ "$dirty_branch" == 1 && "$branch_behind" == 1 ]]; then
echo "$jonjitsu_bash_prompt_dirty_unpulled_symbol"
elif [[ "$branch_behind" == 1 ]]; then
echo "$jonjitsu_bash_prompt_unpulled_symbol"
elif [[ "$dirty_branch" == 1 ]]; then
echo "$jonjitsu_bash_prompt_dirty_synced_symbol"
else # clean
echo "$jonjitsu_bash_prompt_synced_symbol"
fi
}
analyze_git() {
git status --porcelain 2> /dev/null \
| awk '
BEGIN { d=0; s=0; }
/^(\?| )/ { d++; next; }
{ s++; }
END { print d, s }'
}
prompt.get_git_info () {
# Grab the branch
branch="$(jonjitsu_bash_prompt_get_git_branch)"
local other=""
# If there are any branches
if [[ "$branch" != "" ]]; then
read dirty staged <<<$(analyze_git)
if [[ $dirty -eq 0 ]] && [[ $staged -eq 0 ]]; then
output="${DIR_COLOR}${branch}"
else
if [[ $staged -gt 0 ]]; then
output="${COLOR_YELLOW}${branch}"
if [[ $dirty -gt 0 ]]; then
other="${COLOR_RESET}|${GIT_PROGRESS_COLOR}${dirty}${COLOR_YELLOW}"
fi
other="${COLOR_RESET}|${COLOR_YELLOW}${staged}${other}"
else
output="${GIT_PROGRESS_COLOR}${branch}"
other="${COLOR_RESET}|${GIT_PROGRESS_COLOR}${dirty}"
fi
fi
# if [[ ${git_status} =~ "working tree clean" ]]; then
# state="${DIR_COLOR}"
# elif [[ ${git_status} =~ "Changes to be committed" ]]; then
# state="${COLOR_YELLOW}"
# else
# state="${GIT_PROGRESS_COLOR}"
# fi
# # Echo the branch
# output="${state}${branch}"
# Add on the git status
output="$output$(jonjitsu_bash_prompt_get_git_status)${other}"
# Echo our output
echo "$output"
fi
}
PROMPT_COMMAND_HOOKS=()
prompt.add_hook() {
PROMPT_COMMAND_HOOKS+=("$@")
}
prompt.run_hooks() {
for cmd in "${PROMPT_COMMAND_HOOKS[@]}"; do
# type -p "$cmd" && "$cmd"
$cmd
done
}
prompt.hooks() {
for cmd in "${PROMPT_COMMAND_HOOKS[@]}"; do
echo - "$cmd"
done
}
prompt.command()
{
local PREV_RET_VAL=$?
local VE=""
local parts=()
local aws
if [[ -n $AWS_DEFAULT_PROFILE ]]
then echo "prompt: AWS_DEFAULT_PROFILE is deprecated. Switching to AWS_PROFILE" >&2
export AWS_PROFILE="$AWS_DEFAULT_PROFILE"
unset AWS_DEFAULT_PROFILE
fi
if [[ -n $AWS_PROFILE ]]; then
aws="\[$COLOR_BOLD_WHITE\]aws:\[$COLOR_BLUE\]${AWS_PROFILE}\[$COLOR_RESET\]"
elif [[ -n $AWS_ACCESS_KEY_ID ]] && [[ -n $AWS_SECRET_ACCESS_KEY ]]; then
aws="\[$COLOR_BOLD_WHITE\]aws:\[$COLOR_BLUE\]VARS\[$COLOR_RESET\]"
fi
if [[ -n $aws ]]; then
if [[ -n $AWS_DEFAULT_REGION ]]; then
aws="$aws:\[$COLOR_BLUE\]${AWS_DEFAULT_REGION}\[$COLOR_RESET\]"
fi
parts+=($aws)
fi
if [[ -f $HOME/.config/gcloud/active_config ]]
then parts+=("\[$COLOR_BOLD_WHITE\]gcp:\[$COLOR_BLUE\]$(cat "$HOME/.config/gcloud/active_config")\[$COLOR_RESET\]")
fi
if [[ -n $VIRTUAL_ENV ]]; then
local p=${VIRTUAL_ENV##*/}
parts+=("\[$COLOR_BOLD_WHITE\]py:\[$COLOR_BLUE\]${p}\[$COLOR_RESET\]")
# VE="$VE (\[$COLOR_YELLOW\]${p}\[$COLOR_RESET\])"
fi
if [[ -n $RUBYVENV ]]; then
parts+=("\[$COLOR_BOLD_WHITE\]rb:\[$COLOR_BLUE\]${RUBYVENV}\[$COLOR_RESET\]")
# VE="$VE(\[$COLOR_PURPLE\]${RUBYVENV}\[$COLOR_RESET\])"
fi
if [[ -n $NAVE ]]; then
parts+=("\[$COLOR_BOLD_WHITE\]js:\[$COLOR_BLUE\]${NAVE}\[$COLOR_RESET\]")
# VE="$VE(\[$COLOR_PURPLE\]${NAVE}\[$COLOR_RESET\])"
fi
local pulumi_login
if [[ -f $HOME/.pulumi/credentials.json ]]
then pulumi_login="$(jq -r .current ~/.pulumi/credentials.json)"
if [[ -n $pulumi_login ]] && [[ $pulumi_login != "null" ]]
then parts+=("\[$COLOR_BOLD_WHITE\]pu:\[$COLOR_BLUE\]${pulumi_login}\[$COLOR_RESET\]")
fi
fi
# local IFS='|'
VE="${parts[*]} "
local TS="\d \t"
# TS="$(TZ=UTC date '+%a %b %d %T %Z')"
# TS="$(TZ=UTC date --iso-8601=seconds)"
# TS="$(TZ=UTC date '+%Y-%m-%d %T %Z')"
TS="$(TZ=UTC date '+%Y-%m-%d %T') $(date +%:z)"
PS1="$TS \[$USER_COLOR\]\u\[$COLOR_RESET\]\
\[$PREPOSITION_COLOR\]@\[$COLOR_RESET\]\
\[$DEVICE_COLOR\]\h\[$COLOR_RESET\]\
\[$PREPOSITION_COLOR\]\[$COLOR_RESET\] \
\[$DIR_COLOR\]\w\[$COLOR_RESET\]\$(jonjitsu_bash_prompt_is_on_git && \
echo -ne \" \[$PREPOSITION_COLOR\][\[$COLOR_RESET\]\" && \
echo -ne \"\[$COLOR_YELLOW\]\$(prompt.get_git_info)\" && \
echo -ne \"\[$GIT_PROGRESS_COLOR\]\$(jonjitsu_bash_prompt_get_git_progress)\" && \
echo -ne \"\[$PREPOSITION_COLOR\]]\") $VE"
if test $PREV_RET_VAL -eq 0
then
PS1="${PS1}\[$COLOR_RESET\]\n"
else
PS1="(\[$GIT_PROGRESS_COLOR\]${PREV_RET_VAL}\[$COLOR_RESET\]) ${PS1}\[$COLOR_RESET\]\n"
fi
prompt.run_hooks
}
PROMPT_COMMAND=prompt.command
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment