bashrc/.bashrc.d/22-prompt.bashrc

4628 lines
147 KiB
Plaintext
Raw Permalink Normal View History

2023-03-18 12:49:08 +01:00
#!/usr/bin/env bash
# executed by bash(1) for non-login shells.
################################################################################
# LIQUID PROMPT
# An intelligent and non-intrusive prompt for Bash and zsh
################################################################################
_LP_VERSION=(2 2 0 alpha 1)
# 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 <http://www.gnu.org/licenses/>.
# 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>&1
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
_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() {
if ! shopt -q extglob ; then
local _lp_no_extglob=true
shopt -s extglob
fi
ret="${1//"${_LP_OPEN_ESC}"!(*"${_LP_CLOSE_ESC}"*)"${_LP_CLOSE_ESC}"}"
if [[ -n ${_lp_no_extglob-} ]]; then
shopt -u extglob
fi
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>&1
return
fi
_LP_SHELL_bash=0
_LP_SHELL_zsh=1
_LP_OPEN_ESC="%{"
_LP_CLOSE_ESC="%}"
_LP_MARK_SYMBOL='%%'
_LP_FIRST_INDEX=1
_LP_PERCENT='%%'
_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: 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.
# [OPTIONAL?] 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_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_PERCENTS_ALWAYS=${LP_PERCENTS_ALWAYS:-1}
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_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_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_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_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_SSH_COLORS=${LP_ENABLE_SSH_COLORS:-0}
LP_DISABLED_VCS_PATHS=( ${LP_DISABLED_VCS_PATHS[@]+"${LP_DISABLED_VCS_PATHS[@]}"} )
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_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_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_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_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_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"
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
return
fi
# 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=liquidpromptrc-dist
source "$configfile"
break
fi
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
# 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
}
# 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_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
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_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_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; then
(( LP_ENABLE_TITLE = LP_ENABLE_TITLE && LP_ENABLE_SCREEN_TITLE ))
LP_TITLE_OPEN=$'\Ek'
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() {
# 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
local cksum="$(hostname | 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>_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 character (e.g. spaces) between $1 and $2,
# so as to make the resulting string the same width as the current terminal.
_lp_fill() {
local left="$1" right="$2" fillchars="${3:-" "}" splitends="${4:-1}"
# 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"
local gap_width=$((${COLUMNS:-80}-${#left_as_text}-${#right_as_text}))
if [[ $gap_width -lt 0 ]] ; then
lp_fill="${left}${right}"
return 1
fi
local filled=""
__lp_strip_escapes "$fillchars"
local fillchars_as_text="${ret}"
local fillchars_as_text_width=${#fillchars_as_text}
local nb_fillchars=$((gap_width/fillchars_as_text_width))
local i
for (( i=0; i < nb_fillchars; i++ )) ; do
filled+="$fillchars"
done
__lp_strip_escapes "$filled"
local actual_width=${#ret}
# 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
# The last occurence is necessarily stripped from escaped sequences,
# or else we may have dangling sequences.
for (( i=_LP_FIRST_INDEX; i < $((${#fillchars_as_text}+_LP_FIRST_INDEX)); i++ )) ; do
# Get one single character.
if (( _LP_SHELL_zsh )) ; then
filled+="${fillchars_as_text[i,i]}"
else
filled+="${fillchars_as_text:i:1}"
fi
__lp_strip_escapes "$filled"
actual_width=${#ret}
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() {
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_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() {
[[ -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() {
if [[ -r /etc/debian_chroot ]]; then
IFS= read -r lp_chroot </etc/debian_chroot
if [[ -n "$lp_chroot" ]]; then
local ret
__lp_escape "$lp_chroot"
lp_chroot=$ret
return 0
fi
fi
return 1
}
_lp_error() {
(( LP_ENABLE_ERROR )) || return 2
(( lp_error != 0 ))
}
_lp_error_color() {
_lp_error || return "$?"
lp_error_color="${LP_COLOR_ERR}${lp_error}${NO_COL}"
}
_lp_error_meaning() {
# Reasonable codes for a POSIX env.
(( LP_ENABLE_ERROR_MEANING )) || return 2
(( lp_error != 0 )) || return 1
local meaning=
case $lp_error in
# Common
1 )
meaning="error" #1
;;
2 )
meaning="usage" #2
;;
126 )
meaning="no perm" #126
;;
127 )
meaning="not found" #127
;;
128 )
meaning="bad exit" #128
;;
# Portable and meaningful codes from signal.h:
130 )
meaning="interrupted" #128+2 SIGINT Ctrl-C
;;
131 )
meaning="quit" #128+3 SIGQUIT
;;
137 )
meaning="killed" #128+9 SIGKILL
;;
139 )
meaning="segfault" #128+11 SIGSEGV
;;
143 )
meaning="terminated" #128+15 SIGTERM
;;
141 )
meaning="pipe" #128+13 SIGPIPE
;;
# Not including the widespread SIGUSR* because they are not portable across architectures.
*)
if ((LP_ENABLE_ERROR_MEANING_EXTENDED)); then
case $lp_error in
# sysexit.h
64 )
meaning="usage" #64+0
;;
65 )
meaning="data format" #64+1
;;
66 )
meaning="no input" #64+2
;;
67 )
meaning="no user" #64+3
;;
68 )
meaning="no host" #64+4
;;
69 )
meaning="unavailable" #64+5
;;
70 )
meaning="internal" #64+6
;;
71 )
meaning="OS error" #64+7
;;
72 )
meaning="OS file" #64+8
;;
73 )
meaning="file create" #64+9
;;
74 )
meaning="I/O err" #64+10
;;
75 )
meaning="temporary" #64+11
;;
76 )
meaning="protocol" #64+12
;;
77 )
meaning="no perm" #64+13
;;
78 )
meaning="bad config" #64+14
;;
# signal.h for POSIX on x86/ARM (from `man 7 signal`)
# The OS will exit with 128 + the actual signal ID.
# [action] indicates the default OS action, ignored/continue are not raised up to the shell,
# and thus may be commented out if needed (but the OS config may differ, so they are kept here).
129 )
meaning="hangup" #128+1 SIGHUP
;;
132 )
meaning="illegal" #128+4 SIGILL
;;
133 )
meaning="trap" #128+5 SIGTRAP
;;
134 )
meaning="I/O trap" #128+6 SIGIOT
;;
135 )
meaning="bus" #128+7 SIGBUS (10, on SPARC, MIPS and RISC)
;;
136 )
meaning="arithmetic" #128+8 SIGFPE
;;
138 )
meaning="user 1" #128+10 SIGUSR1 (30 on SPARC, 16 on MIPS & RISC)
;;
140 )
meaning="user 2" #128+12 SIGUSR2 (31 on SPARC, 17 on MIPS & RISC)
;;
142 )
meaning="alarm" #128+14 SIGALRM
;;
144 )
meaning="stack fault" #128+16 SIGSTKFLT (7 on RISC, not on SPARC & MPIS)
;;
145 )
meaning="child" #128+17 SIGCHLD (20 on SPARC, 18 on MIPS & RISC) [ignored ]
;;
146 )
meaning="continue" #128+18 SIGCONT (19 on SPARC, 25 on MIPS, 26 on RISC) [continue]
;;
147 )
meaning="stop" #128+19 SIGSTOP (17 on SPARC, 23 on MIPS, 24 on RISC)
;;
148 )
meaning="term stop" #128+20 SIGTSTP (18 on SPARC, 24 on MIPS, 25 on RISC)
;;
149 )
meaning="bckgrd read" #128+21 SIGTTIN (26 on MIPS, 27 on RISC)
;;
150 )
meaning="bckgrd write" #128+22 SIGTTOU (27 on MIPS, 28 on RISC)
;;
151 )
meaning="socket" #128+23 SIGURG (16 on SPARC, 21 on MIPS, 29 on RISC) [ignored ] +BSD
;;
152 )
meaning="CPU limit" #128+24 SIGXCPU (30 on MIPS, 12 on RISC)
;;
153 )
meaning="file size" #128+25 SIGXFSZ (31 on MIPS, 30 on SPARC) +BSD
;;
154 )
meaning="virt. alarm" #128+26 SIGVTALRM (28 o MIPS, 20 on RISC) +BSD
;;
155 )
meaning="profilr time" #128+27 SIGPROF (29 on MIPS, 21 on RISC)
;;
156 )
meaning="win resize" #128+28 SIGWINCH (20 on MIPS, 23 on RISC) [ignored ] +BSD, Sun
;;
157 )
meaning="I/O allowed" #128+29 SIGIO (23 on SPARC, 22 on MIPS & RISC) +BSD
;;
158 )
meaning="power fail" #128+30 SIGPWR (29 on SPARC, 19 on MIPS & RISC)
;;
159 )
meaning="bad sys call" #128+31 SIGSYS (12 on SPARC & MIPS)
;;
*)
return 1 # No known error meaning.
;;
esac
else
return 1 # We do not want to display those error meanings.
fi
;; # End of * for simple case switch.
esac
lp_error_meaning="${meaning}"
}
_lp_error_meaning_color() {
_lp_error_meaning || return "$?"
# Not sure if we would want to make the parentheses configurable.
# Assumed no, as the meaning is straightforward and this function is simple to bypass in a theme.
lp_error_meaning_color="${LP_COLOR_ERR_MEANING}(${lp_error_meaning})${NO_COL}"
}
# shellcheck disable=SC2034
_lp_multiplexer() {
if [[ -n ${TMUX-} ]]; then
lp_multiplexer=tmux
return 0
elif [[ "${TERM-}" == screen* ]]; then
lp_multiplexer=screen
return 0
fi
return 1
}
_lp_http_proxy() {
(( LP_ENABLE_PROXY )) || return 2
if [[ -n "${http_proxy-}${HTTP_PROXY-}${https_proxy-}${HTTPS_PROXY-}${all_proxy-}${ALL_PROXY-}" ]]; then
local ret
__lp_escape "${http_proxy:-${HTTP_PROXY:-${https_proxy:-${HTTPS_PROXY:-${all_proxy:-$ALL_PROXY}}}}}"
# shellcheck disable=SC2034
lp_http_proxy=$ret
else
return 1
fi
}
_lp_http_proxy_color() {
_lp_http_proxy || return "$?"
lp_http_proxy_color="${LP_COLOR_PROXY}${LP_MARK_PROXY}${NO_COL}"
}
_lp_env_vars() { # [color_set, [color_unset]]
# Expects LP_ENV_VARS to be an array containing items of the form:
# "<ENV_VAR_NAME> <string if set>[ <string if unset>]"
# Strings may be `%s`, which will be replaced by the variable's actual content.
(( LP_ENABLE_ENV_VARS )) || return 2
local color_set="${1-}"
local color_unset="${2-}"
local color_no="${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_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_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_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_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
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
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"
}
# 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() {
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_dir}/MERGE_HEAD" ]]; then
lp_vcs_head_status="MERGING"
elif [[ -d "${lp_vcs_dir}/rebase-merge" ]]; then
test -r "${lp_vcs_dir}/rebase-merge/msgnum" && read -r step <"${lp_vcs_dir}/rebase-merge/msgnum"
test -r "${lp_vcs_dir}/rebase-merge/end" && read -r total <"${lp_vcs_dir}/rebase-merge/end"
if [[ -f "${lp_vcs_dir}/rebase-merge/interactive" ]]; then
lp_vcs_head_status="REBASE-i"
else
lp_vcs_head_status="REBASE-m"
fi
elif [[ -d "${lp_vcs_dir}/rebase-apply" ]]; then
test -r "${lp_vcs_dir}/rebase-apply/next" && read -r step <"${lp_vcs_dir}/rebase-apply/next"
test -r "${lp_vcs_dir}/rebase-apply/last" && read -r total <"${lp_vcs_dir}/rebase-apply/last"
if [[ -f "${lp_vcs_dir}/rebase-apply/rebasing" ]]; then
lp_vcs_head_status="REBASE"
elif [[ -f "${lp_vcs_dir}/rebase-apply/applying" ]]; then
lp_vcs_head_status="AM"
else
lp_vcs_head_status="AM/REBASE"
fi
elif [[ -f "${lp_vcs_dir}/CHERRY_PICK_HEAD" ]]; then
lp_vcs_head_status="CHERRY-PICKING"
elif [[ -f "${lp_vcs_dir}/REVERT_HEAD" ]]; then
lp_vcs_head_status="REVERTING"
elif [[ -f "${lp_vcs_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
}
# 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/nojhan/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 ; }
# 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 ; }
# 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 ; }
# 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_fossil_unstaged_files() { return 2 ; }
_lp_fossil_unstaged_lines() { return 2 ; }
_lp_fossil_staged_files() { return 2 ; }
_lp_fossil_staged_lines() { 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_PERCENTS_ALWAYS )); 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_PERCENTS_ALWAYS )); 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
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 )
}
_lp_cpu_load () {
local _ IFS=$' \t'
read -r lp_cpu_load _ < /proc/loadavg
}
;;
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_PERCENTS_ALWAYS )); then
lp_load_color+="${lp_load}"
fi
lp_load_color+="$NO_COL"
}
######################
# 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
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}"
}
##########
# 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_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() {
# Default value for LP_PERM when LP_ENABLE_PERM is 0
LP_PERM=${LP_MARK_PERM} # without color
_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() {
# 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}"
}
# 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_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_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=liquid.ps1
source "$LP_PS1_FILE"
fi
if [[ -z "${LP_PS1-}" ]]; then
# add title escape time, jobs, load and battery
PS1="${LP_PS1_PREFIX}${LP_TIME}${LP_BATT}${LP_LOAD}${LP_TEMP}${LP_WIFI}${LP_JOBS}"
# add user, host, permissions colon, working directory, and dirstack
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 return code and prompt mark
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
# 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/nojhan/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_before_command() {
# For debugging
#printf 'XXX %s\n' "$BASH_COMMAND"
# If this is the first time after the user submitted the command,
# execute the hooks.
if (( _LP_AT_PROMPT )); then
_LP_AT_PROMPT=0
if (( LP_ENABLE_RUNTIME || LP_ENABLE_RUNTIME_BELL )); then
__lp_runtime_before
fi
if (( LP_ENABLE_TITLE_COMMAND )); then
__lp_print_title_command
fi
fi
# If this is when the 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.
if [[ "$BASH_COMMAND" == __lp_set_prompt ]]; then
_LP_AT_PROMPT=1
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
PROMPT_COMMAND+=( "$set_prompt_command" )
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_RUNTIME || LP_ENABLE_RUNTIME_BELL || LP_ENABLE_TITLE_COMMAND )); 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_RUNTIME || LP_ENABLE_RUNTIME_BELL )); 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_runtime_before ${preexec_functions[@]+"${preexec_functions[@]}"} ); then
preexec_functions+=(__lp_runtime_before)
fi
fi
if (( LP_ENABLE_TITLE_COMMAND )); then
# It's less bad to have this be duped than __lp_set_prompt, but let's be sure
if ! ( __lp_array_contains __lp_print_title_command ${preexec_functions[@]+"${preexec_functions[@]}"} ); then
preexec_functions+=(__lp_print_title_command)
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_RUNTIME || LP_ENABLE_RUNTIME_BELL )); then
_LP_RUNTIME_LAST_SECONDS=$SECONDS
add-zsh-hook preexec __lp_runtime_before
fi
if (( LP_ENABLE_TITLE_COMMAND )); then
add-zsh-hook preexec __lp_print_title_command
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_runtime_before" || $value == "__lp_print_title_command" ]]; 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
fi
fi
# Disable the DEBUG trap used by the RUNTIME or TITLE_COMMAND features
if (( ${LP_ENABLE_RUNTIME-0} || ${LP_ENABLE_RUNTIME_BELL-0} || ${LP_ENABLE_TITLE_COMMAND-0} )); then
trap - DEBUG
fi
fi
else # zsh
# Disable previous hooks as options that set them
# may have changed
{
add-zsh-hook -d precmd __lp_set_prompt
add-zsh-hook -d preexec __lp_runtime_before
add-zsh-hook -d preexec __lp_print_title_command
} >/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-}"
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" f_dir="_lp_${theme}_theme_directory" f_activate="_lp_${theme}_theme_activate"
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>&1
return 1
fi
if ! __lp_is_function "$f_prompt"; then
printf 'Loading theme "%s" failed: cannot find function "%s". Please source the theme file first.\n' \
"$theme" "$f_prompt" 2>&1
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
"$f_activate"
prompt_on
}
# By default, sourcing 'liquidprompt' will activate Liquid Prompt
if [ "${1-}" != "--no-activate" ]; then
lp_activate
fi
# vim: set et sts=4 sw=4 tw=120 ft=sh: