bashrc/.bashrc.d/22-prompt.bashrc
2023-03-18 18:49:08 +07:00

4628 lines
147 KiB
Bash
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/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: