## # Prompt that can be customized through vcs_info # zmodload -F zsh/system p:sysparams autoload -Uz add-zle-hook-widget add-zsh-hook vcs_info # Prompt segments # Normal %b and %(...) need extra % in vcs_info -- except in nvcsformats. readonly -gHA _prompt_vcs_ps1_default=( left $'%%(?,%F{green},%F{red})%#%%b%f%%s ' right '%B%F{blue}%n%b%f%k@%F{magenta}%m%f' chpwd $'\n%B%F{blue}%~%%b%f/\n' staged '%B%F{green}+%b%f' unstaged '%B%F{red}*%b%f' action '%B%F{red}%a%%b%f' branch '%B%F{cyan}%b%%b%f' repo '|%B%F{blue}%r%%b%f' ) prompt_vcs_help() { print -r -- \ "Customizable prompt theme that includes VCS info for the current dir (for example, git status) and asynchronously checks for repo changes. To customize it, copy-paste any of the code below to your .zshrc file and edit it there: # For each of the following three entries: # 1st string is the left prompt. # 2nd string is the right prompt. # 3rd string is printed whenever you change dirs (before the left prompt). # Normal prompt: zstyle ':vcs_info:*:prompt_vcs:*' nvcsformats \\ "${(q+)_prompt_vcs_ps1_default[left]//'%%'/%}" \\ "${(q+)_prompt_vcs_ps1_default[right]//'%%'/%}" \\ "${(q+)_prompt_vcs_ps1_default[chpwd]//'%%'/%}" # Prompt inside VCS repo: zstyle ':vcs_info:*:prompt_vcs:*' formats \\ ${(q+)_prompt_vcs_ps1_default[left]} \\ ${(q+):-%u%c$_prompt_vcs_ps1_default[branch]$_prompt_vcs_ps1_default[repo]} \\ ${(q+)_prompt_vcs_ps1_default[chpwd]} # Prompt during ongoing VCS action: zstyle ':vcs_info:*:prompt_vcs:*' actionformats \\ ${(q+)_prompt_vcs_ps1_default[left]} \\ ${(q+):-%u%c$_prompt_vcs_ps1_default[action]$_prompt_vcs_ps1_default[repo]} \\ ${(q+)_prompt_vcs_ps1_default[chpwd]} # These customize the values of %c and %u, respectively: zstyle ':vcs_info:*:prompt_vcs:*' stagedstr ${(q+)_prompt_vcs_ps1_default[staged]} zstyle ':vcs_info:*:prompt_vcs:*' unstagedstr ${(q+)_prompt_vcs_ps1_default[unstaged]} For more info on the settings above, see http://zsh.sourceforge.net/Doc/Release/Prompt-Expansion.html and the end of http://zsh.sourceforge.net/Doc/Release/User-Contributions.html#vcs_005finfo-Configuration" } # Sets a style if it hasn't been set yet. _prompt_vcs_zstyle() { local -a val zstyle -g val "$1" "$2" (( $#val )) || zstyle "$@" } _prompt_vcs_info() { _prompt_vcs_zstyle ':vcs_info:*:prompt_vcs:*' max-exports 3 # Default is 2. _prompt_vcs_zstyle ':vcs_info:*:prompt_vcs:*' nvcsformats \ "${_prompt_vcs_ps1_default[left]//'%%'/%}" \ "${_prompt_vcs_ps1_default[right]//'%%'/%}" \ "${_prompt_vcs_ps1_default[chpwd]//'%%'/%}" _prompt_vcs_zstyle ':vcs_info:*:prompt_vcs:*' formats \ "$_prompt_vcs_ps1_default[left]" \ "%u%c$_prompt_vcs_ps1_default[branch]$_prompt_vcs_ps1_default[repo]" \ "$_prompt_vcs_ps1_default[chpwd]" _prompt_vcs_zstyle ':vcs_info:*:prompt_vcs:*' actionformats \ "$_prompt_vcs_ps1_default[left]" \ "%u%c$_prompt_vcs_ps1_default[action]$_prompt_vcs_ps1_default[repo]" \ "$_prompt_vcs_ps1_default[chpwd]" _prompt_vcs_zstyle ':vcs_info:*:prompt_vcs:*' stagedstr \ "$_prompt_vcs_ps1_default[staged]" _prompt_vcs_zstyle ':vcs_info:*:prompt_vcs:*' unstagedstr \ "$_prompt_vcs_ps1_default[unstaged]" vcs_info prompt_vcs # Call with namespace. print -rNC1 -- \ "${(q+)vcs_info_msg_0_}" "${(q+)vcs_info_msg_1_}" "${(q+)vcs_info_msg_2_}" } prompt_vcs_chpwd() { emulate -L zsh # Don't check for changes at this point, for performance reasons. typeset -ga _prompt_vcs_info_msg_=( "${(0@Q)$( _prompt_vcs_zstyle ':vcs_info:*:prompt_vcs:*' check-for-changes no _prompt_vcs_zstyle ':vcs_info:*:prompt_vcs:*' check-for-staged-changes no _prompt_vcs_info )}" ) PS1="$_prompt_vcs_info_msg_[1]" RPS1="$_prompt_vcs_info_msg_[2]" print -nPr -- "$_prompt_vcs_info_msg_[3]" # Call precmd just in case, because chpwd can happen without a new prompt. prompt_vcs_precmd } prompt_vcs_precmd() { emulate -L zsh # Terminate any pending vcs_info request before starting a new one. # Second test checks if fd is readable, to avoid this error being printed: # failed to close file descriptor XX: bad file descriptor if (( prompt_vcs_fd )) && { : <&$prompt_vcs_fd } 2>/dev/null; then zle -F "$prompt_vcs_fd" 2> /dev/null exec {prompt_vcs_fd}<&- fi if (( prompt_vcs_pid )); then [[ -o monitor ]] && (( prompt_vcs_pid *= -1 )) kill -KILL $prompt_vcs_pid 2> /dev/null fi # Asynchronously check for changes. typeset -gH prompt_vcs_fd= exec {prompt_vcs_fd}< <( print -r -- $sysparams[pid] _prompt_vcs_zstyle ':vcs_info:*:prompt_vcs:*' check-for-changes yes _prompt_vcs_zstyle ':vcs_info:*:prompt_vcs:*' check-for-staged-changes yes _prompt_vcs_info ) typeset -gH prompt_vcs_pid= read -u $prompt_vcs_fd -- prompt_vcs_pid # Add callback. Needs to be a widget, so we can refresh the prompt. zle -Fw "$prompt_vcs_fd" prompt_vcs_fd-widget } # Callback widget for our async fetch of VCS changes zle -N prompt_vcs_fd-widget prompt_vcs_fd-widget() { emulate -L zsh (( $1 )) || return 64 # EX_USAGE; see man 3 sysexits local -i fd=$1 local -a reply { zle -F "$fd" # Detach ourselves, so we don't get called more than once. [[ -z $2 ]] || return 74 # EX_IOERR IFS=$'\0' read -Aru "$fd" typeset -gHa _prompt_vcs_info_msg_=( "${(@Q)reply}" ) PS1="$_prompt_vcs_info_msg_[1]" RPS1="$_prompt_vcs_info_msg_[2]" zle .reset-prompt } always { exec {fd}<&- } } prompt_vcs_setup() { prompt_opts=( cr percent sp ) # Tell promptinit which options to set. # Indent left continuation prompt for each open shell construct. local -a indent=( '%('{1..36}'_, ,)' ) PS2="${(j::)indent}" RPS2='%F{yellow}# %^%f' PS4=$'#->%(?,%F{green},%B%F{red}%S)%?%b%f%s\t%e+%F{green}%1N%f:%I' PS4+=' %(1_,%F{yellow}%K{black}%_%f%k ,)' SPROMPT='Correct %B%F{red}%S%R%b%f%s to %B%F{green}%r%b%f?' SPROMPT+=$'\n%{\e[2m%}%Uy%ues %Un%uo %Ue%udit %Ua%ubort%b ' PROMPT_EOL_MARK='%F{cyan}%S%#%f%s' zle_highlight=( isearch:fg=black,bg=yellow special:fg=cyan,bold region:fg=white,bg=blue,bold suffix:fg=white,bg=blue,bold paste:bold ) add-zsh-hook chpwd prompt_vcs_chpwd add-zsh-hook precmd prompt_vcs_precmd prompt_vcs_chpwd } prompt_vcs_setup "$@"