--- /dev/null
+#
+# My zsh prompt theme
+#
+# Copyright © 1994–2017 martin f. krafft <madduck@madduck.net>
+# Released under the terms of the Artistic Licence 2.0
+#
+# Source repository: http://git.madduck.net/v/etc/zsh.git
+#
+# vcs stuff shamelessly based on http://glandium.org/blog/?p=170
+#
+
+zstyle -m :madduck:prompt:default path-maxlen '*' \
+ || zstyle :madduck:prompt:default path-maxlen 25
+zstyle -m :madduck:prompt:default path-minlen '*' \
+ || zstyle :madduck:prompt:default path-minlen 10
+
+__on_networkfs()
+{
+ emulate -L zsh
+ case $(df -T . | sed -rne '$s,^[^[:space:]]+[[:space:]]+([^[:space:]]+).*,\1,p') in
+ (cifs|nfs) return 0;;
+ esac
+ return 1
+}
+
+__git_get_reporoot()
+{
+ emulate -L zsh
+ # return the full path to the root of the current git repository
+ [ -d "$GIT_DIR" ] && echo "$GIT_DIR" && return 0
+ local dir; dir="$PWD/$(git rev-parse --show-cdup)"
+ # do not use --show-toplevel because it resolves symlinks
+ echo $dir:a
+}
+
+__git_get_branch()
+{
+ emulate -L zsh
+ # use oh-my-zsh prompt info function if it exists
+ $(command -v git_prompt_info) && return
+
+ # return the name of the git branch we're on
+ local ref gitdir
+ gitdir="$(git rev-parse --git-dir)"
+ ref=$(git --git-dir="$gitdir" symbolic-ref -q HEAD 2>/dev/null \
+ || git --git-dir="$gitdir" name-rev --name-only HEAD 2>/dev/null) || return 1
+ echo "${ref#refs/heads/}"
+}
+
+__git_print_preprompt()
+{
+ emulate -L zsh
+ [ "$(git config --get core.bare)" = false ] || return
+ __on_networkfs && return
+
+ local COLUMNS=${COLUMNS:-80}
+ local LINES=${LINES:-25}
+
+ function output() {
+ emulate -L zsh
+ local title="$@"
+ local output=(${(f)"$(cat)"})
+
+ [[ ${#output} -ge 1 ]] || return
+
+ local statl="$(echo ${output[-1]} | sed -re 's@^\s*([0-9]+)[^,]+(, ([0-9]+) [^(]+\(([-+])\))(, ([0-9]+) [^(]+\(([-+])\))?@\1/\4\3/\7\6@')"
+
+ if [[ ${output[-2]## } = '...' ]]; then
+ print "${title} (${statl%/}, abbrev.):"
+ print "${(F)output[1,-3]}"
+ print " …"
+ else
+ print "${title} (${statl%/}):"
+ print "${(F)output[1,-2]}"
+ fi
+ }
+
+ function gitdiffstat() {
+ emulate -L zsh
+ local common_options="--stat=$((COLUMNS/2-1)),$((COLUMNS/4-2)),$(($LINES/3)) --relative"
+ eval git diff $common_options "$@" 2>/dev/null
+ }
+
+ local cached=(${(f)"$(gitdiffstat --cached | output cached)"})
+ local changed=(${(f)"$(gitdiffstat | output changed)"})
+
+ local max=${#changed}
+ [[ $max -lt ${#cached} ]] && max=${#cached}
+
+ ((max == 0)) && return
+
+ local width=$(((COLUMNS-3)/2))
+
+ if (( ${#cached} > 0 && ${#changed} > 0 )); then
+ local i
+ for (( i=1 ; i <= max ; i++ )) do
+ printf "%-${width}s │ %-${width}s\n" "${cached[$i]}" "${changed[$i]}"
+ done
+ else
+ print ${(F)cached}${(F)changed}
+ fi
+}
+
+__hg_get_reporoot()
+{
+ emulate -L zsh
+ hg root
+}
+
+__hg_get_branch()
+{
+ emulate -L zsh
+ echo "hg:$(hg branch)"
+}
+
+__bzr_get_reporoot()
+{
+ emulate -L zsh
+ local reporoot
+ reporoot="$(bzr info | sed -rne 's, *branch root: ,,p')"
+ case "$reporoot" in
+ .) echo "$PWD";;
+ *) echo "$reporoot";;
+ esac
+}
+
+__bzr_get_branch()
+{
+ emulate -L zsh
+ local branch revno
+ bzr version-info | while read i j; do
+ case "$i" in
+ revno:) revno="$j";;
+ branch-nick:) branch="$j";;
+ esac
+ done
+ echo "bzr:${branch}@$revno"
+}
+
+__vcs_get_repo_type()
+{
+ emulate -L zsh
+ # return the type of the closest repository in the path hierarchy
+ local dir
+ while true; do
+ [ -d ${dir}.git ] && echo git && break
+ [ -d "$GIT_DIR" ] && echo git && break
+ [ -d ${dir}.bzr ] && echo bzr && break
+ [ -d ${dir}.hg ] && echo hg && break
+ [ "$(readlink -f ${dir:-.})" = / ] && echo NONE && break
+ dir="../$dir"
+ done
+}
+
+__get_prompt_path_len() {
+ emulate -L zsh
+ local result
+ zstyle -s ":madduck:prompt:$PWD" path-${1}len result
+ [ -z "$result" ] && zstyle -s ':madduck:prompt:default' path-${1}len result
+ echo $result
+}
+
+__vcs_get_prompt_path_components()
+{
+ emulate -L zsh
+ # return formatted path components (prefix branch postfix) given
+ # the repository root and the branch.
+
+ local MAXLEN MINLEN
+ MAXLEN=$(__get_prompt_path_len max)
+ MINLEN=$(__get_prompt_path_len min)
+
+ # shortcut: if there are no arguments, return a default prompt
+ if [ -z "${1:-}" ]; then
+ pwdnamed="${(%):-%${MAXLEN}<…<%~%<<}"
+ echo "$pwdnamed"
+ return
+ fi
+
+ local reporoot branch
+ reporoot="${1%%/}"
+ branch="$2"
+
+ # replace named directories in the PWD, we need thi for the proper component
+ # count later
+ local pwdnamed
+ pwdnamed="${(%):-%~}"
+
+ # store paths in arrays for component count calculation
+ typeset -la apwd apwdnamed areporoot
+ apwd=(${(s:/:)PWD})
+ apwdnamed=(${(s:/:)pwdnamed})
+ areporoot=(${(s:/:)reporoot})
+
+ # get the number of leading and trailing path components. Since we're using
+ # %~ later and then /home/madduck suddenly becomes ~, which is 1, not
+ # 2 components, we calculate the leading component count by using the named
+ # path and the number of post components
+ local precomps postcomps
+ postcomps=$(($#apwd - $#areporoot))
+ precomps=$(($#apwdnamed - $postcomps))
+
+ local postfix
+ (( $postcomps > 0 )) && postfix="${(%):-%${postcomps}~}"
+
+ # we don't want the prompt to get too long, so keep the total prompt length
+ # under $MAXLEN, but ensure that the prefix is not shorter
+ # than $MINLEN, no matter what
+ local prelen minlen prefix
+ prelen=$((${MAXLEN} - $#branch - $#postfix))
+ minlen=${MINLEN}
+ (( $prelen < $minlen )) && prelen=$minlen
+ prefix="${(%):-%${prelen}<…<%-${precomps}~%<<}"
+
+ echo "'$prefix'" "'$branch'" "'$postfix'"
+}
+
+__vcs_set_prompt_variables()
+{
+ emulate -L zsh
+ # set psvar[1..3] depending on repo type, or just psvar[1] if no repo found
+ local reporoot branch repotype
+ repotype="${1:-$(__vcs_get_repo_type)}"
+
+ case "$repotype" in
+ git)
+ reporoot="$(__git_get_reporoot)" ||
+ { zerror "could not determine git repository root"; return 1 }
+ branch="$(__git_get_branch)" ||
+ { zerror "could not determine git branch"; return 1 }
+ if [ -n "$VCSH_REPO_NAME" ]; then
+ # if vcsh is used to get a subshell, then the repo root is the home
+ # directory, but we want to indicate the vcsh context too:
+ eval set -- $(__vcs_get_prompt_path_components "$HOME" "$branch")
+ set -- "vcsh:$VCSH_REPO_NAME" "$2" "$1${3:+/$3}"
+ else
+ eval set -- $(__vcs_get_prompt_path_components "$reporoot" "$branch")
+ if [ -d "$GIT_DIR" ]; then
+ # poor man's replace until I find out how to do named dirs properly
+ # here:
+ local _D="${GIT_DIR/$HOME/~}"
+ set -- "$_D" "$2" "${${1#$_D}%/}"
+ fi
+ fi
+ ;;
+ hg)
+ reporoot="$(__hg_get_reporoot)" ||
+ { zerror "could not determine hg repository root"; return 1 }
+ branch="$(__hg_get_branch)" ||
+ { zerror "could not determine hg branch"; return 1 }
+ eval set -- $(__vcs_get_prompt_path_components "$reporoot" "$branch")
+ ;;
+ bzr)
+ reporoot="$(__bzr_get_reporoot)" ||
+ { zerror "could not determine bzr repository root"; return 1 }
+ branch="$(__bzr_get_branch)" ||
+ { zerror "could not determine bzr branch"; return 1 }
+ eval set -- $(__vcs_get_prompt_path_components "$reporoot" "$branch")
+ ;;
+ *)
+ case "$repotype" in
+ NONE) :;;
+ *) warn "$repotype repositories not (yet) supported in the prompt";;
+ esac
+ local MAXLEN MINLEN
+ MAXLEN=$(__get_prompt_path_len max)
+ local p="%${MAXLEN}<…<%~%<<"
+ #TODO find a better way so we don't have to nuke $psvar, but since the
+ # %(nv.true.false) check for prompts checks element count, not
+ # content, that's all we get for now
+ psvar=("${(%)p}")
+ return
+ esac
+
+ psvar[1,3]=($1 $2 $3)
+}
+
+__vcs_print_preprompt()
+{
+ emulate -L zsh
+ local repotype="${1:-$(__vcs_get_repo_type)}"
+
+ case "$repotype" in
+ git)
+ __git_print_preprompt
+ ;;
+ esac
+}
+
+if ! is_root; then
+ # too dangerous to be run as root
+ autoload -U add-zsh-hook
+
+ _update_vcs_prompt_vars_if_vcs_ran() {
+ local vcs="$(__vcs_get_repo_type)"
+ case "$(history $(($HISTCMD - 1)))" in
+ # $vcs appeared in last command, so be sure to update
+ *${vcs}*) __vcs_set_prompt_variables "$vcs"
+ esac
+ }
+ add-zsh-hook precmd _update_vcs_prompt_vars_if_vcs_ran
+
+ _update_vcs_prompt_vars() {
+ __vcs_set_prompt_variables
+ }
+ add-zsh-hook chpwd _update_vcs_prompt_vars
+
+ _print_preprompt() {
+ [[ $? -eq 0 ]] && __vcs_print_preprompt
+ }
+ add-zsh-hook precmd _print_preprompt
+
+ # call it once
+ _update_vcs_prompt_vars
+fi
+
+function make_ps1() {
+ # start with '+' if in a subshell
+ echo -n '%(2L.+.)'
+
+ # the machine name, bold or underlined based on non-root/root
+ local ps1_hl=B
+ is_root && ps1_hl=U
+ echo -n "%${ps1_hl:=B}%m%${(L)ps1_hl}"
+
+ # if we're in a Debian chroot, make that stand out
+ echo -n "${DEBIAN_CHROOT:+/%S$DEBIAN_CHROOT%s}"
+
+ # we end this with a :
+ echo -n :
+
+ # now comes the working directory, composed from parts in $psvar,
+ # which is managed by $ZDOTDIR/zshrc/06-vcsprompt
+ echo -n '%1v%(2v.|%B%2v%b|.)%(3v.%3v.)'
+
+ # and we finish with #/% for root/non-root, and a space
+ echo -n '%# '
+ echo
+}
+PS1=$(make_ps1)
+unfunction make_ps1
+PS2="%{$fg[red]%}%_>%{$reset_color%}"
+
+function make_rps1() {
+ # First, a comment character and parens
+ echo -n '#('
+
+ # Next, if the returncode was non-zero, make it stand-out
+ # and include a trailing space
+ echo -n "%(0?..%{$fg[red]%}%S%?%s%{$reset_color%} )"
+
+ # If there are background jobs, print their number, followed by
+ # '@':
+ echo -n '%(1j.%j@.)'
+
+ # and then the terminal line we're using
+ echo -n '%l'
+
+ # this concludes the first part, but there's more
+ echo -n ') '
+
+ # the timestamp will finish it off:
+ echo -n '%D{%d %H:%M:%S.%.}'
+ echo
+}
+RPS1=$(make_rps1)
+unfunction make_rps1
+
+# vim:ft=zsh