]> git.madduck.net Git - etc/zsh.git/blob - .zsh/themes/prompt_madduck_setup

madduck's git repository

Every one of the projects in this repository is available at the canonical URL git://git.madduck.net/madduck/pub/<projectpath> — see each project's metadata for the exact URL.

All patches and comments are welcome. Please squash your changes to logical commits before using git-format-patch and git-send-email to patches@git.madduck.net. If you'd read over the Git project's submission guidelines and adhered to them, I'd be especially grateful.

SSH access, as well as push access can be individually arranged.

If you use my repositories frequently, consider adding the following snippet to ~/.gitconfig and using the third clone URL listed for each project:

[url "git://git.madduck.net/madduck/"]
  insteadOf = madduck:

completely disable the vcs prompt on network filesystems
[etc/zsh.git] / .zsh / themes / prompt_madduck_setup
1 #
2 # My zsh prompt theme
3 #
4 # Copyright © 1994–2017 martin f. krafft <madduck@madduck.net>
5 # Released under the terms of the Artistic Licence 2.0
6 #
7 # Source repository: http://git.madduck.net/v/etc/zsh.git
8 #
9 # vcs stuff shamelessly based on http://glandium.org/blog/?p=170
10 #
11
12 zstyle -m :madduck:prompt:default path-maxlen '*' \
13   || zstyle :madduck:prompt:default path-maxlen 25
14 zstyle -m :madduck:prompt:default path-minlen '*' \
15   || zstyle :madduck:prompt:default path-minlen 10
16
17 __on_networkfs()
18 {
19   emulate -L zsh
20   case $(df -T . | sed -rne '$s,^[^[:space:]]+[[:space:]]+([^[:space:]]+).*,\1,p') in
21     (cifs|nfs) return 0;;
22   esac
23   return 1
24 }
25
26 __git_get_reporoot()
27 {
28   emulate -L zsh
29   # return the full path to the root of the current git repository
30   [ -d "$GIT_DIR" ] && echo "$GIT_DIR" && return 0
31   local dir; dir="$PWD/$(git rev-parse --show-cdup)"
32   # do not use --show-toplevel because it resolves symlinks
33   echo $dir:a
34 }
35
36 __git_get_branch()
37 {
38   emulate -L zsh
39   # use oh-my-zsh prompt info function if it exists
40   $(command -v git_prompt_info) && return
41
42   # return the name of the git branch we're on
43   local ref gitdir
44   gitdir="$(git rev-parse --git-dir)"
45   ref=$(git --git-dir="$gitdir" symbolic-ref -q HEAD 2>/dev/null \
46      || git --git-dir="$gitdir" name-rev --name-only HEAD 2>/dev/null) || return 1
47   echo "${ref#refs/heads/}"
48 }
49
50 __git_print_preprompt()
51 {
52   emulate -L zsh
53   [ "$(git config --get core.bare)" = false ] || return
54   __on_networkfs && return
55
56   local COLUMNS=${COLUMNS:-80}
57   local LINES=${LINES:-25}
58
59   function output() {
60     emulate -L zsh
61     local title="$@"
62     local output; output=(${(f)"$(cat)"})
63
64     [[ ${#output} -ge 1 ]] || return
65
66     local statl="$(echo ${output[-1]} | sed -re 's@^\s*([0-9]+)[^,]+(, ([0-9]+) [^(]+\(([-+])\))(, ([0-9]+) [^(]+\(([-+])\))?@\1/\4\3/\7\6@')"
67
68     if [[ ${output[-2]## } = '...' ]]; then
69       print "${title} (${statl%/}, abbrev.):"
70       print "${(F)output[1,-3]}"
71       print " …"
72     else
73       print "${title} (${statl%/}):"
74       print "${(F)output[1,-2]}"
75     fi
76   }
77
78   function gitdiffstat() {
79     emulate -L zsh
80     local common_options="--stat=$((COLUMNS/2-1)),$((COLUMNS/4-2)),$(($LINES/3)) --relative"
81     eval git diff $common_options "$@" 2>/dev/null
82   }
83
84   local cached; cached=(${(f)"$(gitdiffstat --cached | output cached)"})
85   local changed; changed=(${(f)"$(gitdiffstat | output changed)"})
86
87   local max=${#changed}
88   [[ $max -lt ${#cached} ]] && max=${#cached}
89
90   ((max == 0)) && return
91
92   local width=$(((COLUMNS-3)/2))
93
94   if (( ${#cached} > 0 && ${#changed} > 0 )); then
95     local i
96     for (( i=1 ; i <= max ; i++ )) do
97       printf "%-${width}s │ %-${width}s\n" "${cached[$i]}" "${changed[$i]}"
98     done
99   else
100     print ${(F)cached}${(F)changed}
101   fi
102 }
103
104 __hg_get_reporoot()
105 {
106   emulate -L zsh
107   hg root
108 }
109
110 __hg_get_branch()
111 {
112   emulate -L zsh
113   echo "hg:$(hg branch)"
114 }
115
116 __bzr_get_reporoot()
117 {
118   emulate -L zsh
119   local reporoot
120   reporoot="$(bzr info | sed -rne 's, *branch root: ,,p')"
121   case "$reporoot" in
122     .) echo "$PWD";;
123     *) echo "$reporoot";;
124   esac
125 }
126
127 __bzr_get_branch()
128 {
129   emulate -L zsh
130   local branch revno
131   bzr version-info | while read i j; do
132       case "$i" in
133         revno:) revno="$j";;
134         branch-nick:) branch="$j";;
135       esac
136     done
137   echo "bzr:${branch}@$revno"
138 }
139
140 __vcs_get_repo_type()
141 {
142   emulate -L zsh
143   # return the type of the closest repository in the path hierarchy
144   # unless we're on a network filesystem:
145   if __on_networkfs; then
146     echo netfs
147     return
148   fi
149   local dir
150   while true; do
151     [ -d ${dir}.git ] && echo git && break
152     [ -d "$GIT_DIR" ] && echo git && break
153     [ -d ${dir}.bzr ] && echo bzr && break
154     [ -d ${dir}.hg ] && echo hg && break
155     [ "$(readlink -f ${dir:-.})" = / ] && echo NONE && break
156     dir="../$dir"
157   done
158 }
159
160 __get_prompt_path_len() {
161   emulate -L zsh
162   local result
163   zstyle -s ":madduck:prompt:$PWD" path-${1}len result
164   [ -z "$result" ] && zstyle -s ':madduck:prompt:default' path-${1}len result
165   echo $result
166 }
167
168 __vcs_get_prompt_path_components()
169 {
170   emulate -L zsh
171   # return formatted path components (prefix branch postfix) given
172   # the repository root and the branch.
173
174   local MAXLEN MINLEN
175   MAXLEN=$(__get_prompt_path_len max)
176   MINLEN=$(__get_prompt_path_len min)
177
178   # shortcut: if there are no arguments, return a default prompt
179   if [ -z "${1:-}" ]; then
180     pwdnamed="${(%):-%${MAXLEN}<…<%~%<<}"
181     echo "$pwdnamed"
182     return
183   fi
184
185   local reporoot branch
186   reporoot="${1%%/}"
187   branch="$2"
188
189   # replace named directories in the PWD, we need thi for the proper component
190   # count later
191   local pwdnamed
192   pwdnamed="${(%):-%~}"
193
194   # store paths in arrays for component count calculation
195   typeset -la apwd apwdnamed areporoot
196   apwd=(${(s:/:)PWD})
197   apwdnamed=(${(s:/:)pwdnamed})
198   areporoot=(${(s:/:)reporoot})
199
200   # get the number of leading and trailing path components. Since we're using
201   # %~ later and then /home/madduck suddenly becomes ~, which is 1, not
202   # 2 components, we calculate the leading component count by using the named
203   # path and the number of post components
204   local precomps postcomps
205   postcomps=$(($#apwd - $#areporoot))
206   precomps=$(($#apwdnamed - $postcomps))
207
208   local postfix
209   (( $postcomps > 0 )) && postfix="${(%):-%${postcomps}~}"
210
211   # we don't want the prompt to get too long, so keep the total prompt length
212   # under $MAXLEN, but ensure that the prefix is not shorter
213   # than $MINLEN, no matter what
214   local prelen minlen prefix
215   prelen=$((${MAXLEN} - $#branch - $#postfix))
216   minlen=${MINLEN}
217   (( $prelen < $minlen )) && prelen=$minlen
218   prefix="${(%):-%${prelen}<…<%-${precomps}~%<<}"
219
220   echo "'$prefix'" "'$branch'" "'$postfix'"
221 }
222
223 __vcs_set_prompt_variables()
224 {
225   emulate -L zsh
226   # set psvar[1..3] depending on repo type, or just psvar[1] if no repo found
227   local reporoot branch repotype
228   repotype="${1:-$(__vcs_get_repo_type)}"
229
230   case "$repotype" in
231     git)
232       reporoot="$(__git_get_reporoot)" ||
233         { zerror "could not determine git repository root"; return 1 }
234       branch="$(__git_get_branch)" ||
235         { zerror "could not determine git branch"; return 1 }
236       if [ -n "$VCSH_REPO_NAME" ]; then
237         # if vcsh is used to get a subshell, then the repo root is the home
238         # directory, but we want to indicate the vcsh context too:
239         eval set -- $(__vcs_get_prompt_path_components "$HOME" "$branch")
240         set -- "vcsh:$VCSH_REPO_NAME" "$2" "$1${3:+/$3}"
241       else
242         eval set -- $(__vcs_get_prompt_path_components "$reporoot" "$branch")
243         if [ -d "$GIT_DIR" ]; then
244           # poor man's replace until I find out how to do named dirs properly
245           # here:
246           local _D="${GIT_DIR/$HOME/~}"
247           set -- "$_D" "$2" "${${1#$_D}%/}"
248         fi
249       fi
250       ;;
251     hg)
252       reporoot="$(__hg_get_reporoot)" ||
253         { zerror "could not determine hg repository root"; return 1 }
254       branch="$(__hg_get_branch)" ||
255         { zerror "could not determine hg branch"; return 1 }
256       eval set -- $(__vcs_get_prompt_path_components "$reporoot" "$branch")
257       ;;
258     bzr)
259       reporoot="$(__bzr_get_reporoot)" ||
260         { zerror "could not determine bzr repository root"; return 1 }
261       branch="$(__bzr_get_branch)" ||
262         { zerror "could not determine bzr branch"; return 1 }
263       eval set -- $(__vcs_get_prompt_path_components "$reporoot" "$branch")
264       ;;
265     *)
266       case "$repotype" in
267         NONE|netfs) :;;
268         *) warn "$repotype repositories not (yet) supported in the prompt";;
269       esac
270       local MAXLEN MINLEN
271       MAXLEN=$(__get_prompt_path_len max)
272       local p="%${MAXLEN}<…<%~%<<"
273       #TODO find a better way so we don't have to nuke $psvar, but since the
274       #     %(nv.true.false) check for prompts checks element count, not
275       #     content, that's all we get for now
276       psvar=("${(%)p}")
277       return
278   esac
279
280   psvar[1,3]=($1 $2 $3)
281 }
282
283 __vcs_print_preprompt()
284 {
285   emulate -L zsh
286   local repotype="${1:-$(__vcs_get_repo_type)}"
287
288   case "$repotype" in
289     git)
290       __git_print_preprompt
291       ;;
292   esac
293 }
294
295 if ! is_root; then
296   # too dangerous to be run as root
297   autoload -U add-zsh-hook
298
299   _update_vcs_prompt_vars_if_vcs_ran() {
300     local vcs="$(__vcs_get_repo_type)"
301     case "$vcs/$(history $(($HISTCMD - 1)))" in
302       # $vcs appeared in last command, so be sure to update
303       NONE/*) :;;
304       netfs/*) :;;
305       */*${vcs}*) __vcs_set_prompt_variables "$vcs"
306     esac
307   }
308   add-zsh-hook precmd _update_vcs_prompt_vars_if_vcs_ran
309
310   _update_vcs_prompt_vars() {
311     __vcs_set_prompt_variables
312   }
313   add-zsh-hook chpwd _update_vcs_prompt_vars
314
315   _print_preprompt() {
316     [[ $? -eq 0 ]] && __vcs_print_preprompt
317   }
318   add-zsh-hook precmd _print_preprompt
319
320   # call it once
321   _update_vcs_prompt_vars
322 fi
323
324 function make_ps1() {
325   # start with '+' if in a subshell
326   echo -n '%(2L.+.)'
327
328   # the machine name, bold or underlined based on non-root/root
329   local ps1_hl=B
330   is_root && ps1_hl=U
331   echo -n "%${ps1_hl:=B}%m%${(L)ps1_hl}"
332
333   # if we're in a Debian chroot, make that stand out
334   echo -n "${DEBIAN_CHROOT:+/%S$DEBIAN_CHROOT%s}"
335
336   # we end this with a :
337   echo -n :
338
339   # now comes the working directory, composed from parts in $psvar,
340   # which is managed by $ZDOTDIR/zshrc/06-vcsprompt
341   echo -n '%1v%(2v.|%B%2v%b|.)%(3v.%3v.)'
342
343   # and we finish with #/% for root/non-root, and a space
344   echo -n '%# '
345   echo
346 }
347 PS1=$(make_ps1)
348 unfunction make_ps1
349 PS2="%{$fg[red]%}%_>%{$reset_color%}"
350
351 function make_rps1() {
352   # First, a comment character and parens
353   echo -n '#('
354
355   # Next, if the returncode was non-zero, make it stand-out
356   # and include a trailing space
357   echo -n "%(0?..%{$fg[red]%}%S%?%s%{$reset_color%} )"
358
359   # If there are background jobs, print their number, followed by
360   # '@':
361   echo -n '%(1j.%j@.)'
362
363   # and then the terminal line we're using
364   echo -n '%l'
365
366   # this concludes the first part, but there's more
367   echo -n ') '
368
369   # the timestamp will finish it off:
370   echo -n '%D{%d %H:%M:%S.%.}'
371   echo
372 }
373 typeset -g RPS1="$(make_rps1)"
374 unfunction make_rps1
375
376 # vim:ft=zsh