diff options
Diffstat (limited to 'powerlevel9k.zsh-theme')
-rwxr-xr-x | powerlevel9k.zsh-theme | 1297 |
1 files changed, 801 insertions, 496 deletions
diff --git a/powerlevel9k.zsh-theme b/powerlevel9k.zsh-theme index 4939cb35..c022984f 100755 --- a/powerlevel9k.zsh-theme +++ b/powerlevel9k.zsh-theme @@ -1,19 +1,18 @@ # vim:ft=zsh ts=2 sw=2 sts=2 et fenc=utf-8 ################################################################ -# powerlevel9k Theme +# powerlevel10k Theme +# https://github.com/romkatv/powerlevel10k +# +# Forked from powerlevel9k Theme # https://github.com/bhilburn/powerlevel9k # -# This theme was inspired by agnoster's Theme: +# Which was inspired by agnoster's Theme: # https://gist.github.com/3712874 ################################################################ ################################################################ -# For basic documentation, please refer to the README.md in the top-level -# directory. For more detailed documentation, refer to the project wiki, hosted -# on Github: https://github.com/bhilburn/powerlevel9k/wiki -# -# There are a lot of easy ways you can customize your prompt segments and -# theming with simple variables defined in your `~/.zshrc`. +# For basic documentation, please refer to the README.md in the +# top-level directory. ################################################################ ## Turn on for Debugging @@ -66,9 +65,16 @@ source "${p9k_directory}/functions/colors.zsh" source "${p9k_directory}/functions/vcs.zsh" +if [[ $POWERLEVEL9K_DISABLE_GITSTATUS != true ]]; then + source ${POWERLEVEL9K_GITSTATUS_DIR:-${p9k_directory}/gitstatus}/gitstatus.plugin.zsh +fi + # cleanup temporary variables. unset p9k_directory +[[ -v POWERLEVEL9K_LEFT_PROMPT_ELEMENTS ]] || POWERLEVEL9K_LEFT_PROMPT_ELEMENTS=(context dir vcs) +[[ -v POWERLEVEL9K_RIGHT_PROMPT_ELEMENTS ]] || POWERLEVEL9K_RIGHT_PROMPT_ELEMENTS=(status root_indicator background_jobs history time) + ################################################################ # Color Scheme ################################################################ @@ -93,10 +99,100 @@ fi # this naming convention. ################################################################ -# The `CURRENT_BG` variable is used to remember what the last BG color used was -# when building the left-hand prompt. Because the RPROMPT is created from -# right-left but reads the opposite, this isn't necessary for the other side. -CURRENT_BG='NONE' +# Specifies the maximum number of elements in the cache. When the cache grows over this limit, +# it gets cleared. This is meant to avoid memory leaks when a rogue prompt is filling the cache +# with data. +[ -v POWERLEVEL9K_MAX_CACHE_SIZE ] || typeset -gi POWERLEVEL9K_MAX_CACHE_SIZE=10000 + +typeset -gH _P9K_RETVAL +typeset -gH _P9K_CACHE_KEY +typeset -gaH _P9K_CACHE_VAL +typeset -gAH _P9K_CACHE=() + +# Note: Several performance-critical functions return results to the caller via global +# variabls rather than stdout. This is faster. + +# Caching allows storing array-to-array associations. It should be used like this: +# +# if ! _p9k_cache_get "$key1" "$key2"; then +# # Compute val1 and val2 and then store them in the cache. +# _p9k_cache_set "$val1" "$val2" +# fi +# # Here ${_P9K_CACHE_VAL[1]} and ${_P9K_CACHE_VAL[2]} are $val1 and $val2 respectively. +# +# Limitations: +# +# * Calling _p9k_cache_set without arguments clears the cache entry. Subsequent calls to +# _p9k_cache_get for the same key will return an error. +# * There must be no intervening _p9k_cache_get calls between the associated _p9k_cache_get +# and _p9k_cache_set. +_p9k_cache_set() { + # Uncomment to see cache misses. + # echo "caching: ${(@0q)_P9K_CACHE_KEY} => (${(q)@})" >&2 + _P9K_CACHE[$_P9K_CACHE_KEY]="${(pj:\0:)*}" + _P9K_CACHE_VAL=("$@") + (( #_P9K_CACHE < POWERLEVEL9K_MAX_CACHE_SIZE )) || typeset -gAH _P9K_CACHE=() +} + +_p9k_cache_get() { + _P9K_CACHE_KEY="${(pj:\0:)*}" + _P9K_CACHE_VAL=("${(@0)${_P9K_CACHE[$_P9K_CACHE_KEY]}}") + (( #_P9K_CACHE_VAL )) +} + +# Sets _P9K_RETVAL to the icon whose name is supplied via $1. +_p9k_get_icon() { + local var_name=POWERLEVEL9K_$1 + _P9K_RETVAL=${(g::)${${(P)var_name}-$icons[$1]}} +} + +typeset -gaH _P9K_LEFT_JOIN=(1) +() { + local i + for ((i = 2; i <= $#POWERLEVEL9K_LEFT_PROMPT_ELEMENTS; ++i)); do + local elem=$POWERLEVEL9K_LEFT_PROMPT_ELEMENTS[$i] + if [[ ${elem[-7,-1]} == '_joined' ]]; then + _P9K_LEFT_JOIN+=$_P9K_LEFT_JOIN[((i-1))] + else + _P9K_LEFT_JOIN+=$i + fi + done + + typeset -gaH _P9K_RIGHT_JOIN=(1) + for ((i = 2; i <= $#POWERLEVEL9K_RIGHT_PROMPT_ELEMENTS; ++i)); do + local elem=$POWERLEVEL9K_RIGHT_PROMPT_ELEMENTS[$i] + if [[ ${elem[-7,-1]} == '_joined' ]]; then + _P9K_RIGHT_JOIN+=$_P9K_RIGHT_JOIN[((i-1))] + else + _P9K_RIGHT_JOIN+=$i + fi + done +} + +_p9k_should_join_left() [[ $_P9K_LAST_SEGMENT_INDEX -ge ${_P9K_LEFT_JOIN[$1]:-$1} ]] +_p9k_should_join_right() [[ $_P9K_LAST_SEGMENT_INDEX -ge ${_P9K_RIGHT_JOIN[$1]:-$1} ]] + +# Resolves a color to its numerical value, or an empty string. Communicates the result back +# by setting _P9K_RETVAL. +_p9k_color() { + local user_var=POWERLEVEL9K_${(U)${2}#prompt_}_${3} + local color=${${(P)user_var}:-${1}} + # Check if given value is already numerical. + if [[ $color == <-> ]]; then + _P9K_RETVAL=${(l:3::0:)color} + else + # Strip prifices if there are any. + _P9K_RETVAL=$__P9K_COLORS[${${${color#bg-}#fg-}#br}] + fi +} + +_p9k_background() { + [[ -n $1 ]] && _P9K_RETVAL="%K{$1}" || _P9K_RETVAL="%k" +} + +_p9k_foreground() { + [[ -n $1 ]] && _P9K_RETVAL="%F{$1}" || _P9K_RETVAL="%f" +} # Begin a left prompt segment # Takes four arguments: @@ -108,97 +204,83 @@ CURRENT_BG='NONE' # * $5: The segment content # * $6: An identifying icon (must be a key of the icons array) # The latter three can be omitted, -set_default last_left_element_index 1 set_default POWERLEVEL9K_WHITESPACE_BETWEEN_LEFT_SEGMENTS " " left_prompt_segment() { - local segment_name="${1}" - local current_index=$2 - # Check if the segment should be joined with the previous one - local joined - segmentShouldBeJoined $current_index $last_left_element_index "$POWERLEVEL9K_LEFT_PROMPT_ELEMENTS" && joined=true || joined=false - - # Colors - local backgroundColor="${3}" - local foregroundColor="${4}" - - # Overwrite given background-color by user defined variable for this segment. - local BACKGROUND_USER_VARIABLE=POWERLEVEL9K_${(U)${segment_name}#prompt_}_BACKGROUND - local BG_COLOR_MODIFIER=${(P)BACKGROUND_USER_VARIABLE} - [[ -n $BG_COLOR_MODIFIER ]] && backgroundColor="$BG_COLOR_MODIFIER" - - # Overwrite given foreground-color by user defined variable for this segment. - local FOREGROUND_USER_VARIABLE=POWERLEVEL9K_${(U)${segment_name}#prompt_}_FOREGROUND - local FG_COLOR_MODIFIER=${(P)FOREGROUND_USER_VARIABLE} - [[ -n $FG_COLOR_MODIFIER ]] && foregroundColor="$FG_COLOR_MODIFIER" - - # Get color codes here to save some calls later on - backgroundColor="$(getColorCode ${backgroundColor})" - foregroundColor="$(getColorCode ${foregroundColor})" - - local background foreground - [[ -n "${backgroundColor}" ]] && background="$(backgroundColor ${backgroundColor})" || background="%k" - [[ -n "${foregroundColor}" ]] && foreground="$(foregroundColor ${foregroundColor})" || foreground="%f" - - if [[ $CURRENT_BG != 'NONE' ]] && ! isSameColor "${backgroundColor}" "$CURRENT_BG"; then - echo -n "${background}%F{$CURRENT_BG}" - if [[ $joined == false ]]; then - # Middle segment - echo -n "$(print_icon 'LEFT_SEGMENT_SEPARATOR')$POWERLEVEL9K_WHITESPACE_BETWEEN_LEFT_SEGMENTS" - fi - elif isSameColor "$CURRENT_BG" "${backgroundColor}"; then - # Middle segment with same color as previous segment - # We take the current foreground color as color for our - # subsegment (or the default color). This should have - # enough contrast. - local complement - [[ -n "${foregroundColor}" ]] && complement="${foreground}" || complement="$(foregroundColor $DEFAULT_COLOR)" - echo -n "${background}${complement}" - if [[ $joined == false ]]; then - echo -n "$(print_icon 'LEFT_SUBSEGMENT_SEPARATOR')$POWERLEVEL9K_WHITESPACE_BETWEEN_LEFT_SEGMENTS" + _p9k_should_join_left $2 && local join=true || local join=false + if ! _p9k_cache_get "$0" "$1" "$3" "$4" "${5:+1}" "$6" "$_P9K_CURRENT_BG" "$join"; then + _p9k_color $3 $1 BACKGROUND + local background_color=$_P9K_RETVAL + + _p9k_color $4 $1 FOREGROUND + local foreground_color=$_P9K_RETVAL + _p9k_foreground $foreground_color + local foreground=$_P9K_RETVAL + + _p9k_background $background_color + local output=$_P9K_RETVAL + + if [[ $_P9K_CURRENT_BG == NONE ]]; then + # The first segment on the line. + output+=$POWERLEVEL9K_WHITESPACE_BETWEEN_LEFT_SEGMENTS + elif [[ $join == false ]]; then + if [[ $background_color == $_P9K_CURRENT_BG ]]; then + # Middle segment with same color as previous segment + # We take the current foreground color as color for our + # subsegment (or the default color). This should have + # enough contrast. + if [[ $foreground == "%f" ]]; then + _p9k_foreground $DEFAULT_COLOR + output+=$_P9K_RETVAL + else + output+=$foreground + fi + _p9k_get_icon LEFT_SUBSEGMENT_SEPARATOR + output+="${_P9K_RETVAL}${POWERLEVEL9K_WHITESPACE_BETWEEN_LEFT_SEGMENTS}" + else + output+="%F{$_P9K_CURRENT_BG}" + _p9k_get_icon LEFT_SEGMENT_SEPARATOR + output+="${_P9K_RETVAL}${POWERLEVEL9K_WHITESPACE_BETWEEN_LEFT_SEGMENTS}" + fi fi - else - # First segment - echo -n "${background}$POWERLEVEL9K_WHITESPACE_BETWEEN_LEFT_SEGMENTS" - fi - - local visual_identifier - if [[ -n $6 ]]; then - visual_identifier="$(print_icon $6)" - if [[ -n "$visual_identifier" ]]; then - # Add an whitespace if we print more than just the visual identifier. - # To avoid cutting off the visual identifier in some terminal emulators (e.g., Konsole, st), - # we need to color both the visual identifier and the whitespace. - [[ -n "$5" ]] && visual_identifier="$visual_identifier " - # Allow users to overwrite the color for the visual identifier only. - local visual_identifier_color_variable=POWERLEVEL9K_${(U)${segment_name}#prompt_}_VISUAL_IDENTIFIER_COLOR - set_default $visual_identifier_color_variable "${foregroundColor}" - visual_identifier="$(foregroundColor ${(P)visual_identifier_color_variable})${visual_identifier}" + if [[ -n $6 ]]; then + _p9k_get_icon $6 + if [[ -n $_P9K_RETVAL ]]; then + local icon=$_P9K_RETVAL + _p9k_color $foreground_color $1 VISUAL_IDENTIFIER_COLOR + _p9k_foreground $_P9K_RETVAL + + # Add an whitespace if we print more than just the visual identifier. + # To avoid cutting off the visual identifier in some terminal emulators (e.g., Konsole, st), + # we need to color both the visual identifier and the whitespace. + output+="${_P9K_RETVAL}${icon}${5:+ }" + fi fi + output+=$foreground + _p9k_cache_set "$output" "$background_color" fi - # Print the visual identifier - echo -n "${visual_identifier}" - # Print the content of the segment, if there is any - [[ -n "$5" ]] && echo -n "${foreground}${5}" - echo -n "${POWERLEVEL9K_WHITESPACE_BETWEEN_LEFT_SEGMENTS}" - - CURRENT_BG="${backgroundColor}" - last_left_element_index=$current_index + _P9K_PROMPT+="${_P9K_CACHE_VAL[1]}${5}${POWERLEVEL9K_WHITESPACE_BETWEEN_LEFT_SEGMENTS}" + _P9K_LAST_SEGMENT_INDEX=$2 + _P9K_CURRENT_BG=$_P9K_CACHE_VAL[2] } # End the left prompt, closes the final segment. left_prompt_end() { - if [[ -n $CURRENT_BG ]]; then - echo -n "%k$(foregroundColor ${CURRENT_BG})$(print_icon 'LEFT_SEGMENT_SEPARATOR')" - else - echo -n "%k" + if ! _p9k_cache_get "$0" "$_P9K_CURRENT_BG"; then + local output="%k" + if [[ $_P9K_CURRENT_BG != NONE ]]; then + _p9k_foreground $_P9K_CURRENT_BG + output+=$_P9K_RETVAL + _p9k_get_icon LEFT_SEGMENT_SEPARATOR + output+="${_P9K_RETVAL}" + fi + _p9k_get_icon LEFT_SEGMENT_END_SEPARATOR + output+="%f${_P9K_RETVAL}" + _p9k_cache_set "$output" fi - echo -n "%f$(print_icon 'LEFT_SEGMENT_END_SEPARATOR')" - CURRENT_BG='' + _P9K_PROMPT+="${_P9K_CACHE_VAL[1]}" } -CURRENT_RIGHT_BG='NONE' - # Begin a right prompt segment # Takes four arguments: # * $1: Name of the function that was originally invoked (mandatory). @@ -209,87 +291,68 @@ CURRENT_RIGHT_BG='NONE' # * $5: The segment content # * $6: An identifying icon (must be a key of the icons array) # No ending for the right prompt segment is needed (unlike the left prompt, above). -set_default last_right_element_index 1 set_default POWERLEVEL9K_WHITESPACE_BETWEEN_RIGHT_SEGMENTS " " right_prompt_segment() { - local segment_name="${1}" - local current_index=$2 - - # Check if the segment should be joined with the previous one - local joined - segmentShouldBeJoined $current_index $last_right_element_index "$POWERLEVEL9K_RIGHT_PROMPT_ELEMENTS" && joined=true || joined=false - - # Colors - local backgroundColor="${3}" - local foregroundColor="${4}" - - # Overwrite given background-color by user defined variable for this segment. - local BACKGROUND_USER_VARIABLE=POWERLEVEL9K_${(U)${segment_name}#prompt_}_BACKGROUND - local BG_COLOR_MODIFIER=${(P)BACKGROUND_USER_VARIABLE} - [[ -n $BG_COLOR_MODIFIER ]] && backgroundColor="$BG_COLOR_MODIFIER" - - # Overwrite given foreground-color by user defined variable for this segment. - local FOREGROUND_USER_VARIABLE=POWERLEVEL9K_${(U)${segment_name}#prompt_}_FOREGROUND - local FG_COLOR_MODIFIER=${(P)FOREGROUND_USER_VARIABLE} - [[ -n $FG_COLOR_MODIFIER ]] && foregroundColor="$FG_COLOR_MODIFIER" - - # Get color codes here to save some calls later on - backgroundColor="$(getColorCode ${backgroundColor})" - foregroundColor="$(getColorCode ${foregroundColor})" - - local background foreground - [[ -n "${backgroundColor}" ]] && background="$(backgroundColor ${backgroundColor})" || background="%k" - [[ -n "${foregroundColor}" ]] && foreground="$(foregroundColor ${foregroundColor})" || foreground="%f" - - # If CURRENT_RIGHT_BG is "NONE", we are the first right segment. - - if [[ "$CURRENT_RIGHT_BG" != "NONE" ]]; then - # This is the closing whitespace for the previous segment - echo -n "${POWERLEVEL9K_WHITESPACE_BETWEEN_RIGHT_SEGMENTS}%f" - fi - - if [[ $joined == false ]] || [[ "$CURRENT_RIGHT_BG" == "NONE" ]]; then - if isSameColor "$CURRENT_RIGHT_BG" "${backgroundColor}"; then - # Middle segment with same color as previous segment - # We take the current foreground color as color for our - # subsegment (or the default color). This should have - # enough contrast. - local complement - [[ -n "${foregroundColor}" ]] && complement="${foreground}" || complement="$(foregroundColor $DEFAULT_COLOR)" - echo -n "$complement$(print_icon 'RIGHT_SUBSEGMENT_SEPARATOR')%f" + _p9k_should_join_right $2 && local join=true || local join=false + if ! _p9k_cache_get "$0" "$1" "$3" "$4" "$6" "$_P9K_CURRENT_BG" "$join"; then + _p9k_color $3 $1 BACKGROUND + local background_color=$_P9K_RETVAL + _p9k_background $background_color + local background=$_P9K_RETVAL + + _p9k_color $4 $1 FOREGROUND + local foreground_color=$_P9K_RETVAL + _p9k_foreground $foreground_color + local foreground=$_P9K_RETVAL + + local output='' + + if [[ $_P9K_CURRENT_BG == NONE || $join == false ]]; then + if [[ $background_color == $_P9K_CURRENT_BG ]]; then + # Middle segment with same color as previous segment + # We take the current foreground color as color for our + # subsegment (or the default color). This should have + # enough contrast. + if [[ $foreground == "%f" ]]; then + _p9k_foreground $DEFAULT_COLOR + output+=$_P9K_RETVAL + else + output+=$foreground + fi + _p9k_get_icon RIGHT_SUBSEGMENT_SEPARATOR + output+=$_P9K_RETVAL + else + output+="%F{$background_color}" + _p9k_get_icon RIGHT_SEGMENT_SEPARATOR + output+=$_P9K_RETVAL + fi + output+="${background}${POWERLEVEL9K_WHITESPACE_BETWEEN_RIGHT_SEGMENTS}" else - # Use the new Background Color as the foreground of the segment separator - echo -n "$(foregroundColor ${backgroundColor})$(print_icon 'RIGHT_SEGMENT_SEPARATOR')%f" + output+=$background fi - fi - local visual_identifier - if [[ -n "$6" ]]; then - visual_identifier="$(print_icon $6)" - if [[ -n "$visual_identifier" ]]; then - # Add an whitespace if we print more than just the visual identifier. - # To avoid cutting off the visual identifier in some terminal emulators (e.g., Konsole, st), - # we need to color both the visual identifier and the whitespace. - [[ -n "$5" ]] && visual_identifier=" $visual_identifier" - # Allow users to overwrite the color for the visual identifier only. - local visual_identifier_color_variable=POWERLEVEL9K_${(U)${segment_name}#prompt_}_VISUAL_IDENTIFIER_COLOR - set_default $visual_identifier_color_variable "${foregroundColor}" - visual_identifier="$(foregroundColor ${(P)visual_identifier_color_variable})${visual_identifier}" + output+=$foreground + + local icon='' + if [[ -n $6 ]]; then + _p9k_get_icon $6 + if [[ -n $_P9K_RETVAL ]]; then + local icon=$_P9K_RETVAL + _p9k_color $foreground_color $1 VISUAL_IDENTIFIER_COLOR + _p9k_foreground $_P9K_RETVAL + # Add an whitespace if we print more than just the visual identifier. + # To avoid cutting off the visual identifier in some terminal emulators (e.g., Konsole, st), + # we need to color both the visual identifier and the whitespace. + icon="${_P9K_RETVAL}${icon} " + fi fi - fi - - echo -n "${background}${foreground}" - # Print whitespace only if segment is not joined or first right segment - [[ $joined == false ]] || [[ "$CURRENT_RIGHT_BG" == "NONE" ]] && echo -n "${POWERLEVEL9K_WHITESPACE_BETWEEN_RIGHT_SEGMENTS}" - - # Print segment content if there is any - [[ -n "$5" ]] && echo -n "${5}" - # Print the visual identifier - echo -n "${visual_identifier}" + _p9k_cache_set "$output" "$background_color" "$icon" + fi - CURRENT_RIGHT_BG="${backgroundColor}" - last_right_element_index=$current_index + _P9K_PROMPT+="${_P9K_CACHE_VAL[1]}${5}${5:+ }${_P9K_CACHE_VAL[3]}" + _P9K_CURRENT_BG=$_P9K_CACHE_VAL[2] + _P9K_LAST_SEGMENT_INDEX=$2 } ################################################################ @@ -335,18 +398,15 @@ prompt_aws_eb_env() { set_default POWERLEVEL9K_BACKGROUND_JOBS_VERBOSE true set_default POWERLEVEL9K_BACKGROUND_JOBS_VERBOSE_ALWAYS false prompt_background_jobs() { - local background_jobs_number=${$(jobs -l | wc -l)// /} - local wrong_lines=`jobs -l | awk '/pwd now/{ count++ } END {print count}'` - if [[ wrong_lines -gt 0 ]]; then - background_jobs_number=$(( $background_jobs_number - $wrong_lines )) - fi - if [[ background_jobs_number -gt 0 ]]; then - local background_jobs_number_print="" - if [[ "$POWERLEVEL9K_BACKGROUND_JOBS_VERBOSE" == "true" ]] && ([[ "$background_jobs_number" -gt 1 ]] || [[ "$POWERLEVEL9K_BACKGROUND_JOBS_VERBOSE_ALWAYS" == "true" ]]); then - background_jobs_number_print="$background_jobs_number" - fi - "$1_prompt_segment" "$0" "$2" "$DEFAULT_COLOR" "cyan" "$background_jobs_number_print" 'BACKGROUND_JOBS_ICON' + local n && n="${(fw)#$(jobs -rd)}" && ((n > 1)) || return + (( n /= 2 )) + + local prompt='' + if [[ "$POWERLEVEL9K_BACKGROUND_JOBS_VERBOSE" == "true" && + ("$n" -gt 1 || "$POWERLEVEL9K_BACKGROUND_JOBS_VERBOSE_ALWAYS" == "true") ]]; then + prompt=$n fi + "$1_prompt_segment" "$0" "$2" "$DEFAULT_COLOR" "cyan" "$prompt" 'BACKGROUND_JOBS_ICON' } ################################################################ @@ -357,14 +417,15 @@ prompt_newline() { newline=$'\n' lws=$POWERLEVEL9K_WHITESPACE_BETWEEN_LEFT_SEGMENTS if [[ "$POWERLEVEL9K_PROMPT_ON_NEWLINE" == true ]]; then - newline="${newline}$(print_icon 'MULTILINE_NEWLINE_PROMPT_PREFIX')" + _p9k_get_icon MULTILINE_NEWLINE_PROMPT_PREFIX + newline="${newline}${_P9K_RETVAL}" fi POWERLEVEL9K_WHITESPACE_BETWEEN_LEFT_SEGMENTS= "$1_prompt_segment" \ "$0" \ "$2" \ "" "" "${newline}" - CURRENT_BG='NONE' + _P9K_CURRENT_BG=NONE POWERLEVEL9K_WHITESPACE_BETWEEN_LEFT_SEGMENTS=$lws } @@ -617,15 +678,6 @@ set_default POWERLEVEL9K_ALWAYS_SHOW_USER false set_default POWERLEVEL9K_CONTEXT_TEMPLATE "%n@%m" prompt_context() { local current_state="DEFAULT" - typeset -AH context_states - context_states=( - "ROOT" "yellow" - "SUDO" "yellow" - "DEFAULT" "yellow" - "REMOTE" "yellow" - "REMOTE_SUDO" "yellow" - ) - local content="" if [[ "$POWERLEVEL9K_ALWAYS_SHOW_CONTEXT" == true ]] || [[ "$(whoami)" != "$DEFAULT_USER" ]] || [[ -n "$SSH_CLIENT" || -n "$SSH_TTY" ]]; then @@ -648,7 +700,7 @@ prompt_context() { current_state="SUDO" fi - "$1_prompt_segment" "${0}_${current_state}" "$2" "$DEFAULT_COLOR" "${context_states[$current_state]}" "${content}" + "$1_prompt_segment" "${0}_${current_state}" "$2" "$DEFAULT_COLOR" yellow "${content}" } ################################################################ @@ -722,6 +774,9 @@ prompt_custom() { # Get content of custom segment local command="POWERLEVEL9K_CUSTOM_${segment_name}" local segment_content="$(eval ${(P)command})" + # Note: this would be a better implementation. I'm keeping the old one in case anyone + # relies on its quirks. + # local segment_content=$("${(@Q)${(z)${(P)command}}}") if [[ -n $segment_content ]]; then "$1_prompt_segment" "${0}_${3:u}" "$2" $DEFAULT_COLOR_INVERTED $DEFAULT_COLOR "$segment_content" "CUSTOM_${segment_name}_ICON" @@ -798,249 +853,251 @@ set_default POWERLEVEL9K_DIR_PATH_HIGHLIGHT_BOLD false prompt_dir() { # using $PWD instead of "$(print -P '%~')" to allow use of POWERLEVEL9K_DIR_PATH_ABSOLUTE local current_path=$PWD # WAS: local current_path="$(print -P '%~')" - # check if the user wants to use absolute paths or "~" paths - [[ ${(L)POWERLEVEL9K_DIR_PATH_ABSOLUTE} != "true" ]] && current_path=${current_path/#$HOME/"~"} - # declare all local variables - local paths directory test_dir test_dir_length trunc_path threshhold - # if we are not in "~" or "/", split the paths into an array and exclude "~" - (( ${#current_path} > 1 )) && paths=(${(s:/:)${current_path//"~\/"/}}) || paths=() - # only run the code if SHORTEN_DIR_LENGTH is set, or we are using the two strategies that don't rely on it. - if [[ -n "$POWERLEVEL9K_SHORTEN_DIR_LENGTH" || "$POWERLEVEL9K_SHORTEN_STRATEGY" == "truncate_with_folder_marker" || "$POWERLEVEL9K_SHORTEN_STRATEGY" == "truncate_to_last" || "$POWERLEVEL9K_SHORTEN_STRATEGY" == "truncate_with_package_name" ]]; then - set_default POWERLEVEL9K_SHORTEN_DELIMITER "\u2026" - # convert delimiter from unicode to literal character, so that we can get the correct length later - local delim=$(echo -n $POWERLEVEL9K_SHORTEN_DELIMITER) - - case "$POWERLEVEL9K_SHORTEN_STRATEGY" in - truncate_absolute_chars) - if [ ${#current_path} -gt $(( $POWERLEVEL9K_SHORTEN_DIR_LENGTH + ${#POWERLEVEL9K_SHORTEN_DELIMITER} )) ]; then - current_path=$POWERLEVEL9K_SHORTEN_DELIMITER${current_path:(-POWERLEVEL9K_SHORTEN_DIR_LENGTH)} - fi - ;; - truncate_middle) - # truncate characters from the middle of the path - current_path=$(truncatePath $current_path $POWERLEVEL9K_SHORTEN_DIR_LENGTH $POWERLEVEL9K_SHORTEN_DELIMITER "middle") - ;; - truncate_from_right) - # truncate characters from the right of the path - current_path=$(truncatePath "$current_path" $POWERLEVEL9K_SHORTEN_DIR_LENGTH $POWERLEVEL9K_SHORTEN_DELIMITER) - ;; - truncate_absolute) - # truncate all characters except the last POWERLEVEL9K_SHORTEN_DIR_LENGTH characters - if [ ${#current_path} -gt $(( $POWERLEVEL9K_SHORTEN_DIR_LENGTH + ${#POWERLEVEL9K_SHORTEN_DELIMITER} )) ]; then - current_path=$POWERLEVEL9K_SHORTEN_DELIMITER${current_path:(-POWERLEVEL9K_SHORTEN_DIR_LENGTH)} - fi - ;; - truncate_to_last) - # truncate all characters before the current directory - current_path=${current_path##*/} - ;; - truncate_to_first_and_last) - if (( ${#current_path} > 1 )) && (( ${POWERLEVEL9K_SHORTEN_DIR_LENGTH} > 0 )); then - threshhold=$(( ${POWERLEVEL9K_SHORTEN_DIR_LENGTH} * 2)) - # if we are in "~", add it back into the paths array - [[ $current_path == '~'* ]] && paths=("~" "${paths[@]}") - if (( ${#paths} > $threshhold )); then - local num=$(( ${#paths} - ${POWERLEVEL9K_SHORTEN_DIR_LENGTH} )) - # repace the middle elements - for (( i=$POWERLEVEL9K_SHORTEN_DIR_LENGTH; i<$num; i++ )); do - paths[$i+1]=$POWERLEVEL9K_SHORTEN_DELIMITER - done - [[ $current_path != '~'* ]] && current_path="/" || current_path="" - current_path+="${(j:/:)paths}" + [[ "${POWERLEVEL9K_DIR_SHOW_WRITABLE}" == true && ! -w "$PWD" ]] && + local -i not_writable=1 || local -i not_writable=0 + if ! _p9k_cache_get "$0" "$2" "$current_path" "$not_writable"; then + # check if the user wants to use absolute paths or "~" paths + [[ ${(L)POWERLEVEL9K_DIR_PATH_ABSOLUTE} != "true" ]] && current_path=${current_path/#$HOME/"~"} + # declare all local variables + local paths directory test_dir test_dir_length trunc_path threshhold + # if we are not in "~" or "/", split the paths into an array and exclude "~" + (( ${#current_path} > 1 )) && paths=(${(s:/:)${current_path//"~\/"/}}) || paths=() + # only run the code if SHORTEN_DIR_LENGTH is set, or we are using the two strategies that don't rely on it. + if [[ -n "$POWERLEVEL9K_SHORTEN_DIR_LENGTH" || "$POWERLEVEL9K_SHORTEN_STRATEGY" == "truncate_with_folder_marker" || "$POWERLEVEL9K_SHORTEN_STRATEGY" == "truncate_to_last" || "$POWERLEVEL9K_SHORTEN_STRATEGY" == "truncate_with_package_name" ]]; then + set_default POWERLEVEL9K_SHORTEN_DELIMITER "\u2026" + # convert delimiter from unicode to literal character, so that we can get the correct length later + local delim=$(echo -n $POWERLEVEL9K_SHORTEN_DELIMITER) + + case "$POWERLEVEL9K_SHORTEN_STRATEGY" in + truncate_absolute_chars) + if [ ${#current_path} -gt $(( $POWERLEVEL9K_SHORTEN_DIR_LENGTH + ${#POWERLEVEL9K_SHORTEN_DELIMITER} )) ]; then + current_path=$POWERLEVEL9K_SHORTEN_DELIMITER${current_path:(-POWERLEVEL9K_SHORTEN_DIR_LENGTH)} fi - fi - ;; - truncate_to_unique) - # for each parent path component find the shortest unique beginning - # characters sequence. Source: https://stackoverflow.com/a/45336078 - if (( ${#current_path} > 1 )); then # root and home are exceptions and won't have paths - # cheating here to retain ~ as home folder - local home_path="$(getUniqueFolder $HOME)" - trunc_path="$(getUniqueFolder $PWD)" - [[ $current_path == "~"* ]] && current_path="~${trunc_path//${home_path}/}" || current_path="/${trunc_path}" - fi - ;; - truncate_with_folder_marker) - if (( ${#paths} > 0 )); then # root and home are exceptions and won't have paths, so skip this - local last_marked_folder marked_folder - set_default POWERLEVEL9K_SHORTEN_FOLDER_MARKER ".shorten_folder_marker" - - # Search for the folder marker in the parent directories and - # buildup a pattern that is removed from the current path - # later on. - for marked_folder in $(upsearch $POWERLEVEL9K_SHORTEN_FOLDER_MARKER); do - if [[ "$marked_folder" == "/" ]]; then - # If we reached root folder, stop upsearch. - trunc_path="/" - elif [[ "$marked_folder" == "$HOME" ]]; then - # If we reached home folder, stop upsearch. - trunc_path="~" - elif [[ "${marked_folder%/*}" == $last_marked_folder ]]; then - trunc_path="${trunc_path%/}/${marked_folder##*/}" - else - trunc_path="${trunc_path%/}/$POWERLEVEL9K_SHORTEN_DELIMITER/${marked_folder##*/}" + ;; + truncate_middle) + # truncate characters from the middle of the path + current_path=$(truncatePath $current_path $POWERLEVEL9K_SHORTEN_DIR_LENGTH $POWERLEVEL9K_SHORTEN_DELIMITER "middle") + ;; + truncate_from_right) + # truncate characters from the right of the path + current_path=$(truncatePath "$current_path" $POWERLEVEL9K_SHORTEN_DIR_LENGTH $POWERLEVEL9K_SHORTEN_DELIMITER) + ;; + truncate_absolute) + # truncate all characters except the last POWERLEVEL9K_SHORTEN_DIR_LENGTH characters + if [ ${#current_path} -gt $(( $POWERLEVEL9K_SHORTEN_DIR_LENGTH + ${#POWERLEVEL9K_SHORTEN_DELIMITER} )) ]; then + current_path=$POWERLEVEL9K_SHORTEN_DELIMITER${current_path:(-POWERLEVEL9K_SHORTEN_DIR_LENGTH)} + fi + ;; + truncate_to_last) + # truncate all characters before the current directory + current_path=${current_path##*/} + ;; + truncate_to_first_and_last) + if (( ${#current_path} > 1 )) && (( ${POWERLEVEL9K_SHORTEN_DIR_LENGTH} > 0 )); then + threshhold=$(( ${POWERLEVEL9K_SHORTEN_DIR_LENGTH} * 2)) + # if we are in "~", add it back into the paths array + [[ $current_path == '~'* ]] && paths=("~" "${paths[@]}") + if (( ${#paths} > $threshhold )); then + local num=$(( ${#paths} - ${POWERLEVEL9K_SHORTEN_DIR_LENGTH} )) + # repace the middle elements + for (( i=$POWERLEVEL9K_SHORTEN_DIR_LENGTH; i<$num; i++ )); do + paths[$i+1]=$POWERLEVEL9K_SHORTEN_DELIMITER + done + [[ $current_path != '~'* ]] && current_path="/" || current_path="" + current_path+="${(j:/:)paths}" fi - last_marked_folder=$marked_folder - done + fi + ;; + truncate_to_unique) + # for each parent path component find the shortest unique beginning + # characters sequence. Source: https://stackoverflow.com/a/45336078 + if (( ${#current_path} > 1 )); then # root and home are exceptions and won't have paths + # cheating here to retain ~ as home folder + local home_path="$(getUniqueFolder $HOME)" + trunc_path="$(getUniqueFolder $PWD)" + [[ $current_path == "~"* ]] && current_path="~${trunc_path//${home_path}/}" || current_path="/${trunc_path}" + fi + ;; + truncate_with_folder_marker) + if (( ${#paths} > 0 )); then # root and home are exceptions and won't have paths, so skip this + local last_marked_folder marked_folder + set_default POWERLEVEL9K_SHORTEN_FOLDER_MARKER ".shorten_folder_marker" + + # Search for the folder marker in the parent directories and + # buildup a pattern that is removed from the current path + # later on. + for marked_folder in $(upsearch $POWERLEVEL9K_SHORTEN_FOLDER_MARKER); do + if [[ "$marked_folder" == "/" ]]; then + # If we reached root folder, stop upsearch. + trunc_path="/" + elif [[ "$marked_folder" == "$HOME" ]]; then + # If we reached home folder, stop upsearch. + trunc_path="~" + elif [[ "${marked_folder%/*}" == $last_marked_folder ]]; then + trunc_path="${trunc_path%/}/${marked_folder##*/}" + else + trunc_path="${trunc_path%/}/$POWERLEVEL9K_SHORTEN_DELIMITER/${marked_folder##*/}" + fi + last_marked_folder=$marked_folder + done + + # Replace the shortest possible match of the marked folder from + # the current path. + current_path=$trunc_path${current_path#${last_marked_folder}*} + fi + ;; + truncate_with_package_name) + local name repo_path package_path current_dir zero + + # Get the path of the Git repo, which should have the package.json file + if [[ $(git rev-parse --is-inside-work-tree 2> /dev/null) == "true" ]]; then + # Get path from the root of the git repository to the current dir + local gitPath=$(git rev-parse --show-prefix) + # Remove trailing slash from git path, so that we can + # remove that git path from the pwd. + gitPath=${gitPath%/} + package_path=${$(pwd)%%$gitPath} + # Remove trailing slash + package_path=${package_path%/} + elif [[ $(git rev-parse --is-inside-git-dir 2> /dev/null) == "true" ]]; then + package_path=${$(pwd)%%/.git*} + fi + + [[ ${(L)POWERLEVEL9K_DIR_PATH_ABSOLUTE} != "true" ]] && package_path=${package_path/$HOME/"~"} # Replace the shortest possible match of the marked folder from - # the current path. - current_path=$trunc_path${current_path#${last_marked_folder}*} - fi - ;; - truncate_with_package_name) - local name repo_path package_path current_dir zero - - # Get the path of the Git repo, which should have the package.json file - if [[ $(git rev-parse --is-inside-work-tree 2> /dev/null) == "true" ]]; then - # Get path from the root of the git repository to the current dir - local gitPath=$(git rev-parse --show-prefix) - # Remove trailing slash from git path, so that we can - # remove that git path from the pwd. - gitPath=${gitPath%/} - package_path=${$(pwd)%%$gitPath} - # Remove trailing slash - package_path=${package_path%/} - elif [[ $(git rev-parse --is-inside-git-dir 2> /dev/null) == "true" ]]; then - package_path=${$(pwd)%%/.git*} - fi + # the current path. Remove the amount of characters up to the + # folder marker from the left. Count only the visible characters + # in the path (this is done by the "zero" pattern; see + # http://stackoverflow.com/a/40855342/5586433). + local zero='%([BSUbfksu]|([FB]|){*})' + trunc_path=$(pwd) + # Then, find the length of the package_path string, and save the + # subdirectory path as a substring of the current directory's path from 0 + # to the length of the package path's string + subdirectory_path=$(truncatePath "${trunc_path:${#${(S%%)package_path//$~zero/}}}" $POWERLEVEL9K_SHORTEN_DIR_LENGTH $POWERLEVEL9K_SHORTEN_DELIMITER) + # Parse the 'name' from the package.json; if there are any problems, just + # print the file path + defined POWERLEVEL9K_DIR_PACKAGE_FILES || POWERLEVEL9K_DIR_PACKAGE_FILES=(package.json composer.json) + + local pkgFile="unknown" + for file in "${POWERLEVEL9K_DIR_PACKAGE_FILES[@]}"; do + if [[ -f "${package_path}/${file}" ]]; then + pkgFile="${package_path}/${file}" + break; + fi + done - [[ ${(L)POWERLEVEL9K_DIR_PATH_ABSOLUTE} != "true" ]] && package_path=${package_path/$HOME/"~"} - - # Replace the shortest possible match of the marked folder from - # the current path. Remove the amount of characters up to the - # folder marker from the left. Count only the visible characters - # in the path (this is done by the "zero" pattern; see - # http://stackoverflow.com/a/40855342/5586433). - local zero='%([BSUbfksu]|([FB]|){*})' - trunc_path=$(pwd) - # Then, find the length of the package_path string, and save the - # subdirectory path as a substring of the current directory's path from 0 - # to the length of the package path's string - subdirectory_path=$(truncatePath "${trunc_path:${#${(S%%)package_path//$~zero/}}}" $POWERLEVEL9K_SHORTEN_DIR_LENGTH $POWERLEVEL9K_SHORTEN_DELIMITER) - # Parse the 'name' from the package.json; if there are any problems, just - # print the file path - defined POWERLEVEL9K_DIR_PACKAGE_FILES || POWERLEVEL9K_DIR_PACKAGE_FILES=(package.json composer.json) - - local pkgFile="unknown" - for file in "${POWERLEVEL9K_DIR_PACKAGE_FILES[@]}"; do - if [[ -f "${package_path}/${file}" ]]; then - pkgFile="${package_path}/${file}" - break; + local packageName=$(jq '.name' ${pkgFile} 2> /dev/null \ + || node -e 'console.log(require(process.argv[1]).name);' ${pkgFile} 2>/dev/null \ + || cat "${pkgFile}" 2> /dev/null | grep -m 1 "\"name\"" | awk -F ':' '{print $2}' | awk -F '"' '{print $2}' 2>/dev/null \ + ) + if [[ -n "${packageName}" ]]; then + # Instead of printing out the full path, print out the name of the package + # from the package.json and append the current subdirectory + current_path="`echo $packageName | tr -d '"'`$subdirectory_path" fi - done - - local packageName=$(jq '.name' ${pkgFile} 2> /dev/null \ - || node -e 'console.log(require(process.argv[1]).name);' ${pkgFile} 2>/dev/null \ - || cat "${pkgFile}" 2> /dev/null | grep -m 1 "\"name\"" | awk -F ':' '{print $2}' | awk -F '"' '{print $2}' 2>/dev/null \ - ) - if [[ -n "${packageName}" ]]; then - # Instead of printing out the full path, print out the name of the package - # from the package.json and append the current subdirectory - current_path="`echo $packageName | tr -d '"'`$subdirectory_path" - fi - ;; - *) - if [[ $current_path != "~" ]]; then - current_path="$(print -P "%$((POWERLEVEL9K_SHORTEN_DIR_LENGTH+1))(c:$POWERLEVEL9K_SHORTEN_DELIMITER/:)%${POWERLEVEL9K_SHORTEN_DIR_LENGTH}c")" - fi - ;; - esac - fi + ;; + *) + if [[ $current_path != "~" ]]; then + current_path="$(print -P "%$((POWERLEVEL9K_SHORTEN_DIR_LENGTH+1))(c:$POWERLEVEL9K_SHORTEN_DELIMITER/:)%${POWERLEVEL9K_SHORTEN_DIR_LENGTH}c")" + fi + ;; + esac + fi - # save state of path for highlighting and bold options - local path_opt=$current_path + # save state of path for highlighting and bold options + local path_opt=$current_path + local state_path="$(print -P '%~')" + local current_state="DEFAULT" + local icon="FOLDER_ICON" + if [[ $state_path == '/etc'* ]]; then + current_state='ETC' + icon='ETC_ICON' + elif (( not_writable )); then + current_state="NOT_WRITABLE" + icon='LOCK_ICON' + elif [[ $state_path == '~' ]]; then + current_state="HOME" + icon='HOME_ICON' + elif [[ $state_path == '~'* ]]; then + current_state="HOME_SUBFOLDER" + icon='HOME_SUB_ICON' + fi - typeset -AH dir_states - dir_states=( - "DEFAULT" "FOLDER_ICON" - "HOME" "HOME_ICON" - "HOME_SUBFOLDER" "HOME_SUB_ICON" - "NOT_WRITABLE" "LOCK_ICON" - "ETC" "ETC_ICON" - ) - local state_path="$(print -P '%~')" - local current_state="DEFAULT" - if [[ $state_path == '/etc'* ]]; then - current_state='ETC' - elif [[ "${POWERLEVEL9K_DIR_SHOW_WRITABLE}" == true && ! -w "$PWD" ]]; then - current_state="NOT_WRITABLE" - elif [[ $state_path == '~' ]]; then - current_state="HOME" - elif [[ $state_path == '~'* ]]; then - current_state="HOME_SUBFOLDER" - fi - - # declare variables used for bold and state colors - local bld_on bld_off dir_state_foreground dir_state_user_foreground - # test if user wants the last directory printed in bold - if [[ "${(L)POWERLEVEL9K_DIR_PATH_HIGHLIGHT_BOLD}" == "true" ]]; then - bld_on="%B" - bld_off="%b" - else - bld_on="" - bld_off="" - fi - # determine is the user has set a last directory color - local dir_state_user_foreground=POWERLEVEL9K_DIR_${current_state}_FOREGROUND - local dir_state_foreground=${(P)dir_state_user_foreground} - [[ -z ${dir_state_foreground} ]] && dir_state_foreground="${DEFAULT_COLOR}" - - local dir_name base_name - # use ZSH substitution to get the dirname and basename instead of calling external functions - dir_name=${path_opt%/*} - base_name=${path_opt##*/} - - # if the user wants the last directory colored... - if [[ -n ${POWERLEVEL9K_DIR_PATH_HIGHLIGHT_FOREGROUND} ]]; then - # it the path is "/" or "~" - if [[ $path_opt == "/" || $path_opt == "~" ]]; then - current_path="${bld_on}%F{$POWERLEVEL9K_DIR_PATH_HIGHLIGHT_FOREGROUND}${current_path}${bld_off}" - else # has a subfolder - # test if dirname != basename - they are equal if we use truncate_to_last or truncate_absolute - if [[ $dir_name != $base_name ]]; then - current_path="${dir_name}/${bld_on}%F{$POWERLEVEL9K_DIR_PATH_HIGHLIGHT_FOREGROUND}${base_name}${bld_off}" - else - current_path="${bld_on}%F{$POWERLEVEL9K_DIR_PATH_HIGHLIGHT_FOREGROUND}${base_name}${bld_off}" - fi + # declare variables used for bold and state colors + local bld_on bld_off dir_state_foreground dir_state_user_foreground + # test if user wants the last directory printed in bold + if [[ "${(L)POWERLEVEL9K_DIR_PATH_HIGHLIGHT_BOLD}" == "true" ]]; then + bld_on="%B" + bld_off="%b" + else + bld_on="" + bld_off="" fi - else # no coloring - # it the path is "/" or "~" - if [[ $path_opt == "/" || $path_opt == "~" ]]; then - current_path="${bld_on}${current_path}${bld_off}" - else # has a subfolder - # test if dirname != basename - they are equal if we use truncate_to_last or truncate_absolute - if [[ $dir_name != $base_name ]]; then - current_path="${dir_name}/${bld_on}${base_name}${bld_off}" - else - current_path="${bld_on}${base_name}${bld_off}" + # determine is the user has set a last directory color + local dir_state_user_foreground=POWERLEVEL9K_DIR_${current_state}_FOREGROUND + local dir_state_foreground=${(P)dir_state_user_foreground} + [[ -z ${dir_state_foreground} ]] && dir_state_foreground="${DEFAULT_COLOR}" + + local dir_name base_name + # use ZSH substitution to get the dirname and basename instead of calling external functions + dir_name=${path_opt%/*} + base_name=${path_opt##*/} + + # if the user wants the last directory colored... + if [[ -n ${POWERLEVEL9K_DIR_PATH_HIGHLIGHT_FOREGROUND} ]]; then + # it the path is "/" or "~" + if [[ $path_opt == "/" || $path_opt == "~" ]]; then + current_path="${bld_on}%F{$POWERLEVEL9K_DIR_PATH_HIGHLIGHT_FOREGROUND}${current_path}${bld_off}" + else # has a subfolder + # test if dirname != basename - they are equal if we use truncate_to_last or truncate_absolute + if [[ $dir_name != $base_name ]]; then + current_path="${dir_name}/${bld_on}%F{$POWERLEVEL9K_DIR_PATH_HIGHLIGHT_FOREGROUND}${base_name}${bld_off}" + else + current_path="${bld_on}%F{$POWERLEVEL9K_DIR_PATH_HIGHLIGHT_FOREGROUND}${base_name}${bld_off}" + fi + fi + else # no coloring + # it the path is "/" or "~" + if [[ $path_opt == "/" || $path_opt == "~" ]]; then + current_path="${bld_on}${current_path}${bld_off}" + else # has a subfolder + # test if dirname != basename - they are equal if we use truncate_to_last or truncate_absolute + if [[ $dir_name != $base_name ]]; then + current_path="${dir_name}/${bld_on}${base_name}${bld_off}" + else + current_path="${bld_on}${base_name}${bld_off}" + fi fi fi - fi - # check if we need to omit the first character and only do it if we are not in "~" or "/" - if [[ "${POWERLEVEL9K_DIR_OMIT_FIRST_CHARACTER}" == "true" && $path_opt != "/" && $path_opt != "~" ]]; then - current_path="${current_path[2,-1]}" - fi + # check if we need to omit the first character and only do it if we are not in "~" or "/" + if [[ "${POWERLEVEL9K_DIR_OMIT_FIRST_CHARACTER}" == "true" && $path_opt != "/" && $path_opt != "~" ]]; then + current_path="${current_path[2,-1]}" + fi - # check if the user wants the separator colored. - if [[ -n ${POWERLEVEL9K_DIR_PATH_SEPARATOR_FOREGROUND} && $path_opt != "/" ]]; then - # because this contains color changing codes, it is easier to set a variable for what should be replaced - local repl="%F{$POWERLEVEL9K_DIR_PATH_SEPARATOR_FOREGROUND}/%F{$dir_state_foreground}" - # escape the / with a \ - current_path=${current_path//\//$repl} - fi + # check if the user wants the separator colored. + if [[ -n ${POWERLEVEL9K_DIR_PATH_SEPARATOR_FOREGROUND} && $path_opt != "/" ]]; then + # because this contains color changing codes, it is easier to set a variable for what should be replaced + local repl="%F{$POWERLEVEL9K_DIR_PATH_SEPARATOR_FOREGROUND}/%F{$dir_state_foreground}" + # escape the / with a \ + current_path=${current_path//\//$repl} + fi - if [[ "${POWERLEVEL9K_DIR_PATH_SEPARATOR}" != "/" && $path_opt != "/" ]]; then - current_path=${current_path//\//$POWERLEVEL9K_DIR_PATH_SEPARATOR} - fi + if [[ "${POWERLEVEL9K_DIR_PATH_SEPARATOR}" != "/" && $path_opt != "/" ]]; then + current_path=${current_path//\//$POWERLEVEL9K_DIR_PATH_SEPARATOR} + fi + + if [[ "${POWERLEVEL9K_HOME_FOLDER_ABBREVIATION}" != "~" && ! "${(L)POWERLEVEL9K_DIR_OMIT_FIRST_CHARACTER}" == "true" ]]; then + # use :s to only replace the first occurance + current_path=${current_path:s/~/$POWERLEVEL9K_HOME_FOLDER_ABBREVIATION} + fi - if [[ "${POWERLEVEL9K_HOME_FOLDER_ABBREVIATION}" != "~" && ! "${(L)POWERLEVEL9K_DIR_OMIT_FIRST_CHARACTER}" == "true" ]]; then - # use :s to only replace the first occurance - current_path=${current_path:s/~/$POWERLEVEL9K_HOME_FOLDER_ABBREVIATION} + _p9k_cache_set "$current_state" "$current_path" "$icon" fi - "$1_prompt_segment" "$0_${current_state}" "$2" "blue" "$DEFAULT_COLOR" "${current_path}" "${dir_states[$current_state]}" + "$1_prompt_segment" "$0_${_P9K_CACHE_VAL[1]}" "$2" blue "$DEFAULT_COLOR" "${_P9K_CACHE_VAL[2]}" "${_P9K_CACHE_VAL[3]}" } ################################################################ @@ -1377,51 +1434,58 @@ set_default POWERLEVEL9K_STATUS_OK_IN_NON_VERBOSE false exit_code_or_status() { local ec=$1 - if [[ "$POWERLEVEL9K_STATUS_HIDE_SIGNAME" = true ]]; then - echo "$ec" - elif (( ec <= 128 )); then - echo "$ec" + if [[ "$POWERLEVEL9K_STATUS_HIDE_SIGNAME" = true ]] || (( ec <= 128 )); then + _P9K_RETVAL=$ec else - local sig=$(( ec - 128 )) - local idx=$(( sig + 1 )) - echo "SIG${signals[$idx]}(${sig})" + _P9K_RETVAL="SIG${signals[$((sig + 1))]}($((ec - 128)))" fi } prompt_status() { - local ec_text - local ec_sum - local ec - - if [[ $POWERLEVEL9K_STATUS_SHOW_PIPESTATUS == true ]]; then - if (( $#RETVALS > 1 )); then - ec_text=$(exit_code_or_status "${RETVALS[1]}") - ec_sum=${RETVALS[1]} + if ! _p9k_cache_get "$0" "$2" "$RETVAL" "${(@)RETVALS}"; then + local ec_text + local ec_sum + local ec + + if [[ $POWERLEVEL9K_STATUS_SHOW_PIPESTATUS == true ]]; then + if (( $#RETVALS > 1 )); then + ec_sum=${RETVALS[1]} + exit_code_or_status "${RETVALS[1]}" + + else + ec_sum=${RETVAL} + exit_code_or_status "${RETVAL}" + fi + ec_text=$_P9K_RETVAL + for ec in "${(@)RETVALS[2,-1]}"; do + (( ec_sum += ec )) + exit_code_or_status "$ec" + ec_text+="|$_P9K_RETVAL" + done else - ec_text=$(exit_code_or_status "${RETVAL}") ec_sum=${RETVAL} + # We use RETVAL instead of the right-most RETVALS item because + # PIPE_FAIL may be set. + exit_code_or_status "${RETVAL}" + ec_text=$_P9K_RETVAL fi - for ec in "${(@)RETVALS[2,-1]}"; do - ec_text="${ec_text}|$(exit_code_or_status "$ec")" - ec_sum=$(( $ec_sum + $ec )) - done - else - # We use RETVAL instead of the right-most RETVALS item because - # PIPE_FAIL may be set. - ec_text=$(exit_code_or_status "${RETVAL}") - ec_sum=${RETVAL} - fi - - if (( ec_sum > 0 )); then - if [[ "$POWERLEVEL9K_STATUS_CROSS" == false && "$POWERLEVEL9K_STATUS_VERBOSE" == true ]]; then - "$1_prompt_segment" "$0_ERROR" "$2" "red" "yellow1" "$ec_text" 'CARRIAGE_RETURN_ICON' + if (( ec_sum > 0 )); then + if [[ "$POWERLEVEL9K_STATUS_CROSS" == false && "$POWERLEVEL9K_STATUS_VERBOSE" == true ]]; then + _P9K_CACHE_VAL=("$0_ERROR" "$2" red yellow1 "$ec_text" CARRIAGE_RETURN_ICON) + else + _P9K_CACHE_VAL=("$0_ERROR" "$2" "$DEFAULT_COLOR" red "" FAIL_ICON) + fi + elif [[ "$POWERLEVEL9K_STATUS_OK" == true ]] && [[ "$POWERLEVEL9K_STATUS_VERBOSE" == true || "$POWERLEVEL9K_STATUS_OK_IN_NON_VERBOSE" == true ]]; then + _P9K_CACHE_VAL=("$0_OK" "$2" "$DEFAULT_COLOR" green "" OK_ICON) else - "$1_prompt_segment" "$0_ERROR" "$2" "$DEFAULT_COLOR" "red" "" 'FAIL_ICON' + return + fi + if (( $#RETVALS < 3 )); then + _p9k_cache_set "${(@)_P9K_CACHE_VAL}" fi - elif [[ "$POWERLEVEL9K_STATUS_OK" == true ]] && [[ "$POWERLEVEL9K_STATUS_VERBOSE" == true || "$POWERLEVEL9K_STATUS_OK_IN_NON_VERBOSE" == true ]]; then - "$1_prompt_segment" "$0_OK" "$2" "$DEFAULT_COLOR" "green" "" 'OK_ICON' fi + "$1_prompt_segment" "${(@)_P9K_CACHE_VAL}" } ################################################################ @@ -1491,18 +1555,33 @@ build_test_stats() { ################################################################ # System time -prompt_time() { - set_default POWERLEVEL9K_TIME_FORMAT "%D{%H:%M:%S}" +# If set to true, `time` prompt will update every second. +set_default POWERLEVEL9K_EXPERIMENTAL_TIME_REALTIME false +set_default POWERLEVEL9K_TIME_FORMAT "%D{%H:%M:%S}" +prompt_time() { "$1_prompt_segment" "$0" "$2" "$DEFAULT_COLOR_INVERTED" "$DEFAULT_COLOR" "$POWERLEVEL9K_TIME_FORMAT" "TIME_ICON" + # For the reference, here's how the code should ideally look like. However, it's 2ms slower + # for a tiny gain in usability. The difference is that the current code will cause time + # to update when vcs segment goes from grey to green/yellow, but the commented-out code + # won't (unless POWERLEVEL9K_EXPERIMENTAL_TIME_REALTIME is true). + #if [[ $POWERLEVEL9K_EXPERIMENTAL_TIME_REALTIME == true ]]; then + # local _P9K_TIME=$POWERLEVEL9K_TIME_FORMAT + #else + # [[ -v _P9K_REFRESH_PROMPT ]] || typeset -gH _P9K_TIME=$(print -P $POWERLEVEL9K_TIME_FORMAT) + # typeset -gH _P9K_TIME=$POWERLEVEL9K_TIME_FORMAT + #fi + #"$1_prompt_segment" "$0" "$2" "$DEFAULT_COLOR_INVERTED" "$DEFAULT_COLOR" "$_P9K_TIME" "TIME_ICON" } ################################################################ # System date +set_default POWERLEVEL9K_DATE_FORMAT "%D{%d.%m.%y}" prompt_date() { - set_default POWERLEVEL9K_DATE_FORMAT "%D{%d.%m.%y}" - "$1_prompt_segment" "$0" "$2" "$DEFAULT_COLOR_INVERTED" "$DEFAULT_COLOR" "$POWERLEVEL9K_DATE_FORMAT" "DATE_ICON" + # See comments in prompt_time. + # [[ -v _P9K_REFRESH_PROMPT ]] || typeset -gH _P9K_DATE=$(print -P $POWERLEVEL9K_DATE_FORMAT) + # "$1_prompt_segment" "$0" "$2" "$DEFAULT_COLOR_INVERTED" "$DEFAULT_COLOR" "$_P9K_DATE" "DATE_ICON" } ################################################################ @@ -1522,6 +1601,7 @@ prompt_todo() { set_default POWERLEVEL9K_VCS_ACTIONFORMAT_FOREGROUND "red" # Default: Just display the first 8 characters of our changeset-ID. set_default POWERLEVEL9K_VCS_INTERNAL_HASH_LENGTH "8" + powerlevel9k_vcs_init() { if [[ -n "$POWERLEVEL9K_CHANGESET_HASH_LENGTH" ]]; then POWERLEVEL9K_VCS_INTERNAL_HASH_LENGTH="$POWERLEVEL9K_CHANGESET_HASH_LENGTH" @@ -1539,6 +1619,7 @@ powerlevel9k_vcs_init() { 'clean' 'green' 'modified' 'yellow' 'untracked' 'green' + 'loading' 'grey' ) VCS_CHANGESET_PREFIX='' @@ -1546,7 +1627,6 @@ powerlevel9k_vcs_init() { VCS_CHANGESET_PREFIX="$(print_icon 'VCS_COMMIT_ICON')%0.$POWERLEVEL9K_VCS_INTERNAL_HASH_LENGTH""i " fi - zstyle ':vcs_info:*' enable git hg svn zstyle ':vcs_info:*' check-for-changes true VCS_DEFAULT_FORMAT="$VCS_CHANGESET_PREFIX%b%c%u%m" @@ -1582,30 +1662,200 @@ powerlevel9k_vcs_init() { fi } +# git workdir => the last prompt we've shown for it +typeset -gAH _P9K_LAST_GIT_PROMPT + +# git workdir => 1 if gitstatus is slow on it, 0 if it's fast. +typeset -gAH _P9K_GIT_SLOW + +typeset -fH _p9k_vcs_render() { + if [[ -v _P9K_NEXT_VCS_DIR ]]; then + local prompt + local dir=$PWD + while true; do + prompt=${_P9K_LAST_GIT_PROMPT[$dir]} + [[ -n $prompt || $dir == / ]] && break + dir=${dir:h} + done + $2_prompt_segment $1_LOADING $3 "${vcs_states[loading]}" "$DEFAULT_COLOR" ${prompt:-loading} + return 0 + fi + + [[ $VCS_STATUS_RESULT == ok-* ]] || return 1 + local -a cache_key=( + "$VCS_STATUS_LOCAL_BRANCH" + "$VCS_STATUS_REMOTE_BRANCH" + "$VCS_STATUS_REMOTE_URL" + "$VCS_STATUS_ACTION" + "$VCS_STATUS_HAS_STAGED" + "$VCS_STATUS_HAS_UNSTAGED" + "$VCS_STATUS_HAS_UNTRACKED" + "$VCS_STATUS_COMMITS_AHEAD" + "$VCS_STATUS_COMMITS_BEHIND" + "$VCS_STATUS_STASHES" + ) + if ! _p9k_cache_get "${(@)cache_key}"; then + local state + if [[ $VCS_STATUS_HAS_STAGED != 0 || $VCS_STATUS_HAS_UNSTAGED != 0 ]]; then + state='modified' + elif [[ $VCS_STATUS_HAS_UNTRACKED != 0 ]]; then + state='untracked' + else + state='clean' + fi + + local vcs_prompt + if [[ "$VCS_STATUS_REMOTE_URL" == *github* ]] then + _p9k_get_icon VCS_GIT_GITHUB_ICON + vcs_prompt+=$_P9K_RETVAL + elif [[ "$VCS_STATUS_REMOTE_URL" == *bitbucket* ]] then + _p9k_get_icon VCS_GIT_BITBUCKET_ICON + vcs_prompt+=$_P9K_RETVAL + elif [[ "$VCS_STATUS_REMOTE_URL" == *stash* ]] then + _p9k_get_icon VCS_GIT_GITHUB_ICON + vcs_prompt+=$_P9K_RETVAL + elif [[ "$VCS_STATUS_REMOTE_URL" == *gitlab* ]] then + _p9k_get_icon VCS_GIT_GITLAB_ICON + vcs_prompt+=$_P9K_RETVAL + else + _p9k_get_icon VCS_GIT_ICON + vcs_prompt+=$_P9K_RETVAL + fi + + _p9k_get_icon VCS_BRANCH_ICON + vcs_prompt+="$_P9K_RETVAL$VCS_STATUS_LOCAL_BRANCH" + if [[ -n $VCS_STATUS_ACTION ]]; then + vcs_prompt+=" %F{${POWERLEVEL9K_VCS_ACTIONFORMAT_FOREGROUND}}| $VCS_STATUS_ACTION%f" + else + if [[ -n $VCS_STATUS_REMOTE_BRANCH && + $VCS_STATUS_LOCAL_BRANCH != $VCS_STATUS_REMOTE_BRANCH ]]; then + _p9k_get_icon VCS_REMOTE_BRANCH_ICON + vcs_prompt+=" $_P9K_RETVAL$VCS_STATUS_REMOTE_BRANCH" + fi + if [[ $VCS_STATUS_HAS_STAGED == 1 ]]; then + _p9k_get_icon VCS_STAGED_ICON + vcs_prompt+=" $_P9K_RETVAL" + fi + if [[ $VCS_STATUS_HAS_UNSTAGED == 1 ]]; then + _p9k_get_icon VCS_UNSTAGED_ICON + vcs_prompt+=" $_P9K_RETVAL" + fi + if [[ $VCS_STATUS_HAS_UNTRACKED == 1 ]]; then + _p9k_get_icon VCS_UNTRACKED_ICON + vcs_prompt+=" $_P9K_RETVAL" + fi + if [[ $VCS_STATUS_COMMITS_AHEAD -gt 0 ]]; then + _p9k_get_icon VCS_OUTGOING_CHANGES_ICON + vcs_prompt+=" $_P9K_RETVAL$VCS_STATUS_COMMITS_AHEAD" + fi + if [[ $VCS_STATUS_COMMITS_BEHIND -gt 0 ]]; then + _p9k_get_icon VCS_INCOMING_CHANGES_ICON + vcs_prompt+=" $_P9K_RETVAL$VCS_STATUS_COMMITS_BEHIND" + fi + if [[ $VCS_STATUS_STASHES -gt 0 ]]; then + _p9k_get_icon VCS_STASH_ICON + vcs_prompt+=" $_P9K_RETVAL$VCS_STATUS_STASHES" + fi + fi + + _p9k_cache_set "${1}_${(U)state}" "${vcs_states[$state]}" "$vcs_prompt" + fi + + _P9K_LAST_GIT_PROMPT[$VCS_STATUS_WORKDIR]="${_P9K_CACHE_VAL[3]}" + "$2_prompt_segment" "${_P9K_CACHE_VAL[1]}" $3 "${_P9K_CACHE_VAL[2]}" "$DEFAULT_COLOR" "${_P9K_CACHE_VAL[3]}" + return 0 +} + +typeset -fH _p9k_vcs_resume() { + if [[ $VCS_STATUS_RESULT == ok-async ]]; then + local latency=$((EPOCHREALTIME - _P9K_GITSTATUS_START_TIME)) + if (( latency > POWERLEVEL9K_VCS_MAX_SYNC_LATENCY_SECONDS )); then + _P9K_GIT_SLOW[$VCS_STATUS_WORKDIR]=1 + elif (( latency < 0.8 * POWERLEVEL9K_VCS_MAX_SYNC_LATENCY_SECONDS )); then # 0.8 to avoid flip-flopping + _P9K_GIT_SLOW[$VCS_STATUS_WORKDIR]=0 + fi + fi + + if [[ -z $_P9K_NEXT_VCS_DIR ]]; then + unset _P9K_NEXT_VCS_DIR + _p9k_update_prompts + else + typeset -gFH _P9K_GITSTATUS_START_TIME=$EPOCHREALTIME + if ! gitstatus_query -d $_P9K_NEXT_VCS_DIR -t 0 -c _p9k_vcs_resume POWERLEVEL9K; then + unset _P9K_NEXT_VCS_DIR + return + fi + case $VCS_STATUS_RESULT in + *-sync) + unset _P9K_NEXT_VCS_DIR + _p9k_update_prompts + ;; + tout) + typeset -gH _P9K_NEXT_VCS_DIR="" + ;; + esac + fi +} + +typeset -fH _p9k_vcs_gitstatus() { + [[ $POWERLEVEL9K_DISABLE_GITSTATUS == true ]] && return 1 + if [[ ! -v _P9K_REFRESH_PROMPT ]]; then + if [[ -v _P9K_NEXT_VCS_DIR ]]; then + typeset -gH _P9K_NEXT_VCS_DIR=$PWD + else + local dir=$PWD + local -F timeout=$POWERLEVEL9K_VCS_MAX_SYNC_LATENCY_SECONDS + while true; do + case "$_P9K_GIT_SLOW[$dir]" in + "") [[ $dir == / ]] && break; dir=${dir:h};; + 0) break;; + 1) timeout=0; break;; + esac + done + typeset -gFH _P9K_GITSTATUS_START_TIME=$EPOCHREALTIME + gitstatus_query -t $timeout -c _p9k_vcs_resume POWERLEVEL9K || return 1 + [[ $VCS_STATUS_RESULT == tout ]] && typeset -gH _P9K_NEXT_VCS_DIR="" + fi + fi + return 0 +} + ################################################################ # Segment to show VCS information -prompt_vcs() { - VCS_WORKDIR_DIRTY=false - VCS_WORKDIR_HALF_DIRTY=false - local current_state="" - # Actually invoke vcs_info manually to gather all information. - vcs_info - local vcs_prompt="${vcs_info_msg_0_}" +# If it takes longer than this to fetch git repo status, display the prompt with a greyed out +# vcs segment and fix it asynchronously when the results come it. +[ -v POWERLEVEL9K_VCS_MAX_SYNC_LATENCY_SECONDS ] || typeset -gF POWERLEVEL9K_VCS_MAX_SYNC_LATENCY_SECONDS=0.05 - if [[ -n "$vcs_prompt" ]]; then - if [[ "$VCS_WORKDIR_DIRTY" == true ]]; then - # $vcs_visual_identifier gets set in +vi-vcs-detect-changes in functions/vcs.zsh, - # as we have there access to vcs_info internal hooks. - current_state='modified' - else - if [[ "$VCS_WORKDIR_HALF_DIRTY" == true ]]; then - current_state='untracked' +prompt_vcs() { + local -a backends + [[ -v POWERLEVEL9K_VCS_BACKENDS ]] && backends=($POWERLEVEL9K_VCS_BACKENDS) || backends=(git) + if (( ${backends[(I)git]} )) && _p9k_vcs_gitstatus; then + _p9k_vcs_render $0 $1 $2 && return + backends=(${backends:#git}) + fi + if (( #backends )); then + VCS_WORKDIR_DIRTY=false + VCS_WORKDIR_HALF_DIRTY=false + local current_state="" + # Actually invoke vcs_info manually to gather all information. + zstyle ':vcs_info:*' enable ${backends} + vcs_info + local vcs_prompt="${vcs_info_msg_0_}" + if [[ -n "$vcs_prompt" ]]; then + if [[ "$VCS_WORKDIR_DIRTY" == true ]]; then + # $vcs_visual_identifier gets set in +vi-vcs-detect-changes in functions/vcs.zsh, + # as we have there access to vcs_info internal hooks. + current_state='modified' else - current_state='clean' + if [[ "$VCS_WORKDIR_HALF_DIRTY" == true ]]; then + current_state='untracked' + else + current_state='clean' + fi fi + "$1_prompt_segment" "${0}_${(U)current_state}" "$2" "${vcs_states[$current_state]}" "$DEFAULT_COLOR" "$vcs_prompt" "$vcs_visual_identifier" fi - "$1_prompt_segment" "${0}_${(U)current_state}" "$2" "${vcs_states[$current_state]}" "$DEFAULT_COLOR" "$vcs_prompt" "$vcs_visual_identifier" fi } @@ -1752,6 +2002,9 @@ prompt_java_version() { ################################################################ # Main prompt build_left_prompt() { + typeset -gH _P9K_CURRENT_BG=NONE + typeset -gH _P9K_LAST_SEGMENT_INDEX=0 + local index=1 local element for element in "${POWERLEVEL9K_LEFT_PROMPT_ELEMENTS[@]}"; do @@ -1760,7 +2013,7 @@ build_left_prompt() { # Check if it is a custom command, otherwise interpet it as # a prompt. - if [[ $element[0,7] =~ "custom_" ]]; then + if [[ $element == custom_* ]]; then "prompt_custom" "left" "$index" $element[8,-1] else "prompt_$element" "left" "$index" @@ -1770,19 +2023,25 @@ build_left_prompt() { done left_prompt_end + + unset _P9K_CURRENT_BG _P9K_LAST_SEGMENT_INDEX } # Right prompt build_right_prompt() { + typeset -gH _P9K_CURRENT_BG=NONE + typeset -gH _P9K_LAST_SEGMENT_INDEX=0 + local index=1 local element + for element in "${POWERLEVEL9K_RIGHT_PROMPT_ELEMENTS[@]}"; do # Remove joined information in direct calls element=${element%_joined} # Check if it is a custom command, otherwise interpet it as # a prompt. - if [[ $element[0,7] =~ "custom_" ]]; then + if [[ $element == custom_* ]]; then "prompt_custom" "right" "$index" $element[8,-1] else "prompt_$element" "right" "$index" @@ -1792,33 +2051,31 @@ build_right_prompt() { done # Clear to the end of the line - echo -n "%E" + _P9K_PROMPT+="%E" + + unset _P9K_CURRENT_BG _P9K_LAST_SEGMENT_INDEX } powerlevel9k_preexec() { _P9K_TIMER_START=$EPOCHREALTIME } -set_default POWERLEVEL9K_PROMPT_ADD_NEWLINE false -powerlevel9k_prepare_prompts() { - # Return values. These need to be global, because - # they are used in prompt_status. Also, we need - # to get the return value of the last command at - # very first in this function. Do not move the - # lines down, otherwise the last command is not - # what you expected it to be. - RETVAL=$? - RETVALS=( "$pipestatus[@]" ) +typeset -fH _p9k_set_prompts() { + typeset -gH _P9K_PROMPT='' + build_left_prompt + local left=$_P9K_PROMPT + _P9K_PROMPT='' + build_right_prompt + local right=$_P9K_PROMPT + unset _P9K_PROMPT + local NEWLINE=$'\n' local RPROMPT_SUFFIX RPROMPT_PREFIX - _P9K_COMMAND_DURATION=$((EPOCHREALTIME - _P9K_TIMER_START)) - - # Reset start time - _P9K_TIMER_START=0x7FFFFFFF - if [[ "$POWERLEVEL9K_PROMPT_ON_NEWLINE" == true ]]; then - PROMPT='$(print_icon 'MULTILINE_FIRST_PROMPT_PREFIX')%f%b%k$(build_left_prompt) -$(print_icon 'MULTILINE_LAST_PROMPT_PREFIX')' + _p9k_get_icon MULTILINE_FIRST_PROMPT_PREFIX + PROMPT="$_P9K_RETVAL%f%b%k$left$NEWLINE" + _p9k_get_icon MULTILINE_LAST_PROMPT_PREFIX + PROMPT+=$_P9K_RETVAL if [[ "$POWERLEVEL9K_RPROMPT_ON_NEWLINE" != true ]]; then # The right prompt should be on the same line as the first line of the left # prompt. To do so, there is just a quite ugly workaround: Before zsh draws @@ -1833,20 +2090,17 @@ $(print_icon 'MULTILINE_LAST_PROMPT_PREFIX')' RPROMPT_SUFFIX='' fi else - PROMPT='%f%b%k$(build_left_prompt)' + PROMPT="%f%b%k$left" RPROMPT_PREFIX='' RPROMPT_SUFFIX='' fi if [[ "$POWERLEVEL9K_DISABLE_RPROMPT" != true ]]; then - RPROMPT="${RPROMPT_PREFIX}"'%f%b%k$(build_right_prompt)%{$reset_color%}'"${RPROMPT_SUFFIX}" + RPROMPT="${RPROMPT_PREFIX}%f%b%k$right%{$reset_color%}${RPROMPT_SUFFIX}" fi -local NEWLINE=' -' - if [[ $POWERLEVEL9K_PROMPT_ADD_NEWLINE == true ]]; then - NEWLINES="" + local NEWLINES="" repeat ${POWERLEVEL9K_PROMPT_ADD_NEWLINE_COUNT:-1} { NEWLINES+=$NEWLINE } PROMPT="$NEWLINES$PROMPT" fi @@ -1855,6 +2109,29 @@ local NEWLINE=' [[ $ITERM_SHELL_INTEGRATION_INSTALLED == "Yes" ]] && PROMPT="%{$(iterm2_prompt_mark)%}$PROMPT" } +typeset -fH _p9k_update_prompts() { + [[ $_P9K_LOADED == true ]] || return + typeset -gH _P9K_REFRESH_PROMPT='' + _p9k_set_prompts + unset _P9K_REFRESH_PROMPT + zle && zle .reset-prompt +} + +set_default POWERLEVEL9K_PROMPT_ADD_NEWLINE false +powerlevel9k_prepare_prompts() { + # Return values. These need to be global, because + # they are used in prompt_status. Also, we need + # to get the return value of the last command at + # very first in this function. Do not move the + # lines down, otherwise the last command is not + # what you expected it to be. + RETVAL=$? + RETVALS=( "$pipestatus[@]" ) + _P9K_COMMAND_DURATION=$((EPOCHREALTIME - _P9K_TIMER_START)) + _P9K_TIMER_START=0x7FFFFFFF + _p9k_set_prompts +} + zle-keymap-select () { zle reset-prompt zle -R @@ -1896,9 +2173,6 @@ prompt_powerlevel9k_setup() { fi fi - defined POWERLEVEL9K_LEFT_PROMPT_ELEMENTS || POWERLEVEL9K_LEFT_PROMPT_ELEMENTS=(context dir vcs) - defined POWERLEVEL9K_RIGHT_PROMPT_ELEMENTS || POWERLEVEL9K_RIGHT_PROMPT_ELEMENTS=(status root_indicator background_jobs history time) - # Display a warning if deprecated segments are in use. typeset -AH deprecated_segments # old => new @@ -1928,6 +2202,36 @@ prompt_powerlevel9k_setup() { add-zsh-hook preexec powerlevel9k_preexec zle -N zle-keymap-select + + local -i max_dirty=${POWERLEVEL9K_VCS_MAX_INDEX_SIZE_DIRTY:--1} + [[ $POWERLEVEL9K_DISABLE_GITSTATUS == true ]] || gitstatus_start -m $max_dirty POWERLEVEL9K + + if [[ $POWERLEVEL9K_EXPERIMENTAL_TIME_REALTIME == true ]]; then + local fifo && fifo=$(mktemp -u "${TMPDIR:-/tmp}"/p10k.$$.pipe.time.XXXXXXXXXX) + typeset -giH _P9K_TIMER_FD=0 + _p9k_start_timer() { + emulate -L zsh + setopt err_return + unsetopt bg_nice + mkfifo $fifo + exec {_P9K_TIMER_FD}<>$fifo + typeset -gfH _p9k_on_timer() { + local _ && IFS='' read -u $_P9K_TIMER_FD _ && zle && zle .reset-prompt + } + zle -F $_P9K_TIMER_FD _p9k_on_timer + zsh -c "while kill -0 $$; do sleep 1; echo; done" >&$_P9K_TIMER_FD 2>/dev/null &! + } + if ! _p9k_start_timer ; then + echo "powerlevel10k: failed to initialize realtime clock" >&2 + zle -F $_P9K_TIMER_FD + (( _P9K_TIMER_FD )) && exec {_P9K_TIMER_FD}>&- + unset _P9K_TIMER_FD + unset -f _p9k_on_timer + fi + rm -f "$fifo" + fi + + _P9K_LOADED=true } prompt_powerlevel9k_teardown() { @@ -1935,6 +2239,7 @@ prompt_powerlevel9k_teardown() { add-zsh-hook -D preexec powerlevel9k_\* PROMPT='%m%# ' RPROMPT= + _P9K_LOADED=false } prompt_powerlevel9k_setup "$@" |