Skip to content

Commit

Permalink
Rework awesome bash prompt
Browse files Browse the repository at this point in the history
* Address issue surfaced in PR #68, improving on submitted code
* Prefix all prompt globals with __, eg __prompt_vcs_info
* Prefer git to svn
  • Loading branch information
cowboy committed Mar 21, 2017
1 parent 2dd4182 commit 0737a37
Showing 1 changed file with 59 additions and 55 deletions.
114 changes: 59 additions & 55 deletions source/50_prompt.sh
Original file line number Diff line number Diff line change
Expand Up @@ -22,115 +22,119 @@
# 32 42 green 36 46 cyan
# 33 43 yellow 37 47 white

if [[ ! "${prompt_colors[@]}" ]]; then
prompt_colors=(
if [[ ! "${__prompt_colors[@]}" ]]; then
__prompt_colors=(
"36" # information color
"37" # bracket color
"31" # error color
)

if [[ "$SSH_TTY" ]]; then
# connected via ssh
prompt_colors[0]="32"
__prompt_colors[0]="32"
elif [[ "$USER" == "root" ]]; then
# logged in as root
prompt_colors[0]="35"
__prompt_colors[0]="35"
fi
fi

# Inside a prompt function, run this alias to setup local $c0-$c9 color vars.
alias prompt_getcolors='prompt_colors[9]=; local i; for i in ${!prompt_colors[@]}; do local c$i="\[\e[0;${prompt_colors[$i]}m\]"; done'
alias __prompt_get_colors='__prompt_colors[9]=; local i; for i in ${!__prompt_colors[@]}; do local c$i="\[\e[0;${__prompt_colors[$i]}m\]"; done'

# Exit code of previous command.
function prompt_exitcode() {
prompt_getcolors
function __prompt_exit_code() {
__prompt_get_colors
[[ $1 != 0 ]] && echo " $c2$1$c9"
}

# Git status.
function prompt_git() {
prompt_getcolors
local status output flags branch
function __prompt_git() {
__prompt_get_colors
local status branch flags
status="$(git status 2>/dev/null)"
[[ $? != 0 ]] && return;
output="$(echo "$status" | awk '/# Initial commit/ {print "(init)"}')"
[[ "$output" ]] || output="$(echo "$status" | awk '/# On branch/ {print $4}')"
[[ "$output" ]] || output="$(git branch | perl -ne '/^\* \(detached from (.*)\)$/ ? print "($1)" : /^\* (.*)/ && print $1')"
[[ $? != 0 ]] && return 1;
branch="$(echo "$status" | awk '/# Initial commit/ {print "(init)"}')"
[[ "$branch" ]] || branch="$(echo "$status" | awk '/# On branch/ {print $4}')"
[[ "$branch" ]] || branch="$(git branch | perl -ne '/^\* \(detached from (.*)\)$/ ? print "($1)" : /^\* (.*)/ && print $1')"
flags="$(
echo "$status" | awk 'BEGIN {r=""} \
/^(# )?Changes to be committed:$/ {r=r "+"}\
/^(# )?Changes not staged for commit:$/ {r=r "!"}\
/^(# )?Untracked files:$/ {r=r "?"}\
END {print r}'
)"
output=${output//[^a-z0-9\/]/-}
if [[ "$flags" ]]; then
output="$output$c1:$c0$flags"
fi
echo "$c1[$c0$output$c1]$c9"
__prompt_vcs_info=("$branch" "$flags")
}

# hg status.
function prompt_hg() {
prompt_getcolors
local summary output bookmark flags
function __prompt_hg() {
__prompt_get_colors
local summary branch bookmark flags
summary="$(hg summary 2>/dev/null)"
[[ $? != 0 ]] && return;
output="$(echo "$summary" | awk '/branch:/ {print $2}')"
[[ $? != 0 ]] && return 1;
branch="$(echo "$summary" | awk '/branch:/ {print $2}')"
bookmark="$(echo "$summary" | awk '/bookmarks:/ {print $2}')"
flags="$(
echo "$summary" | awk 'BEGIN {r="";a=""} \
/(modified)/ {r= "+"}\
/(unknown)/ {a= "?"}\
END {print r a}'
)"
output=${output//[^a-z0-9\/]/-}
bookmark=${bookmark//[^a-z0-9\/]/-}
output="$output:$bookmark"
if [[ "$flags" ]]; then
output="$output$c1:$c0$flags"
fi
echo "$c1[$c0$output$c1]$c9"
__prompt_vcs_info=("$branch" "$bookmark" "$flags")
}

# SVN info.
function prompt_svn() {
prompt_getcolors
local info="$(svn info . 2> /dev/null)"
local last current
if [[ "$info" ]]; then
last="$(echo "$info" | awk '/Last Changed Rev:/ {print $4}')"
current="$(echo "$info" | awk '/Revision:/ {print $2}')"
echo "$c1[$c0$last$c1:$c0$current$c1]$c9"
fi
function __prompt_svn() {
__prompt_get_colors
local info last current
info="$(svn info . 2> /dev/null)"
[[ ! "$info" ]] && return 1
last="$(echo "$info" | awk '/Last Changed Rev:/ {print $4}')"
current="$(echo "$info" | awk '/Revision:/ {print $2}')"
__prompt_vcs_info=("$last" "$current")
}

# Maintain a per-execution call stack.
prompt_stack=()
trap 'prompt_stack=("${prompt_stack[@]}" "$BASH_COMMAND")' DEBUG
__prompt_stack=()
trap '__prompt_stack=("${__prompt_stack[@]}" "$BASH_COMMAND")' DEBUG

function prompt_command() {
local exit_code=$?
# If the first command in the stack is prompt_command, no command was run.
function __prompt_command() {
local i exit_code=$?
# If the first command in the stack is __prompt_command, no command was run.
# Set exit_code to 0 and reset the stack.
[[ "${prompt_stack[0]}" == "prompt_command" ]] && exit_code=0
prompt_stack=()
[[ "${__prompt_stack[0]}" == "__prompt_command" ]] && exit_code=0
__prompt_stack=()

# Manually load z here, after $? is checked, to keep $? from being clobbered.
[[ "$(type -t _z)" ]] && _z --add "$(pwd -P 2>/dev/null)" 2>/dev/null

# While the simple_prompt environment var is set, disable the awesome prompt.
[[ "$simple_prompt" ]] && PS1='\n$ ' && return

prompt_getcolors
__prompt_get_colors
# http://twitter.com/cowboy/status/150254030654939137
PS1="\n"
# svn: [repo:lastchanged]
PS1="$PS1$(prompt_svn)"
__prompt_vcs_info=()
# git: [branch:flags]
PS1="$PS1$(prompt_git)"
# hg: [branch:flags]
PS1="$PS1$(prompt_hg)"
__prompt_git || \
# hg: [branch:bookmark:flags]
__prompt_hg || \
# svn: [repo:lastchanged]
__prompt_svn
# Iterate over all vcs info parts, outputting an escaped var name that will
# be interpolated automatically. This ensures that malicious branch names
# can't execute arbitrary commands. For more info, see this PR:
# https://github.com/cowboy/dotfiles/pull/68
if [[ "${#__prompt_vcs_info[@]}" != 0 ]]; then
PS1="$PS1$c1[$c0"
for i in "${!__prompt_vcs_info[@]}"; do
if [[ "${__prompt_vcs_info[i]}" ]]; then
[[ $i != 0 ]] && PS1="$PS1$c1:$c0"
PS1="$PS1\${__prompt_vcs_info[$i]}"
fi
done
PS1="$PS1$c1]$c9"
fi
# misc: [cmd#:hist#]
# PS1="$PS1$c1[$c0#\#$c1:$c0!\!$c1]$c9"
# path: [user@host:path]
Expand All @@ -139,8 +143,8 @@ function prompt_command() {
# date: [HH:MM:SS]
PS1="$PS1$c1[$c0$(date +"%H$c1:$c0%M$c1:$c0%S")$c1]$c9"
# exit code: 127
PS1="$PS1$(prompt_exitcode "$exit_code")"
PS1="$PS1$(__prompt_exit_code "$exit_code")"
PS1="$PS1 \$ "
}

PROMPT_COMMAND="prompt_command"
PROMPT_COMMAND="__prompt_command"

0 comments on commit 0737a37

Please sign in to comment.