Skip to content

Instantly share code, notes, and snippets.

@moshen
Last active February 20, 2022 01:40
Show Gist options
  • Save moshen/9676359 to your computer and use it in GitHub Desktop.
Save moshen/9676359 to your computer and use it in GitHub Desktop.
A Fancy Bash Prompt
#!/bin/bash
#
# Fancy prompt
#
# Source this file in your .bashrc
#
# Configuration options:
#
# Set _prompt_config to an array of strings. Each string is in the
# format of: "function_name background_color foreground_color"
#
# The default _prompt_config is:
# _prompt_config=(
# "_prompt_seg_command_state 8 0"
# "_prompt_seg_hostname $_prompt_hostname_color 15"
# "_prompt_seg_dir 12 8"
# "_prompt_seg_git 5 7"
# )
#
# The functions are called in the order listed, and are passed in the
# name of a variable to assign text for the segment, and the last
# status code.
#
# If no text is written to the varible, then the segment is not
# displayed at all. Segments may also overwrite a variable called
# `seg` to modify their own function name or color codes.
#
# To create additional prompt segments, simply include a function that
# matches this signature and include it in the _prompt_config.
#
#
# Set POWERLINE_FONT=1 to enable, otherwise defaults to a basic prompt
#
# Setup git_ps1 options if you want to see them
# export GIT_PS1_SHOWDIRTYSTATE=1
# export GIT_PS1_SHOWSTASHSTATE=1
# export GIT_PS1_SHOWUNTRACKEDFILES=1
#
# Set _prompt_rainbow to an array of color indexes to cycle through
# otherwise, a default rainbow will be set.
#
# Set _prompt_rainbow_index to a starting color index of _prompt_rainbow
# otherwise, a random index will be used each time the prompt
# is initialized.
#
# Set _prompt_rainbow_pause=1 to keep the same color. Useful
# if you want to identify multiple windows/terms.
#
# Set _prompt_hostname_color to a valid 256 color code otherwise,
# one will be randomly set for you.
#
_prompt_hostname="$(perl -pe 's/\..+$//' <<<"$HOSTNAME")"
_prompt_rainbow_pause=""
function _prompt_sanitize_text {
perl -e '$ARGV[0] =~ s/([`\\\$\!])/\\$1/g; print @ARGV[0];' "$1"
}
function _prompt_set_arrow_char {
if [[ $POWERLINE_FONT ]]; then
__prompt_arrow_char=""
else
__prompt_arrow_char="▚"
fi
}
_prompt_set_arrow_char
if [[ -z $_prompt_rainbow ]]; then
_prompt_rainbow=( 154 154 154 184 184 214 214 208 208 202 203 203 198 198 199 163 164 128 128 93 93 57 63 63 33 33 39 39 45 44 44 49 49 48 84 83 119 118 154 154 190 184 )
fi
if [[ -z $_prompt_rainbow_index ]]; then
_prompt_rainbow_index=$(( RANDOM % (${#_prompt_rainbow[@]}-1) ))
fi
function _prompt_rainbow_inc_index {
[[ -z $_prompt_rainbow_pause ]] && _prompt_rainbow_index=$((_prompt_rainbow_index + 1))
if [[ $_prompt_rainbow_index -gt $((${#_prompt_rainbow[@]}-1)) ]]; then
_prompt_rainbow_index=0
fi
_rc="${_prompt_rainbow[$_prompt_rainbow_index]}"
}
function _prompt_hostname_random_color {
_prompt_hostname_color="$(( ( RANDOM % 203 ) + 17 ))"
}
if [[ -z $_prompt_hostname_color ]]; then
_prompt_hostname_random_color
fi
# shellcheck disable=SC2154
if [[ -n $_prompt_do_set_title ]]; then
# shellcheck disable=SC2016
_prompt_set_title_trap_cmd=' _prompt_set_title "$BASH_COMMAND"'
else
_prompt_set_title_trap_cmd=''
fi
function _prompt_seg_command_state {
local _last_status=$2
local _res=""
if [[ $_previous_command != "_prompt_command" ]]; then
case $_last_status in
0 )
_res='\[\e[38;5;34m\]'" ✔ "
;;
* )
_res='\[\e[38;5;160m\]'" ✘ "
;;
esac
else
_res=" "
fi
_seg_text="$_res"
}
function _prompt_seg_dir {
_seg_text=' \W '
}
function _prompt_seg_git {
# shellcheck disable=SC2155
local _git_prompt="$(__git_ps1)"
local _res=""
if [[ -n "$_git_prompt" ]]; then
# Strip leading space and parenthesis
_git_prompt="${_git_prompt:2:$((${#_git_prompt} - 3))}"
# Strip potential single quotes
_git_prompt="${_git_prompt//\'/}"
_res="  $_git_prompt "
_seg_text="$(_prompt_sanitize_text "$_res")"
else
_seg_text=''
fi
}
function _prompt_seg_hostname {
local _res=""
if [[ -n $SSH_CLIENT ]]; then
_res=" $_prompt_hostname "
_seg_text="$(_prompt_sanitize_text "$_res")"
else
_seg_text=''
fi
}
function _prompt_seg_virtualenv {
# Get Virtual Env
if [[ -n $VIRTUAL_ENV ]]; then
# Strip out the path and just leave the env name
_seg_text=" ${VIRTUAL_ENV##*/}"
else
_seg_text=''
fi
}
_previous_command=""
_cur_command=""
function _prompt_command {
# Stash the status before we clobber it
local _last_status=$?
_prompt_set_arrow_char
local __arrow_char=$__prompt_arrow_char
local _rc
_prompt_rainbow_inc_index
PS1='\[\e[38;5;'"$_rc"'m\]'
local total_chars=0
local seg
local _seg
local _seg_text=""
for i in "${!_prompt_config[@]}"; do
seg="${_prompt_config[$i]}"
# shellcheck disable=SC2206
_seg=( $seg )
"${_seg[0]}" _seg_text $_last_status
if [[ -n $_seg_text ]]; then
PS1+='\[\e[48;5;'"${_seg[1]}"'m\]'"$__arrow_char"
PS1+='\[\e[38;5;'"${_seg[2]}"'m\]'"$_seg_text"
PS1+='\[\e[38;5;'"${_seg[1]}"'m\]'
total_chars=$((total_chars + ${#_seg_text} + 2))
fi
_prompt_config[$i]="$seg"
_seg_text=""
done
PS1+='\[\e[49m\]'"$__arrow_char"'\[\e[0m\] '
if [[ $total_chars -gt $(($(tput cols) / 3 )) ]]; then
PS1+="\n"'\[\e[38;5;'"$_rc"'m\]'"$__arrow_char"'\[\e[0m\] '
fi
# Reset status
(exit $_last_status)
}
function _prompt_set_title {
if [[ $1 == "_prompt_command" ]]; then
printf '\e]0;%s\007' "bash - $(basename "$(pwd)")"
else
printf '\e]0;%s\007' "$1"
fi
}
# Trap commands to display status and title
# Only do this for login shells on the local machine
if [[ -z $SSH_CLIENT ]] && shopt -q login_shell; then
trap '_previous_command="$_cur_command"; _cur_command="$BASH_COMMAND";'"$_prompt_set_title_trap_cmd" DEBUG
else
trap '_previous_command="$_cur_command"; _cur_command="$BASH_COMMAND";' DEBUG
fi
# Set a default config
# shellcheck disable=SC2128
if [[ -z $_prompt_config ]]; then
_prompt_config=(
"_prompt_seg_command_state 23 0"
"_prompt_seg_hostname $_prompt_hostname_color 231"
"_prompt_seg_dir 249 23"
# "_prompt_seg_virtualenv 99 23"
"_prompt_seg_git 162 255"
)
fi
PROMPT_COMMAND="_prompt_command"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment