dotfiles/.bash_prompt

127 lines
4.0 KiB
Bash

# shellcheck shell=bash
# Customize bash prompt
# lolcat reference: http://apple.stackexchange.com/a/266112/216733
# If dynamic can use literal control characters. Otherwise,
# must use PS1 escapes or else line wrap issue.
PROMPT_COMMAND="__prompt_command${PROMPT_COMMAND:+"; $PROMPT_COMMAND"}"
PS1_COLORIZE_COMMAND='__ps1_lolcat'
# shellcheck disable=SC2034
PS1_DEBUG_COMMAND='__ps1_debug'
PS1_DEBUG='false'
__prompt_command() {
# Save last exit code
PS1_exit=$?
if [[ "$PS1_DEBUG" = 'true' ]]; then
if [[ "$PS1" = "$PS1_DEBUG_DISABLED" ]]; then
PS1=$PS1_DEBUG_ENABLED
fi
else
if [[ "$PS1" = "$PS1_DEBUG_ENABLED" ]]; then
PS1=$PS1_DEBUG_DISABLED
fi
fi
}
ps1_set_debug_mode() {
if [[ "$PS1_DEBUG" = 'true' ]]; then
PS1=$PS1_DEBUG_ENABLED
else
PS1=$PS1_DEBUG_DISABLED
fi
}
TERM_DEFAULT=$(tput sgr0)
TERM_BOLD=$(tput bold) # I added this, as of now unused
TERM_RED=$(tput setaf 1)
TERM_GREEN=$(tput setaf 2)
TERM_BLUE=$(tput setaf 4)
ESC=$'\033'
SOH=$'\001'
STX=$'\002'
PS1_DEFAULT_LITERAL=$SOH$TERM_DEFAULT$STX
PS1_RED_LITERAL=$SOH$TERM_RED$STX
PS1_GREEN_LITERAL=$SOH$TERM_GREEN$STX
PS1_BLUE_LITERAL=$SOH$TERM_BLUE$STX
PS1_DEFAULT_ESCAPED='\['$TERM_DEFAULT'\]'
# Caveat:
# to have a literal '\' or '"' work in both $PS1_DEBUG_DISABLED and
# $PS1_DEBUG_ENABLED, '\' must be represented with '\\\\' and
# '"' must be represented with '\\"' or '\"' to have the proper amount of
# escaping in both modes. PS1 seems to have an initial interpret phase where
# it strips off one level of backslashes, then an echo phase where it strips
# off the second level so '\\\\' =interpret> '\\' =echo> '\'
# '\\"' =interpret> '\"' =echo> '"'
# The following is a bit trickier. Normally, echo would treat the double quote
# as starting a string. PS1 seems to treat it as a literal when echoing. As
# a result, '\\"' is preferred over '\"' as it is easier to understand what is
# happening.
# '\"' =interpret> 'literal(")' =echo> '"'
# In $PS1_DEBUG_ENABLED, the interpret phase is still run by PS1. The echo
# phase is emulated by wrapping $PS1 in double quotes: ...<<<"'PS1'"...
unset PS1
# vvv - actually change PS1 here - vvv
PS1_COLORLESS=$(sed 's/[[:space:]]*$//' <<<"${PS1:-[\u@\h]:\w \\$}")
PS1_SPACING=' '
PS1=''
__ps1_remove_newline() {
tr -d '\n'
}
__ps1_color_wrap_non_printing() {
local REGEX=$ESC'\[[[:digit:];]*m'
local REPLACE=$SOH'&'$STX
sed "s/$REGEX/$REPLACE/g"
}
# change lolcat arguments here
__ps1_lolcat() {
lolcat-c -f -h 1.4 | __ps1_remove_newline | __ps1_color_wrap_non_printing
}
PS1+='$(eval "$PS1_COLORIZE_COMMAND" <<<"'$PS1_COLORLESS'")'
PS1+=$PS1_SPACING
PS1_DEBUG_DISABLED=$PS1
# long colored prompt text that go over a line have a line wrap issue.
# Problem occurs with literal and PS1 escaped control characters. Tried
# escaping each individual character with perl at
# "$SCRIPTS_DIR"'/ps1_colorize_debug.pl' but still doesn't work.
# can't colorize debug literal text with sed because it does not have the
# following:
# non-greedy quantifiers to do \^B\(.*?\)\^A if running a second sed to
# consume \^B and \^A again.
# lookahead to do \(.*?)\(?=\^A) which wouldn't require a second sed because
# \^A is is looked for but not consumed.
# can use perl regex if want to colorize debug literal text.
# sed also doesn't have non capturing groups so groups are offset.
__ps1_colorize_debug() {
local SOH_REGEX='(\^A)'
local COLOR_CODE_REGEX='((\^\[\(B)?\^\[\[[[:digit:];]*m)'
local STX_REGEX='(\^B)'
local REGEX=$SOH_REGEX$COLOR_CODE_REGEX$STX_REGEX
local SOH_REPLACE=$PS1_RED_LITERAL'\1'$PS1_DEFAULT_LITERAL
local COLOR_CODE_REPLACE=$PS1_BLUE_LITERAL'\2'$PS1_DEFAULT_LITERAL
local STX_REPLACE=$PS1_GREEN_LITERAL'\4'$PS1_DEFAULT_LITERAL
local REPLACE=$SOH_REPLACE$COLOR_CODE_REPLACE$STX_REPLACE
sed -E "s/$REGEX/$REPLACE/g"
}
__ps1_debug() {
cat -v | __ps1_colorize_debug
}
__ps1_debug_sed() {
sed -e 's/'$ESC'/[ESC]/g' -e 's/'$SOH'/[SOH]/g' -e 's/'$STX'/[STX]/g'
}
# wrap PS1 for debugging
# shellcheck disable=SC2016
PS1_DEBUG_ENABLED='$(eval "$PS1_DEBUG_COMMAND" <<<"'$PS1'")'