# Emacs? -*- shell-script -*- please. # Environment variables: # # PS1_SHOWHOSTNAME # 'auto' (default): Only show hostname over SSH # 'yes': Always show hostname # anything else: Never show hostname __show-hostname () { case ${PS1_SHOWHOSTNAME} in (''|auto) test "${SSH_CONNECTION}";; (yes) true;; (*) false;; esac } __have-gitprompt () { test -f /usr/lib/git-core/git-sh-prompt } __set-title () { local title=${USER} __show-hostname && title+=@${HOSTNAME} title+=: title+=${PWD/~/\~} # Cf. console_codes(4): # # ESC ] 2 ; txt ST Set window title to txt. # # ST is a "string terminator", either "ESC \" or "BEL". printf $'\E]2;%s\a' "${title}" } # Bash relies on \[ \] to distinguish non-printing characters in prompts. __start-nonprinting () { test ${BUILDING_PS} && printf '\[' } __end-nonprinting () { test ${BUILDING_PS} && printf '\]' } __fontify () { local -r text=$1 shift local -Ar codes=( [bold]=1 [dim]=2 [reverse]=7 [red]=31 [green]=32 [blue]=34 ) local -a params=() local c for c do params+=(${codes[${c}]}) done local params_seq IFS=';' eval 'params_seq="${params[*]}"' __start-nonprinting printf $'\E[%sm' "${params_seq}" __end-nonprinting printf %s "${text}" __start-nonprinting printf $'\E[0m' __end-nonprinting } __current-column () { # Cf. console_codes(4) § ECMA-48 Status Report Commands. local position read -sdR -p$'\E[6n' position local -r pattern='\[[0-9]+;([0-9]+)' [[ ${position} =~ ${pattern} ]] || { echo 0 return } echo ${BASH_REMATCH[1]} } __signal-no-newline () { __fontify ∅ bold red echo } __draw-rule () { local -ir pos=$(__current-column) local -ir rule_len=$((COLUMNS-pos)) local rule # Hey Future Me 👋 Breaking this down: # -v rule "put this in LINE, do not write to stdout" # "%…s" "pad string…" # - " … with spaces…" # * " … with length given by next argument" # ${rule_len} (padding length) # ∅ (psych! no arg for %s, default to null string) printf -v rule '%-*s' ${rule_len} rule=${rule// /─} # Flush line to avoid future text being shoved offscreen when autowrap # is off. rule+=$'\n' __fontify "${rule}" "$@" } __write-context () { __fontify '\u' green __show-hostname && { __fontify @ dim __fontify '\H' bold green } printf ' ' __fontify '\w' bold blue test "${PS1_SHOWGITSTATUS}" && { printf ' ' __fontify "$(__git_ps1 '(%s)')" red } } __smart-term/set-ps1 () { BUILDING_PS=t PS1="$(__write-context)\n$(__fontify '\$' dim) " unset BUILDING_PS } __smart-term/refresh () { local -ir rc=$? __set-title local -ir pos=$(__current-column) ((pos != 1)) && __signal-no-newline if ((rc)) then __fontify "${rc} " bold red __draw-rule dim red else __draw-rule dim fi __smart-term/set-ps1 } __smart-term/init () { # Prompts. __have-gitprompt && { . /usr/lib/git-core/git-sh-prompt GIT_PS1_SHOWDIRTYSTATE=t GIT_PS1_SHOWUPSTREAM=auto PS1_SHOWGITSTATUS=t } PROMPT_COMMAND=__smart-term/refresh PS2=$( BUILDING_PS=t __fontify '… ' dim ) # Bindings. bind -f ~/.bash_inputrc # Unset the TTY's "stop" char, so that readline receives C-s. tty -s && stty stop '' } __dumb-term/set-ps1 () { local -ir rc=$? BUILDING_PS=t PS1='' ((rc)) && PS1+=$(__fontify "${rc} " bold red) PS1+='\W\$ ' unset BUILDING_PS } __dumb-term/init () { PROMPT_COMMAND=__dumb-term/set-ps1 } case "${TERM}" in dumb) __dumb-term/init ;; *) __smart-term/init ;; esac