#!/usr/bin/env bash THEME_PROMPT_HOST='\H' SCM_CHECK=${SCM_CHECK:=true} SCM_THEME_PROMPT_DIRTY=' ✗' SCM_THEME_PROMPT_CLEAN=' ✓' SCM_THEME_PROMPT_PREFIX=' |' SCM_THEME_PROMPT_SUFFIX='|' SCM_THEME_BRANCH_PREFIX='' SCM_THEME_TAG_PREFIX='tag:' SCM_THEME_DETACHED_PREFIX='detached:' SCM_THEME_BRANCH_TRACK_PREFIX=' → ' SCM_THEME_BRANCH_GONE_PREFIX=' ⇢ ' SCM_THEME_CURRENT_USER_PREFFIX=' ☺︎ ' SCM_THEME_CURRENT_USER_SUFFIX='' CLOCK_CHAR='⌚' THEME_CLOCK_CHAR_COLOR=${THEME_CLOCK_CHAR_COLOR:="${red}"} THEME_SHOW_CLOCK=${THEME_CLOCK_CHECK:-true} THEME_CLOCK_FORMAT=${THEME_CLOCK_FORMAT:="%Y-%m-%d %H:%M:%S"} THEME_CLOCK_COLOR=${THEME_CLOCK_COLOR:="${bold_cyan}"} THEME_BATTERY_PERCENTAGE_CHECK=${THEME_BATTERY_PERCENTAGE_CHECK:=true} SCM_GIT_SHOW_DETAILS=${SCM_GIT_SHOW_DETAILS:=true} SCM_GIT_SHOW_REMOTE_INFO=${SCM_GIT_SHOW_REMOTE_INFO:=auto} SCM_GIT_IGNORE_UNTRACKED=${SCM_GIT_IGNORE_UNTRACKED:=false} SCM_GIT_SHOW_CURRENT_USER=${SCM_GIT_SHOW_CURRENT_USER:=false} SCM_GIT='git' SCM_GIT_CHAR='±' SCM_GIT_DETACHED_CHAR='⌿' SCM_GIT_AHEAD_CHAR="↑" SCM_GIT_BEHIND_CHAR="↓" SCM_GIT_UNTRACKED_CHAR="?:" SCM_GIT_UNSTAGED_CHAR="U:" SCM_GIT_STAGED_CHAR="S:" SCM_HG='hg' SCM_HG_CHAR='☿' SCM_SVN='svn' SCM_SVN_CHAR='⑆' SCM_NONE='NONE' SCM_NONE_CHAR='○' RVM_THEME_PROMPT_PREFIX=' |' RVM_THEME_PROMPT_SUFFIX='|' VIRTUALENV_THEME_PROMPT_PREFIX=' |' VIRTUALENV_THEME_PROMPT_SUFFIX='|' RBENV_THEME_PROMPT_PREFIX=' |' RBENV_THEME_PROMPT_SUFFIX='|' RBFU_THEME_PROMPT_PREFIX=' |' RBFU_THEME_PROMPT_SUFFIX='|' function scm { if [[ "$SCM_CHECK" = false ]]; then SCM=$SCM_NONE elif [[ -f .git/HEAD ]]; then SCM=$SCM_GIT elif which git &> /dev/null && [[ -n "$(git rev-parse --is-inside-work-tree 2> /dev/null)" ]]; then SCM=$SCM_GIT elif [[ -d .hg ]]; then SCM=$SCM_HG elif which hg &> /dev/null && [[ -n "$(hg root 2> /dev/null)" ]]; then SCM=$SCM_HG elif [[ -d .svn ]]; then SCM=$SCM_SVN else SCM=$SCM_NONE fi } function scm_prompt_char { if [[ -z $SCM ]]; then scm; fi if [[ $SCM == $SCM_GIT ]]; then SCM_CHAR=$SCM_GIT_CHAR elif [[ $SCM == $SCM_HG ]]; then SCM_CHAR=$SCM_HG_CHAR elif [[ $SCM == $SCM_SVN ]]; then SCM_CHAR=$SCM_SVN_CHAR else SCM_CHAR=$SCM_NONE_CHAR fi } function scm_prompt_vars { scm scm_prompt_char SCM_DIRTY=0 SCM_STATE='' [[ $SCM == $SCM_GIT ]] && git_prompt_vars && return [[ $SCM == $SCM_HG ]] && hg_prompt_vars && return [[ $SCM == $SCM_SVN ]] && svn_prompt_vars && return } function scm_prompt_info { scm_prompt_vars if [ $SCM == $SCM_HG ]; then echo -e "$SCM_PREFIX$SCM_BRANCH:${SCM_CHANGE#*:}$SCM_STATE$SCM_SUFFIX" else echo -e "$SCM_PREFIX$SCM_BRANCH$SCM_STATE$SCM_SUFFIX" fi return } function git_status_summary { awk ' BEGIN { untracked=0; unstaged=0; staged=0; } { if (!after_first && $0 ~ /^##.+/) { print $0 seen_header = 1 } else if ($0 ~ /^\?\? .+/) { untracked += 1 } else { if ($0 ~ /^.[^ ] .+/) { unstaged += 1 } if ($0 ~ /^[^ ]. .+/) { staged += 1 } } after_first = 1 } END { if (!seen_header) { print } print untracked "\t" unstaged "\t" staged }' } function git_prompt_vars { local details='' SCM_STATE=${GIT_THEME_PROMPT_CLEAN:-$SCM_THEME_PROMPT_CLEAN} if [[ "$(git config --get bash-it.hide-status)" != "1" ]]; then [[ "${SCM_GIT_IGNORE_UNTRACKED}" = "true" ]] && local git_status_flags='-uno' local status_lines=$((git status --porcelain ${git_status_flags} -b 2> /dev/null || git status --porcelain ${git_status_flags} 2> /dev/null) | git_status_summary) local status=$(awk 'NR==1' <<< "$status_lines") local counts=$(awk 'NR==2' <<< "$status_lines") IFS=$'\t' read untracked_count unstaged_count staged_count <<< "$counts" if [[ "${untracked_count}" -gt 0 || "${unstaged_count}" -gt 0 || "${staged_count}" -gt 0 ]]; then SCM_DIRTY=1 if [[ "${SCM_GIT_SHOW_DETAILS}" = "true" ]]; then [[ "${staged_count}" -gt 0 ]] && details+=" ${SCM_GIT_STAGED_CHAR}${staged_count}" && SCM_DIRTY=3 [[ "${unstaged_count}" -gt 0 ]] && details+=" ${SCM_GIT_UNSTAGED_CHAR}${unstaged_count}" && SCM_DIRTY=2 [[ "${untracked_count}" -gt 0 ]] && details+=" ${SCM_GIT_UNTRACKED_CHAR}${untracked_count}" && SCM_DIRTY=1 fi SCM_STATE=${GIT_THEME_PROMPT_DIRTY:-$SCM_THEME_PROMPT_DIRTY} fi fi [[ "${SCM_GIT_SHOW_CURRENT_USER}" == "true" ]] && details+="$(git_user_info)" SCM_CHANGE=$(git rev-parse --short HEAD 2>/dev/null) local ref=$(git symbolic-ref -q HEAD 2> /dev/null) if [[ -n "$ref" ]]; then SCM_BRANCH=${SCM_THEME_BRANCH_PREFIX}${ref#refs/heads/} local tracking_info="$(grep "${SCM_BRANCH}\.\.\." <<< "${status}")" if [[ -n "${tracking_info}" ]]; then [[ "${tracking_info}" =~ .+\[gone\]$ ]] && local branch_gone="true" tracking_info=${tracking_info#\#\# ${SCM_BRANCH}...} tracking_info=${tracking_info% [*} local remote_name=${tracking_info%%/*} local remote_branch=${tracking_info#${remote_name}/} local remote_info="" local num_remotes=$(git remote | wc -l 2> /dev/null) [[ "${SCM_BRANCH}" = "${remote_branch}" ]] && local same_branch_name=true if ([[ "${SCM_GIT_SHOW_REMOTE_INFO}" = "auto" ]] && [[ "${num_remotes}" -ge 2 ]]) || [[ "${SCM_GIT_SHOW_REMOTE_INFO}" = "true" ]]; then remote_info="${remote_name}" [[ "${same_branch_name}" != "true" ]] && remote_info+="/${remote_branch}" elif [[ ${same_branch_name} != "true" ]]; then remote_info="${remote_branch}" fi if [[ -n "${remote_info}" ]];then if [[ "${branch_gone}" = "true" ]]; then SCM_BRANCH+="${SCM_THEME_BRANCH_GONE_PREFIX}${remote_info}" else SCM_BRANCH+="${SCM_THEME_BRANCH_TRACK_PREFIX}${remote_info}" fi fi fi SCM_GIT_DETACHED="false" else local detached_prefix="" ref=$(git describe --tags --exact-match 2> /dev/null) if [[ -n "$ref" ]]; then detached_prefix=${SCM_THEME_TAG_PREFIX} else ref=$(git describe --contains --all HEAD 2> /dev/null) ref=${ref#remotes/} [[ -z "$ref" ]] && ref=${SCM_CHANGE} detached_prefix=${SCM_THEME_DETACHED_PREFIX} fi SCM_BRANCH=${detached_prefix}${ref} SCM_GIT_DETACHED="true" fi local ahead_re='.+ahead ([0-9]+).+' local behind_re='.+behind ([0-9]+).+' [[ "${status}" =~ ${ahead_re} ]] && SCM_BRANCH+=" ${SCM_GIT_AHEAD_CHAR}${BASH_REMATCH[1]}" [[ "${status}" =~ ${behind_re} ]] && SCM_BRANCH+=" ${SCM_GIT_BEHIND_CHAR}${BASH_REMATCH[1]}" local stash_count="$(git stash list 2> /dev/null | wc -l | tr -d ' ')" [[ "${stash_count}" -gt 0 ]] && SCM_BRANCH+=" {${stash_count}}" SCM_BRANCH+=${details} SCM_PREFIX=${GIT_THEME_PROMPT_PREFIX:-$SCM_THEME_PROMPT_PREFIX} SCM_SUFFIX=${GIT_THEME_PROMPT_SUFFIX:-$SCM_THEME_PROMPT_SUFFIX} } function svn_prompt_vars { if [[ -n $(svn status 2> /dev/null) ]]; then SCM_DIRTY=1 SCM_STATE=${SVN_THEME_PROMPT_DIRTY:-$SCM_THEME_PROMPT_DIRTY} else SCM_DIRTY=0 SCM_STATE=${SVN_THEME_PROMPT_CLEAN:-$SCM_THEME_PROMPT_CLEAN} fi SCM_PREFIX=${SVN_THEME_PROMPT_PREFIX:-$SCM_THEME_PROMPT_PREFIX} SCM_SUFFIX=${SVN_THEME_PROMPT_SUFFIX:-$SCM_THEME_PROMPT_SUFFIX} SCM_BRANCH=$(svn info 2> /dev/null | awk -F/ '/^URL:/ { for (i=0; i<=NF; i++) { if ($i == "branches" || $i == "tags" ) { print $(i+1); break }; if ($i == "trunk") { print $i; break } } }') || return SCM_CHANGE=$(svn info 2> /dev/null | sed -ne 's#^Revision: ##p' ) } # this functions returns absolute location of .hg directory if one exists # It starts in the current directory and moves its way up until it hits /. # If we get to / then no Mercurial repository was found. # Example: # - lets say we cd into ~/Projects/Foo/Bar # - .hg is located in ~/Projects/Foo/.hg # - get_hg_root starts at ~/Projects/Foo/Bar and sees that there is no .hg directory, so then it goes into ~/Projects/Foo function get_hg_root { local CURRENT_DIR=$(pwd) while [ "$CURRENT_DIR" != "/" ]; do if [ -d "$CURRENT_DIR/.hg" ]; then echo "$CURRENT_DIR/.hg" return fi CURRENT_DIR=$(dirname $CURRENT_DIR) done } function hg_prompt_vars { if [[ -n $(hg status 2> /dev/null) ]]; then SCM_DIRTY=1 SCM_STATE=${HG_THEME_PROMPT_DIRTY:-$SCM_THEME_PROMPT_DIRTY} else SCM_DIRTY=0 SCM_STATE=${HG_THEME_PROMPT_CLEAN:-$SCM_THEME_PROMPT_CLEAN} fi SCM_PREFIX=${HG_THEME_PROMPT_PREFIX:-$SCM_THEME_PROMPT_PREFIX} SCM_SUFFIX=${HG_THEME_PROMPT_SUFFIX:-$SCM_THEME_PROMPT_SUFFIX} HG_ROOT=$(get_hg_root) if [ -f "$HG_ROOT/branch" ]; then # Mercurial holds it's current branch in .hg/branch file SCM_BRANCH=$(cat "$HG_ROOT/branch") else SCM_BRANCH=$(hg summary 2> /dev/null | grep branch: | awk '{print $2}') fi if [ -f "$HG_ROOT/dirstate" ]; then # Mercurial holds various information about the working directory in .hg/dirstate file. More on http://mercurial.selenic.com/wiki/DirState SCM_CHANGE=$(hexdump -n 10 -e '1/1 "%02x"' "$HG_ROOT/dirstate" | cut -c-12) else SCM_CHANGE=$(hg summary 2> /dev/null | grep parent: | awk '{print $2}') fi } function rvm_version_prompt { if which rvm &> /dev/null; then rvm=$(rvm-prompt) || return if [ -n "$rvm" ]; then echo -e "$RVM_THEME_PROMPT_PREFIX$rvm$RVM_THEME_PROMPT_SUFFIX" fi fi } function rbenv_version_prompt { if which rbenv &> /dev/null; then rbenv=$(rbenv version-name) || return $(rbenv commands | grep -q gemset) && gemset=$(rbenv gemset active 2> /dev/null) && rbenv="$rbenv@${gemset%% *}" if [ $rbenv != "system" ]; then echo -e "$RBENV_THEME_PROMPT_PREFIX$rbenv$RBENV_THEME_PROMPT_SUFFIX" fi fi } function rbfu_version_prompt { if [[ $RBFU_RUBY_VERSION ]]; then echo -e "${RBFU_THEME_PROMPT_PREFIX}${RBFU_RUBY_VERSION}${RBFU_THEME_PROMPT_SUFFIX}" fi } function chruby_version_prompt { if declare -f -F chruby &> /dev/null; then if declare -f -F chruby_auto &> /dev/null; then chruby_auto fi ruby_version=$(ruby --version | awk '{print $1, $2;}') || return if [[ ! $(chruby | grep '*') ]]; then ruby_version="${ruby_version} (system)" fi echo -e "${CHRUBY_THEME_PROMPT_PREFIX}${ruby_version}${CHRUBY_THEME_PROMPT_SUFFIX}" fi } function ruby_version_prompt { echo -e "$(rbfu_version_prompt)$(rbenv_version_prompt)$(rvm_version_prompt)$(chruby_version_prompt)" } function virtualenv_prompt { if [[ -n "$VIRTUAL_ENV" ]]; then virtualenv=`basename "$VIRTUAL_ENV"` echo -e "$VIRTUALENV_THEME_PROMPT_PREFIX$virtualenv$VIRTUALENV_THEME_PROMPT_SUFFIX" fi } function condaenv_prompt { if [[ $CONDA_DEFAULT_ENV ]]; then echo -e "${CONDAENV_THEME_PROMPT_PREFIX}${CONDA_DEFAULT_ENV}${CONDAENV_THEME_PROMPT_SUFFIX}" fi } function py_interp_prompt { py_version=$(python --version 2>&1 | awk '{print "py-"$2;}') || return echo -e "${PYTHON_THEME_PROMPT_PREFIX}${py_version}${PYTHON_THEME_PROMPT_SUFFIX}" } function python_version_prompt { echo -e "$(virtualenv_prompt)$(condaenv_prompt)$(py_interp_prompt)" } function git_user_info { # support two or more initials, set by 'git pair' plugin SCM_CURRENT_USER=$(git config user.initials | sed 's% %+%') # if `user.initials` weren't set, attempt to extract initials from `user.name` [[ -z "${SCM_CURRENT_USER}" ]] && SCM_CURRENT_USER=$(printf "%s" $(for word in $(git config user.name | tr 'A-Z' 'a-z'); do printf "%1.1s" $word; done)) [[ -n "${SCM_CURRENT_USER}" ]] && printf "%s" "$SCM_THEME_CURRENT_USER_PREFFIX$SCM_CURRENT_USER$SCM_THEME_CURRENT_USER_SUFFIX" } # backwards-compatibility function git_prompt_info { git_prompt_vars echo -e "$SCM_PREFIX$SCM_BRANCH$SCM_STATE$SCM_SUFFIX" } function svn_prompt_info { svn_prompt_vars echo -e "$SCM_PREFIX$SCM_BRANCH$SCM_STATE$SCM_SUFFIX" } function hg_prompt_info() { hg_prompt_vars echo -e "$SCM_PREFIX$SCM_BRANCH:${SCM_CHANGE#*:}$SCM_STATE$SCM_SUFFIX" } function scm_char { scm_prompt_char echo -e "$SCM_CHAR" } function prompt_char { scm_char } function clock_char { echo -e "${THEME_CLOCK_CHAR_COLOR}$CLOCK_CHAR" } function clock_prompt { if [[ "${THEME_SHOW_CLOCK}" = true ]]; then DATE_STRING=$(date +"${THEME_CLOCK_FORMAT}") echo -e "${THEME_CLOCK_COLOR}$DATE_STRING" fi } function battery_char { if [[ "${THEME_BATTERY_PERCENTAGE_CHECK}" = true ]]; then echo -e "${bold_red}$(battery_percentage)%" fi } if [ ! -e $BASH_IT/plugins/enabled/battery.plugin.bash ]; then # if user has installed battery plugin, skip this... function battery_charge (){ # no op echo -n } function battery_char (){ # no op echo -n } fi function aws_profile { if [[ $AWS_DEFAULT_PROFILE ]]; then echo -e "${AWS_DEFAULT_PROFILE}" else echo -e "default" fi } function safe_append_prompt_command { if [[ -n $1 ]] ; then case $PROMPT_COMMAND in *$1*) ;; "") PROMPT_COMMAND="$1";; *) PROMPT_COMMAND="$1;$PROMPT_COMMAND";; esac fi }