freedombone-powerline 9.2KB

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