################################################################################ # LIQUID PROMPT # An intelligent and non-intrusive prompt for Bash and zsh ################################################################################ # shellcheck shell=bash _LP_VERSION=(2 2 0 rc 2) # Licensed under the AGPL version 3 # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as # published by the Free Software Foundation, either version 3 of the # License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Affero General Public License for more details. # # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . # See the README.md file for a summary of features. # Issue #161: do not load if not an interactive shell # Do not exit if '--no-activate' flag was passed, as it overrides this check # shellcheck disable=SC2268 [ "x${-##*i}" = "x$-" ] || [ -z "${TERM-}" ] || [ "x${TERM-}" = xdumb ] || [ "x${TERM-}" = xunknown ] && [ "x${1-}" != "x--no-activate" ] && return if test -n "${BASH_VERSION-}"; then # Check for recent enough version of bash. if (( ${BASH_VERSINFO[0]:-0} < 3 || ( ${BASH_VERSINFO[0]:-0} == 3 && ${BASH_VERSINFO[1]:-0} < 2 ) )); then echo "liquidprompt: Bash version $BASH_VERSION not supported" >&2 return fi _LP_SHELL_bash=1 _LP_SHELL_zsh=0 _LP_OPEN_ESC="\[" _LP_CLOSE_ESC="\]" _LP_MARK_SYMBOL='$' _LP_FIRST_INDEX=0 _LP_PERCENT='%' # must be escaped on zsh # shellcheck disable=SC1003 _LP_BACKSLASH='\\' # must be escaped on bash # Escape the given strings # Must be used for all strings injected in PS1 that may comes from remote sources, # like $PWD, VCS branch names... __lp_escape() { ret="${1//\\/\\\\}" if shopt -q promptvars ; then ret="${ret//\$/\\\$}" ret="${ret//\`/\\\`}" fi } __lp_strip_escapes() { ret="$1" while [[ "$ret" == *"$_LP_OPEN_ESC"* ]]; do ret="${ret%%"$_LP_OPEN_ESC"*}${ret#*"$_LP_CLOSE_ESC"}" done ret="${ret//\\\\/\\}" if shopt -q promptvars ; then ret="${ret//\\\$/\$}" ret="${ret//\\\`/\`}" fi } elif test -n "${ZSH_VERSION-}" ; then # Check for recent enough version of zsh. if (( ${ZSH_VERSION:0:1} < 5 )); then echo "liquidprompt: Zsh version $ZSH_VERSION not supported" >&2 return fi _LP_SHELL_bash=0 _LP_SHELL_zsh=1 _LP_OPEN_ESC="%{" _LP_CLOSE_ESC="%}" _LP_MARK_SYMBOL='%%' if [[ -o ksh_arrays ]]; then _LP_FIRST_INDEX=0 else _LP_FIRST_INDEX=1 fi _LP_PERCENT='%%' # shellcheck disable=SC1003 _LP_BACKSLASH='\' __lp_escape() { ret="${1//\\/\\\\}" ret="${ret//\%/%%}" if [[ -o promptbang ]]; then ret="${ret//!/!!}" fi if [[ -o promptsubst ]]; then ret="${ret//\$/\\\$}" ret="${ret//\`/\\\`}" fi } __lp_strip_escapes() { # shellcheck disable=SC2296 ret="${(S)1//"${_LP_OPEN_ESC}"*"${_LP_CLOSE_ESC}"}" ret="${ret//\\\\/\\}" ret="${ret//\%\%/%}" if [[ -o promptbang ]]; then ret="${ret//!!/!}" fi if [[ -o promptsubst ]]; then ret="${ret//\\\$/\$}" ret="${ret//\\\`/\`}" fi } else echo "liquidprompt: shell not supported" >&2 return fi __lp_use_bash_preexec() { # If https://github.com/rcaloras/bash-preexec is present, we can (and should, because it interferes with # PROMPT_COMMAND and DEBUG) use the zsh-hook like behavior it provides. [[ "${__bp_imported-}" == "defined" ]] } __lp_array_contains() { local target="$1" shift for element; do if [[ $element == "$target" ]]; then return 0 fi done return 1 } ############### # OS specific # ############### # LP_OS detection, default to Linux case "$(uname)" in FreeBSD) LP_OS=FreeBSD ;; DragonFly) LP_OS=FreeBSD ;; OpenBSD) LP_OS=OpenBSD ;; Darwin) LP_OS=Darwin ;; SunOS) LP_OS=SunOS ;; *) LP_OS=Linux ;; esac _lp_os() { # Fine-grained OS detection: arch, family, kernel, distrib, version. (( LP_ENABLE_OS )) || return 2 # Output variables: lp_os_arch= lp_os_family= lp_os_kernel= lp_os_distrib= lp_os_version= local uname_sm kernel # Get the kernel (and possibly the arch). if (( LP_ENABLE_OS_ARCH )); then # We can get the arch along, with a single subshell. uname_sm="$(uname -s -m)" IFS=' ' read -r kernel lp_os_arch <<<"$uname_sm" else # In any case, we need to know the kernel. kernel="$(uname -s)" fi if (( LP_ENABLE_OS_KERNEL || LP_ENABLE_OS_FAMILY || LP_ENABLE_OS_DISTRIB || LP_ENABLE_OS_VERSION )); then case "$kernel" in FreeBSD) if (( LP_ENABLE_OS_FAMILY )); then lp_os_family=BSD; fi if (( LP_ENABLE_OS_KERNEL )); then lp_os_kernel=FreeBSD; fi ;; DragonFly) if (( LP_ENABLE_OS_FAMILY )); then lp_os_family=BSD; fi if (( LP_ENABLE_OS_KERNEL )); then lp_os_kernel=DragonFly; fi ;; OpenBSD) if (( LP_ENABLE_OS_FAMILY )); then lp_os_family=BSD; fi if (( LP_ENABLE_OS_KERNEL )); then lp_os_kernel=OpenBSD; fi ;; Darwin) if (( LP_ENABLE_OS_FAMILY )); then lp_os_family=BSD; fi if (( LP_ENABLE_OS_KERNEL )); then lp_os_kernel=Darwin; fi ;; SunOS) if (( LP_ENABLE_OS_FAMILY )); then lp_os_family=UNIX; fi if (( LP_ENABLE_OS_KERNEL )); then lp_os_kernel=SunOS; fi ;; CYGWIN*) if (( LP_ENABLE_OS_FAMILY )); then lp_os_family=Windows; fi if (( LP_ENABLE_OS_KERNEL )); then lp_os_kernel=Cygwin; fi ;; MSYS*) if (( LP_ENABLE_OS_FAMILY )); then lp_os_family=Windows; fi if (( LP_ENABLE_OS_KERNEL )); then lp_os_kernel=MSYS; fi ;; MINGW*) if (( LP_ENABLE_OS_FAMILY )); then lp_os_family=Windows; fi if (( LP_ENABLE_OS_KERNEL )); then lp_os_kernel=MinGW; fi ;; Linux) if (( LP_ENABLE_OS_KERNEL )); then lp_os_kernel=Linux; fi if (( LP_ENABLE_OS_FAMILY )); then if [[ "$kernel" == "Linux" ]]; then osfamily="$(uname -o)" if [[ "$osfamily" == "Android" ]]; then lp_os_family=Android return fi # Android fi # Linux lp_os_family=GNU fi # TODO parse HOME_URL and add an hyperlink. if (( LP_ENABLE_OS_DISTRIB && LP_ENABLE_OS_VERSION )); then if _lp_grep_fields "/etc/os-release" "=" "ID" "VERSION_CODENAME"; then lp_os_distrib=${lp_grep_fields[_LP_FIRST_INDEX+0]-} lp_os_version=${lp_grep_fields[_LP_FIRST_INDEX+1]-} fi elif (( LP_ENABLE_OS_DISTRIB )); then if _lp_grep_fields "/etc/os-release" "=" "ID"; then lp_os_distrib=${lp_grep_fields[_LP_FIRST_INDEX+0]-} fi elif (( LP_ENABLE_OS_VERSION )); then if _lp_grep_fields "/etc/os-release" "=" "VERSION_CODENAME"; then lp_os_version=${lp_grep_fields[_LP_FIRST_INDEX+0]-} fi fi ;; esac fi if [[ -z "${lp_os_arch}${lp_os_family}${lp_os_kernel}${lp_os_distrib}${lp_os_version}" ]]; then return 1 fi } _lp_os_color() { # Substitute lp_os_* names with icons/colored strings (from LP_MARK_OS), # or hash colored versions. # Join everything with LP_MARK_OS_SEP. lp_os_color= local lp_os_arch lp_os_family lp_os_kernel lp_os_distrib lp_os_version _lp_os || return "$?" local lp_substitute lp_hash_color lp_join local active active=() for value in \ "$lp_os_arch" "$lp_os_family" "$lp_os_kernel" "$lp_os_distrib" "$lp_os_version"; do if [[ -n "$value" ]]; then if _lp_substitute "$value" "${LP_MARK_OS[@]}"; then active+=("$lp_substitute") elif _lp_hash_color "$value"; then active+=("$lp_hash_color") else active+=("$value") fi fi done if [[ ${#active} -gt 0 ]]; then _lp_join "$LP_MARK_OS_SEP" "${active[@]}" lp_os_color="$lp_join" else return 1 fi } ################# # CONFIGURATION # ################# # Return true if the current Liquid Prompt version is greater than or equal to the specified version. # Return 1 if minor or less version difference, 2 if major difference. _lp_version_greatereq() { # major, minor, [patch], [string], [number] local major="$1" minor="$2" patch="${3-}" string="${4-}" number="${5-}" if (( major > _LP_VERSION[_LP_FIRST_INDEX+0] )); then return 2 elif (( major == _LP_VERSION[_LP_FIRST_INDEX+0] )); then if (( minor > _LP_VERSION[_LP_FIRST_INDEX+1] )); then return 1 elif (( minor == _LP_VERSION[_LP_FIRST_INDEX+1] )) && [[ -n $patch ]]; then if (( patch > _LP_VERSION[_LP_FIRST_INDEX+2] )); then return 1 elif (( patch == _LP_VERSION[_LP_FIRST_INDEX+2] )) && [[ -n $string && -n $number ]]; then if [[ $string == "${_LP_VERSION[_LP_FIRST_INDEX+3]}" ]]; then if (( number > _LP_VERSION[_LP_FIRST_INDEX+4] )); then return 1 fi elif [[ ${_LP_VERSION[_LP_FIRST_INDEX+3]} == "alpha" || \ ( ${_LP_VERSION[_LP_FIRST_INDEX+3]} == "beta" && \ $string == "rc" ) ]]; then return 1 fi fi fi fi return 0 } # Load the user configuration and setup defaults. # shellcheck disable=SC2034 __lp_source_config() { local lp_terminal_format af_color='' ab_color='' # Colors: variables are local so they will have a value only # during config loading and will not conflict with other values # with the same names defined by the user outside the config. local BOLD="${_LP_OPEN_ESC}${_LP_TI_BOLD-}${_LP_CLOSE_ESC}" # Foreground colors __lp_foreground_color 0 local BLACK="${_LP_OPEN_ESC}${af_color}${_LP_CLOSE_ESC}" local BOLD_GRAY="${_LP_OPEN_ESC}${_LP_TI_BOLD-}${af_color}${_LP_CLOSE_ESC}" __lp_foreground_color 1 local RED="${_LP_OPEN_ESC}${af_color}${_LP_CLOSE_ESC}" local BOLD_RED="${_LP_OPEN_ESC}${_LP_TI_BOLD-}${af_color}${_LP_CLOSE_ESC}" __lp_foreground_color 0 __lp_background_color 1 local WARN_RED="${_LP_OPEN_ESC}${af_color}${ab_color}${_LP_CLOSE_ESC}" __lp_foreground_color 7 local CRIT_RED="${_LP_OPEN_ESC}${_LP_TI_BOLD-}${af_color}${ab_color}${_LP_CLOSE_ESC}" __lp_foreground_color 3 local DANGER_RED="${_LP_OPEN_ESC}${_LP_TI_BOLD-}${af_color}${ab_color}${_LP_CLOSE_ESC}" __lp_foreground_color 2 local GREEN="${_LP_OPEN_ESC}${af_color}${_LP_CLOSE_ESC}" local BOLD_GREEN="${_LP_OPEN_ESC}${_LP_TI_BOLD-}${af_color}${_LP_CLOSE_ESC}" __lp_foreground_color 3 local YELLOW="${_LP_OPEN_ESC}${af_color}${_LP_CLOSE_ESC}" local BOLD_YELLOW="${_LP_OPEN_ESC}${_LP_TI_BOLD-}${af_color}${_LP_CLOSE_ESC}" __lp_foreground_color 4 local BLUE="${_LP_OPEN_ESC}${af_color}${_LP_CLOSE_ESC}" local BOLD_BLUE="${_LP_OPEN_ESC}${_LP_TI_BOLD-}${af_color}${_LP_CLOSE_ESC}" __lp_foreground_color 5 local PURPLE="${_LP_OPEN_ESC}${af_color}${_LP_CLOSE_ESC}" local MAGENTA="${PURPLE}" local PINK="${_LP_OPEN_ESC}${_LP_TI_BOLD-}${af_color}${_LP_CLOSE_ESC}" local BOLD_PURPLE="${PINK}" local BOLD_MAGENTA="${PINK}" __lp_foreground_color 6 local CYAN="${_LP_OPEN_ESC}${af_color}${_LP_CLOSE_ESC}" local BOLD_CYAN="${_LP_OPEN_ESC}${_LP_TI_BOLD-}${af_color}${_LP_CLOSE_ESC}" __lp_foreground_color 7 local WHITE="${_LP_OPEN_ESC}${af_color}${_LP_CLOSE_ESC}" local BOLD_WHITE="${_LP_OPEN_ESC}${_LP_TI_BOLD-}${af_color}${_LP_CLOSE_ESC}" # NO_COL is special: it will be used at runtime, not just during config loading NO_COL="${_LP_OPEN_ESC}${_LP_TI_RESET-}${_LP_CLOSE_ESC}" # compute the hash of the hostname and get the corresponding number in # [1-6] (red,green,yellow,blue,purple or cyan) local lp_hostname_hash __lp_hostname_hash __lp_foreground_color "$(( 1 + lp_hostname_hash % 6 ))" LP_COLOR_HOST_HASH="${_LP_OPEN_ESC}${af_color}${_LP_CLOSE_ESC}" # Default values (globals) LP_TIME_FORMAT=${LP_TIME_FORMAT:-"%H:%M:%S"} LP_BATTERY_THRESHOLD=${LP_BATTERY_THRESHOLD:-75} LP_DISK_THRESHOLD=${LP_DISK_THRESHOLD:-102400} # 100 MiB in KiB LP_DISK_THRESHOLD_PERC=${LP_DISK_THRESHOLD_PERC:-10} # 10% or less LP_DISK_PRECISION=${LP_DISK_PRECISION:-2} LP_RAM_THRESHOLD=${LP_RAM_THRESHOLD:-102400} # 100 MiB in KiB LP_RAM_THRESHOLD_PERC=${LP_RAM_THRESHOLD_PERC:-10} # 10% or less LP_RAM_PRECISION=${LP_RAM_PRECISION:-2} LP_LOAD_THRESHOLD=${LP_LOAD_THRESHOLD:-0.60} LP_LOAD_CAP=${LP_LOAD_CAP:-2.0} LP_TEMP_THRESHOLD=${LP_TEMP_THRESHOLD:-60} LP_WIFI_STRENGTH_THRESHOLD=${LP_WIFI_STRENGTH_THRESHOLD:-40} LP_RUNTIME_THRESHOLD=${LP_RUNTIME_THRESHOLD:-2} LP_RUNTIME_BELL_THRESHOLD=${LP_RUNTIME_BELL_THRESHOLD:-10} LP_PATH_LENGTH=${LP_PATH_LENGTH:-35} LP_PATH_KEEP=${LP_PATH_KEEP:-2} LP_PATH_CHARACTER_KEEP=${LP_PATH_CHARACTER_KEEP:-3} LP_PATH_METHOD=${LP_PATH_METHOD:-truncate_chars_from_path_left} LP_PATH_VCS_ROOT=${LP_PATH_VCS_ROOT:-1} LP_HOSTNAME_ALWAYS=${LP_HOSTNAME_ALWAYS:-0} LP_HOSTNAME_METHOD=${LP_HOSTNAME_METHOD:-short} LP_USER_ALWAYS=${LP_USER_ALWAYS:-1} LP_ALWAYS_DISPLAY_VALUES=${LP_ALWAYS_DISPLAY_VALUES:-1} LP_DISPLAY_VALUES_AS_PERCENTS=${LP_DISPLAY_VALUES_AS_PERCENTS:-0} LP_PS1=${LP_PS1:-""} LP_PS1_PREFIX=${LP_PS1_PREFIX:-""} LP_PS1_POSTFIX=${LP_PS1_POSTFIX:-""} LP_DELIMITER_KUBECONTEXT_SUFFIX=${LP_DELIMITER_KUBECONTEXT_SUFFIX:-""} LP_DELIMITER_KUBECONTEXT_PREFIX=${LP_DELIMITER_KUBECONTEXT_PREFIX:-""} LP_ENV_VARS=( ${LP_ENV_VARS[@]+"${LP_ENV_VARS[@]}"} ) LP_ENABLE_DISPLAY=${LP_ENABLE_DISPLAY:-1} LP_ENABLE_CHROOT=${LP_ENABLE_CHROOT:-1} LP_ENABLE_MULTIPLEXER=${LP_ENABLE_MULTIPLEXER:-1} LP_ENABLE_PERM=${LP_ENABLE_PERM:-1} LP_ENABLE_SHORTEN_PATH=${LP_ENABLE_SHORTEN_PATH:-1} LP_ENABLE_PROXY=${LP_ENABLE_PROXY:-1} LP_ENABLE_ENV_VARS=${LP_ENABLE_ENV_VARS:-1} LP_ENABLE_TEMP=${LP_ENABLE_TEMP:-1} LP_ENABLE_JOBS=${LP_ENABLE_JOBS:-1} LP_ENABLE_DETACHED_SESSIONS=${LP_ENABLE_DETACHED_SESSIONS:-1} LP_ENABLE_LOAD=${LP_ENABLE_LOAD:-1} LP_ENABLE_BATT=${LP_ENABLE_BATT:-1} LP_ENABLE_RAM=${LP_ENABLE_RAM:-1} LP_ENABLE_DISK=${LP_ENABLE_DISK:-0} LP_ENABLE_GIT=${LP_ENABLE_GIT:-1} LP_ENABLE_SVN=${LP_ENABLE_SVN:-1} LP_ENABLE_FOSSIL=${LP_ENABLE_FOSSIL:-1} LP_ENABLE_HG=${LP_ENABLE_HG:-1} LP_HG_COMMAND=${LP_HG_COMMAND:-hg} LP_ENABLE_BZR=${LP_ENABLE_BZR:-1} LP_ENABLE_VCS_REMOTE=${LP_ENABLE_VCS_REMOTE:-0} LP_ENABLE_TIME=${LP_ENABLE_TIME:-0} LP_TIME_ANALOG=${LP_TIME_ANALOG:-0} LP_ENABLE_RUNTIME=${LP_ENABLE_RUNTIME:-1} LP_ENABLE_RUNTIME_BELL=${LP_ENABLE_RUNTIME_BELL:-0} LP_ENABLE_VIRTUALENV=${LP_ENABLE_VIRTUALENV:-1} LP_ENABLE_NODE_VENV=${LP_ENABLE_NODE_VENV:-0} LP_ENABLE_RUBY_VENV=${LP_ENABLE_RUBY_VENV:-1} LP_RUBY_RVM_PROMPT_OPTIONS=( ${LP_RUBY_RVM_PROMPT_OPTIONS[@]+"${LP_RUBY_RVM_PROMPT_OPTIONS[@]}"} ) [[ ${#LP_RUBY_RVM_PROMPT_OPTIONS[@]} == 0 ]] && LP_RUBY_RVM_PROMPT_OPTIONS=(i v g s) LP_ENABLE_PERL_VENV=${LP_ENABLE_PERL_VENV:-1} LP_ENABLE_TERRAFORM=${LP_ENABLE_TERRAFORM:-0} LP_ENABLE_CONTAINER=${LP_ENABLE_CONTAINER:-0} LP_ENABLE_SCLS=${LP_ENABLE_SCLS:-1} LP_ENABLE_AWS_PROFILE=${LP_ENABLE_AWS_PROFILE:-1} LP_ENABLE_MODULES=${LP_ENABLE_MODULES:-1} LP_ENABLE_MODULES_VERSIONS=${LP_ENABLE_MODULES_VERSIONS:-1} LP_ENABLE_MODULES_HASHCOLOR=${LP_ENABLE_MODULES_HASHCOLOR:-0} LP_ENABLE_VCS_ROOT=${LP_ENABLE_VCS_ROOT:-0} LP_ENABLE_TITLE=${LP_ENABLE_TITLE:-0} LP_ENABLE_SCREEN_TITLE=${LP_ENABLE_SCREEN_TITLE:-0} LP_ENABLE_TITLE_COMMAND=${LP_ENABLE_TITLE_COMMAND:-1} LP_ENABLE_TMUX_TITLE_PANES=${LP_ENABLE_TMUX_TITLE_PANES:-1} LP_ENABLE_SSH_COLORS=${LP_ENABLE_SSH_COLORS:-0} LP_DISABLED_VCS_PATHS=( ${LP_DISABLED_VCS_PATHS[@]+"${LP_DISABLED_VCS_PATHS[@]}"} ) LP_TEMP_SYSFS_IGNORE_FILES=( ${LP_TEMP_SYSFS_IGNORE_FILES[@]+"${LP_TEMP_SYSFS_IGNORE_FILES[@]}"} ) LP_ENABLE_SUDO=${LP_ENABLE_SUDO:-0} LP_ENABLE_COLOR=${LP_ENABLE_COLOR:-1} LP_ENABLE_ERROR=${LP_ENABLE_ERROR:-1} LP_ENABLE_ERROR_MEANING=${LP_ENABLE_ERROR_MEANING:-0} LP_ENABLE_ERROR_MEANING_EXTENDED=${LP_ENABLE_ERROR_MEANING_EXTENDED:-0} LP_ENABLE_DIRSTACK=${LP_ENABLE_DIRSTACK:-0} LP_ENABLE_KUBECONTEXT=${LP_ENABLE_KUBECONTEXT:-0} LP_ENABLE_KUBE_NAMESPACE=${LP_ENABLE_KUBE_NAMESPACE:-0} LP_ENABLE_CMAKE=${LP_ENABLE_CMAKE:-0} LP_ENABLE_SHLVL=${LP_ENABLE_SHLVL:-1} LP_ENABLE_WIFI_STRENGTH=${LP_ENABLE_WIFI_STRENGTH:-0} LP_ENABLE_OS=${LP_ENABLE_OS:-0} LP_ENABLE_OS_ARCH=${LP_ENABLE_OS_ARCH:-0} LP_ENABLE_OS_FAMILY=${LP_ENABLE_OS_FAMILY:-0} LP_ENABLE_OS_KERNEL=${LP_ENABLE_OS_KERNEL:-1} LP_ENABLE_OS_DISTRIB=${LP_ENABLE_OS_DISTRIB:-0} LP_ENABLE_OS_VERSION=${LP_ENABLE_OS_VERSION:-1} LP_ENABLE_HYPERLINKS=${LP_ENABLE_HYPERLINKS:-0} LP_ENABLE_PATH=${LP_ENABLE_PATH:-1} LP_MARK_DEFAULT="${LP_MARK_DEFAULT:-$_LP_MARK_SYMBOL}" LP_MARK_BATTERY="${LP_MARK_BATTERY:-"⌁"}" LP_MARK_ADAPTER="${LP_MARK_ADAPTER:-"⏚"}" LP_MARK_LOAD="${LP_MARK_LOAD:-"⌂"}" LP_MARK_TEMP="${LP_MARK_TEMP:-"θ"}" LP_MARK_PROXY="${LP_MARK_PROXY:-"↥"}" LP_MARK_ENV_VARS_OPEN="${LP_MARK_ENV_VARS_OPEN:-"("}" LP_MARK_ENV_VARS_SEP="${LP_MARK_ENV_VARS_SEP:-" "}" LP_MARK_ENV_VARS_CLOSE="${LP_MARK_ENV_VARS_CLOSE:-")"}" LP_MARK_HG="${LP_MARK_HG:-"☿"}" LP_MARK_SVN="${LP_MARK_SVN:-"‡"}" LP_MARK_GIT="${LP_MARK_GIT:-"±"}" LP_MARK_VCSH="${LP_MARK_VCSH:-"|"}" LP_MARK_FOSSIL="${LP_MARK_FOSSIL:-"⌘"}" LP_MARK_BZR="${LP_MARK_BZR:-"⚯"}" LP_MARK_DISABLED="${LP_MARK_DISABLED:-"⌀"}" LP_MARK_UNTRACKED="${LP_MARK_UNTRACKED:-"*"}" LP_MARK_STASH="${LP_MARK_STASH:-"+"}" LP_MARK_VCS_REMOTE="${LP_MARK_VCS_REMOTE:-"⭚"}" LP_MARK_BRACKET_OPEN="${LP_MARK_BRACKET_OPEN:-"["}" LP_MARK_BRACKET_CLOSE="${LP_MARK_BRACKET_CLOSE:-"]"}" LP_MARK_MULTIPLEXER_OPEN="${LP_MARK_MULTIPLEXER_OPEN:-"$LP_MARK_BRACKET_OPEN"}" LP_MARK_MULTIPLEXER_CLOSE="${LP_MARK_MULTIPLEXER_CLOSE:-"$LP_MARK_BRACKET_CLOSE"}" LP_MARK_SHORTEN_PATH="${LP_MARK_SHORTEN_PATH:-" … "}" LP_MARK_PREFIX="${LP_MARK_PREFIX:-" "}" LP_MARK_PERM="${LP_MARK_PERM:-":"}" LP_MARK_DIRSTACK="${LP_MARK_DIRSTACK:-"⚞"}" LP_MARK_SHLVL="${LP_MARK_SHLVL:-"└"}" LP_MARK_WIFI="${LP_MARK_WIFI:-"📶"}" LP_MARK_DEV_OPEN="${LP_MARK_DEV_OPEN:-"<"}" LP_MARK_DEV_CLOSE="${LP_MARK_DEV_CLOSE:-">"}" LP_MARK_DEV_MID="${LP_MARK_DEV_MID:-"|"}" LP_MARK_MODULES_OPEN="${LP_MARK_MODULES_OPEN:-""}" LP_MARK_MODULES_SEP="${LP_MARK_MODULES_SEP:-":"}" LP_MARK_MODULES_CLOSE="${LP_MARK_MODULES_CLOSE:-""}" LP_MARK_CMAKE="${LP_MARK_CMAKE:-":"}" LP_MARK_KUBECONTEXT=${LP_MARK_KUBECONTEXT:-"⎈"} LP_MARK_JOBS_SEPARATOR="${LP_MARK_JOBS_SEPARATOR:-"/"}" LP_MARK_OS_SEP=${LP_MARK_OS_SEP:-"/"} LP_MARK_OS=( ${LP_MARK_OS[@]+"${LP_MARK_OS[@]}"} ) LP_MARK_DISK="${LP_MARK_DISK:-"🖴 "}" LP_MARK_RAM="${LP_MARK_RAM:-"M"}" LP_COLOR_CMAKE_DEBUG=${LP_COLOR_CMAKE_DEBUG:-$MAGENTA} LP_COLOR_CMAKE_RWDI=${LP_COLOR_CMAKE_RWDI:-$BLUE} LP_COLOR_CMAKE_RELEASE=${LP_COLOR_CMAKE_RELEASE:-$CYAN} LP_COLOR_PATH=${LP_COLOR_PATH:-$NO_COL} lp_terminal_format 8 -1 0 0 -1 LP_COLOR_PATH_SEPARATOR=${LP_COLOR_PATH_SEPARATOR:-$lp_terminal_format} LP_COLOR_PATH_SHORTENED=${LP_COLOR_PATH_SHORTENED:-$lp_terminal_format} lp_terminal_format -1 -1 1 0 LP_COLOR_PATH_VCS_ROOT=${LP_COLOR_PATH_VCS_ROOT:-$lp_terminal_format} LP_COLOR_PATH_LAST_DIR=${LP_COLOR_PATH_LAST_DIR:-$lp_terminal_format} LP_COLOR_PATH_ROOT=${LP_COLOR_PATH_ROOT:-$BOLD_YELLOW} LP_COLOR_PROXY=${LP_COLOR_PROXY:-$BOLD_BLUE} LP_COLOR_ENV_VARS_UNSET=${LP_COLOR_ENV_VARS_UNSET:-$BLUE} LP_COLOR_ENV_VARS_SET=${LP_COLOR_ENV_VARS_SET:-$BOLD_BLUE} LP_COLOR_JOB_D=${LP_COLOR_JOB_D:-$YELLOW} LP_COLOR_JOB_R=${LP_COLOR_JOB_R:-$BOLD_YELLOW} LP_COLOR_JOB_Z=${LP_COLOR_JOB_Z:-$BOLD_YELLOW} LP_COLOR_ERR=${LP_COLOR_ERR:-$PURPLE} LP_COLOR_ERR_MEANING=${LP_COLOR_ERR_MEANING:-$LP_COLOR_ERR} LP_COLOR_MARK=${LP_COLOR_MARK:-$BOLD} LP_COLOR_MARK_ROOT=${LP_COLOR_MARK_ROOT:-$BOLD_RED} LP_COLOR_MARK_SUDO=${LP_COLOR_MARK_SUDO:-$LP_COLOR_MARK_ROOT} LP_COLOR_USER_LOGGED=${LP_COLOR_USER_LOGGED:-""} LP_COLOR_USER_ALT=${LP_COLOR_USER_ALT:-$BOLD} LP_COLOR_USER_ROOT=${LP_COLOR_USER_ROOT:-$BOLD_YELLOW} LP_COLOR_HOST=${LP_COLOR_HOST:-""} LP_COLOR_SSH=${LP_COLOR_SSH:-$BLUE} LP_COLOR_SU=${LP_COLOR_SU:-$BOLD_YELLOW} LP_COLOR_TELNET=${LP_COLOR_TELNET:-$WARN_RED} LP_COLOR_X11_ON=${LP_COLOR_X11_ON:-$GREEN} LP_COLOR_X11_OFF=${LP_COLOR_X11_OFF:-$YELLOW} LP_COLOR_WRITE=${LP_COLOR_WRITE:-$GREEN} LP_COLOR_NOWRITE=${LP_COLOR_NOWRITE:-$RED} LP_COLOR_UP=${LP_COLOR_UP:-$GREEN} LP_COLOR_COMMITS=${LP_COLOR_COMMITS:-$YELLOW} LP_COLOR_COMMITS_BEHIND=${LP_COLOR_COMMITS_BEHIND:-$BOLD_RED} LP_COLOR_CHANGES=${LP_COLOR_CHANGES:-$RED} LP_COLOR_DIFF=${LP_COLOR_DIFF:-$PURPLE} LP_COLOR_CHARGING_ABOVE=${LP_COLOR_CHARGING_ABOVE:-$GREEN} LP_COLOR_CHARGING_UNDER=${LP_COLOR_CHARGING_UNDER:-$YELLOW} LP_COLOR_DISCHARGING_ABOVE=${LP_COLOR_DISCHARGING_ABOVE:-$YELLOW} LP_COLOR_DISCHARGING_UNDER=${LP_COLOR_DISCHARGING_UNDER:-$RED} LP_COLOR_TIME=${LP_COLOR_TIME:-$BLUE} LP_COLOR_IN_MULTIPLEXER=${LP_COLOR_IN_MULTIPLEXER:-$BOLD_BLUE} LP_COLOR_RUNTIME=${LP_COLOR_RUNTIME:-$YELLOW} LP_COLOR_VIRTUALENV=${LP_COLOR_VIRTUALENV:-$CYAN} LP_COLOR_NODE_VENV=${LP_COLOR_NODE_VENV:-$LP_COLOR_VIRTUALENV} LP_COLOR_RUBY_VENV=${LP_COLOR_RUBY_VENV:-$LP_COLOR_VIRTUALENV} LP_COLOR_PERL_VENV=${LP_COLOR_PERL_VENV:-$LP_COLOR_VIRTUALENV} LP_COLOR_TERRAFORM=${LP_COLOR_TERRAFORM:-$PINK} LP_COLOR_CONTAINER=${LP_COLOR_CONTAINER:-$BOLD_BLUE} LP_COLOR_DIRSTACK=${LP_COLOR_DIRSTACK:-$BOLD_YELLOW} LP_COLOR_KUBECONTEXT=${LP_COLOR_KUBECONTEXT:-$CYAN} LP_COLOR_AWS_PROFILE=${LP_COLOR_AWS_PROFILE:-$YELLOW} LP_COLOR_MODULES=${LP_COLOR_MODULES:-$BLUE} LP_COLOR_SHLVL=${LP_COLOR_SHLVL:-$BOLD_GREEN} LP_COLOR_DISK=${LP_COLOR_DISK:-$BOLD_RED} LP_COLOR_DISK_UNITS=${LP_COLOR_DISK_UNITS:-$RED} LP_COLOR_RAM=${LP_COLOR_RAM:-$BOLD_RED} LP_COLOR_RAM_UNITS=${LP_COLOR_RAM_UNITS:-$RED} LP_COLORMAP=( ${LP_COLORMAP[@]+"${LP_COLORMAP[@]}"} ) if [[ ${#LP_COLORMAP[@]} == 0 ]]; then LP_COLORMAP=( "" # 0 "$GREEN" # 1 "$BOLD_GREEN" # 2 "$YELLOW" # 3 "$BOLD_YELLOW" # 4 "$RED" # 5 "$BOLD_RED" # 6 "$WARN_RED" # 7 "$CRIT_RED" # 8 "$DANGER_RED" # 9 ) fi # For mocking tests. _LP_LINUX_POWERSUPPLY_PATH="/sys/class/power_supply" _LP_LINUX_WIRELESS_FILE="/proc/net/wireless" _LP_AIRPORT_BIN="/System/Library/PrivateFrameworks/Apple80211.framework/Versions/Current/Resources/airport" _LP_LINUX_RAM_FILE="/proc/meminfo" _LP_BSD_RAM_FILE="/var/run/dmesg.boot" if (( _LP_SHELL_zsh )); then setopt local_options nullglob fi _LP_LINUX_TEMPERATURE_FILES=( /sys/class/hwmon/hwmon*/temp*_input # CentOS has an intermediate /device directory: /sys/class/hwmon/hwmon*/device/temp*_input /sys/devices/platform/coretemp.*/hwmon/hwmon*/temp*_input # Older, fallback option /sys/class/thermal/thermal_zone*/temp ) # Debugging flags LP_DEBUG_TIME=${LP_DEBUG_TIME:-0} if [[ ${1-} == --no-config ]]; then shift else # Default config file may be the XDG standard ~/.config/liquidpromptrc, # but heirloom dotfile has priority. local -a configfiles configfiles=("$HOME/.liquidpromptrc" "${XDG_CONFIG_HOME:-"$HOME/.config"}/liquidpromptrc") # trailing ":" is so that ${search#*:} always removes something local configfile search="${XDG_CONFIG_DIRS:-/etc/xdg}:" while [[ -n "$search" ]]; do configfiles+=("${search%%:*}/liquidpromptrc") search="${search#*:}" done configfiles+=("/etc/liquidpromptrc") for configfile in "${configfiles[@]}"; do if [[ -f "$configfile" ]]; then # shellcheck source=/dev/null source "$configfile" break fi done fi for file in "$@"; do # shellcheck disable=SC1090 . "$file" done # Deprecations and compatibility shims if [[ -n "${LP_DISABLED_VCS_PATH-}" ]]; then echo "liquidprompt: LP_DISABLED_VCS_PATH is deprecated. Update your config to use LP_DISABLED_VCS_PATHS array." >&2 (( _LP_SHELL_zsh )) && setopt local_options && setopt sh_word_split local _path IFS=: for _path in $LP_DISABLED_VCS_PATH; do LP_DISABLED_VCS_PATHS+=("$_path") done fi # FIXME Delete this code in version 1.11 if [[ -n "${LP_COLORMAP_1-}" ]]; then echo "liquidprompt: LP_COLORMAP_x variables are deprecated. Update your theme to use LP_COLORMAP array." >&2 LP_COLORMAP=( "$LP_COLORMAP_0" "$LP_COLORMAP_1" "$LP_COLORMAP_2" "$LP_COLORMAP_3" "$LP_COLORMAP_4" "$LP_COLORMAP_5" "$LP_COLORMAP_6" "$LP_COLORMAP_7" "$LP_COLORMAP_8" "$LP_COLORMAP_9" ) unset LP_COLORMAP_0 LP_COLORMAP_1 LP_COLORMAP_2 LP_COLORMAP_3 LP_COLORMAP_4 \ LP_COLORMAP_5 LP_COLORMAP_6 LP_COLORMAP_7 LP_COLORMAP_8 LP_COLORMAP_9 fi if [[ -n ${LP_PATH_DEFAULT-} ]]; then echo "liquidprompt: LP_PATH_DEFAULT is deprecated. Update your config to set LP_PATH_METHOD." >&2 if (( ! LP_ENABLE_SHORTEN_PATH )); then # There is just no elegant way to handle this. Fallback to the old way with basic formatting support. _lp_path_format() { lp_path=$LP_PATH_DEFAULT lp_path_format="${LP_COLOR_PATH}${lp_path}${NO_COL}" } fi fi if [[ -n ${PROMPT_DIRTRIM-} ]] && (( ! LP_ENABLE_SHORTEN_PATH )); then echo "liquidprompt: PROMPT_DIRTRIM support is deprecated. Update your config to set LP_PATH_METHOD='truncate_chars_from_path_left' instead." >&2 # This does mostly the same thing, but with our formatting. LP_ENABLE_SHORTEN_PATH=1 LP_PATH_METHOD="truncate_chars_from_path_left" LP_PATH_KEEP=1 fi if [[ $LP_PATH_KEEP == "-1" ]]; then echo "liquidprompt: LP_PATH_KEEP set to '-1' is deprecated. Update your config to set LP_PATH_METHOD='truncate_to_last_dir' instead." >&2 LP_PATH_METHOD="truncate_to_last_dir" fi if [[ ${LP_ENABLE_FQDN-} == 1 ]]; then echo "liquidprompt: LP_ENABLE_FQDN is deprecated. Update your config to set LP_HOSTNAME_METHOD=full or LP_HOSTNAME_METHOD=fqdn instead." >&2 LP_HOSTNAME_METHOD="full" fi if [[ -n ${LP_PERCENTS_ALWAYS-} ]]; then echo "liquidprompt: LP_PERCENTS_ALWAYS is deprecated. Update your config to set LP_ALWAYS_DISPLAY_VALUES and LP_DISPLAY_VALUES_AS_PERCENTS instead." >&2 LP_ALWAYS_DISPLAY_VALUES=${LP_PERCENTS_ALWAYS} LP_DISPLAY_VALUES_AS_PERCENTS=${LP_PERCENTS_ALWAYS} fi } # Initialize features based on the user config. # shellcheck disable=SC2120 lp_activate() { if (( _LP_SHELL_bash )); then complete -F __lp_theme_bash_complete lp_theme else # zsh # For ZSH, autoload required functions autoload -Uz add-zsh-hook # Enable the autocomplete if the autocomplete system is initialized. __lp_is_function compdef && compdef __lp_theme_zsh_complete lp_theme fi # Disable hooks that we don't need if features will be disabled. __lp_disable_hooks # TermInfo feature detection _lp_af_colors=() _lp_ab_colors=() __lp_foreground_color() { return 2 ; } __lp_background_color() { return 2 ; } # TODO handle this case better. With no colors, no need for any escaping if ! command -v tput >/dev/null; then echo "liquidprompt: 'tput' not available; will not be able to format terminal" >&2 LP_ENABLE_COLOR=0 else _LP_TI_RESET="$( { tput sgr0 || tput me ; } 2>/dev/null )" _LP_TI_BOLD="$( { tput bold || tput md ; } 2>/dev/null )" _LP_TI_UNDERLINE="$( { tput smul || tput us ; } 2>/dev/null )" _LP_TI_COLORS="$( tput colors 2>/dev/null )" _LP_TI_COLORS=${_LP_TI_COLORS:-8} _LP_TI_BELL="$( { tput bel || tput bl ; } 2>/dev/null )" if tput setaf 0 >/dev/null 2>&1; then __lp_foreground_color() { af_color="${_lp_af_colors[$1+1]:=$(tput setaf "$1")}"; } elif tput AF 0 >/dev/null 2>&1; then # FreeBSD __lp_foreground_color() { af_color="${_lp_af_colors[$1+1]:=$(tput AF "$1")}"; } elif tput AF 0 0 0 >/dev/null 2>&1; then # OpenBSD __lp_foreground_color() { af_color="${_lp_af_colors[$1+1]:=$(tput AF "$1" 0 0)}"; } else echo "liquidprompt: terminal '${TERM-}' does not support foreground colors" >&2 fi if tput setab 0 >/dev/null 2>&1; then __lp_background_color() { ab_color="${_lp_ab_colors[$1+1]:=$(tput setab "$1")}"; } elif tput AB 0 >/dev/null 2>&1; then # FreeBSD __lp_background_color() { ab_color="${_lp_ab_colors[$1+1]:=$(tput AB "$1")}"; } elif tput AB 0 0 0 >/dev/null 2>&1; then # OpenBSD __lp_background_color() { ab_color="${_lp_ab_colors[$1+1]:=$(tput AB "$1" 0 0)}"; } else echo "liquidprompt: terminal '${TERM-}' does not support background colors" >&2 fi fi # If tput doesn't exist or lookup failed, still try to send bell _LP_TI_BELL=${_LP_TI_BELL:-$'\a'} __lp_source_config "$@" # Disable feature if the tool is not installed _lp_require_tool() { # zsh does not allow quoting here. # shellcheck disable=SC1105,SC2086 (( LP_ENABLE_$1 )) && { command -v "$2" >/dev/null || eval "LP_ENABLE_$1=0" ; } } _lp_require_tool TIME date _lp_require_tool GIT git _lp_require_tool SVN svn _lp_require_tool FOSSIL fossil _lp_require_tool HG "$LP_HG_COMMAND" _lp_require_tool BZR bzr _lp_require_tool CMAKE cmake _lp_require_tool DISK df _LP_ENABLED_VCSS=() (( LP_ENABLE_GIT )) && _LP_ENABLED_VCSS+=(git) (( LP_ENABLE_SVN )) && _LP_ENABLED_VCSS+=(svn) (( LP_ENABLE_HG )) && _LP_ENABLED_VCSS+=(hg) (( LP_ENABLE_BZR )) && _LP_ENABLED_VCSS+=(bzr) if [[ "$LP_OS" = Darwin ]]; then _lp_require_tool BATT pmset _lp_require_tool RAM vm_stat elif (( LP_ENABLE_BATT )); then __lp_battery_detect || LP_ENABLE_BATT=0 fi _lp_require_tool KUBECONTEXT kubectl _lp_require_tool TERRAFORM terraform unset -f _lp_require_tool _LP_ENABLE_PREEXEC=$(( LP_ENABLE_RUNTIME \ || LP_ENABLE_RUNTIME_BELL \ || LP_ENABLE_TITLE_COMMAND \ )) # LP_ENABLE_RUBY_VENV depends either from rvm or rbenv. Thus we cannot # directly use _lp_require_tool for it. # Also, to avoid to check twice which is the current ruby virtual env # program in use, we set here an internal variable holding its name if we # detect one of them. if (( LP_ENABLE_RUBY_VENV )) ; then if command -v rvm-prompt >/dev/null ; then _LP_RUBY_VENV_PROGRAM=rvm elif command -v rbenv >/dev/null ; then _LP_RUBY_VENV_PROGRAM=rbenv else LP_ENABLE_RUBY_VENV=0 fi fi if (( LP_ENABLE_PERL_VENV )) ; then if command -v perlbrew >/dev/null ; then _LP_PERL_VENV_PROGRAM=perlbrew elif command -v plenv >/dev/null ; then _LP_PERL_VENV_PROGRAM=plenv else LP_ENABLE_PERL_VENV=0 fi fi if (( LP_ENABLE_DETACHED_SESSIONS )); then command -v screen >/dev/null ; _LP_ENABLE_SCREEN=$(( ! $? )) command -v tmux >/dev/null ; _LP_ENABLE_TMUX=$(( ! $? )) fi # Use standard path symbols inside Midnight Commander [[ -n "${MC_SID-}" ]] && LP_ENABLE_SHORTEN_PATH=0 # If we are running in a terminal multiplexer, special title escapes if _lp_multiplexer --internal; then (( LP_ENABLE_TITLE = LP_ENABLE_TITLE && LP_ENABLE_SCREEN_TITLE )) if (( LP_ENABLE_TMUX_TITLE_PANES )) && [[ $lp_multiplexer == tmux ]]; then LP_TITLE_OPEN=$'\E]2' else LP_TITLE_OPEN=$'\Ek' fi LP_TITLE_CLOSE=$'\E\\' else LP_TITLE_OPEN=$'\E]0;' LP_TITLE_CLOSE=$'\a' fi [[ "_${TERM-}" == _linux* ]] && LP_ENABLE_TITLE=0 # Can not show title command if the title feature is disabled. (( LP_ENABLE_TITLE )) || LP_ENABLE_TITLE_COMMAND=0 # update_terminal_cwd is a shell function available on MacOS X Lion that # will update an icon of the directory displayed in the title of the terminal # window. # See http://hints.macworld.com/article.php?story=20110722211753852 if [[ "${TERM_PROGRAM-}" == Apple_Terminal ]] && command -v update_terminal_cwd >/dev/null; then _LP_TERM_UPDATE_DIR=update_terminal_cwd # Remove "update_terminal_cwd; " that has been add by Apple in /et/bashrc. # See issue #196 PROMPT_COMMAND="${PROMPT_COMMAND//update_terminal_cwd; /}" else _LP_TERM_UPDATE_DIR=: fi ############### # Who are we? # ############### _lp_user local -i user="$?" if (( user < 2 )); then # if user is not root # "sudo -n" is only supported from sudo 1.7.0 if (( LP_ENABLE_SUDO )); then if command -v sudo >/dev/null \ && LC_MESSAGES=C sudo -V \ | GREP_OPTIONS='' \grep -qE '^Sudo version (1(\.([789]\.|[1-9][0-9])|[0-9])|[2-9])'; then # If user can run any command without password input. # This command does not invalidate credentials. if \sudo -nvk 2>/dev/null; then _LP_SUDO_NOPASSWORD=1 else _LP_SUDO_NOPASSWORD=0 fi else LP_ENABLE_SUDO=0 fi fi if (( user == 1 )); then LP_COLOR_USER=$LP_COLOR_USER_ALT else LP_COLOR_USER=$LP_COLOR_USER_LOGGED fi else # root! LP_ENABLE_SUDO=0 if (( ! LP_ENABLE_VCS_ROOT )); then LP_DISABLED_VCS_PATHS=("/") fi LP_MARK_DEFAULT='#' LP_COLOR_MARK=$LP_COLOR_MARK_ROOT LP_COLOR_PATH=$LP_COLOR_PATH_ROOT LP_COLOR_USER=$LP_COLOR_USER_ROOT fi ################# # Where are we? # ################# if (( LP_ENABLE_TEMP )); then # Try each _lp_temp method # If no function worked, disable the feature __lp_temp_detect || LP_ENABLE_TEMP=0 fi if (( LP_ENABLE_LOAD )); then # Find and save the number of CPUs __lp_cpu_count # Convert load config values into usable integers. local ret # if load threshold does not have a period in it. if [[ -z ${LP_LOAD_THRESHOLD//[!\.]} ]]; then # This is an old value, no need to convert _LP_LOAD_THRESHOLD=$LP_LOAD_THRESHOLD else __lp_floating_scale "$LP_LOAD_THRESHOLD" 100 _LP_LOAD_THRESHOLD=$ret fi __lp_floating_scale "$LP_LOAD_CAP" 100 _LP_LOAD_CAP=$ret fi if [[ -n ${_LP_THEME_ACTIVATE_FUNCTION-} ]]; then # Reactivate current theme "$_LP_THEME_ACTIVATE_FUNCTION" prompt_on else # Set default theme if no theme set lp_theme default fi } ##################### # Utility Functions # ##################### # Remove all colors and escape characters of the given string and return a pure text # Deprecated since v2.1. _lp_as_text() { # Remove all terminal sequences that we wrapped with $_LP_OPEN_ESC and # $_LP_CLOSE_ESC. local ret __lp_strip_escapes "$1" printf '%s' "$ret" } # Store $2 (or $?) as a true/false value in variable named $1 # Deprecated since v2.0. _lp_bool() { local res="${2:-$?}" if (( res )); then eval "$1=false" else eval "$1=true" fi return "$res" } _lp_color_map() { # value [scale=100] # Default scale: 0..100 # Custom scale: 0..$2 local -i scale value scale=${2:-100} if (( $1 >= scale )); then (( value = scale - 1 )) elif (( $1 < 0 )); then value=0 else value=$1 fi # Transform the value to a 0..${#COLOR_MAP} scale ret="${LP_COLORMAP[_LP_FIRST_INDEX+value*${#LP_COLORMAP[*]}/scale]}" } # Generate a terminal hyperlink from a URL and text. _lp_create_link() { # url, text (( LP_ENABLE_HYPERLINKS )) || return 2 lp_link="$_LP_OPEN_ESC"$'\E]8;;'"${1}"$'\E'"${_LP_BACKSLASH}${_LP_CLOSE_ESC}${2}${_LP_OPEN_ESC}"$'\E]8;;\E'"${_LP_BACKSLASH}$_LP_CLOSE_ESC" } # Add a link to a path element, with protocol depending on the connection. _lp_create_link_path() { # path local _path="$1" if (( LP_ENABLE_HYPERLINKS )); then _lp_connection if [[ "$lp_connection" == "ssh" ]]; then # SSH connection needs the `sftp` protocol to reach the remote host. local client_ip client_port server_ip server_port IFS=" " read -r client_ip client_port server_ip server_port <<<"$SSH_CONNECTION" local username=${USER:-${USERNAME:-${LOGNAME-}}} _lp_create_link "sftp://${username}@${server_ip}:${client_port}/${PWD}/" "$_path" lp_link_path="$lp_link" elif [[ "$lp_connection" == "su" || "$lp_connection" == "lcl" ]]; then # Reach local host with the `file` protocol. _lp_create_link "file://${PWD}/" "$_path" lp_link_path="$lp_link" else # no link for telnet. lp_link_path="$_path" fi else # No link. lp_link_path="$_path" fi } # Return true if the input is the name of a function. __lp_is_function() { if (( _LP_SHELL_bash )); then [[ $(LC_ALL=C \type -t "$1") == function ]] else [[ $(LC_ALL=C \type -w "$1") == *function ]] fi } # Count the number of lines in the input string. A faster substitute for 'wc -l'. __lp_line_count() { local var="${1//[!$'\n']}" count=${#var} } __lp_hostname_hash() { # cksum is separated with tab on SunOS, space on others # Newline is required to keep the same color as before. local cksum="$(printf '%s\n' "${HOSTNAME:-${HOST-}}" | cksum)" lp_hostname_hash=${cksum%%[$' \t']*} } __lp_floating_scale() { local integer decimal=0 scale=$(( ${#2} - 1 )) integer=${1%\.*} if [[ -n ${1//[!\.]} ]]; then decimal=${1#*\.} decimal=${decimal:0:$scale} while (( ${#decimal} < scale )); do decimal+='0' done while [[ ${decimal:0:1} == '0' ]]; do decimal=${decimal:1} done fi ret=$(( integer * $2 + decimal )) } # Return $PWD with $HOME at the start replaced by "~". __lp_pwd_tilde() { # [path] # Needs to be in a variable, as different versions of Bash treat '~' in a # substitution differently local _path="${1:-$PWD}" tilde="~" lp_pwd_tilde="${_path/#$HOME/$tilde}" } # insert a space on the right # Deprecated since v2.0. _lp_sr() { [[ -n "$1" ]] && echo -nE "$1 " } # insert a space on the left # Deprecated since v2.0. _lp_sl() { [[ -n "$1" ]] && echo -nE " $1" } # insert two spaces, before and after # Deprecated since v2.0. _lp_sb() { [[ -n "$1" ]] && echo -nE " $1 " } # Generates a terminal escape sequence to format the terminal. lp_terminal_format() { # fg, bg, bold, underline, fallback_fg, fallback_bg lp_terminal_format= (( LP_ENABLE_COLOR )) || return 2 local af_color ab_color fg bg previous_af_color fg=$1 bg=${2:-"-1"} previous_af_color=${_lp_last_af_color-} lp_terminal_format=${_LP_OPEN_ESC}${_LP_TI_RESET} if (( fg >= _LP_TI_COLORS )) && [[ -n ${5-} ]]; then _lp_last_af_color=$5 elif (( fg == -2 )); then : # do nothing, _lp_last_af_color already correct elif (( fg == -3 )); then _lp_last_af_color=$_lp_last_ab_color elif (( fg >= 0 )); then _lp_last_af_color=$fg else # -1 _lp_last_af_color=-1 fi if (( ${_lp_last_af_color:-"-1"} >= 0 )); then __lp_foreground_color "$_lp_last_af_color" && lp_terminal_format+=$af_color fi if (( bg >= _LP_TI_COLORS )) && [[ -n ${6-} ]]; then _lp_last_ab_color=$6 elif (( bg == -2 )); then : # do nothing, _lp_last_ab_color already correct elif (( bg == -3 )); then _lp_last_ab_color=$previous_af_color elif (( bg >= 0 )); then _lp_last_ab_color=$bg else # -1 _lp_last_ab_color=-1 fi if (( ${_lp_last_ab_color:-"-1"} >= 0 )); then __lp_background_color "$_lp_last_ab_color" && lp_terminal_format+=$ab_color fi # It turns out there are sequences to reset bold and underline to normal # (\E[22m and \E[24m in xterm), but they aren't universally supported. This # means we must reset to all defaults then enable if they are wanted. # Explicit is safer anyway. if (( ${3:-0} )); then lp_terminal_format+=$_LP_TI_BOLD fi if (( ${4:-0} )); then lp_terminal_format+=$_LP_TI_UNDERLINE fi lp_terminal_format+=$_LP_CLOSE_ESC } # Get a list of themes currently loaded. Looks for functions matching # _lp__theme_prompt(). __lp_theme_list() { lp_theme_list=() local -a _functions if (( _LP_SHELL_zsh )); then # shellcheck disable=SC2296 _functions=( "${(ko)functions[@]}" ) else local IFS=$'\n' # shellcheck disable=SC2207 _functions=( $(declare -F) ) fi local function for function in "${_functions[@]}"; do if [[ $function == *_lp_*_theme_prompt ]]; then function=${function#*_lp_} lp_theme_list+=("${function%_theme_prompt}") fi done } __lp_theme_bash_complete() { COMPREPLY=() local -a lp_theme_list local theme partial_theme partial_theme=${2-} __lp_theme_list for theme in "${lp_theme_list[@]}"; do [[ -n $partial_theme && $theme != "$partial_theme"* ]] && continue COMPREPLY+=("$theme") done } __lp_theme_zsh_complete() { local -a lp_theme_list __lp_theme_list _describe 'theme' lp_theme_list } _lp_hash_color() { (( LP_ENABLE_COLOR )) || return 2 # compute the hash of the argument and color it with the corresponding color in: # (green, yellow, blue, purple, cyan) # Red (would be index 1) is not used here, as it should be reserved for alerts. local str=${1-} local cksum="$(printf '%s' "$str" | cksum)" local hash="${cksum%%[$' \t']*}" __lp_foreground_color "$(( 2 + hash % 5 ))" local color="${_LP_OPEN_ESC}${af_color-}${_LP_CLOSE_ESC}" lp_hash_color="${color}${str}${NO_COL}" } _lp_join() { # Join the $2... array with the $1 string. local delimiter=${1-} local IFS="" if (( _LP_SHELL_zsh )); then shift 1 # shellcheck disable=SC2296 lp_join="${(pj/$delimiter/)*}" else local first=${2-} shift 2 lp_join="${first}${*/#/$delimiter}" fi } _lp_grep_fields() { # Open file $1 and parse it for key/value pairs having delimiter $2, for each key in $3... # Silently bypass inexisting fields. local file="$1" [[ -f "$file" ]] || return 1 local delimiter="$2" shift 2 local -a fields fields=("$@") lp_grep_fields=() # We test for $line in the loop is here to ensure that we read the last line, # even if the file does not ends with a \n. # This bypass a known behavior of the C standard, not fixed in POSIX. while IFS='' read -r line || [[ -n "$line" ]] ; do for (( i=_LP_FIRST_INDEX; i < ${#fields[@]} + _LP_FIRST_INDEX; i++ )); do key="${fields[i]}" if [[ "$line" == "${key}${delimiter}"* ]] ; then line="${line#*"${key}${delimiter}"}" # Remove first part until delimiter. lp_grep_fields[i]="${line}" fi done done <"$file" } # Adds as much $3 (fillchars) character(s) (e.g. spaces) between $1 (left) and $2 (right), # so as to make the resulting string the same width as the current terminal. # If $4 (splitends) is true, then will cut $3 (fillchars) so as to perfectly fill the available space. # # This function would have been slow if you put escaped characters in $3 (fillchars) # and the width of the gap to fill is large. Thus, any escaped character is # removed from $3 (fillchars) at the beginning. # # To color the filling sequence, you can add formating at the end of $1 (left) # and/or at the beginning of $2 (right). _lp_fill() { local left="$1" right="$2" fillchars="${3:-" "}" splitends="${4:-1}" # Remove any escaped character from fillchars, # to avoid any costly __lp_strip_escape call on a potentially long # sequence of characters. __lp_strip_escapes "$fillchars" fillchars="$ret" # Compute the gap between left and right. local ret __lp_strip_escapes "${left}" local left_as_text="$ret" __lp_strip_escapes "${right}" local right_as_text="$ret" # Width of the gap is width of the screen minus every printable character. local gap_width=$((${COLUMNS:-80}-${#left_as_text}-${#right_as_text})) if [[ $gap_width -lt 0 ]] ; then lp_fill="${left}${right}" return 1 fi # Assemble a candidate filling sequence of characters. local filled="" local fillchars_width=${#fillchars} local nb_fillchars=$((gap_width/fillchars_width)) local i for (( i=0; i < nb_fillchars; i++ )) ; do filled+="$fillchars" done local actual_width=${#filled} # If there is still a gap (i.e. we have an unaligned multi-character fillchars). if [[ ${actual_width} -lt ${gap_width} ]] ; then # User asked for splitends. if [[ $splitends -ne 0 ]] ; then for (( i=_LP_FIRST_INDEX; i < $((${#fillchars}+_LP_FIRST_INDEX)); i++ )) ; do # Get one single character. if (( _LP_SHELL_zsh )) ; then filled+="${fillchars[i,i]}" else filled+="${fillchars:i:1}" fi actual_width=${#filled} if [[ ${actual_width} -ge ${gap_width} ]] ; then # Stop at this char if we're full. break fi done else # User asked for no splitends. for (( i=actual_width; i < gap_width; i++ )) ; do # Fill with spaces. filled+=" " done fi fi # shellcheck disable=SC2034 lp_fill="${left}${filled}${right}" } _lp_version_string() { # [major, [minor, [patch, [string, [number]]]]] if [[ $# -eq 0 ]]; then local major="${_LP_VERSION[_LP_FIRST_INDEX+0]}" local minor="${_LP_VERSION[_LP_FIRST_INDEX+1]}" local patch="${_LP_VERSION[_LP_FIRST_INDEX+2]-}" local string="${_LP_VERSION[_LP_FIRST_INDEX+3]-}" local number="${_LP_VERSION[_LP_FIRST_INDEX+4]-}" else local major="${1}" local minor="${2}" local patch="${3-}" local string="${4-}" local number="${5-}" fi #shellcheck disable=SC2034 lp_version="${major}.${minor}" if [[ -n "$patch" ]]; then lp_version+=".${patch}" if [[ -n "$string" ]]; then lp_version+="-${string}" if [[ -n "$number" ]]; then lp_version+=".${number}" fi fi fi } _lp_substitute() { # string pairs_array # Replace string with the "right" part of the first pair in the given array, if the "left" part matches the string. # FIXME If we ever support Bash>4.3, this would be better implemented with associative arrays. lp_substitute="" local string="${1}" shift 1 local pairs pairs=("$@") [[ -n "${pairs[*]}" ]] || return 2 local i key for (( i=0; i < ${#pairs[@]}; i+=2 )); do key=${pairs[_LP_FIRST_INDEX+i]} if [[ "$key" == "$string" ]]; then lp_substitute=${pairs[_LP_FIRST_INDEX+i+1]-} return 0 # Found. fi done return 1 # Not found. } ########################## # Working Directory Path # ########################## __lp_get_unique_directory() { local directory="${1##*/}" root="${1%/*}" local -a matching local -i i for (( i=1; i < ${#directory}; i++ )); do lp_unique_directory=${directory:0:$i} matching=("${root}/${lp_unique_directory}"*/) if (( ${#matching[@]} == 1 )); then return 0 fi done return 1 } __lp_end_path_left_shortening() { if (( is_shortening )); then shortened_path_length+=${#separator} if (( shortened_path_length < unshortened_path_length )); then lp_path_format+="${shortened_directory_format}${LP_MARK_SHORTEN_PATH}" # This indescriminate adding of a separator can sometimes mean the path # is slightly longer than it should be, but it is more clear. lp_path_format+="${separator_format}${separator}" lp_path+="${LP_MARK_SHORTEN_PATH}/" else lp_path+=$unshortened_path_shorten_string lp_path_format+=$unshortened_path_format_shorten_string shortened_path_length=$unshortened_path_length fi is_shortening=0 fi } # methods: # truncate_chars_from_path_left # truncate_chars_from_dir_middle # truncate_chars_from_dir_right # truncate_chars_to_unique_dir # truncate_to_last_dir _lp_path_format() { ((LP_ENABLE_PATH)) || return 2 local path_format="${1-$LP_COLOR_PATH}" local last_directory_format="${2:-$path_format}" local vcs_root_format="${3:-$last_directory_format}" local shortened_directory_format="${4:-$path_format}" local separator="${5-"/"}" local separator_format="${6-}" lp_path= lp_path_format= local ret lp_link_path local lp_pwd_tilde __lp_pwd_tilde local display_path="$lp_pwd_tilde" local -i path_length="${#display_path}" local lp_vcs_root lp_vcs_dir lp_vcs_specific_dir lp_vcs_type lp_vcs_subtype local vcs_root_directory= if (( LP_PATH_VCS_ROOT )) && _lp_find_vcs; then __lp_pwd_tilde "$lp_vcs_root" vcs_root_directory=$lp_pwd_tilde fi if [[ $path_length -eq 1 || $LP_PATH_METHOD == "truncate_to_last_dir" ]]; then if [[ $path_length -gt 1 ]]; then lp_path=${display_path##*/} else # only root or home to show lp_path=$display_path fi __lp_escape "$lp_path" if [[ $display_path == "$vcs_root_directory" ]]; then _lp_create_link_path "${ret}" lp_path_format="${vcs_root_format}$lp_link_path" else _lp_create_link_path "${ret}" lp_path_format="${last_directory_format}$lp_link_path" fi return else if [[ $separator != "/" && ${display_path:0:1} == "/" ]]; then # The root directory ('/') becomes a directory name instead of a leading separator # Add one to account for the first / needing to be both replaced and shown path_length+=1 fi if [[ ${#separator} -gt 1 ]]; then # Add length to account for multichar separators local slash_count="${display_path//[!\/]}" path_length+=$(( ${#slash_count} * ( ${#separator} - 1 ) )) fi fi local path_to_proccess="${display_path}/" current_path="" current_directory="" lp_unique_directory local -i max_len=$(( ${COLUMNS:-80} * LP_PATH_LENGTH / 100 )) directory_count=0 needed_length local -i shortened_path_length="$path_length" is_shortening=0 unshortened_path_length local unshortened_path_shorten_string unshortened_path_format_shorten_string shortened_path while [[ -n $path_to_proccess ]]; do if [[ ${path_to_proccess:0:1} == "/" ]]; then # Start of root current_directory="/" else current_directory=${path_to_proccess%%/*} if [[ -n $current_path && $current_path != "/" ]]; then current_path+="/" fi fi directory_count+=1 current_path+=${current_directory} path_to_proccess=${path_to_proccess#*/} if [[ $current_path == "$vcs_root_directory" ]]; then __lp_end_path_left_shortening # No shortening lp_path+=$current_directory __lp_escape "$current_directory" lp_path_format+="${vcs_root_format}" elif [[ -z $path_to_proccess ]]; then __lp_end_path_left_shortening # Last directory lp_path+=$current_directory __lp_escape "$current_directory" lp_path_format+="${last_directory_format}" elif (( LP_ENABLE_SHORTEN_PATH && directory_count > LP_PATH_KEEP \ && ( shortened_path_length > max_len || ( shortened_path_length >= max_len && is_shortening ) ) )); then if [[ $LP_PATH_METHOD == "truncate_chars_to_unique_dir" ]] && \ __lp_get_unique_directory "$current_path"; then lp_path+=$lp_unique_directory __lp_escape "$lp_unique_directory" lp_path_format+="${shortened_directory_format}" shortened_path_length=$(( shortened_path_length - ${#current_directory} + ${#lp_unique_directory} )) elif [[ $LP_PATH_METHOD == "truncate_chars_from_path_left" ]]; then # The only way to know if this consecutive directory shortening # will actually shorten the path is to both do it and do not and # compare at the end. if (( ! is_shortening )); then unshortened_path_shorten_string= unshortened_path_format_shorten_string= unshortened_path_length=$shortened_path_length shortened_path_length+=${#LP_MARK_SHORTEN_PATH} fi needed_length=$(( shortened_path_length - max_len )) unshortened_path_shorten_string+="${current_directory}/" unshortened_path_format_shorten_string+="${path_format}${current_directory}${separator_format}${separator}" if (( needed_length >= ${#current_directory} )); then # One directory was not enough, need to shorten more. # Shorten by current directory length plus separator. shortened_path_length=$(( shortened_path_length - ${#current_directory} - ${#separator} )) is_shortening=1 else # Do not need to check if the shortened version is actually shorter. # If we got to here, it wasn't a forced ending, which means it is. shortened_path_length=$(( shortened_path_length - needed_length )) shortened_path="${LP_MARK_SHORTEN_PATH}${current_directory:$needed_length}" lp_path+=$shortened_path __lp_escape "$shortened_path" lp_path_format+="${shortened_directory_format}" is_shortening=0 fi elif [[ $LP_PATH_METHOD == "truncate_chars_from_dir_right" ]] && \ (( ${#LP_MARK_SHORTEN_PATH} + LP_PATH_CHARACTER_KEEP < ${#current_directory} )); then shortened_path="${current_directory:0:$LP_PATH_CHARACTER_KEEP}${LP_MARK_SHORTEN_PATH}" lp_path+=$shortened_path __lp_escape "$shortened_path" lp_path_format+="${shortened_directory_format}" shortened_path_length=$(( shortened_path_length - ${#current_directory} + ${#LP_MARK_SHORTEN_PATH} + LP_PATH_CHARACTER_KEEP )) elif [[ $LP_PATH_METHOD == "truncate_chars_from_dir_middle" ]] && \ (( ${#LP_MARK_SHORTEN_PATH} + LP_PATH_CHARACTER_KEEP * 2 < ${#current_directory} )); then shortened_path="${current_directory:0:$LP_PATH_CHARACTER_KEEP}${LP_MARK_SHORTEN_PATH}${current_directory: -$LP_PATH_CHARACTER_KEEP}" lp_path+=$shortened_path __lp_escape "$shortened_path" lp_path_format+="${shortened_directory_format}" shortened_path_length=$(( shortened_path_length - ${#current_directory} + ${#LP_MARK_SHORTEN_PATH} + LP_PATH_CHARACTER_KEEP * 2 )) else # Need to shorten, but no method matched, or the matched method # did not make the string any shorter. lp_path+=$current_directory __lp_escape "$current_directory" lp_path_format+="${path_format}" fi else __lp_end_path_left_shortening lp_path+=$current_directory __lp_escape "$current_directory" lp_path_format+="${path_format}" fi if (( ! is_shortening )); then _lp_create_link_path "$ret" lp_path_format+="$lp_link_path" if [[ -n $path_to_proccess && ( $current_path != "/" || $separator != "/" ) ]]; then if [[ $current_path != "/" ]]; then lp_path+="/" fi lp_path_format+="${separator_format}${separator}" fi fi done } ############### # Environment # ############### # If we are connected with a X11 support _lp_connected_display() { (( LP_ENABLE_DISPLAY )) || return 2 [[ -n "${DISPLAY-}" ]] } _lp_connection() { if [[ -n "${SSH_CLIENT-}${SSH2_CLIENT-}${SSH_TTY-}" ]]; then lp_connection=ssh return elif [[ -n ${REMOTEHOST-} ]]; then lp_connection=tel # Telnet return fi local sess_parent="$(ps -o comm= -p "$PPID" 2> /dev/null)" if [[ "$sess_parent" = "su" || "$sess_parent" = "sudo" ]]; then lp_connection=su # su/sudo else lp_connection=lcl # Local fi } _lp_chroot() { (( LP_ENABLE_CHROOT )) || return 2 if [[ -r /etc/debian_chroot ]]; then IFS= read -r lp_chroot [ ]" # Strings may be `%s`, which will be replaced by the variable's actual content. # is added after each variable. # If end_color is not passed, it will default to $NO_COL. (( LP_ENABLE_ENV_VARS )) || return 2 local color_set="${1-}" local color_unset="${2-}" local color_no="${3-$NO_COL}" if [[ -z "$color_set" && -z "$color_unset" ]]; then color_no="" fi local var_rep var evar fmt_if_set fmt_if_unset lp_env_vars=() # For all user-defined setup. for var_rep in ${LP_ENV_VARS[@]+"${LP_ENV_VARS[@]}"}; do IFS=' ' read -r var fmt_if_set fmt_if_unset <<<"$var_rep" # Variable name and set format has to be set, but unset format is optional. if [[ -n "${var}" && -n "${fmt_if_set}" ]]; then # Expands the underlying variable name. local var_is_set= if (( _LP_SHELL_zsh )); then # From https://www.shellcheck.net/wiki/SC2296 : # "Some Zsh specific parameter expansions like ${(q)value} trigger this warning, # but ShellCheck does not support Zsh." # shellcheck disable=SC2296 var_is_set="${(P)var+IS_SET}" else # NOTE: indirection expansion are deprecated starting at bash 4.3, should use nameref. var_is_set="${!var+IS_SET}" fi if [[ -n "$var_is_set" ]]; then if [[ "${fmt_if_set}" == *"%s"* ]]; then local evar= if (( _LP_SHELL_zsh )); then # shellcheck disable=SC2296 evar="${(P)var}" else evar="${!var}" fi # Print content. lp_env_vars+=( "${color_set}${fmt_if_set/\%s/${evar}}${color_no}" ) else # Print tag. lp_env_vars+=( "${color_set}${fmt_if_set}${color_no}" ) fi elif [[ -n "$fmt_if_unset" ]]; then # Print default. lp_env_vars+=( "${color_unset}${fmt_if_unset}${color_no}" ) fi fi done if [[ -z "${lp_env_vars-}" ]]; then return 1 fi } _lp_env_vars_color() { if _lp_env_vars "${LP_COLOR_ENV_VARS_SET}" "${LP_COLOR_ENV_VARS_UNSET}"; then _lp_join "${LP_MARK_ENV_VARS_SEP}" "${lp_env_vars[@]}" lp_env_vars_color="${LP_MARK_ENV_VARS_OPEN}${lp_join}${LP_MARK_ENV_VARS_CLOSE}" else return "$?" fi } _lp_python_env() { (( LP_ENABLE_VIRTUALENV )) || return 2 local ret # Truncate to the last '/' section, which is the directory name. if [[ -n "${VIRTUAL_ENV-}" ]]; then virtualenv_config="${VIRTUAL_ENV}/pyvenv.cfg" if [[ -r "$virtualenv_config" ]]; then local line while IFS='' read -r line ; do if [[ $line == "prompt"*"="* ]]; then line="${line#"prompt"*"="}" line="${line#[[:space:]]}" line="${line#[\'\"]}" line="${line%[\'\"]}" __lp_escape "$line" break fi done <"$virtualenv_config" fi if [[ -z "${ret-}" ]]; then __lp_escape "${VIRTUAL_ENV##*/}" fi lp_python_env=$ret elif [[ -n "${CONDA_DEFAULT_ENV-}" ]]; then __lp_escape "${CONDA_DEFAULT_ENV##*/}" lp_python_env=$ret else return 1 fi } _lp_python_env_color() { unset lp_python_env_color _lp_python_env || return "$?" lp_python_env_color="${LP_COLOR_VIRTUALENV}${lp_python_env}${NO_COL}" } _lp_node_env() { (( LP_ENABLE_NODE_VENV )) || return 2 local ret if [[ -n "${NODE_VIRTUAL_ENV-}" ]]; then # Truncate to the last '/' section, which is the directory name. __lp_escape "${NODE_VIRTUAL_ENV##*/}" lp_node_env=$ret elif [[ -n "${NVM_BIN-}" ]]; then # Get the version string from the path. ret="${NVM_BIN##*/node/}" __lp_escape "${ret%/bin}" lp_node_env=$ret else return 1 fi } _lp_node_env_color() { unset lp_node_env_color _lp_node_env || return "$?" lp_node_env_color="${LP_COLOR_NODE_VENV}${lp_node_env}${NO_COL}" } _lp_perl_env() { (( LP_ENABLE_PERL_VENV )) || return 2 local ret if [[ "$_LP_PERL_VENV_PROGRAM" = "perlbrew" ]] ; then local perlbrew_perl_ver="$(perlbrew use)" # Example: Currently using perl-5.38.0 __lp_escape "${perlbrew_perl_ver##*perl-}" lp_perl_env=$ret elif [[ "$_LP_PERL_VENV_PROGRAM" = "plenv" ]] ; then local plenv_ver="$(plenv version)" # Only first word is necessary as for rbenv __lp_escape "${plenv_ver%%" (set"*}" lp_perl_env=$ret else return 1 fi } _lp_perl_env_color() { unset lp_perl_env_color _lp_perl_env || return "$?" lp_perl_env_color="${LP_COLOR_PERL_VENV}${lp_perl_env}${NO_COL}" } _lp_ruby_env() { (( LP_ENABLE_RUBY_VENV )) || return 2 local ret if [[ "$_LP_RUBY_VENV_PROGRAM" = "rvm" ]] ; then __lp_escape "$(rvm-prompt "${LP_RUBY_RVM_PROMPT_OPTIONS[@]}")" lp_ruby_env=$ret elif [[ "$_LP_RUBY_VENV_PROGRAM" = "rbenv" ]] ; then local rbenv_ver="$(rbenv version)" # Only first word is necessary __lp_escape "${rbenv_ver%%" (set"*}" lp_ruby_env=$ret else return 1 fi } _lp_ruby_env_color() { unset lp_ruby_env_color _lp_ruby_env || return "$?" lp_ruby_env_color="${LP_COLOR_RUBY_VENV}${lp_ruby_env}${NO_COL}" } _lp_terraform_env() { (( LP_ENABLE_TERRAFORM )) || return 2 local ret if [[ -d .terraform ]]; then local _tf_workspace _tf_workspace="$(\terraform workspace show 2>/dev/null)" if [[ -n "$_tf_workspace" ]]; then __lp_escape "${_tf_workspace}" lp_terraform_env=$ret else return 1 fi else return 1 fi } _lp_terraform_env_color() { unset lp_terraform_env_color _lp_terraform_env || return "$?" lp_terraform_env_color="${LP_COLOR_TERRAFORM}${lp_terraform_env}${NO_COL}" } _lp_software_collections() { (( LP_ENABLE_SCLS )) || return 2 if [[ -n "${X_SCLS-}" ]]; then local ret __lp_escape "${X_SCLS%"${X_SCLS##*[![:space:]]}"}" lp_software_collections=$ret else return 1 fi } _lp_software_collections_color() { unset lp_software_collections_color _lp_software_collections || return "$?" lp_software_collections_color="${LP_COLOR_VIRTUALENV}${lp_software_collections}${NO_COL}" } _lp_kubernetes_context() { (( LP_ENABLE_KUBECONTEXT )) || return 2 local kubernetes_context if (( LP_ENABLE_KUBE_NAMESPACE )); then local line kubernetes_namespace line=$(kubectl config view --minify --output \ 'jsonpath={.current-context}{"/"}{..namespace}' 2>/dev/null) || return 1 kubernetes_context=${line%/*} kubernetes_namespace=${line##*/} else kubernetes_context=$(kubectl config current-context 2>/dev/null) || return 1 fi if [[ -n "$LP_DELIMITER_KUBECONTEXT_PREFIX" ]]; then # shellcheck disable=SC2295 kubernetes_context="${kubernetes_context##*${LP_DELIMITER_KUBECONTEXT_PREFIX}}" fi if [[ -n "$LP_DELIMITER_KUBECONTEXT_SUFFIX" ]]; then # shellcheck disable=SC2295 kubernetes_context="${kubernetes_context%%${LP_DELIMITER_KUBECONTEXT_SUFFIX}*}" fi local ret __lp_escape "$kubernetes_context" lp_kubernetes_context=$ret if [[ -n ${kubernetes_namespace-} ]]; then __lp_escape "$kubernetes_namespace" lp_kubernetes_namespace=$ret else unset lp_kubernetes_namespace fi } _lp_kubernetes_context_color() { unset lp_kubernetes_context_color _lp_kubernetes_context || return "$?" lp_kubernetes_context_color="${LP_MARK_KUBECONTEXT}${LP_COLOR_KUBECONTEXT}${lp_kubernetes_context}${lp_kubernetes_namespace+:}${lp_kubernetes_namespace-}${NO_COL}" } _lp_aws_profile() { (( LP_ENABLE_AWS_PROFILE )) || return 2 local ret local aws_profile="${AWS_PROFILE-${AWS_DEFAULT_PROFILE-${AWS_VAULT-}}}" if [[ -n $aws_profile ]]; then __lp_escape "${aws_profile}" lp_aws_profile=$ret else return 1 fi } _lp_aws_profile_color() { unset lp_aws_profile_color _lp_aws_profile || return "$?" lp_aws_profile_color="${LP_COLOR_AWS_PROFILE}${lp_aws_profile}${NO_COL}" } _lp_cmake() { (( LP_ENABLE_CMAKE )) || return 2 [[ -f CMakeCache.txt ]] || return 1 _lp_grep_fields "CMakeCache.txt" "=" "CMAKE_C_COMPILER:FILEPATH" "CMAKE_CXX_COMPILER:FILEPATH" "CMAKE_GENERATOR:INTERNAL" "CMAKE_BUILD_TYPE:STRING" local cmake_c_compiler=${lp_grep_fields[_LP_FIRST_INDEX+0]-} lp_cmake_c_compiler="${cmake_c_compiler##*/}" # Only the part after the last slash. local cmake_cxx_compiler=${lp_grep_fields[_LP_FIRST_INDEX+1]-} lp_cmake_cxx_compiler="${cmake_cxx_compiler##*/}" # Only the part after the last slash. local cmake_generator=${lp_grep_fields[_LP_FIRST_INDEX+2]-} # Shorten: Makefiles -> Make, and Visual Studio -> VS. cmake_generator="${cmake_generator/%Makefiles/Make}" cmake_generator="${cmake_generator/#Visual Studio/VS}" # Remove all white spaces. lp_cmake_generator="${cmake_generator// /}" lp_cmake_buildtype=${lp_grep_fields[_LP_FIRST_INDEX+3]-} [[ -n "${lp_cmake_c_compiler}${lp_cmake_cxx_compiler}${lp_cmake_generator}${lp_cmake_buildtype}" ]] || return 1 } _lp_cmake_color() { unset lp_cmake_color _lp_cmake || return "$?" local lp_hash_color lp_cmake_color="" if [[ -n "$lp_cmake_c_compiler" ]] ; then _lp_hash_color "$lp_cmake_c_compiler" lp_cmake_color+="${lp_hash_color}${LP_MARK_CMAKE}" fi if [[ -n "$lp_cmake_cxx_compiler" ]] ; then _lp_hash_color "$lp_cmake_cxx_compiler" lp_cmake_color+="${lp_hash_color}${LP_MARK_CMAKE}" fi if [[ -n "$lp_cmake_generator" ]] ; then _lp_hash_color "$lp_cmake_generator" lp_cmake_color+="$lp_hash_color${LP_MARK_CMAKE}" fi if [[ -n "$lp_cmake_buildtype" ]] ; then if [[ "$lp_cmake_buildtype" == "Release" ]] ; then lp_cmake_color+="${LP_COLOR_CMAKE_RELEASE}${lp_cmake_buildtype}${NO_COL}" elif [[ "$lp_cmake_buildtype" == "RelWithDebInfo" ]] ; then lp_cmake_color+="${LP_COLOR_CMAKE_RWDI}${lp_cmake_buildtype}${NO_COL}" elif [[ "$lp_cmake_buildtype" == "Debug" ]] ; then lp_cmake_color+="${LP_COLOR_CMAKE_DEBUG}${lp_cmake_buildtype}${NO_COL}" else _lp_hash_color "$lp_cmake_buildtype" lp_cmake_color+="$lp_hash_color" fi fi } _lp_modules() { (( LP_ENABLE_MODULES )) || return 2 lp_modules=() # # Module sets the LOADEDMODULES environment variable. if [[ -n "${LOADEDMODULES-}" ]]; then # Outer test should be faster. if (( LP_ENABLE_MODULES_VERSIONS )); then local IFS=':' for mod in $LOADEDMODULES; do lp_modules+=("${mod}") done else local IFS=':' for mod in $LOADEDMODULES; do # Simply remove the part after the slash. # Should work on both Bash and Zsh. lp_modules+=("${mod%/*}") done fi if (( ${#lp_modules[@]} == 0 )); then return 1 fi else return 1 fi } _lp_modules_color() { lp_modules_color= if _lp_modules; then if (( LP_ENABLE_COLOR )); then local lp_join if (( LP_ENABLE_MODULES_HASHCOLOR )); then local modules=() for mod in "${lp_modules[@]}"; do _lp_hash_color "${mod}" modules+=("${lp_hash_color}") done # Do not color marks and separators. _lp_join "${NO_COL}${LP_MARK_MODULES_SEP}" "${modules[@]}" lp_modules_color="${LP_MARK_MODULES_OPEN}${lp_join}${NO_COL}${LP_MARK_MODULES_CLOSE}" else # No hashcolor. _lp_join "${NO_COL}${LP_MARK_MODULES_SEP}${LP_COLOR_MODULES}" "${lp_modules[@]}" lp_modules_color="${LP_MARK_MODULES_OPEN}${LP_COLOR_MODULES}${lp_join}${NO_COL}${LP_MARK_MODULES_CLOSE}" fi else # No color. _lp_join "${LP_MARK_MODULES_SEP}" "${lp_modules[@]}" lp_modules_color="${LP_MARK_MODULES_OPEN}${lp_join}${LP_MARK_MODULES_CLOSE}" fi else return "$?" fi } # Same as bash '\l', but can be inlined as a constant as the value will not # change during the shell's life. _lp_terminal_device() { lp_terminal_device="$(basename -- "$(tty)" 2>/dev/null)" } # Determine what type of user we are _lp_user() { if (( EUID == 0 )); then # user is root return 2 elif [[ "${USER-}" != "$(logname 2>/dev/null || printf '%s' "${LOGNAME-}")" ]]; then # user is not login user return 1 else return 0 fi } # Return the username (if we should display one). _lp_username() { if (( LP_USER_ALWAYS == -1 )); then # No username ever return 2 elif (( LP_USER_ALWAYS )) || ! _lp_user; then lp_username=${USER:-${USERNAME:-${LOGNAME-}}} if [[ -z $lp_username ]]; then lp_username=$(id -nu 2>/dev/null) fi local ret __lp_escape "$lp_username" lp_username=$ret return 0 else return 1 fi } _lp_username_color() { _lp_username || return "$?" lp_username_color="${LP_COLOR_USER}${lp_username}${NO_COL}" } # Test the code with the commands: # sudo id # sudo, enter your credentials # sudo -K # revoke your credentials # sudo -v # return non-zero when no credentials are cached # sudo -nvk # return true if user can run commands without password input _lp_sudo_active() { (( LP_ENABLE_SUDO )) || return 2 (( _LP_SUDO_NOPASSWORD )) && return 0 \sudo -nv 2>/dev/null || return 1 } _lp_sudo_active_color() { (( LP_ENABLE_SUDO )) || return 2 if _lp_sudo_active; then lp_sudo_active_color=$LP_COLOR_MARK_SUDO else lp_sudo_active_color=$LP_COLOR_MARK_NO_SUDO fi } _lp_hostname() { # Only process hostname elements if we haven't turned them off if (( LP_HOSTNAME_ALWAYS != -1 )); then _lp_connection if [[ $lp_connection == lcl ]] && ! (( LP_HOSTNAME_ALWAYS )); then # no hostname if local return 1 fi if [[ $LP_HOSTNAME_METHOD == fqdn ]]; then lp_hostname=$(hostname -f 2>/dev/null) elif [[ $LP_HOSTNAME_METHOD == pretty ]]; then if [[ $LP_OS == Darwin ]]; then lp_hostname=$(scutil --get ComputerName 2>/dev/null) else lp_hostname=$(hostnamectl --pretty 2>/dev/null) fi fi if [[ -z ${lp_hostname-} ]]; then lp_hostname=${HOSTNAME:-${HOST-}} fi # Truncate to the first subdomain if [[ $LP_HOSTNAME_METHOD == short ]]; then lp_hostname=${lp_hostname%%.*} fi local ret __lp_escape "$lp_hostname" lp_hostname=$ret else return 2 fi } # Put the hostname if not locally connected # color it in cyan within SSH, and a warning red if within telnet # else display the host without color _lp_hostname_color() { if _lp_connected_display; then lp_hostname_color="${LP_COLOR_X11_ON}" else lp_hostname_color="${LP_COLOR_X11_OFF}" fi if _lp_chroot; then lp_hostname_color+="(${lp_chroot})" fi if _lp_hostname; then case "$lp_connection" in lcl) lp_hostname_color+="@${LP_COLOR_HOST}${lp_hostname}${NO_COL}" ;; ssh) local client_ip client_port server_ip server_port hostname= # client_* are unused # shellcheck disable=SC2034 IFS=" " read -r client_ip client_port server_ip server_port <<<"$SSH_CONNECTION" local username=${USER:-${USERNAME:-${LOGNAME-}}} if _lp_create_link "ssh://${username}@${server_ip}:${server_port}" "$lp_hostname"; then hostname="$lp_link" else hostname="$lp_hostname" fi # If we want a different color for each host (( LP_ENABLE_SSH_COLORS )) && LP_COLOR_SSH="$LP_COLOR_HOST_HASH" lp_hostname_color+="@${LP_COLOR_SSH}${hostname}${NO_COL}" ;; su) lp_hostname_color+="@${LP_COLOR_SU}${lp_hostname}${NO_COL}" ;; tel) lp_hostname_color+="@${LP_COLOR_TELNET}${lp_hostname}${NO_COL}" ;; *) lp_hostname_color+="@${NO_COL}${lp_hostname}" # defaults to no color ;; esac else if [[ -n ${lp_chroot-} ]]; then # End the color of the chroot lp_hostname_color+=${NO_COL} else # Nothing to display lp_hostname_color="" return 1 fi fi } _lp_dirstack() { (( LP_ENABLE_DIRSTACK )) || return 2 local count dir_stack dir_stack=$(dirs -p; printf x) __lp_line_count "${dir_stack%x}" lp_dirstack=$count (( lp_dirstack > 1 )) } _lp_dirstack_color() { _lp_dirstack || return "$?" lp_dirstack_color="${LP_COLOR_DIRSTACK}${LP_MARK_DIRSTACK}${lp_dirstack}${NO_COL}" } _lp_shell_level() { (( LP_ENABLE_SHLVL )) || return 2 lp_shell_level=${SHLVL:-1} (( lp_shell_level > 1 )) } _lp_shell_level_color() { _lp_shell_level || return "$?" lp_shell_level_color="${LP_COLOR_SHLVL}${LP_MARK_SHLVL}${lp_shell_level}${NO_COL}" } ################ # Related jobs # ################ # Return the count of detached screen and/or tmux sessions. _lp_detached_sessions() { (( LP_ENABLE_DETACHED_SESSIONS )) || return 2 local -i count=0 (( _LP_ENABLE_SCREEN )) && count=$(screen -ls 2> /dev/null | GREP_OPTIONS='' \grep -c '[Dd]etach[^)]*)$') (( _LP_ENABLE_TMUX )) && count+=$(tmux list-sessions 2> /dev/null | GREP_OPTIONS='' \grep -cv 'attached') lp_detached_sessions=$count (( lp_detached_sessions )) } # Return the count of attached running shell jobs (started with $ myjob &) and/or # stopped jobs (suspended with Ctrl-Z). _lp_jobcount() { (( LP_ENABLE_JOBS )) || return 2 local jobs local -i count # Count running jobs # The $(...) syntax strips trailing newlines, so add a character to the end # then remove it to prevent that. Otherwise 0 and 1 jobs look the same. jobs="$(jobs -r; printf x)" __lp_line_count "${jobs%x}" lp_running_jobs=$count # Count stopped jobs jobs="$(jobs -s; printf x)" __lp_line_count "${jobs%x}" lp_stopped_jobs=$count (( lp_running_jobs || lp_stopped_jobs )) } # Display the count of detached sessions and shell jobs if not zero. _lp_jobcount_color() { (( LP_ENABLE_JOBS || LP_ENABLE_DETACHED_SESSIONS )) || return 2 lp_jobcount_color= _lp_detached_sessions && lp_jobcount_color="${LP_COLOR_JOB_D}${lp_detached_sessions}d${NO_COL}" if _lp_jobcount; then if (( lp_running_jobs > 0 )); then [[ -n "$lp_jobcount_color" ]] && lp_jobcount_color+="$LP_MARK_JOBS_SEPARATOR" lp_jobcount_color+="${LP_COLOR_JOB_R}${lp_running_jobs}&${NO_COL}" fi if (( lp_stopped_jobs > 0 )); then [[ -n "$lp_jobcount_color" ]] && lp_jobcount_color+="$LP_MARK_JOBS_SEPARATOR" lp_jobcount_color+="${LP_COLOR_JOB_Z}${lp_stopped_jobs}z${NO_COL}" fi fi [[ -n "$lp_jobcount_color" ]] } ###################### # VCS branch display # ###################### _lp_are_vcs_enabled() { local _path for _path in ${LP_DISABLED_VCS_PATHS[@]+"${LP_DISABLED_VCS_PATHS[@]}"}; do if [[ -n "$_path" && "$PWD" == "$_path"* ]]; then return 1 fi done return 0 } # Search upwards through a directory structure looking for a sign of a VCS # repository. Used to avoid invoking VCS binaries to discover if in a repo. _lp_find_vcs() { if ! _lp_are_vcs_enabled; then lp_vcs_type="disabled" lp_vcs_root="" return 2 fi lp_vcs_type="" # Based on the Git behavior here: # https://git-scm.com/book/en/v2/Git-Internals-Environment-Variables if [[ -d "${GIT_DIR-}" ]]; then lp_vcs_type="git" lp_vcs_dir="$GIT_DIR" lp_vcs_root="${GIT_WORK_TREE:-"$(\git rev-parse --show-toplevel 2>/dev/null)"}" fi if [[ -z $lp_vcs_type ]]; then local vcs lp_vcs_root="$PWD" while [[ -n "$lp_vcs_root" ]]; do for vcs in ${_LP_ENABLED_VCSS[@]+"${_LP_ENABLED_VCSS[@]}"}; do lp_vcs_dir="$lp_vcs_root/.$vcs" if [[ -d "$lp_vcs_dir" ]]; then lp_vcs_type="$vcs" break 2 fi done unset lp_vcs_dir if (( LP_ENABLE_GIT )) && [[ -f "$lp_vcs_root/.git" ]]; then lp_vcs_type="git" lp_vcs_dir="$(\git rev-parse --git-dir 2>/dev/null)" break fi if (( LP_ENABLE_FOSSIL )) && [[ -f "$lp_vcs_root/_FOSSIL_" || -f "$lp_vcs_root/.fslckout" ]]; then lp_vcs_type="fossil" return 0 fi lp_vcs_root="${lp_vcs_root%/*}" done fi if [[ $lp_vcs_type == git ]]; then lp_vcs_specific_dir="$lp_vcs_dir" if [[ -f "${lp_vcs_dir}/gitdir" ]]; then # Then this is a worktree, and the config exists two levels up. lp_vcs_dir="${lp_vcs_dir}/../.." fi if [[ -n "${VCSH_DIRECTORY-}" ]]; then lp_vcs_subtype="vcsh" elif [[ -d "${lp_vcs_dir}/svn" ]]; then lp_vcs_subtype="svn" else lp_vcs_subtype="" fi fi if [[ -z $lp_vcs_type ]]; then lp_vcs_root="" return 1 fi } # Set the prompt mark depending on the current VCS. # shellcheck disable=SC2120 _lp_smart_mark() { local subtype="${2:-"${lp_vcs_subtype-}"}" case "${1:-$lp_vcs_type}" in git) if [[ $subtype == vcsh ]]; then lp_smart_mark="$LP_MARK_VCSH${VCSH_DIRECTORY-}$LP_MARK_GIT$LP_MARK_VCSH" elif [[ $subtype == svn ]]; then lp_smart_mark="$LP_MARK_GIT$LP_MARK_SVN" else lp_smart_mark="$LP_MARK_GIT" fi ;; hg) lp_smart_mark="$LP_MARK_HG" ;; svn) lp_smart_mark="$LP_MARK_SVN" ;; fossil) lp_smart_mark="$LP_MARK_FOSSIL" ;; bzr) lp_smart_mark="$LP_MARK_BZR" ;; disabled) lp_smart_mark="$LP_MARK_DISABLED" ;; *) lp_smart_mark="$LP_MARK_DEFAULT" ;; esac } # GENERIC VCS # # Create a formatted string describing the status of the repo. _lp_vcs_details_color() { local branch if _lp_vcs_branch; then branch="$lp_vcs_branch" if _lp_vcs_bookmark; then branch+=": $lp_vcs_bookmark" fi elif _lp_vcs_bookmark; then branch="$lp_vcs_bookmark" elif _lp_vcs_tag; then branch="tag: $lp_vcs_tag" else _lp_vcs_commit_id branch="${lp_vcs_commit_id:0:7}" fi lp_vcs_details_color="$LP_COLOR_UP" local has_commit= if _lp_vcs_commits_off_remote; then lp_vcs_details_color="$LP_COLOR_COMMITS_BEHIND" if [[ "$lp_vcs_commit_ahead" -ne "0" && "$lp_vcs_commit_behind" -ne "0" ]]; then has_commit="${LP_COLOR_COMMITS}+$lp_vcs_commit_ahead${NO_COL}/${LP_COLOR_COMMITS_BEHIND}-$lp_vcs_commit_behind${NO_COL}" elif [[ "$lp_vcs_commit_ahead" -ne "0" ]]; then has_commit="${LP_COLOR_COMMITS}$lp_vcs_commit_ahead${NO_COL}" lp_vcs_details_color="$LP_COLOR_COMMITS" elif [[ "$lp_vcs_commit_behind" -ne "0" ]]; then has_commit="${LP_COLOR_COMMITS_BEHIND}-$lp_vcs_commit_behind${NO_COL}" fi fi local ret has_lines= if _lp_vcs_uncommitted_files; then _lp_vcs_unstaged_lines; ret=$? # Only show unstaged changes if the VCS supports staging, otherwise # show uncommitted changes if (( ret == 0 )); then has_lines="+$lp_vcs_unstaged_i_lines/-$lp_vcs_unstaged_d_lines" elif (( ret == 1 )); then has_lines="+0/-0" else _lp_vcs_uncommitted_lines has_lines="+$lp_vcs_uncommitted_i_lines/-$lp_vcs_uncommitted_d_lines" fi lp_vcs_details_color="$LP_COLOR_CHANGES" fi lp_vcs_details_color+="$branch" if [[ -n "$has_lines" || -n "$has_commit" ]]; then lp_vcs_details_color+="${NO_COL}(" if [[ -n "$has_lines" ]]; then lp_vcs_details_color+="${LP_COLOR_DIFF}${has_lines}${NO_COL}${has_commit:+,}" fi lp_vcs_details_color+="${has_commit})" fi if _lp_vcs_stash_count; then lp_vcs_details_color+="$LP_COLOR_COMMITS$LP_MARK_STASH" fi if _lp_vcs_untracked_files; then lp_vcs_details_color+="$LP_COLOR_CHANGES$LP_MARK_UNTRACKED" fi if _lp_vcs_head_status; then lp_vcs_details_color+=" $LP_COLOR_CHANGES$lp_vcs_head_status" if [[ -n "${lp_vcs_head_details-}" ]]; then lp_vcs_details_color+="(${lp_vcs_head_details})" fi fi if _lp_vcs_remote; then # Commits behind but not ahead. if [[ "$lp_vcs_commit_behind" -ne 0 && "$lp_vcs_commit_ahead" -eq 0 ]]; then lp_vcs_details_color+="${LP_COLOR_COMMITS_BEHIND}${LP_MARK_VCS_REMOTE}${lp_vcs_remote}${NO_COL}" # Commits ahead but not behind. elif [[ "$lp_vcs_commit_behind" -eq 0 && "$lp_vcs_commit_ahead" -ne 0 ]]; then lp_vcs_details_color+="${LP_COLOR_COMMITS}${LP_MARK_VCS_REMOTE}${lp_vcs_remote}${NO_COL}" # Commits both ahead and behind: the mark is colored differently. elif [[ "$lp_vcs_commit_behind" -ne 0 && "$lp_vcs_commit_ahead" -ne 0 ]]; then lp_vcs_details_color+="${LP_COLOR_COMMITS_BEHIND}${LP_MARK_VCS_REMOTE}${LP_COLOR_COMMITS}${lp_vcs_remote}${NO_COL}" # else: display nothing. fi fi lp_vcs_details_color+="$NO_COL" } # Check if the detected VCS is enabled in Liquid Prompt and the current # directory is a valid repository of that type. _lp_vcs_active() { "_lp_${lp_vcs_type}_active" } # Get the branch name of the repo in the current directory. _lp_vcs_branch() { "_lp_${lp_vcs_type}_branch" } # Get the bookmark name of the repo in the current directory. _lp_vcs_bookmark() { "_lp_${lp_vcs_type}_bookmark" } # Get a tag name of the repo in the current directory. _lp_vcs_tag() { "_lp_${lp_vcs_type}_tag" } # Get the current commit string for the repo in the current directory. _lp_vcs_commit_id() { "_lp_${lp_vcs_type}_commit_id" } # Get additional information if the repo is in a special or unusual state. _lp_vcs_head_status() { "_lp_${lp_vcs_type}_head_status" # TODO: set lp_vcs_head_details if not set? } # Get the number of stashes in the repo. _lp_vcs_stash_count() { "_lp_${lp_vcs_type}_stash_count" } # Get the number of commits ahead and behind the upstream branch. _lp_vcs_commits_off_remote() { "_lp_${lp_vcs_type}_commits_off_remote" } # Get the number of untracked aka extra files in the repo. _lp_vcs_untracked_files() { "_lp_${lp_vcs_type}_untracked_files" } # Get the number of changed files compared to the last or checked out commit. _lp_vcs_uncommitted_files() { "_lp_${lp_vcs_type}_uncommitted_files" } # Get the number of changed lines compared to the last or checked out commit. _lp_vcs_uncommitted_lines() { "_lp_${lp_vcs_type}_uncommitted_lines" } # Get the number of changed files compared to staging. _lp_vcs_unstaged_files() { "_lp_${lp_vcs_type}_unstaged_files" } # Get the number of changed lines compared to staging. _lp_vcs_unstaged_lines() { "_lp_${lp_vcs_type}_unstaged_lines" } # Get the number of changed files in staging compared to the last or checked out commit. _lp_vcs_staged_files() { "_lp_${lp_vcs_type}_staged_files" } # Get the number of changed lines in staging compared to the last or checked out commit. _lp_vcs_staged_lines() { "_lp_${lp_vcs_type}_staged_lines" } # Get the remote name for the current branch. _lp_vcs_remote() { (( LP_ENABLE_VCS_REMOTE )) || return 2 "_lp_${lp_vcs_type}_remote" } # GIT # # Check if Git is enabled in Liquid Prompt and the current directory is a valid # Git repository. _lp_git_active() { (( LP_ENABLE_GIT )) || return 2 \git rev-parse --is-inside-work-tree >/dev/null 2>&1 || return 1 } # Get the branch name of the Git repo in the current directory. _lp_git_branch() { lp_vcs_branch="" local branch ret # Recent versions of Git support the --short option for symbolic-ref, but # not 1.7.9 (Ubuntu 12.04) if branch="$(\git symbolic-ref -q HEAD 2>/dev/null)"; then __lp_escape "${branch#refs/heads/}" lp_vcs_branch="$ret" else return 1 fi } # Git does not support bookmarks. _lp_git_bookmark() { return 2 ; } # Get a tag name of the Git repo in the current directory. _lp_git_tag() { local tag ret if tag="$(\git describe --tags --exact-match 2>/dev/null)"; then __lp_escape "$tag" lp_vcs_tag="$ret" else return 1 fi } # Get the current full commit hash of the repo in the current directory. _lp_git_commit_id() { lp_vcs_commit_id="$(\git rev-parse -q HEAD 2>/dev/null)" } # Get additional information if HEAD is in merging, rebasing or cherry-picking state. _lp_git_head_status() { local IFS="" step total if [[ -f "${lp_vcs_specific_dir}/MERGE_HEAD" ]]; then lp_vcs_head_status="MERGING" elif [[ -d "${lp_vcs_specific_dir}/rebase-merge" ]]; then test -r "${lp_vcs_specific_dir}/rebase-merge/msgnum" && read -r step <"${lp_vcs_specific_dir}/rebase-merge/msgnum" test -r "${lp_vcs_specific_dir}/rebase-merge/end" && read -r total <"${lp_vcs_specific_dir}/rebase-merge/end" if [[ -f "${lp_vcs_specific_dir}/rebase-merge/interactive" ]]; then lp_vcs_head_status="REBASE-i" else lp_vcs_head_status="REBASE-m" fi elif [[ -d "${lp_vcs_specific_dir}/rebase-apply" ]]; then test -r "${lp_vcs_specific_dir}/rebase-apply/next" && read -r step <"${lp_vcs_specific_dir}/rebase-apply/next" test -r "${lp_vcs_specific_dir}/rebase-apply/last" && read -r total <"${lp_vcs_specific_dir}/rebase-apply/last" if [[ -f "${lp_vcs_specific_dir}/rebase-apply/rebasing" ]]; then lp_vcs_head_status="REBASE" elif [[ -f "${lp_vcs_specific_dir}/rebase-apply/applying" ]]; then lp_vcs_head_status="AM" else lp_vcs_head_status="AM/REBASE" fi elif [[ -f "${lp_vcs_specific_dir}/CHERRY_PICK_HEAD" ]]; then lp_vcs_head_status="CHERRY-PICKING" elif [[ -f "${lp_vcs_specific_dir}/REVERT_HEAD" ]]; then lp_vcs_head_status="REVERTING" elif [[ -f "${lp_vcs_specific_dir}/BISECT_START" ]]; then lp_vcs_head_status="BISECTING" else return 1 fi if [[ -n "$step" && -n "$total" ]]; then lp_vcs_head_details="${step}/${total}" else lp_vcs_head_details="" fi } # Get the number of Git stashes in the repo. _lp_git_stash_count() { lp_vcs_stash_count="$(\git rev-list --walk-reflogs --count refs/stash 2>/dev/null)" (( lp_vcs_stash_count )) } # Count commits behind and ahead on the remote tracking branch of the current # local branch. _lp_git_commits_off_remote() { local counts # The "@{upstream}" notation was added in Git 1.7.0, so this should work for everyone counts="$(\git rev-list --count --left-right '@{upstream}...HEAD' 2>/dev/null)" || return 2 IFS=$' \t' read -r lp_vcs_commit_behind lp_vcs_commit_ahead <<<"$counts" (( lp_vcs_commit_behind || lp_vcs_commit_ahead )) } # Get the number of untracked files in the repo. _lp_git_untracked_files() { lp_vcs_untracked_files="$(LC_ALL=C \git status --porcelain 2>/dev/null | GREP_OPTIONS='' \grep -c '^??')" (( lp_vcs_untracked_files )) } # Get the number of changed files. __lp_git_diff_shortstat_files() { # diff_shortstat local stat="$1" if [[ "$stat" = *changed* ]]; then stat="${stat/ file*}" lp_git_diff_shortstat_files=${stat//[$' \t']} else return 1 fi } # Get the number of changed lines. __lp_git_diff_shortstat_lines() { # diff_shortstat local stat="$1" stat=${stat/*changed, /} # removing "n file(s) changed" if [[ "$stat" = *insertion* ]]; then lp_git_diff_shortstat_i_lines=${stat/ inser*} else lp_git_diff_shortstat_i_lines=0 fi if [[ "$stat" = *deletion* ]]; then stat=${stat/*\(+\), } lp_git_diff_shortstat_d_lines=${stat/ del*/} else lp_git_diff_shortstat_d_lines=0 fi (( lp_git_diff_shortstat_i_lines || lp_git_diff_shortstat_d_lines )) } __lp_git_diff_shortstat_uncommitted() { if [[ -z ${_lp_git_diff_shortstat_uncommitted-} ]]; then _lp_git_diff_shortstat_uncommitted="$(LC_ALL=C \git diff --shortstat HEAD -- 2>/dev/null)" fi } # Get the number of changed files compared to HEAD. _lp_git_uncommitted_files() { __lp_git_diff_shortstat_uncommitted local lp_git_diff_shortstat_files __lp_git_diff_shortstat_files "$_lp_git_diff_shortstat_uncommitted" || return "$?" lp_vcs_uncommitted_files=$lp_git_diff_shortstat_files } # Get the number of changed lines compared to HEAD. _lp_git_uncommitted_lines() { __lp_git_diff_shortstat_uncommitted local lp_git_diff_shortstat_i_lines lp_git_diff_shortstat_d_lines __lp_git_diff_shortstat_lines "$_lp_git_diff_shortstat_uncommitted" || return "$?" lp_vcs_uncommitted_i_lines=$lp_git_diff_shortstat_i_lines lp_vcs_uncommitted_d_lines=$lp_git_diff_shortstat_d_lines } __lp_git_diff_shortstat_unstaged() { if [[ -z ${_lp_git_diff_shortstat_unstaged-} ]]; then _lp_git_diff_shortstat_unstaged="$(LC_ALL=C \git diff --shortstat 2>/dev/null)" fi } # Get the number of changed files compared to staging. _lp_git_unstaged_files() { __lp_git_diff_shortstat_unstaged local lp_git_diff_shortstat_files __lp_git_diff_shortstat_files "$_lp_git_diff_shortstat_unstaged" || return "$?" # shellcheck disable=SC2034 lp_vcs_unstaged_files=$lp_git_diff_shortstat_files } # Get the number of changed lines compared to staging. _lp_git_unstaged_lines() { __lp_git_diff_shortstat_unstaged local lp_git_diff_shortstat_i_lines lp_git_diff_shortstat_d_lines __lp_git_diff_shortstat_lines "$_lp_git_diff_shortstat_unstaged" || return "$?" lp_vcs_unstaged_i_lines=$lp_git_diff_shortstat_i_lines lp_vcs_unstaged_d_lines=$lp_git_diff_shortstat_d_lines } __lp_git_diff_shortstat_staged() { if [[ -z ${_lp_git_diff_shortstat_staged-} ]]; then _lp_git_diff_shortstat_staged="$(LC_ALL=C \git diff --shortstat --cached 2>/dev/null)" fi } # Get the number of changed files in staging compared to HEAD. _lp_git_staged_files() { __lp_git_diff_shortstat_staged local lp_git_diff_shortstat_files __lp_git_diff_shortstat_files "$_lp_git_diff_shortstat_staged" || return "$?" # shellcheck disable=SC2034 lp_vcs_staged_files=$lp_git_diff_shortstat_files } # Get the number of changed lines in staging compared to HEAD. # shellcheck disable=SC2034 _lp_git_staged_lines() { __lp_git_diff_shortstat_staged local lp_git_diff_shortstat_i_lines lp_git_diff_shortstat_d_lines __lp_git_diff_shortstat_lines "$_lp_git_diff_shortstat_staged" || return "$?" lp_vcs_staged_i_lines=$lp_git_diff_shortstat_i_lines lp_vcs_staged_d_lines=$lp_git_diff_shortstat_d_lines } # Get the remote name for the current branch. # NOTE: Assumes that `_lp_find_vcs` and `_lp_vcs_branch` (costly functions) have been called. _lp_git_remote() { lp_vcs_remote="" if [[ -z "$lp_vcs_branch" ]]; then # There is no remote. return 1 fi if [[ -z "$lp_vcs_dir" ]]; then # Cannot find the config file. return 1 fi local file="${lp_vcs_dir}/config" local delimiter=" = " # With spaces. local in_branch=0 # We test for $line in the loop is here to ensure that we read the last line, # even if the file does not ends with a \n. # This bypass a known behavior of the C standard, not fixed in POSIX. while IFS='' read -r line || [[ -n "$line" ]] ; do # If we are in the branch config section. if [[ "$line" == "[branch \"${lp_vcs_branch}\"]" ]]; then in_branch=1 elif [[ "$line" == *"remote${delimiter}"* ]] && (( in_branch )); then # Remove first part until delimiter. lp_vcs_remote="${line#*"remote${delimiter}"}" return 0 fi done <"$file" # Remote not found. return 1 } # MERCURIAL # # Check if Mercurial is enabled in Liquid Prompt and the current directory is a # valid Mercurial repository. _lp_hg_active() { (( LP_ENABLE_HG )) || return 2 "$LP_HG_COMMAND" root >/dev/null 2>&1 || return 1 } # Get the branch name of the Mercurial repo in the current directory. _lp_hg_branch() { local branch ret if branch="$("$LP_HG_COMMAND" branch 2>/dev/null)"; then __lp_escape "$branch" lp_vcs_branch="$ret" else # This should never happen. Should this function return a branch name # only if the head of the branch is checked out? But there can be # multiple heads of a branch... return 1 fi } # Get the bookmark name of the Mercurial repo in the current directory. _lp_hg_bookmark() { local bookmark ret if bookmark="$("$LP_HG_COMMAND" bookmark --list --quiet . 2>/dev/null)"; then __lp_escape "$bookmark" lp_vcs_bookmark="$ret" else return 1 fi } # Get the most recent tag that refers to the current revision. _lp_hg_tag() { local tags ret tags="$("$LP_HG_COMMAND" identify --template='{tags}' 2>/dev/null)" if [[ -n "$tags" ]]; then # Tags are separated by ':', get the first one __lp_escape "${tags%%:*}" lp_vcs_tag="$ret" else return 1 fi } # Get the current global revision id for the repo in the current directory. _lp_hg_commit_id() { lp_vcs_commit_id="$("$LP_HG_COMMAND" identify --id 2>/dev/null)" } # Get additional information if the repo is in any unfinished state. _lp_hg_head_status() { if [[ -d "${lp_vcs_dir}/merge" ]]; then lp_vcs_head_status="MERGING" elif [[ -f "${lp_vcs_dir}/rebasestate" ]]; then lp_vcs_head_status="REBASING" elif [[ -f "${lp_vcs_dir}/updatestate" ]]; then lp_vcs_head_status="UPDATING" elif [[ -f "${lp_vcs_dir}/bisect.state" ]]; then lp_vcs_head_status="BISECTING" elif [[ -f "${lp_vcs_dir}/shelvedstate" ]]; then lp_vcs_head_status="SHELVING" elif [[ -f "${lp_vcs_dir}/graftstate" ]]; then lp_vcs_head_status="GRAFTING" else return 1 fi } # Get the number of Mercurial shelves in the repo. _lp_hg_stash_count() { local shelves count shelves="$("$LP_HG_COMMAND" shelve --list 2>/dev/null; printf x)" __lp_line_count "${shelves%x}" lp_vcs_stash_count="$count" (( lp_vcs_stash_count )) } # https://github.com/liquidprompt/liquidprompt/issues/217 # return: always false (2: disabled). _lp_hg_commits_off_remote() { #commits=$("$LP_HG_COMMAND" outgoing --no-merges 2>/dev/null | GREP_OPTIONS='' \grep -c '\(^changeset\:\)') return 2 } # Get the number of untracked files in the Mercurial repo. _lp_hg_untracked_files() { local untracked untracked="$("$LP_HG_COMMAND" status --unknown --template '{status}' 2>/dev/null)" lp_vcs_untracked_files="${#untracked}" (( lp_vcs_untracked_files )) } # Get the number of changed files compared to the base revision. _lp_hg_uncommitted_files() { local files files="$("$LP_HG_COMMAND" status --modified --template '{status}' 2>/dev/null)" lp_vcs_uncommitted_files="${#files}" (( lp_vcs_uncommitted_files )) } # Get the number of changed lines compared to the base revision. _lp_hg_uncommitted_lines() { IFS=' ' read -r lp_vcs_uncommitted_i_lines lp_vcs_uncommitted_d_lines \ <<<"$("$LP_HG_COMMAND" diff --stat 2>/dev/null | sed -n '$ s/^.*, \([0-9]*\) .*, \([0-9]*\).*$/\1 \2/p')" (( lp_vcs_uncommitted_i_lines || lp_vcs_uncommitted_d_lines )) } # Mercurial does not support a staging area. _lp_hg_unstaged_files() { return 2 ; } _lp_hg_unstaged_lines() { return 2 ; } _lp_hg_staged_files() { return 2 ; } _lp_hg_staged_lines() { return 2 ; } _lp_hg_remote() { return 2 ; } # FIXME This should be implemented. # SUBVERSION # # Check if Subversion is enabled in Liquid Prompt and the current directory is a # valid Subversion repository. _lp_svn_active() { (( LP_ENABLE_SVN )) || return 2 \svn info >/dev/null 2>&1 || return 1 } # Get the branch name of the repo in the current directory. _lp_svn_branch() { local ret url # SVN info shows repository-relative URLs since v1.8 url="$(LC_ALL=C \svn info 2>/dev/null)" url="${url#*Relative URL: }" url="${url%%$'\n'*}" [[ -z "$url" ]] && return 1 if [[ "$url" == */trunk* ]]; then lp_vcs_branch=trunk elif [[ "$url" == */branches/?* ]]; then url="${url##*/branches/}" __lp_escape "${url%/*}" lp_vcs_branch="$ret" elif [[ "$url" == */tags/?* ]]; then url="${url##*/tags/}" __lp_escape "${url%/*}" lp_vcs_branch="tag/$ret" else return 1 fi } # Subversion does not support bookmarks. _lp_svn_bookmark() { return 2 ; } # Subversion does not support tags. What are generally agreed upon as # being tags are internally branches. These are returned by _lp_svn_branch(). _lp_svn_tag() { return 2 ; } # Get the current revision number for the repo in the current directory. _lp_svn_commit_id() { lp_vcs_commit_id="$(\svn info --show-item revision 2>/dev/null)" } # Subversion does not have extra head statuses. A Subversion merge is no different # than a manual file change, so the repository has no extra state to track. _lp_svn_head_status() { return 2 ; } # Subversion does not support stashes. _lp_svn_stash_count() { return 2 ; } # Subversion does not support remote tracking branches (as it is not a # distributed version control system). _lp_svn_commits_off_remote() { return 2 ; } # Get the number of untracked files in the Subversion repo. _lp_svn_untracked_files() { lp_vcs_untracked_files="$(LC_ALL=C \svn status 2>/dev/null | GREP_OPTIONS='' \grep -c '^?')" (( lp_vcs_untracked_files )) } # Get the number of changed files compared to the base revision. _lp_svn_uncommitted_files() { local files count # svn status is unsafe with newline chars in filenames, which will throw # off this count files="$(\svn status --quiet 2>/dev/null; printf x)" __lp_line_count "${files%x}" lp_vcs_uncommitted_files="$count" (( lp_vcs_uncommitted_files )) } # Get the number of changed lines compared to the base revision. _lp_svn_uncommitted_lines() { IFS=' ' read -r lp_vcs_uncommitted_i_lines lp_vcs_uncommitted_d_lines \ <<<"$(\svn diff --internal-diff 2>/dev/null | awk ' BEGIN { plus=0; minus=0 } /^(\+[^+])|(\+$)/ { plus+=1 } /^(-[^-])|(-$)/ { minus+=1 } END { print plus" "minus }')" (( lp_vcs_uncommitted_i_lines || lp_vcs_uncommitted_d_lines )) } # Subversion does not support a staging area. _lp_svn_unstaged_files() { return 2 ; } _lp_svn_unstaged_lines() { return 2 ; } _lp_svn_staged_files() { return 2 ; } _lp_svn_staged_lines() { return 2 ; } # Subversion does not have remotes. _lp_svn_remote() { return 2 ; } # FOSSIL # # Check if Fossil is enabled in Liquid Prompt and the current directory is a # valid Fossil repository. _lp_fossil_active() { (( LP_ENABLE_FOSSIL )) || return 2 \fossil status >/dev/null 2>&1 || return 1 } # Get the branch name of the repo in the current directory. _lp_fossil_branch() { local branch ret # branch current command added in fossil 2.7 if ! branch="$(\fossil branch current 2>/dev/null)"; then # Almost any character can be in a branch name, but we have no way of # knowing if a newline is part of the name or not. In fact, there is no # way to prevent a branch containing the string '\n* ' to not break # this. Just hope that no one crazy enough to do that to their branch # names is running Fossil <2.7 branch="$(\fossil branch list 2>/dev/null)" branch="${branch#*$'\n\* '}" # If the current branch is the first in the list, the above check would # not have removed anything branch="${branch#\* }" branch="${branch%%$'\n'*}" fi if [[ -n "$branch" ]]; then __lp_escape "$branch" lp_vcs_branch="$ret" else return 1 fi } # Fossil does not support bookmarks. _lp_fossil_bookmark() { return 2 ; } # Fossil does not support unique tags. Fossil tags can refer to multiple checkin IDs, # so a matching tag is not a useful unique ID. _lp_fossil_tag() { return 2 ; } # Get the current full commit hash of the Fossil repo in the current directory. _lp_fossil_commit_id() { lp_vcs_commit_id="$(LC_ALL=C \fossil status 2>/dev/null | sed -n 's/^checkout:[[:space:]]*\([^[:space:]]*\).*/\1/p')" } # Get additional information if the check-out is in merging before a commit. _lp_fossil_head_status() { local option option="$(LC_ALL=C \fossil undo --dry-run 2>/dev/null)" if [[ "$option" == *"fossil merge"* ]]; then lp_vcs_head_status="MERGING" else return 1 fi } # Get the number of Fossil stashes in the repo. _lp_fossil_stash_count() { local stashes count stashes="$(\fossil stash list 2>/dev/null; printf x)" __lp_line_count "${stashes%x}" # Each stash takes up two lines, and no stashes is one line lp_vcs_stash_count=$(( count / 2 )) (( lp_vcs_stash_count )) } # Fossil does not support remote tracking branches. Fossil by default keeps the local # repository in sync with the remote. Even if a user disables that, it is not possible # to have a local and remote branch named the same not in sync. _lp_fossil_commits_off_remote() { return 2 ; } # Get the number of extra files in the Fossil repo. _lp_fossil_untracked_files() { local extras count extras="$(\fossil extras 2>/dev/null; printf x)" __lp_line_count "${extras%x}" lp_vcs_untracked_files=$count (( lp_vcs_untracked_files )) } # Get the number of changed files compared to the checked-out version. _lp_fossil_uncommitted_files() { local files files="$(\fossil changes 2>/dev/null; printf x)" __lp_line_count "${files%x}" lp_vcs_uncommitted_files=$count (( lp_vcs_uncommitted_files )) } # Get the number of changed lines compared to the checked-out version. _lp_fossil_uncommitted_lines() { IFS=' ' read -r lp_vcs_uncommitted_i_lines lp_vcs_uncommitted_d_lines \ <<<"$(\fossil diff --internal --verbose 2>/dev/null | awk ' BEGIN { plus=0; minus=0 } /^(\+[^+])|(\+$)/ { plus+=1 } /^(-[^-])|(-$)/ { minus+=1 } END { print plus" "minus }')" (( lp_vcs_uncommitted_i_lines || lp_vcs_uncommitted_d_lines )) } # Fossil does not support a staging area. _lp_fossil_unstaged_files() { return 2 ; } _lp_fossil_unstaged_lines() { return 2 ; } _lp_fossil_staged_files() { return 2 ; } _lp_fossil_staged_lines() { return 2 ; } _lp_fossil_remote() { return 2 ; } # Bazaar # # Check if Bazaar is enabled in Liquid Prompt and the current directory is a # valid Bazaar repository. This check should be done before running any other # _lp_bzr_* data functions. _lp_bzr_active() { (( LP_ENABLE_BZR )) || return 2 \bzr status >/dev/null 2>&1 || return 1 } # Get the branch name of the current directory _lp_bzr_branch() { local branch ret if branch="$(\bzr nick 2> /dev/null)"; then __lp_escape "$branch" lp_vcs_branch="$ret" else return 1 fi } # Bazaar does not support bookmarks. A nick is somewhat like a bookmark, but there is # no command to view a naked branch name, so the nick command is used for branches. _lp_bzr_bookmark() { return 2 ; } # Get the most recent tag that refers to the current revision. _lp_bzr_tag() { local tag ret _ IFS=$' \t' read -r tag _ <<<"$(LC_ALL=C \bzr tags --revision=last:1 2>/dev/null)" if [[ -n "$tag" ]]; then __lp_escape "$tag" lp_vcs_tag="$ret" else return 1 fi } # Get the current full commit hash of the repo in the current directory. _lp_bzr_commit_id() { lp_vcs_commit_id="$(\bzr revno 2>/dev/null)" } # Bazaar does not have extra head statuses. A Bazaar merge can be partially complete, # but there is no command to test for it. _lp_bzr_head_status() { return 2 ; } # Get the number of Bazaar shelves in the repo. _lp_bzr_stash_count() { local shelves count shelves="$(\bzr shelve --list 2>/dev/null)" local -i ret="$?" if (( ret == 0 )); then # No error code means no shelves. lp_vcs_stash_count=0 elif (( ret == 1 )); then # Return of 1 means there are shelves. # The usual "printf x" trick can't be used, as it squashes the error code. __lp_line_count "$shelves" lp_vcs_stash_count=$(( count + 1 )) else return 1 fi (( lp_vcs_stash_count )) } # Bazaar does not support getting details of remote tracking branches. Bazaar does not # keep a local copy of the remote state, so checking this would be impossible anyway. _lp_bzr_commits_off_remote() { return 2 ; } # Get the number of unknown files in the repo. _lp_bzr_untracked_files() { lp_vcs_untracked_files="$(LC_ALL=C \bzr status --short 2>/dev/null | GREP_OPTIONS='' \grep -c '^?')" (( lp_vcs_untracked_files )) } # Get the number of changed files compared to the checked-out version. _lp_bzr_uncommitted_files() { lp_vcs_uncommitted_files="$(LC_ALL=C \bzr status --short 2>/dev/null | GREP_OPTIONS='' \grep -vc '^?')" (( lp_vcs_uncommitted_files )) } # Get the number of changed lines compared to the checked-out version. _lp_bzr_uncommitted_lines() { IFS=' ' read -r lp_vcs_uncommitted_i_lines lp_vcs_uncommitted_d_lines \ <<<"$(\bzr diff 2>/dev/null | awk ' BEGIN { plus=0; minus=0 } /^(\+[^+])|(\+$)/ { plus+=1 } /^(-[^-])|(-$)/ { minus+=1 } END { print plus" "minus }')" (( lp_vcs_uncommitted_i_lines || lp_vcs_uncommitted_d_lines )) } # Bazaar does not support a staging area. _lp_bzr_unstaged_files() { return 2 ; } _lp_bzr_unstaged_lines() { return 2 ; } _lp_bzr_staged_files() { return 2 ; } _lp_bzr_staged_lines() { return 2 ; } _lp_bzr_remote() { return 2 ; } #################### # Wifi link status # #################### _lp_wifi_signal_strength() { (( LP_ENABLE_WIFI_STRENGTH )) || return 2 local level __lp_wifi_signal_strength_raw || return "$?" if (( level > 110 && level < 256 )); then # assume old-style WEXT 8-bit unsigned signal level. (( level -= 256 )) # subtract 256 to convert to dBm. fi # normalize to 0. (( level > -40 )) && (( level = -40 )) (( level < -100 )) && (( level = -100 )) (( lp_wifi_signal_strength = 100 - (100 * ((level + 40) * -1 ) / 60 ) )) (( lp_wifi_signal_strength < LP_WIFI_STRENGTH_THRESHOLD )) } case "$LP_OS" in Linux) __lp_wifi_signal_strength_raw() { [[ -r "$_LP_LINUX_WIRELESS_FILE" ]] || return 2 unset lp_wifi_signal_strength local strength _ while IFS=$' \t' read -r _ _ _ strength _; do strength="${strength%%[![:digit:]-]*}" if [[ -n $strength ]] && ( [[ -z ${lp_wifi_signal_strength-} ]] \ || (( strength < level )) ); then level="$strength" fi done <"$_LP_LINUX_WIRELESS_FILE" [[ -n ${level-} ]] || return 2 } ;; Darwin) __lp_wifi_signal_strength_raw() { level="$("$_LP_AIRPORT_BIN" --getinfo 2>/dev/null)" || return 2 level="${level#*"agrCtlRSSI: "}" level="${level%%[![:digit:]-]*}" [[ -n $level ]] || return 2 } ;; *) _lp_wifi_signal_strength() { return 2 } ;; esac _lp_wifi_signal_strength_color() { _lp_wifi_signal_strength || return "$?" local ret _lp_color_map "$(( 100 - lp_wifi_signal_strength ))" lp_wifi_signal_strength_color="${ret}${LP_MARK_WIFI}" if (( LP_ALWAYS_DISPLAY_VALUES )); then lp_wifi_signal_strength_color+="${lp_wifi_signal_strength}" fi lp_wifi_signal_strength_color+="${NO_COL}" } ################## # Battery status # ################## # Get the battery status in percent. case "$LP_OS" in Linux) __lp_battery_sysfs() { if (( _LP_SHELL_zsh )); then setopt local_options nullglob fi local power_supply for power_supply in "${_LP_LINUX_POWERSUPPLY_PATH}/"*; do if ! [[ -r "${power_supply}/type" && -r "${power_supply}/present" && \ -r "${power_supply}/status" && -r "${power_supply}/capacity" ]]; then continue fi local power_supply_type power_supply_present power_supply_scope IFS= read -r power_supply_type <"${power_supply}/type" 2>/dev/null || continue [[ $power_supply_type == 'Battery' ]] || continue IFS= read -r power_supply_present <"${power_supply}/present" 2>/dev/null || continue [[ $power_supply_present == '1' ]] || continue # Scope is a property of a power supply # Scope = System or missing - power supply powers the system # Scope = Device - power supply powers a device if [[ -r "${power_supply}/scope" ]]; then IFS= read -r power_supply_scope <"${power_supply}/scope" 2>/dev/null || continue [[ $power_supply_scope == 'System' ]] || continue fi IFS= read -r lp_battery_status <"${power_supply}/status" 2>/dev/null || continue IFS= read -r lp_battery <"${power_supply}/capacity" 2>/dev/null || continue return 0 done return 1 } __lp_battery_acpi() { local acpi acpi="$(acpi --battery 2>/dev/null)" # Extract the battery load value in percent # First, remove the beginning of the line... lp_battery="${acpi#Battery *, }" lp_battery="${lp_battery%%%*}" # remove everything starting at '%' lp_battery_status="${acpi}" } __lp_battery_detect() { local lp_battery lp_battery_status unset _LP_BATTERY_FUNCTION # First check SYSFS way. _LP_BATTERY_FUNCTION=__lp_battery_sysfs "$_LP_BATTERY_FUNCTION" 2>/dev/null && \ [[ -n "${lp_battery-}" ]] && return 0 # Try with ACPI. if command -v apci >/dev/null; then _LP_BATTERY_FUNCTION=__lp_battery_acpi "$_LP_BATTERY_FUNCTION" 2>/dev/null && \ [[ -n "${lp_battery-}" ]] && return 0 fi unset _LP_BATTERY_FUNCTION return 1 } _lp_battery() { (( LP_ENABLE_BATT )) || return 5 unset lp_battery lp_battery_status "$_LP_BATTERY_FUNCTION" if [[ -z "${lp_battery-}" ]]; then # no battery level found return 4 fi # discharging if [[ "$lp_battery_status" == *"Discharging"* ]]; then # under => 0, above => 1 return "$(( lp_battery > LP_BATTERY_THRESHOLD ))" # not charging elif [[ "$lp_battery_status" == *"Not charging"* ]]; then return 4 # charging else # under => 2, above => 3 return "$(( 2 + ( lp_battery > LP_BATTERY_THRESHOLD ) ))" fi } ;; Darwin) _lp_battery() { (( LP_ENABLE_BATT )) || return 5 local batt_status IFS=';' read -r lp_battery batt_status <<<"$(pmset -g batt | sed -n 's/^ -InternalBattery.*[[:space:]]\([0-9]*[0-9]\)%; \([^;]*\).*$/\1;\2/p')" case "$batt_status" in charged | "") return 4 ;; discharging) # under => 0, above => 1 return "$(( lp_battery > LP_BATTERY_THRESHOLD ))" ;; *) # "charging", "AC attached" # under => 2, above => 3 return "$(( 2 + ( lp_battery > LP_BATTERY_THRESHOLD ) ))" ;; esac } ;; *) _lp_battery() { return 5 } ;; esac # Compute a gradient of background/foreground colors depending on the battery status. _lp_battery_color() { (( LP_ENABLE_BATT )) || return 2 _lp_battery local -i _status="$?" if (( _status >= 4 || lp_battery == 100 )); then # no battery support or battery full: nothing displayed return 1 elif (( _status == 3 && lp_battery != 100 )); then # charging and above threshold and not 100% # green ⏚ lp_battery_color="${LP_COLOR_CHARGING_ABOVE}${LP_MARK_ADAPTER}${NO_COL}" elif (( _status == 2 )); then # charging but under threshold # yellow ⏚ lp_battery_color="${LP_COLOR_CHARGING_UNDER}${LP_MARK_ADAPTER}${NO_COL}" elif (( _status == 1 )); then # discharging but above threshold # yellow ⌁ lp_battery_color="${LP_COLOR_DISCHARGING_ABOVE}${LP_MARK_BATTERY}${NO_COL}" # discharging and under threshold else lp_battery_color="${LP_COLOR_DISCHARGING_UNDER}${LP_MARK_BATTERY}${NO_COL}" if (( LP_ALWAYS_DISPLAY_VALUES )); then local -i idx if (( lp_battery <= 0 )); then idx=0 elif (( lp_battery <= 5 )); then # 5 idx=9 elif (( lp_battery <= 10 )); then # 5 idx=8 elif (( lp_battery <= 20 )); then # 10 idx=7 elif (( lp_battery <= 30 )); then # 10 idx=6 elif (( lp_battery <= 40 )); then # 10 idx=5 elif (( lp_battery <= 50 )); then # 10 idx=4 elif (( lp_battery <= 65 )); then # 15 idx=3 elif (( lp_battery <= 80 )); then # 15 idx=2 elif (( lp_battery < 100 )); then # 20 idx=1 else # >= 100 idx=0 fi local ret _lp_color_map "$idx" 10 lp_battery_color+="${ret}${lp_battery}${_LP_PERCENT}${NO_COL}" fi fi } ########################### # Runtime of last command # ########################### _lp_runtime_format() { (( LP_ENABLE_RUNTIME )) || return 2 lp_runtime_format= if (( _LP_RUNTIME_SECONDS >= LP_RUNTIME_THRESHOLD )); then # display runtime seconds as days, hours, minutes, and seconds (( _LP_RUNTIME_SECONDS >= 86400 )) && lp_runtime_format=$((_LP_RUNTIME_SECONDS / 86400))d (( _LP_RUNTIME_SECONDS >= 3600 )) && lp_runtime_format+=$((_LP_RUNTIME_SECONDS % 86400 / 3600))h (( _LP_RUNTIME_SECONDS >= 60 )) && lp_runtime_format+=$((_LP_RUNTIME_SECONDS % 3600 / 60))m lp_runtime_format+=$((_LP_RUNTIME_SECONDS % 60))s else return 1 fi } __lp_runtime_before() { _LP_RUNTIME_LAST_SECONDS=$SECONDS _LP_RUNTIME_SECONDS=-1 } # Compute number of seconds since the command was started __lp_runtime_after() { if [[ -n "${_LP_RUNTIME_LAST_SECONDS-}" ]]; then (( _LP_RUNTIME_SECONDS=SECONDS-_LP_RUNTIME_LAST_SECONDS )) unset _LP_RUNTIME_LAST_SECONDS else _LP_RUNTIME_SECONDS=0 fi } _lp_runtime_color() { _lp_runtime_format || return "$?" lp_runtime_color="${LP_COLOR_RUNTIME}${lp_runtime_format}${NO_COL}" } ############### # System load # ############### # Get CPU count and current load case "$LP_OS" in Linux) __lp_cpu_count() { _lp_CPUNUM=$( nproc 2>/dev/null || GREP_OPTIONS='' \grep -c '^[Pp]rocessor' /proc/cpuinfo ) } if [[ -r '/proc/loadavg' ]]; then _lp_cpu_load () { local _ IFS=$' \t' read -r lp_cpu_load _ < /proc/loadavg } elif command -v uptime >/dev/null; then _lp_cpu_load () { lp_cpu_load="$( LP_ALL=C uptime | sed 's/.*load average: \([0-9.]*\).*/\1/' )" } else LP_ENABLE_LOAD=0 fi ;; FreeBSD|Darwin|OpenBSD) __lp_cpu_count() { _lp_CPUNUM=$( sysctl -n hw.ncpu ) } _lp_cpu_load () { local _ IFS=$' \t' # If you have problems with syntax coloring due to the following # line, do this: ln -s liquidprompt liquidprompt.bash # and edit liquidprompt.bash read -r _ lp_cpu_load _ <<<"$( LC_ALL=C sysctl -n vm.loadavg )" } ;; SunOS) __lp_cpu_count() { _lp_CPUNUM=$( kstat -m cpu_info | GREP_OPTIONS='' \grep -c "module: cpu_info" ) } _lp_cpu_load () { lp_cpu_load="$( LC_ALL=C uptime | sed 's/.*load average: *\([0-9.]*\).*/\1/' )" } esac _lp_load() { (( LP_ENABLE_LOAD )) || return 2 local lp_cpu_load ret # Get value (OS-specific) into lp_cpu_load _lp_cpu_load __lp_escape "${lp_cpu_load:-0}" lp_load=$ret __lp_floating_scale "${lp_cpu_load:-0}" 100 lp_load_adjusted=$(( ret / _lp_CPUNUM )) (( lp_load_adjusted >= _LP_LOAD_THRESHOLD )) } # Compute a gradient of background/forground colors depending on the load. _lp_load_color() { _lp_load || return "$?" local ret _lp_color_map "$lp_load_adjusted" "$_LP_LOAD_CAP" lp_load_color="${ret}${LP_MARK_LOAD}" if (( LP_ALWAYS_DISPLAY_VALUES )); then lp_load_color+="${lp_load}" fi lp_load_color+="$NO_COL" } ################# # Available RAM # ################# # Get RAM percentage. case "$LP_OS" in Linux) # Parse /proc/meminfo __lp_ram_bytes() { lp_ram_avail_bytes= lp_ram_total_bytes= local id value free _ while IFS=$' \t' read -r id value _ ; do if [[ $id == 'MemTotal:' ]]; then lp_ram_total_bytes=$value elif [[ $id == 'MemAvailable:' ]]; then lp_ram_avail_bytes=$value # Note: this is the best conservative estimate from the kernel # of the memory than may be available for a process. # See https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=34e431b0ae398fc54ea69ff85ec700722c9da773 elif [[ $id == 'MemFree:' ]]; then free=$value fi if [[ -n "$lp_ram_total_bytes" && -n "$lp_ram_avail_bytes" ]]; then break fi # Assumes file exists. done < "$_LP_LINUX_RAM_FILE" if [[ -z $lp_ram_avail_bytes && -n ${free-} ]]; then # Very old kernels and Cygwin lack MemAvailable, so fallback to MemFree. lp_ram_avail_bytes=$free fi # Values in file are in KiB. lp_ram_total_bytes=$(( lp_ram_total_bytes * 1024 )) lp_ram_avail_bytes=$(( lp_ram_avail_bytes * 1024 )) # Used memory may be used in a theme. # shellcheck disable=SC2034 lp_ram_used_bytes=$((lp_ram_total_bytes-lp_ram_avail_bytes)) } ;; FreeBSD|OpenBSD) # Parse dmesg.boot __lp_ram_bytes() { lp_ram_avail_bytes= lp_ram_total_bytes= local first second _ value while IFS=' ' read -r first second _ value _ _ ; do if [[ $second == 'memory' ]]; then if [[ $first == 'usable' || $first == 'real' ]]; then lp_ram_total_bytes=$value elif [[ $first == 'avail' ]]; then lp_ram_avail_bytes=$value fi fi if [[ -n "$lp_ram_total_bytes" && -n "$lp_ram_avail_bytes" ]]; then break fi done < "$_LP_BSD_RAM_FILE" # shellcheck disable=SC2034 lp_ram_used_bytes=$((lp_ram_total_bytes-lp_ram_avail_bytes)) } ;; Darwin) # Parse vm_stat __lp_ram_bytes() { lp_ram_avail_bytes=0 lp_ram_total_bytes=0 # Call vm_stat. local stat="$(vm_stat 2>/dev/null)" # Read first line. local page_size _ second third fourth fifth IFS=' ' read -r _ _ _ _ _ _ _ page_size _ <<< "$stat" # Read all other lines. while IFS=' ' read -r _ second third fourth fifth ; do case "$second" in free:|inactive:) # Remove the last "." character. lp_ram_avail_bytes=$((lp_ram_avail_bytes + ${third:0:${#third}-1})) ;; active:|speculative:|throttled:) lp_ram_total_bytes=$((lp_ram_total_bytes + ${third:0:${#third}-1})) ;; wired) lp_ram_total_bytes=$((lp_ram_total_bytes + ${fourth:0:${#fourth}-1})) ;; occupied) lp_ram_total_bytes=$((lp_ram_total_bytes + ${fifth:0:${#fifth}-1})) ;; esac done <<< "$stat" lp_ram_avail_bytes=$((lp_ram_avail_bytes * page_size)) lp_ram_total_bytes=$((lp_ram_avail_bytes + lp_ram_total_bytes * page_size)) # shellcheck disable=SC2034 lp_ram_used_bytes=$((lp_ram_total_bytes-lp_ram_avail_bytes)) } ;; *) # Not implemented. __lp_ram_bytes() { return 5 # Same code as in _lp_battery. } ;; esac _lp_ram() { (( LP_ENABLE_RAM )) || return 2 # Call the OS-specific function measuring RAM. __lp_ram_bytes || return "$?" # Now we have: lp_ram_avail_bytes, lp_ram_total_bytes, lp_ram_used_bytes. lp_ram_human= lp_ram_perc= lp_ram=$(( lp_ram_avail_bytes / 1024 )) # in KiB lp_ram_perc=$(( 100 * lp_ram_avail_bytes / lp_ram_total_bytes )) if (( lp_ram <= LP_RAM_THRESHOLD || lp_ram_perc <= LP_RAM_THRESHOLD_PERC )); then local lp_bytes lp_bytes_units __lp_bytes_to_human "${lp_ram_avail_bytes}" "$LP_RAM_PRECISION" lp_ram_human="${lp_bytes}" lp_ram_human_units="${lp_bytes_units}" return 0 else return 1 fi } _lp_ram_color() { if _lp_ram; then if (( LP_ALWAYS_DISPLAY_VALUES )); then if (( LP_DISPLAY_VALUES_AS_PERCENTS )); then lp_ram_color="${LP_COLOR_RAM}${LP_MARK_RAM}${lp_ram_perc}${NO_COL}${LP_COLOR_RAM_UNITS}${_LP_PERCENT}${NO_COL}" else # Metric SI norms ask for a space, but we prefer to stay compact. # Coloring value differently from unit helps readability. # The mark should be different enough to not be a problem. lp_ram_color="${LP_COLOR_RAM}${LP_MARK_RAM}${lp_ram_human}${NO_COL}${LP_COLOR_RAM_UNITS}${lp_ram_human_units}${NO_COL}" fi else lp_ram_color="${LP_COLOR_RAM}${LP_MARK_RAM}${NO_COL}" fi else return $? fi } ###################### # System temperature # ###################### # Backends for TEMP. Each backend must return the result in $lp_temperature. # Return the hottest system temperature we get. __lp_temp_sysfs() { local -i temperature for temp_file in ${_LP_LINUX_TEMPERATURE_FILES[@]+"${_LP_LINUX_TEMPERATURE_FILES[@]}"}; do for ignore_file in ${LP_TEMP_SYSFS_IGNORE_FILES[@]+"${LP_TEMP_SYSFS_IGNORE_FILES[@]}"}; do # We want to glob match here. # shellcheck disable=SC2053 [[ "$temp_file" == $ignore_file ]] && continue 2 done if [[ ! -r $temp_file ]]; then continue fi IFS='' read -r temperature <"$temp_file" 2>/dev/null || continue # Input is in millidegrees Celsius. (( temperature = temperature / 1000 )) if [[ -z ${lp_temperature-} ]] || (( temperature > ${lp_temperature:-0} )); then lp_temperature=$temperature fi done } # Implementation using lm-sensors __lp_temp_sensors() { # Return the hottest system temperature we get through the sensors command # Only the integer part is retained local -i i local IFS=$' \t\n' for i in $(LC_ALL=C \sensors -u 2>/dev/null | sed -n 's/^ temp[0-9][0-9]*_input: \([0-9]*\)\..*$/\1/p'); do if [[ -z ${lp_temperature-} ]] || (( i > ${lp_temperature:-0} )); then lp_temperature=$i fi done } # Implementation using 'acpi -t' __lp_temp_acpi() { local -i i local IFS=$' \t\n' # Only the integer part is retained for i in $(LC_ALL=C \acpi -t | sed -n 's/.* \(-\{0,1\}[0-9]*\)\.[0-9]* degrees C$/\1/p'); do if [[ -z ${lp_temperature-} ]] || (( i > ${lp_temperature:-0} )); then lp_temperature=$i fi done } # Dynamic selection of backend __lp_temp_detect() { local lp_temperature cmd if [[ $LP_OS == 'Linux' ]]; then _LP_TEMP_FUNCTION="__lp_temp_sysfs" # Check that we can retrieve temperature at least once "$_LP_TEMP_FUNCTION" 2>/dev/null # If $lp_temperature is set, success! [[ -n "${lp_temperature-}" ]] && return 0 fi for cmd in acpi sensors ; do command -v "$cmd" >/dev/null || continue _LP_TEMP_FUNCTION="__lp_temp_$cmd" "$_LP_TEMP_FUNCTION" 2>/dev/null [[ -n "${lp_temperature-}" ]] && return 0 done unset _LP_TEMP_FUNCTION return 1 } # Returns current highest system temperature. _lp_temperature() { (( LP_ENABLE_TEMP )) || return 2 unset lp_temperature "$_LP_TEMP_FUNCTION" [[ -z ${lp_temperature-} ]] && return 1 (( lp_temperature >= LP_TEMP_THRESHOLD )) } # Display the numeric value as we get from _lp_temperature and colorize it through _lp_color_map. _lp_temperature_color() { _lp_temperature || return "$?" local ret _lp_color_map "$lp_temperature" 120 lp_temperature_color="${LP_MARK_TEMP}${ret}${lp_temperature}°${NO_COL}" } ###################### # Current Disk Usage # ###################### # Convert bytes in human-readable, binary-prefixed notation (using powers of 2). # Precision can be 0, 1 or 2 digits. If not given, precision is two digits. __lp_bytes_to_human() { # bytes, [precision] -> lp_bytes, lp_bytes_units local i="$1" prec="${2:-2}" size remainder="" # This has to be split on multiple lines for Zsh 5.0 (see wiki). local -a units # Not much point in going past PiB, as Bash and Zsh start failing int math. units=("B" "KiB" "MiB" "GiB" "TiB" "PiB" "EiB" "YiB" "ZiB") for ((size=0; i > 1024 && size < ${#units[@]}-1; size++)); do remainder=$((i % 1024 * 100 / 1024)) i=$((i / 1024)) done if [[ -n $remainder ]]; then if ((remainder < 10)); then remainder="0$remainder" fi else # Bytes case. remainder="00" fi # Precision formating. remainder="${remainder:0:$prec}" if [[ -n $remainder ]]; then remainder=".$remainder" fi lp_bytes="$i$remainder" lp_bytes_units="${units[$size+$_LP_FIRST_INDEX]}" } _lp_disk() { ((LP_ENABLE_DISK)) || return 2 lp_disk= lp_disk_human= lp_disk_perc= # df -P . prints only the current FS, in a portable format. # -k to get values in kB (supposedly more portable). # LANG is set to avoid locale mangling with the words number. # Backslash avoids any alias of df. local df="$(LANG=C \df -k -P . 2>/dev/null)" # Example output: # Filesystem 1024-blocks Used Available Capacity Mounted on # /dev/nvme0n1p2 479608528 425188220 30011332 94% / # Placeholder and capacity. local _ capa # Compute location of the targeted column on the safe line. local short="${df#*1024-blocks}" # Second line. local second="${df#*$'\n'}" # Consider only targeted columns. # This may be extended to get other information if needed: # +- total # | +- used +- mount point (may have spaces) # | | | # v v v read -r _ _ lp_disk capa _ <<<"${second:${#df}-${#short}-11}" # Without percent character. lp_disk_perc=$(( 100 - ${capa:0:${#capa}-1} )) [[ -z ${lp_disk} || -z ${lp_disk_perc} ]] && return 1 # Comparison in KiB. if (( lp_disk <= LP_DISK_THRESHOLD || lp_disk_perc <= LP_DISK_THRESHOLD_PERC )); then # df outputs in KiB, but it's more robust to implement lp_human in bytes, # so we need to convert b in KiB here. local lp_bytes lp_bytes_units __lp_bytes_to_human $((lp_disk*1024)) "$LP_DISK_PRECISION" lp_disk_human="${lp_bytes}" lp_disk_human_units="${lp_bytes_units}" return 0 else return 1 fi } _lp_disk_color() { if _lp_disk; then if (( LP_ALWAYS_DISPLAY_VALUES )); then if (( LP_DISPLAY_VALUES_AS_PERCENTS )); then lp_disk_color="${LP_COLOR_DISK}${LP_MARK_DISK}${lp_disk_perc}${NO_COL}${LP_COLOR_DISK_UNITS}${_LP_PERCENT}${NO_COL}" else # Metric SI norms ask for a space, but we prefer to stay compact. # Coloring value differently from unit helps readability. # The mark should be different enough to not be a problem. lp_disk_color="${LP_COLOR_DISK}${LP_MARK_DISK}${lp_disk_human}${NO_COL}${LP_COLOR_DISK_UNITS}${lp_disk_human_units}${NO_COL}" fi else lp_disk_color="${LP_COLOR_DISK}${LP_MARK_DISK}${NO_COL}" fi else return $? fi } ########## # Title # ########## # Deprecated since 2.0 _lp_title() { (( LP_ENABLE_TITLE )) || return # Get the input as pure text local ret __lp_strip_escapes "${1-}" printf '%s' "${_LP_OPEN_ESC}${LP_TITLE_OPEN}${ret}${LP_TITLE_CLOSE}${_LP_CLOSE_ESC}" } _lp_formatted_title() { (( LP_ENABLE_TITLE )) || return 2 # Get the input as pure text local ret __lp_strip_escapes "${1-}" _lp_generated_title="$ret" } _lp_raw_title() { (( LP_ENABLE_TITLE )) || return 2 _lp_generated_title=${1-} } lp_title() { (( LP_ENABLE_TITLE )) || return 2 if [[ -n ${1+x} ]]; then _lp_manual_title=$1 else unset _lp_manual_title fi } if (( _LP_SHELL_zsh )); then __lp_get_last_command_line() { # shellcheck disable=SC2154 command=${history[$HISTCMD]} } else __lp_get_last_command_line() { command=$(HISTTIMEFORMAT='' builtin history 1 2>/dev/null) command=${command#*[[:digit:]][* ] } # Fallback measure if something goes wrong. if [[ -z $command ]]; then command=$BASH_COMMAND fi } fi __lp_print_title_command() { local command __lp_get_last_command_line printf '%s' "${LP_TITLE_OPEN}${_lp_manual_title:-${_lp_generated_title-${SHELL+"$SHELL \$ "}}}${command}${LP_TITLE_CLOSE}" } ################### # CURRENT TIME # ################### # The targeted unicode characters are the "CLOCK FACE" ones # They are located in the codepages between: # U+1F550 (ONE OCLOCK) and U+1F55B (TWELVE OCLOCK), for the plain hours # U+1F55C (ONE-THIRTY) and U+1F567 (TWELVE-THIRTY), for the thirties # Generated with: # perl -C -E 'say join("", map {chr(0x1F550+$_)." ".chr(0x1F55C+$_)." "} 0..11)' _LP_CLOCK=(🕐 🕜 🕑 🕝 🕒 🕞 🕓 🕟 🕔 🕠 🕕 🕡 🕖 🕢 🕗 🕣 🕘 🕤 🕙 🕥 🕚 🕦 🕛 🕧 ) _lp_analog_time() { (( LP_ENABLE_TIME && LP_TIME_ANALOG )) || return 2 # %I: "00".."12" %M: "00".."59" # hh: 1..12 mm: 0..59 local hh mm IFS=' ' read -r hh mm <<<"$(date '+%I %M')" # Bash interprets a '0' prefix as octal # so we have to clean that hh="${hh#0}" mm="${mm#0}" # clock: 0 .. 25 # 1:00..1:14 -> 0 # 1:15..1:44 -> 1 # 1:45..2:15 -> 2 # ... # 12:15..12:44 -> 23 # 12:45..12:59 -> 0 # There is a space just after the clock char because the glyph # width is twice usual glyphs lp_analog_time="${_LP_CLOCK[((hh*60+mm-45)/30)%24+_LP_FIRST_INDEX]} " } _lp_analog_time_color() { _lp_analog_time || return "$?" lp_analog_time_color="${LP_COLOR_TIME}${lp_analog_time}${NO_COL}" } _lp_time() { (( LP_ENABLE_TIME && ! LP_TIME_ANALOG )) || return 2 local ret __lp_escape "$(date "+${LP_TIME_FORMAT}")" lp_time=$ret } _lp_time_color() { _lp_time || return "$?" lp_time_color="${LP_COLOR_TIME}${lp_time}${NO_COL}" } ####################### # Container detection # ####################### _lp_container() { (( LP_ENABLE_CONTAINER )) || return 2 lp_container="" if [[ -n "${SINGULARITY_CONTAINER-}" || -n "${SINGULARITY_NAME-}" ]]; then lp_container="Singlrty" elif [[ -e /run/.containerenv ]]; then if [[ -f /run/.toolboxenv ]]; then lp_container="Toolbox" else lp_container="Podman" fi elif [[ -e /proc/self/cgroup ]]; then local cgroup="$(< /proc/self/cgroup)" if [[ "$cgroup" == *"docker"* ]]; then lp_container="Docker" elif [[ "$cgroup" == *"lxc"* ]]; then lp_container="LXC" else return 1 fi elif [[ -r /run/host/container-manager ]]; then local ret IFS='' read -r ret < /run/host/container-manager __lp_escape "${ret#systemd-}" lp_container="$ret" else return 1 fi } _lp_container_color() { unset lp_container_color _lp_container || return "$?" lp_container_color="«${LP_COLOR_CONTAINER}${lp_container}${NO_COL}»" } _lp_dev_env_color() { # add development environments local -a dev_all dev_all=( "${lp_modules_color-}" "${lp_software_collections_color-}" "${lp_aws_profile_color-}" "${lp_container_color-}" "${lp_python_env_color-}" "${lp_node_env_color-}" "${lp_ruby_env_color-}" "${lp_perl_env_color-}" "${lp_terraform_env_color-}" "${lp_kubernetes_context_color-}" "${lp_cmake_color-}" "${lp_os_color-}" ) local -a dev_active dev_active=() for i in "${dev_all[@]}"; do if [[ "$i" ]]; then dev_active+=("$i") fi done if [[ ${#dev_active[@]} -gt 0 ]] ; then local lp_join _lp_join "${LP_MARK_DEV_MID}" "${dev_active[@]}" lp_dev_env_color="${LP_MARK_DEV_OPEN}${lp_join}${LP_MARK_DEV_CLOSE}" return 0 else unset lp_dev_env_color return 1 fi } ################# # Default theme # ################# _lp_default_theme_activate() { if ((LP_ENABLE_PATH)); then # Default value for LP_PERM when LP_ENABLE_PERM is 0 LP_PERM=${LP_MARK_PERM} # without color else LP_PERM= fi _lp_user local -i user_type="$?" if (( user_type < 2 )); then # if user is not root if (( LP_ENABLE_SUDO )); then LP_COLOR_MARK_NO_SUDO="$LP_COLOR_MARK" fi else # root! if (( ! LP_ENABLE_VCS_ROOT )); then LP_MARK_DISABLED="$LP_MARK_DEFAULT" fi fi # The user or connection type is not expected to change from inside the # shell, so we build this just once. if _lp_username_color; then LP_USER="$lp_username_color" else LP_USER= fi _lp_hostname_color LP_HOST="$lp_hostname_color" # If we are running in a terminal multiplexer, brackets are colored if _lp_multiplexer; then LP_BRACKET_OPEN="${LP_COLOR_IN_MULTIPLEXER}${LP_MARK_MULTIPLEXER_OPEN}${NO_COL}" LP_BRACKET_CLOSE="${LP_COLOR_IN_MULTIPLEXER}${LP_MARK_MULTIPLEXER_CLOSE}${NO_COL}" else LP_BRACKET_OPEN="${LP_MARK_BRACKET_OPEN}" LP_BRACKET_CLOSE="${LP_MARK_BRACKET_CLOSE}" fi _lp_terminal_device # shellcheck disable=SC2034 LP_TTYN=$lp_terminal_device } _lp_default_theme_directory() { if ((LP_ENABLE_PATH)); then # LP_PERM: shows a ":" # - colored in green if user has write permission on the current dir # - colored in red if not # - can set another symbol with LP_MARK_PERM if (( LP_ENABLE_PERM )); then if [[ -w "${PWD}" ]]; then LP_PERM="${LP_COLOR_WRITE}${LP_MARK_PERM}${NO_COL}" else LP_PERM="${LP_COLOR_NOWRITE}${LP_MARK_PERM}${NO_COL}" fi fi local lp_path_format _lp_path_format "$LP_COLOR_PATH" "$LP_COLOR_PATH_LAST_DIR" "$LP_COLOR_PATH_VCS_ROOT" "$LP_COLOR_PATH_SHORTENED" "/" "$LP_COLOR_PATH_SEPARATOR" LP_PWD="${lp_path_format}${NO_COL}" else LP_PWD= return 2 fi } # Do not complain about unused variables. # shellcheck disable=SC2034 _lp_default_theme_prompt_data() { # left of main prompt: space at right if _lp_jobcount_color; then LP_JOBS="$lp_jobcount_color " else LP_JOBS= fi if _lp_temperature_color; then LP_TEMP="$lp_temperature_color " else LP_TEMP= fi if _lp_load_color; then LP_LOAD="$lp_load_color " else LP_LOAD= fi if _lp_battery_color; then LP_BATT="$lp_battery_color " else LP_BATT= fi if _lp_wifi_signal_strength_color; then LP_WIFI="$lp_wifi_signal_strength_color " else LP_WIFI= fi if _lp_ram_color; then LP_RAM="$lp_ram_color " else LP_RAM= fi if _lp_disk_color; then LP_DISK="$lp_disk_color " else LP_DISK= fi if _lp_time_color; then LP_TIME="$lp_time_color " elif _lp_analog_time_color; then LP_TIME="$lp_analog_time_color " else LP_TIME= fi if _lp_sudo_active_color; then LP_COLOR_MARK="$lp_sudo_active_color" fi if _lp_dirstack_color; then LP_DIRSTACK=" $lp_dirstack_color" else LP_DIRSTACK= fi # in main prompt: no space if _lp_http_proxy_color; then LP_PROXY="$lp_http_proxy_color" else LP_PROXY= fi if _lp_env_vars_color; then LP_ENVVARS="$lp_env_vars_color" else LP_ENVVARS= fi if _lp_shell_level_color; then LP_SHLVL="$lp_shell_level_color" else LP_SHLVL= fi # All the following "dev-related" variables may be used alone by theme designers. # If you need the whole section, you can also use LP_DEV_ENV defined below. if _lp_python_env_color; then LP_VENV=" $lp_python_env_color" else LP_VENV= fi if _lp_node_env_color; then LP_NODE_VENV=" $lp_node_env_color" else LP_NODE_VENV= fi if _lp_ruby_env_color; then LP_RUBY_VENV=" $lp_ruby_env_color" else LP_RUBY_VENV= fi if _lp_perl_env_color; then LP_PERL_VENV=" $lp_perl_env_color" else LP_PERL_VENV= fi if _lp_kubernetes_context_color; then LP_KUBECONTEXT=" $lp_kubernetes_context_color" else LP_KUBECONTEXT= fi if _lp_terraform_env_color; then LP_TFSPACE=" $lp_terraform_env_color" else LP_TFSPACE= fi if _lp_container_color; then LP_CONTAINER=" $lp_container_color" else LP_CONTAINER= fi if _lp_software_collections_color; then LP_SCLS=" $lp_software_collections_color" else LP_SCLS= fi if _lp_aws_profile_color; then LP_AWS_PROFILE=" $lp_aws_profile_color" else LP_AWS_PROFILE= fi if _lp_cmake_color; then LP_CMAKE=" $lp_cmake_color" else LP_CMAKE= fi if _lp_os_color; then LP_OPSYS=" $lp_os_color" else LP_OPSYS= fi if _lp_modules_color; then LP_MODULES=" $lp_modules_color" else LP_MODULES= fi # End of the section for dev-related variables. # Alternative to atomic variables above: # a correctly separated string list with everything. if _lp_dev_env_color ; then LP_DEV_ENV=" $lp_dev_env_color" else LP_DEV_ENV= fi if _lp_runtime_color; then LP_RUNTIME=" $lp_runtime_color" else LP_RUNTIME= fi if _lp_error_color; then LP_ERR=" $lp_error_color" else LP_ERR= fi if _lp_error_meaning_color; then LP_ERR_MEANING="$lp_error_meaning_color" else LP_ERR_MEANING= fi if _lp_find_vcs && _lp_vcs_details_color; then LP_VCS=" $lp_vcs_details_color" else LP_VCS= fi _lp_smart_mark LP_MARK="${lp_smart_mark}${NO_COL} " } _lp_default_theme_prompt_template() { if [[ -f "${LP_PS1_FILE-}" ]]; then # shellcheck source=templates/minimal/minimal.ps1 source "$LP_PS1_FILE" fi if [[ -z "${LP_PS1-}" ]]; then # Add title escape time, battery, load, temperature, RAM, disk, wifi, jobs. PS1="${LP_PS1_PREFIX}${LP_TIME}${LP_BATT}${LP_LOAD}${LP_TEMP}${LP_RAM}${LP_DISK}${LP_WIFI}${LP_JOBS}" # Add multiplexer brackets, user, host, permissions colon, working directory, dirstack, proxy, watched environment variables and nested shell level. PS1+="${LP_BRACKET_OPEN}${LP_USER}${LP_HOST}${LP_PERM}${LP_PWD}${LP_DIRSTACK}${LP_BRACKET_CLOSE}${LP_PROXY}${LP_ENVVARS}${LP_SHLVL}" # Add the list of development environments/config/etc. PS1+="${LP_DEV_ENV}" # Add VCS infos # If root, the info has not been collected unless LP_ENABLE_VCS_ROOT # is set. PS1+="${LP_VCS}" # Add last runtime, return code & meaning, prompt mark and user-defined postfix. PS1+="${LP_RUNTIME}${LP_ERR}${LP_ERR_MEANING}${LP_MARK_PREFIX}${LP_COLOR_MARK}${LP_MARK}${LP_PS1_POSTFIX}" # Get the core sections without prompt escapes and make them into a title. _lp_formatted_title "${LP_PS1_PREFIX}${LP_BRACKET_OPEN}${LP_USER}${LP_HOST}${LP_MARK_PERM}${lp_path-}${LP_BRACKET_CLOSE}${LP_MARK_PREFIX}${LP_MARK}${LP_PS1_POSTFIX}" else PS1=$LP_PS1 fi } _lp_default_theme_prompt() { _lp_default_theme_prompt_data _lp_default_theme_prompt_template } ######################## # Construct the prompt # ######################## __lp_set_prompt() { # Display the return value of the last command, if different from zero # As this get the last returned code, it should be called first local -i lp_error="$?" if (( LP_ENABLE_RUNTIME || LP_ENABLE_RUNTIME_BELL )); then __lp_runtime_after fi # bash: execute the old prompt hook if [[ -n ${LP_OLD_PROMPT_COMMAND-} ]]; then eval "$LP_OLD_PROMPT_COMMAND" fi if (( LP_ENABLE_RUNTIME_BELL && ${_LP_RUNTIME_SECONDS-0} >= LP_RUNTIME_BELL_THRESHOLD )); then printf '%s' "$_LP_TI_BELL" fi if [[ "$_LP_OLD_THEME" != "$LP_THEME" ]]; then lp_theme "$LP_THEME" >/dev/null 2>&1 fi # Localize cache data variables local _lp_git_diff_shortstat_uncommitted _lp_git_diff_shortstat_unstaged _lp_git_diff_shortstat_staged # if change of working directory if [[ "${LP_OLD_PWD-}" != "LP:$PWD" ]]; then # Update directory icon for MacOS X "$_LP_TERM_UPDATE_DIR" "$_LP_THEME_DIRECTORY_FUNCTION" # Prefix with 'LP:' to prevent Zsh with AUTO_NAME_DIRS enabled using # this var as a name for the working directory, that will be used by # the '%' and related prompt sequences. # See https://github.com/liquidprompt/liquidprompt/issues/124 for details. LP_OLD_PWD="LP:$PWD" fi "$_LP_THEME_PROMPT_FUNCTION" if (( LP_ENABLE_TITLE )); then printf '%s' "${LP_TITLE_OPEN}${_lp_manual_title:-${_lp_generated_title-${SHELL-}}}${LP_TITLE_CLOSE}" fi } __lp_preexec() { if (( LP_ENABLE_RUNTIME || LP_ENABLE_RUNTIME_BELL )); then __lp_runtime_before fi if (( LP_ENABLE_TITLE_COMMAND )); then __lp_print_title_command fi } __lp_before_command() { # For debugging #printf 'XXX %s\n' "$BASH_COMMAND" # If this is the last thing before prompt is being drawn, the command is done, # so mark the next trap. Note these two events could be at the same # time, so no elif is used. local first_command last_command first_command=$PROMPT_COMMAND if (( ${BASH_VERSINFO[0]:-0} > 5 || ( ${BASH_VERSINFO[0]:-0} == 5 && ${BASH_VERSINFO[1]:-0} >= 1 ) )); then last_command=${PROMPT_COMMAND[-1]} else last_command=$PROMPT_COMMAND fi if [[ "$last_command" == *"$BASH_COMMAND" ]]; then _LP_AT_PROMPT=1 # Return early, because the PROMPT_COMMAND should never trigger preexec hooks. return fi # If this is the first time after the user submitted the command, # execute the hooks, unless the user submitted an empty command. if (( _LP_AT_PROMPT )); then _LP_AT_PROMPT=0 if [[ "$first_command" != "$BASH_COMMAND"* ]]; then __lp_preexec fi fi } prompt_tag() { if [[ -n "${1-}" ]]; then export LP_PS1_PREFIX="$1 " else export LP_PS1_PREFIX= fi } # Activate Liquid Prompt prompt_on() { # Reset so all PWD dependent variables are computed after loading LP_OLD_PWD="" # if Liquid Prompt has not been already set if [[ -z "${LP_OLD_PS1-}" ]]; then LP_OLD_PS1="$PS1" fi if (( _LP_SHELL_bash )); then # Prevent some cases where the user shoots in his own foot. # PROMPT_COMMAND is not exported by default, but some users # incorrectly export it from their profile/bashrc (GitHub #450), # so we preventively UNexport it. # TODO: warn the user if it was exported if (( ${BASH_VERSINFO[0]:-0} > 4 || ( ${BASH_VERSINFO[0]:-0} == 4 && ${BASH_VERSINFO[1]:-0} >= 2 ) )); then # -g is only available since bash 4.2 declare -g +x PROMPT_COMMAND fi if ! __lp_use_bash_preexec; then local set_prompt_command if (( LP_DEBUG_TIME )); then set_prompt_command="time __lp_set_prompt" else set_prompt_command=__lp_set_prompt fi if (( ${BASH_VERSINFO[0]:-0} > 5 || ( ${BASH_VERSINFO[0]:-0} == 5 && ${BASH_VERSINFO[1]:-0} >= 1 ) )); then # PROMPT_COMMAND is an array since bash 5.1 if ! ( __lp_array_contains __lp_set_prompt ${PROMPT_COMMAND[@]+"${PROMPT_COMMAND[@]}"} \ || __lp_array_contains "time __lp_set_prompt" ${PROMPT_COMMAND[@]+"${PROMPT_COMMAND[@]}"} ); then PROMPT_COMMAND+=( "$set_prompt_command" ) fi else if [[ -z ${LP_OLD_PROMPT_COMMAND+x} ]]; then LP_OLD_PROMPT_COMMAND="${PROMPT_COMMAND-}" fi # shellcheck disable=SC2178 PROMPT_COMMAND="$set_prompt_command" fi if (( ${_LP_ENABLE_PREEXEC-0} )); then _LP_AT_PROMPT=0 _LP_RUNTIME_LAST_SECONDS=$SECONDS # __lp_before_command gets called just before bash executes a command, # including $PROMPT_COMMAND # Pass $_ to this call, because it sets $_ to what it already was trap '__lp_before_command "$_"' DEBUG fi else # We do not want __lp_set_prompt to show up twice in precmd_functions; if it does, then LP_ENABLE_ERROR # breaks because even if the first call of __lp_set_prompt has $? != 0, the second one will have $? == 0. # (Same for __lp_debug_timed_lp_set_prompt.) # This conditional is intended to check $precmd_functions for the presence of '__lp_set_prompt' or # '__lp_debug_timed_lp_set_prompt' so they get added at most once if ! ( __lp_array_contains __lp_set_prompt ${precmd_functions[@]+"${precmd_functions[@]}"} \ || __lp_array_contains __lp_debug_timed_set_prompt ${precmd_functions[@]+"${precmd_functions[@]}"} ); then if (( LP_DEBUG_TIME )); then __lp_debug_timed_lp_set_prompt() { time __lp_set_prompt } precmd_functions+=(__lp_debug_timed_lp_set_prompt) else precmd_functions+=(__lp_set_prompt) fi fi if (( ${_LP_ENABLE_PREEXEC-0} )); then _LP_RUNTIME_LAST_SECONDS=$SECONDS # It's less bad to have this be duped than __lp_set_prompt, but let's be sure if ! ( __lp_array_contains __lp_preexec ${preexec_functions[@]+"${preexec_functions[@]}"} ); then preexec_functions+=(__lp_preexec) fi fi fi else # zsh if [[ -n "${prompt_theme-}" && "$prompt_theme" != off ]]; then _LP_ZSH_PROMPT_THEME="$prompt_theme" # Disable the prompt to disable its precmd hook prompt off fi if [[ -z ${_LP_OLD_SETOPT-} ]]; then # Dump option names: echo ${(ko)options} # shellcheck disable=SC2154 if [[ "${options[promptpercent]}" == on ]]; then _LP_OLD_SETOPT="promptpercent" else _LP_OLD_SETOPT="nopromptpercent" fi fi # Set options that affect PS1 evaluation; enable percent expansion setopt promptpercent add-zsh-hook precmd __lp_set_prompt if (( ${_LP_ENABLE_PREEXEC-0} )); then _LP_RUNTIME_LAST_SECONDS=$SECONDS declare -gi _LP_RUNTIME_SECONDS add-zsh-hook preexec __lp_preexec fi fi } __lp_disable_hooks() { if (( _LP_SHELL_bash )); then if __lp_use_bash_preexec; then # Disable previous hooks as options that set them may have changed. for i in ${precmd_functions[@]+"${!precmd_functions[@]}"}; do local value="${precmd_functions[i]}" if [[ $value == "__lp_set_prompt" || $value == "__lp_debug_timed_lp_set_prompt" ]]; then unset 'precmd_functions[i]' fi done for i in ${preexec_functions[@]+"${!preexec_functions[@]}"}; do local value="${preexec_functions[i]}" if [[ $value == "__lp_preexec" ]]; then unset 'preexec_functions[i]' fi done else if (( ${BASH_VERSINFO[0]:-0} > 5 || ( ${BASH_VERSINFO[0]:-0} == 5 && ${BASH_VERSINFO[1]:-0} >= 1 ) )); then # PROMPT_COMMAND is an array since bash 5.1 for i in ${PROMPT_COMMAND[@]+"${!PROMPT_COMMAND[@]}"}; do local value="${PROMPT_COMMAND[i]}" if [[ $value == "__lp_set_prompt" || $value == "time __lp_set_prompt" ]]; then unset 'PROMPT_COMMAND[i]' fi done else if [[ -n ${LP_OLD_PROMPT_COMMAND+x} ]]; then # shellcheck disable=SC2178 PROMPT_COMMAND="${LP_OLD_PROMPT_COMMAND-}" unset LP_OLD_PROMPT_COMMAND else unset PROMPT_COMMAND fi fi # Disable the DEBUG trap used by the RUNTIME or TITLE_COMMAND features # TODO: Fix this: Bash will unset the function's DEBUG trap, not the global one. if (( ${_LP_ENABLE_PREEXEC-0} )); then trap - DEBUG fi fi else # zsh { add-zsh-hook -d precmd __lp_set_prompt add-zsh-hook -d preexec __lp_preexec } >/dev/null fi } # Come back to the old prompt prompt_off() { __lp_disable_hooks PS1=$LP_OLD_PS1 if (( _LP_SHELL_zsh )); then setopt "${_LP_OLD_SETOPT}" if [[ -n ${_LP_ZSH_PROMPT_THEME-} ]]; then prompt "$_LP_ZSH_PROMPT_THEME" unset _LP_ZSH_PROMPT_THEME fi fi } # Use an empty prompt: just the \$ mark prompt_OFF() { __lp_disable_hooks PS1="$_LP_MARK_SYMBOL " } lp_theme() { local theme="${1-}" LP_THEME="${_LP_OLD_THEME-}" if [[ $theme == '--list' ]]; then local -a lp_theme_list __lp_theme_list printf '%s\n' "${lp_theme_list[@]}" return fi local f_prompt="_lp_${theme}_theme_prompt" local f_dir="_lp_${theme}_theme_directory" local f_activate="_lp_${theme}_theme_activate" local f_version="_lp_${theme}_theme_version_check" if [[ -z $theme ]]; then printf '%s\n%s\n' \ 'Must pass in the name of a theme. If you meant the default Liquid Prompt theme, try "default".' \ 'Run "lp_theme --list" to see all loaded and available themes.' >&2 return 1 fi if __lp_is_function "$f_version"; then local -a lp_version_check "$f_version" if ! _lp_version_greatereq ${lp_version_check[@]+"${lp_version_check[@]}"}; then local lp_version _lp_version_string ${lp_version_check[@]+"${lp_version_check[@]}"} printf 'ERROR: Loading theme "%s" failed: theme requires version %s or greater of Liquid Prompt.\n' \ "$theme" "$lp_version" >&2 return 2 fi fi if ! __lp_is_function "$f_prompt"; then printf 'ERROR: Loading theme "%s" failed: cannot find function "%s". Please source the theme file first.\n' \ "$theme" "$f_prompt" >&2 return 2 fi if ! __lp_is_function "$f_dir"; then f_dir=":" fi if ! __lp_is_function "$f_activate"; then f_activate=":" fi _LP_THEME_ACTIVATE_FUNCTION=$f_activate _LP_THEME_DIRECTORY_FUNCTION=$f_dir _LP_THEME_PROMPT_FUNCTION=$f_prompt LP_THEME="$theme" _LP_OLD_THEME="$theme" "$f_activate" prompt_on } # By default, sourcing 'liquidprompt' will activate Liquid Prompt if [ "${1-}" != "--no-activate" ]; then lp_activate fi # vim: ft=sh et sts=4 sw=4 tw=120