bash/bashrc
ray 05afd18b1a add git alignment calculation for prompt display
Enhance the prompt by calculating alignment based on the
hostname and branch lengths, ensuring better visibility
of git status in the terminal.
2025-09-30 21:01:00 +01:00

327 lines
11 KiB
Bash

# Color definitions
_WHITE='\033[1;37m' # White
_NC='\033[0m' # Get the current day of the week, day of the month, and year
day_of_week=$(date "+%A")
day_of_month=$(date "+%d")
month_name=$(date "+%B")
year=$(date "+%Y")
# Determine the day suffix (th, nd, rd)
case "$day_of_month" in
1[0-9]) day_suffix="th";;
*1) day_suffix="st";;
*2) day_suffix="nd";;
*3) day_suffix="rd";;
*) day_suffix="th";;
esac
_RED='\033[0;31m' # Red
_GREEN='\033[01;32m' # Bright Green
_YELLOW='\033[0;33m' # Yellow
_BLUE='\033[1;34m' # Bright Blue
_BRIGHT_GREEN='\033[1;32m' # Bright Green (for positive)
_BRIGHT_RED='\033[1;31m' # Bright Red (for negative)
_BRIGHT_WHITE='\033[1;37m' # Bright White
_DIM_RED='\033[91m' # Bright Red (for TODO files)
export SHELL_CONFIG="$SHELL_CONFIG:.bashrc"
#
# ~/.bashrc
#
function show_date() {
# Define the dot file to store the selected adjective
dotfile="$HOME/.adjective_of_the_day"
# Check if the dot file exists and was modified since yesterday
if [[ -f "$dotfile" && $(date -d "$(stat -c %y "$dotfile")" +%s) -ge $(date -d 'yesterday' +%s) ]]; then
# If the dot file exists and was modified since yesterday
# read the stored adjective from the file
selected_adjective=$(cat "$dotfile")
else
# Define an array of adjectives with classifications
adjectives=(
"beautiful:positive"
"engaging:positive"
"majestic:positive"
"distinguished:positive"
"venerable:positive"
"formal:positive"
"elegant:positive"
"serene:positive"
"graceful:positive"
"regal:positive"
"noble:positive"
"miserable:negative"
"worthless:negative"
"depressing:negative"
"soulless:negative"
"desolate:negative"
"archaic:negative"
"grim:negative"
"dreadful:negative"
"forlorn:negative"
"wretched:negative"
"neutral:neutral"
"ordinary:neutral"
"common:neutral"
"antique:neutral"
"ceremonial:neutral"
"timeless:neutral"
"classic:neutral"
"historic:neutral"
"ancient:neutral"
)
# Select a random adjective from the array
selected_adjective="${adjectives[$RANDOM % ${#adjectives[@]}]}"
# Store the selected adjective in the dot file
echo "$selected_adjective" > "$dotfile"
fi
# Split the selected adjective into the adjective and classification parts
IFS=':' read -r adjective classification <<< "$selected_adjective"
# Get the current day of the week, day of the month, and year
day_of_week=$(date "+%A")
day_of_month=$(date "+%d")
month_name=$(date "+%B")
year=$(date "+%Y")
# Determine the day suffix (th, nd, rd)
case "$day_of_month" in
1[0-9]) day_suffix="th";;
*1) day_suffix="st";;
*2) day_suffix="nd";;
*3) day_suffix="rd";;
*) day_suffix="th";;
esac
# Determine the color based on the classification
case "$classification" in
"positive") text_color="$_BRIGHT_GREEN";;
"negative") text_color="$_BRIGHT_RED";;
*) text_color="";;
esac
# Create the full date format with colored parts and the selected adjective
full_date="Pray, be informed, on this ${text_color}${adjective}${_NC} day,\n${_BLUE}${day_of_week}${_NC} the ${_BLUE}${day_of_month}${day_suffix}${_NC} day of ${_BLUE}${month_name}${_NC}, in the year of our Lord ${_BLUE}${year}${_NC}!"
# Print the full date
echo -e "$full_date"
echo ""
}
# If not running interactively, don't do anything
[[ $- != *i* ]] && return
# ============================================================================
# SHELL CONFIGURATION
# ============================================================================
export HISTCONTROL=ignoreboth:erasedups
# Function to draw a terminal-width divider line with timestamp
function draw_prompt_divider() {
local width=${COLUMNS:-80} # Use terminal width, fallback to 80
local timestamp=$(date '+%H:%M:%S')
local prefix="$timestamp ├"
local prefix_length=${#prefix}
local line_length=$((width - prefix_length))
# Print timestamp + junction + line to fill remaining width
printf '%s' "$prefix"
printf '%.0s─' $(seq 1 $line_length)
echo
}
# Function to calculate git status alignment based on hostname and branch lengths
function calculate_git_alignment() {
# Get the raw hostname length (without color codes) - use actual prompt format
local user_host="$USER@$HOSTNAME"
local hostname_only="$HOSTNAME" # Just the hostname part after @
local hostname_length=${#hostname_only}
# Extract branch name from GITSTATUS_PROMPT if it exists
local branch_name=""
if [[ -n "$GITSTATUS_PROMPT" ]]; then
# Remove all escape sequences: \001, \002, and ANSI color codes
local cleaned_prompt=$(echo "$GITSTATUS_PROMPT" | sed -e 's/\x01//g' -e 's/\x02//g' -e 's/\x1b\[[0-9;]*m//g')
# Extract first word as branch name and trim any whitespace
branch_name=$(echo "$cleaned_prompt" | awk '{print $1}' | tr -d '[:space:]')
fi
local branch_length=${#branch_name}
# Calculate padding to align the END of branch name with END of hostname
# We want: [padding spaces] + branch_name to end at same position as hostname
# Total user@hostname length is where we want to align to
local total_user_host_length=${#user_host} # "ray@MachineOne" = 14 chars
local needed_padding=$((total_user_host_length - branch_length))
# Ensure minimum indentation of 8 spaces even if branch is very long
if [[ $needed_padding -lt 8 ]]; then
needed_padding=8
fi
# Generate the padding string
GIT_ALIGNMENT_PADDING=$(printf '%*s' $needed_padding '')
}
# Load custom gitstatus configuration
[ -f "$HOME/.config/bash/gitstatus-custom.sh" ] && . "$HOME/.config/bash/gitstatus-custom.sh"
# Set up prompt command to draw divider, update git status, and calculate alignment
PROMPT_COMMAND="draw_prompt_divider; my_gitstatus_prompt_update; calculate_git_alignment"
# Set PS1 with user@host, directory on first line, dynamically aligned git status, prompt on third line
PS1='\[\033[01;32m\]\u@\h\[\033[00m\] \[\033[1;34m\]\w\[\033[00m\]\n${GITSTATUS_PROMPT:+$GIT_ALIGNMENT_PADDING$GITSTATUS_PROMPT\n}\[\033[01;$((31+!$?))m\]\$\[\033[00m\] \[\e]0;\u@\h: \w\a\]'
# If there are multiple matches for completion, Tab should cycle through them
bind TAB:menu-complete
# Display a list of the matching files
bind "set show-all-if-ambiguous on"
# Perform partial (common) completion on the first Tab press, only start
# cycling full results on the second Tab press (from bash version 5)
bind "set menu-complete-display-prefix on"
show_date;
source ~/.profile
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm
[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion" # This loads nvm bash_completion
# https://github.com/skywind3000/z.lua
if command -v lua ; then
eval "$(lua $HOME/.z.lua/z.lua --init bash enhanced once echo)"
fi > /dev/null
_mem_available=$(awk '/MemAvailable/ { printf "%.1f \n", $2/1024/1024 }' /proc/meminfo)
_updates_available="$(wc -l $HOME/.checkupdates | cut -d' ' -f1)"
[ -z $_updates_available ] && _updates_available="0"
echo "Remember your commands:"
echo -e " ${_WHITE}lftp cheat vimv 'flameshot gui' rclone mid3v2 zathura tesseract remmina$ v4l2 ${_NC}(webcam)"
echo ""
if [ ${_mem_available%.*} -lt 5 ] ; then
echo -ne "Memory available: ${_YELLOW}"
[ ${_mem_available%.*} -lt 3 ] && echo -ne "${_RED}"
echo -n "${_mem_available}"
echo -e "GB${_NC}"
fi
if [ $_updates_available -gt 0 ] ; then
update_color="$_BRIGHT_WHITE" # default white
if [ $_updates_available -gt 29 ] ; then
update_color="$_RED" # red
elif [ $_updates_available -gt 15 ] ; then
update_color="$_YELLOW" # yellow
fi
echo -ne "Updates available: ${update_color}${_updates_available}${_NC}"
echo -ne " [ update with: ${update_color}update${_NC} ]"
echo ""
fi
_orphans_available="$(pacman -Qdtq 2>/dev/null | wc -l)"
if [ $_orphans_available -gt 0 ] ; then
echo -ne "Orphaned packages: $_BRIGHT_WHITE"
orphan_color="$_BRIGHT_WHITE"
if [ $_orphans_available -gt 9 ] ; then
orphan_color="$_RED"
elif [ $_orphans_available -gt 4 ] ; then
orphan_color="$_YELLOW"
fi
echo -ne "${orphan_color}${_orphans_available}${_NC}"
echo -ne " [ remove with: ${orphan_color}sudo pacman -Rns \$(pacman -Qdtq)${_NC} ]"
echo ""
fi
echo ""
echo -e "Remember \"${_BLUE}TODO.md${_NC}\" and \"${_BLUE}autonote.md${_NC}\" files have content automatically displayed."
echo -e "Edit these with ${_BLUE}et${_NC} and ${_BLUE}ea${_NC}"
echo ""
. "$HOME/.config/bash/cheat.bash"
if command -v checkupdates ; then
(checkupdates > "$HOME/.checkupdates") & disown
fi > /dev/null
. "$HOME/.cargo/env"
alias backup='sudo borgmatic create --verbosity 1 --files'
# Function to check for TODO.md and echo its contents
function check_todo() {
if [ -f "TODO.md" ]; then
echo
echo -e "${_DIM_RED}TODO.md${_NC}"
echo "---------------------------"
mdcat TODO.md
echo "---------------------------"
echo
fi
}
# Function to check for .autonote.md and echo its contents
function check_autonote() {
if [ -f ".autonote.md" ]; then
echo
echo -e "${_DIM_RED}autonote.md${_NC}"
echo "---------------------------"
mdcat .autonote.md
echo "---------------------------"
echo
fi
}
# Hook to execute check_todo, check_autonote when changing directories
cd() {
builtin cd "$@"
check_todo
check_autonote
}
random-bg() {
local color='xxx'
while getopts ":mrgb" opt; do
case $opt in
m) muted=true ;;
r) color="ffxx" ;;
g) color="xffx" ;;
b) color="xxff" ;;
*) echo "Invalid option: -$OPTARG" >&2
return 1 ;;
esac
done
if [ "$muted" = true ]; then
# FIXME sometimes gives 7 digit value
# note value of 30 works
hue_range=360 # Restricted hue range (0-360 degrees)
hue=$((RANDOM % $hue_range))
saturation_range="50-80" # Adjust as needed
saturation=$((RANDOM % ($saturation_range + 1) + ${saturation_range%-*}))
lightness_range="60-80" # Adjust as needed
lightness=$((RANDOM % ($lightness_range + 1) + ${lightness_range%-*}))
color=$(printf "%02x%02x%02x" "$hue" "$saturation" "$lightness")
else
# FIXME: each x is replaced with same value, need a different value for each x
color=${color//x/$(openssl rand -hex 1)}
fi
echo "#$color"
xsetroot -solid "#$color"
}