freedombone-powerline 9.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357
  1. #!/usr/bin/env bash
  2. # Based on https://github.com/undu/bash-powerline
  3. __powerline() {
  4. # User config variables,
  5. # it's recommended to override those variables through .bashrc or similar
  6. #
  7. # Use powerline mode
  8. # readonly POWERLINE_FONT=''
  9. #
  10. # Always show user in the prompt
  11. # readonly SHOW_USER=''
  12. #
  13. # Never show a default user
  14. # readonly DEFAULT_USER='user'
  15. # Default background and foreground ANSI colours
  16. readonly DEFAULT_BG=0
  17. readonly DEFAULT_FG=7
  18. # Max length of full path
  19. readonly MAX_PATH_LENGTH=30
  20. # Unicode symbols
  21. if [ -z "${POWERLINE_FONT+x}" ]; then
  22. readonly GIT_BRANCH_SYMBOL='⑂'
  23. else
  24. readonly GIT_BRANCH_SYMBOL=''
  25. fi
  26. readonly GIT_BRANCH_CHANGED_SYMBOL='Δ'
  27. readonly GIT_NEED_PUSH_SYMBOL='↑'
  28. readonly GIT_NEED_PULL_SYMBOL='↓'
  29. # Powerline symbols
  30. readonly BLOCK_START=''
  31. # ANSI Colours
  32. readonly BLACK=0
  33. readonly RED=1
  34. readonly GREEN=2
  35. readonly YELLOW=3
  36. readonly BLUE=4
  37. readonly MAGENTA=5
  38. readonly CYAN=6
  39. readonly WHITE=7
  40. readonly BLACK_BRIGHT=8
  41. readonly RED_BRIGHT=9
  42. readonly GREEN_BRIGHT=10
  43. readonly YELLOW_BRIGHT=11
  44. readonly BLUE_BRIGHT=12
  45. readonly MAGENTA_BRIGHT=13
  46. readonly CYAN_BRIGHT=14
  47. readonly WHITE_BRIGHT=15
  48. # Font effects
  49. readonly DIM="\[$(tput dim)\]"
  50. readonly REVERSE="\[$(tput rev)\]"
  51. readonly RESET="\[$(tput sgr0)\]"
  52. readonly BOLD="\[$(tput bold)\]"
  53. # Generate terminal colour codes
  54. # $1 is an int (a colour) and $2 must be 'fg' or 'bg'
  55. __colour() {
  56. case "$2" in
  57. 'fg'*)
  58. echo "\[$(tput setaf "$1")\]"
  59. ;;
  60. 'bg'*)
  61. echo "\[$(tput setab "$1")\]"
  62. ;;
  63. *)
  64. echo "\[$(tput setab "$1")\]"
  65. ;;
  66. esac
  67. }
  68. # Generate a single-coloured block for the prompt
  69. __prompt_block() {
  70. local bg; local fg
  71. if [ ! -z "${1+x}" ]; then
  72. bg=$1
  73. else
  74. if [ ! -z "$last_bg" ]; then
  75. bg=$last_bg
  76. else
  77. bg=$DEFAULT_BG
  78. fi
  79. fi
  80. if [ ! -z "${2+x}" ]; then
  81. fg=$2
  82. else
  83. fg=$DEFAULT_FG
  84. fi
  85. local block
  86. # Need to generate a separator if the background changes
  87. if [[ ! -z "$last_bg" && "$bg" != "$last_bg" && ! -z "${POWERLINE_FONT+x}" ]]; then
  88. block+="$(__colour "$bg" 'bg')"
  89. block+="$(__colour "$last_bg" 'fg')"
  90. block+="$BLOCK_START $RESET"
  91. block+="$(__colour "$bg" 'bg')"
  92. block+="$(__colour "$fg" 'fg')"
  93. else
  94. block+="$(__colour "$bg" 'bg')"
  95. block+="$(__colour "$fg" 'fg')"
  96. block+=" "
  97. fi
  98. if [ ! -z "${3+x}" ]; then
  99. block+="$3 $RESET"
  100. fi
  101. last_bg=$bg
  102. __block_text="$block"
  103. }
  104. function __end_block() {
  105. __block_text=''
  106. if [ ! -z "$last_bg" ]; then
  107. if [ ! -z "${POWERLINE_FONT+x}" ]; then
  108. __block_text+="$(__colour $DEFAULT_BG 'bg')"
  109. __block_text+="$(__colour "$last_bg" 'fg')"
  110. __block_text+="$BLOCK_START$RESET"
  111. __block_text+="$(__colour $DEFAULT_BG 'bg')"
  112. __block_text+="$(__colour "$DEFAULT_FG" 'fg')"
  113. else
  114. __block_text+="$(__colour $DEFAULT_BG 'bg')"
  115. __block_text+="$(__colour "$DEFAULT_FG" 'fg')"
  116. fi
  117. fi
  118. __block_text+=' '
  119. }
  120. ### Prompt components
  121. __git_block() {
  122. if ! command -V git > /dev/null; then
  123. # git not found
  124. __block_text=''
  125. return
  126. fi
  127. # force git output in English to make our work easier
  128. local git_eng="env LANG=C git"
  129. # check if pwd is under git
  130. if ! git rev-parse --is-inside-git-dir > /dev/null 2> /dev/null; then
  131. # not in a git repo, bail out
  132. __block_text=''
  133. return
  134. fi
  135. # get current branch name or short SHA1 hash for detached head
  136. local branch; local ref_symbol
  137. branch="$($git_eng symbolic-ref --short HEAD 2>/dev/null)"
  138. # shellcheck disable=SC2181
  139. if [ $? != 0 ]; then
  140. branch="$($git_eng describe --tags --always 2>/dev/null)"
  141. ref_symbol='➦'
  142. else
  143. ref_symbol=$GIT_BRANCH_SYMBOL
  144. fi
  145. # In pcmode (and only pcmode) the contents of
  146. # $gitstring are subject to expansion by the shell.
  147. # Avoid putting the raw ref name in the prompt to
  148. # protect the user from arbitrary code execution via
  149. # specially crafted ref names (e.g., a ref named
  150. # '$(IFS=_;cmd=sudo_rm_-rf_/;$cmd)' would execute
  151. # 'sudo rm -rf /' when the prompt is drawn). Instead,
  152. # put the ref name in a new global variable (in the
  153. # __git_ps1_* namespace to avoid colliding with the
  154. # user's environment) and reference that variable from
  155. # PS1.
  156. # note that the $ is escaped -- the variable will be
  157. # expanded later (when it's time to draw the prompt)
  158. if shopt -q promptvars; then
  159. export __git_ps1_block="$branch"
  160. ref="$ref_symbol \${__git_ps1_block}"
  161. else
  162. ref="$ref_symbol $branch"
  163. fi
  164. local marks
  165. # check if HEAD is dirty
  166. if [ -n "$($git_eng status --porcelain 2>/dev/null)" ]; then
  167. dirty='y'
  168. marks+=" $GIT_BRANCH_CHANGED_SYMBOL"
  169. fi
  170. # how many commits local branch is ahead/behind of remote?
  171. local stat; local aheadN; local behindN
  172. stat="$($git_eng status --porcelain --branch 2>/dev/null | grep '^##' | grep -o '\[.\+\]$')"
  173. aheadN="$(echo "$stat" | grep -o 'ahead [[:digit:]]\+' | grep -o '[[:digit:]]\+')"
  174. behindN="$(echo "$stat" | grep -o 'behind [[:digit:]]\+' | grep -o '[[:digit:]]\+')"
  175. [ -n "$aheadN" ] && marks+=" $GIT_NEED_PUSH_SYMBOL$aheadN"
  176. [ -n "$behindN" ] && marks+=" $GIT_NEED_PULL_SYMBOL$behindN"
  177. local bg; local fg
  178. fg=$BLACK
  179. if [ -z "$dirty" ]; then
  180. bg=$GREEN
  181. else
  182. bg=$YELLOW
  183. fi
  184. __prompt_block $bg $fg "$ref$marks"
  185. }
  186. __virtualenv_block() {
  187. # Copied from Python virtualenv's activate.sh script.
  188. # https://github.com/pypa/virtualenv/blob/a9b4e673559a5beb24bac1a8fb81446dd84ec6ed/virtualenv_embedded/activate.sh#L62
  189. # License: MIT
  190. if [ -n "$VIRTUAL_ENV" ]; then
  191. local venv
  192. # In pcmode (and only pcmode) the contents of
  193. # $gitstring are subject to expansion by the shell.
  194. # Avoid putting the raw ref name in the prompt to
  195. # protect the user from arbitrary code execution via
  196. # specially crafted ref names (e.g., a ref named
  197. # '$(IFS=_;cmd=sudo_rm_-rf_/;$cmd)' would execute
  198. # 'sudo rm -rf /' when the prompt is drawn). Instead,
  199. # put the ref name in a new global variable (in the
  200. # __git_ps1_* namespace to avoid colliding with the
  201. # user's environment) and reference that variable from
  202. # PS1.
  203. # note that the $ is escaped -- the variable will be
  204. # expanded later (when it's time to draw the prompt)
  205. if shopt -q promptvars; then
  206. export __venv_ps1_block
  207. __venv_ps1_block=$(basename "$VIRTUAL_ENV")
  208. venv="$ref_symbol \${__venv_ps1_block}"
  209. else
  210. venv="$(basename "$VIRTUAL_ENV")"
  211. fi
  212. __prompt_block $WHITE $BLACK "$venv"
  213. else
  214. __block_text=''
  215. fi
  216. }
  217. __pwd_block() {
  218. # Use ~ to represent $HOME prefix
  219. local pwd; pwd=$(pwd | sed -e "s|^$HOME|~|")
  220. # shellcheck disable=SC1001,SC2088
  221. if [[ ( $pwd = ~\/*\/* || $pwd = \/*\/*/* ) && ${#pwd} -gt $MAX_PATH_LENGTH ]]; then
  222. local IFS='/'
  223. read -ra split <<< "$pwd"
  224. if [[ $pwd = ~* ]]; then
  225. pwd="~/${split[1]}/.../${split[*]:(-2):1}/${split[*]:(-1)}"
  226. else
  227. pwd="/${split[1]}/.../${split[*]:(-2):1}/${split[*]:(-1)}"
  228. fi
  229. fi
  230. __prompt_block $BLACK_BRIGHT $WHITE_BRIGHT "$pwd"
  231. }
  232. # superuser or not, here I go!
  233. __user_block() {
  234. # Colours to use
  235. local fg=$WHITE_BRIGHT
  236. local bg=$BLUE
  237. if [[ ! -z "$SSH_CLIENT" ]]; then
  238. local show_host="y"
  239. bg=$CYAN
  240. fi
  241. if [ -z "$(id -u "$USER")" ]; then
  242. bg=$RED
  243. fi
  244. # shellcheck disable=SC2153
  245. if [[ ! -z "${SHOW_USER+x}" || ( ! -z "${DEFAULT_USER+x}" && "$DEFAULT_USER" != "$(whoami)" ) ]]; then
  246. local show_user="y"
  247. fi
  248. local text
  249. if [ ! -z ${show_user+x} ]; then
  250. text+="$BOLD$(whoami)"
  251. fi
  252. if [ ! -z ${show_host+x} ]; then
  253. if [ ! -z "${text+x}" ]; then
  254. text+="@"
  255. fi
  256. text+="\h"
  257. fi
  258. if [ ! -z ${text+x} ]; then
  259. __prompt_block $bg $fg $text
  260. fi
  261. }
  262. __status_block() {
  263. local text
  264. if [ "$exit_code" != 0 ]; then
  265. __prompt_block $BLACK $RED '✘'
  266. text+=$__block_text
  267. fi
  268. if [ "$(id -u "$USER")" == 0 ]; then
  269. __prompt_block $BLACK $YELLOW '⚡'
  270. text+=$__block_text
  271. fi
  272. if [ "$(jobs -l | wc -l)" != 0 ]; then
  273. __prompt_block $BLACK $CYAN '⚙'
  274. text+=$__block_text
  275. fi
  276. if [ ! -z "$text" ]; then
  277. __block_text=$text
  278. else
  279. __block_text=''
  280. fi
  281. }
  282. # Build the prompt
  283. prompt() {
  284. # I don't like bash; execute first to capture correct status code
  285. local exit_code=$?
  286. # shellcheck disable=SC2091
  287. $(history -a ; history -n)
  288. last_bg=''
  289. PS1=''
  290. __status_block
  291. PS1+=$__block_text
  292. __virtualenv_block
  293. PS1+=$__block_text
  294. __user_block
  295. PS1+=$__block_text
  296. __pwd_block
  297. PS1+=$__block_text
  298. __git_block
  299. PS1+=$__block_text
  300. __end_block
  301. PS1+=$__block_text
  302. }
  303. PROMPT_COMMAND=prompt
  304. }
  305. __powerline
  306. unset __powerline