X-Git-Url: http://git.grml.org/?p=grml-etc-core.git;a=blobdiff_plain;f=etc%2Fzsh%2Fzshrc;h=b3da7ff7b6fc1e9cadd4106feb38bd5303f1ed3d;hp=305e0ec98af851039db68fa643071d17ee90d0c8;hb=21be0f44c81ec3a902d0db8cfff04b0bb571e1cb;hpb=2ebcbd6409fb56c44bbe4671e4d845d8be7284b7 diff --git a/etc/zsh/zshrc b/etc/zsh/zshrc index 305e0ec..b3da7ff 100644 --- a/etc/zsh/zshrc +++ b/etc/zsh/zshrc @@ -109,7 +109,7 @@ if [[ $ZSH_PROFILE_RC -gt 0 ]] ; then fi # load .zshrc.pre to give the user the chance to overwrite the defaults -[[ -r ${HOME}/.zshrc.pre ]] && source ${HOME}/.zshrc.pre +[[ -r ${ZDOTDIR:-${HOME}}/.zshrc.pre ]] && source ${ZDOTDIR:-${HOME}}/.zshrc.pre # check for version/system # check for versions (compatibility reasons) @@ -144,6 +144,12 @@ is433(){ return 1 } +is437(){ + [[ $ZSH_VERSION == 4.3.<7->* || $ZSH_VERSION == 4.<4->* \ + || $ZSH_VERSION == <5->* ]] && return 0 + return 1 +} + is439(){ [[ $ZSH_VERSION == 4.3.<9->* || $ZSH_VERSION == 4.<4->* \ || $ZSH_VERSION == <5->* ]] && return 0 @@ -179,6 +185,11 @@ isdarwin(){ return 1 } +isfreebsd(){ + [[ $OSTYPE == freebsd* ]] && return 0 + return 1 +} + #f1# are we running within an utf environment? isutfenv() { case "$LANG $CHARSET $LANGUAGE" in @@ -237,7 +248,7 @@ function zrcautoload() { local -i ffound ffile=$1 - (( found = 0 )) + (( ffound = 0 )) for fdir in ${fpath} ; do [[ -e ${fdir}/${ffile} ]] && (( ffound = 1 )) done @@ -395,6 +406,7 @@ check_com() { salias() { emulate -L zsh local only=0 ; local multi=0 + local key val while [[ $1 == -* ]] ; do case $1 in (-o) only=1 ;; @@ -484,8 +496,7 @@ xcat() { xunfunction() { emulate -L zsh local -a funcs - funcs=(salias xcat xsource xunfunction zrcautoload) - + funcs=(salias xcat xsource xunfunction zrcautoload zrcautozle) for func in $funcs ; do [[ -n ${functions[$func]} ]] \ && unfunction $func @@ -497,7 +508,7 @@ xunfunction() { # modifications in ~/.zshrc.local zrclocal() { xsource "/etc/zsh/zshrc.local" - xsource "${HOME}/.zshrc.local" + xsource "${ZDOTDIR:-${HOME}}/.zshrc.local" return 0 } @@ -533,8 +544,9 @@ export SHELL='/bin/zsh' # color setup for ls: check_com -c dircolors && eval $(dircolors -b) -# color setup for ls on OS X: +# color setup for ls on OS X / FreeBSD: isdarwin && export CLICOLOR=1 +isfreebsd && export CLICOLOR=1 # do MacPorts setup on darwin if isdarwin && [[ -d /opt/local ]]; then @@ -549,7 +561,7 @@ isdarwin && xsource /sw/bin/init.sh # load our function and completion directories for fdir in /usr/share/grml/zsh/completion /usr/share/grml/zsh/functions; do fpath=( ${fdir} ${fdir}/**/*(/N) ${fpath} ) - if [[ ${fpath} == '/usr/share/grml/zsh/functions' ]] ; then + if [[ ${fdir} == '/usr/share/grml/zsh/functions' ]] ; then for func in ${fdir}/**/[^_]*[^~](N.) ; do zrcautoload ${func:t} done @@ -579,36 +591,213 @@ watch=(notme root) # automatically remove duplicates from these arrays typeset -U path cdpath fpath manpath -# keybindings -if [[ "$TERM" != emacs ]] ; then - [[ -z "$terminfo[kdch1]" ]] || bindkey -M emacs "$terminfo[kdch1]" delete-char - [[ -z "$terminfo[khome]" ]] || bindkey -M emacs "$terminfo[khome]" beginning-of-line - [[ -z "$terminfo[kend]" ]] || bindkey -M emacs "$terminfo[kend]" end-of-line - [[ -z "$terminfo[kdch1]" ]] || bindkey -M vicmd "$terminfo[kdch1]" vi-delete-char - [[ -z "$terminfo[khome]" ]] || bindkey -M vicmd "$terminfo[khome]" vi-beginning-of-line - [[ -z "$terminfo[kend]" ]] || bindkey -M vicmd "$terminfo[kend]" vi-end-of-line - [[ -z "$terminfo[cuu1]" ]] || bindkey -M viins "$terminfo[cuu1]" vi-up-line-or-history - [[ -z "$terminfo[cuf1]" ]] || bindkey -M viins "$terminfo[cuf1]" vi-forward-char - [[ -z "$terminfo[kcuu1]" ]] || bindkey -M viins "$terminfo[kcuu1]" vi-up-line-or-history - [[ -z "$terminfo[kcud1]" ]] || bindkey -M viins "$terminfo[kcud1]" vi-down-line-or-history - [[ -z "$terminfo[kcuf1]" ]] || bindkey -M viins "$terminfo[kcuf1]" vi-forward-char - [[ -z "$terminfo[kcub1]" ]] || bindkey -M viins "$terminfo[kcub1]" vi-backward-char - # ncurses stuff: - [[ "$terminfo[kcuu1]" == $'\eO'* ]] && bindkey -M viins "${terminfo[kcuu1]/O/[}" vi-up-line-or-history - [[ "$terminfo[kcud1]" == $'\eO'* ]] && bindkey -M viins "${terminfo[kcud1]/O/[}" vi-down-line-or-history - [[ "$terminfo[kcuf1]" == $'\eO'* ]] && bindkey -M viins "${terminfo[kcuf1]/O/[}" vi-forward-char - [[ "$terminfo[kcub1]" == $'\eO'* ]] && bindkey -M viins "${terminfo[kcub1]/O/[}" vi-backward-char - [[ "$terminfo[khome]" == $'\eO'* ]] && bindkey -M viins "${terminfo[khome]/O/[}" beginning-of-line - [[ "$terminfo[kend]" == $'\eO'* ]] && bindkey -M viins "${terminfo[kend]/O/[}" end-of-line - [[ "$terminfo[khome]" == $'\eO'* ]] && bindkey -M emacs "${terminfo[khome]/O/[}" beginning-of-line - [[ "$terminfo[kend]" == $'\eO'* ]] && bindkey -M emacs "${terminfo[kend]/O/[}" end-of-line +# Load a few modules +is4 && \ +for mod in parameter complist deltochar mathfunc ; do + zmodload -i zsh/${mod} 2>/dev/null || print "Notice: no ${mod} available :(" +done + +# autoload zsh modules when they are referenced +if is4 ; then + zmodload -a zsh/stat zstat + zmodload -a zsh/zpty zpty + zmodload -ap zsh/mapfile mapfile +fi + +# completion system +if zrcautoload compinit ; then + compinit || print 'Notice: no compinit available :(' +else + print 'Notice: no compinit available :(' + function compdef { } fi -## keybindings (run 'bindkeys' for details, more details via man zshzle) -# use emacs style per default: +# completion system + +# called later (via is4 && grmlcomp) +# note: use 'zstyle' for getting current settings +# press ^xh (control-x h) for getting tags in context; ^x? (control-x ?) to run complete_debug with trace output +grmlcomp() { + # TODO: This could use some additional information + + # Make sure the completion system is initialised + (( ${+_comps} )) || return 1 + + # allow one error for every three characters typed in approximate completer + zstyle ':completion:*:approximate:' max-errors 'reply=( $((($#PREFIX+$#SUFFIX)/3 )) numeric )' + + # don't complete backup files as executables + zstyle ':completion:*:complete:-command-::commands' ignored-patterns '(aptitude-*|*\~)' + + # start menu completion only if it could find no unambiguous initial string + zstyle ':completion:*:correct:*' insert-unambiguous true + zstyle ':completion:*:corrections' format $'%{\e[0;31m%}%d (errors: %e)%{\e[0m%}' + zstyle ':completion:*:correct:*' original true + + # activate color-completion + zstyle ':completion:*:default' list-colors ${(s.:.)LS_COLORS} + + # format on completion + zstyle ':completion:*:descriptions' format $'%{\e[0;31m%}completing %B%d%b%{\e[0m%}' + + # automatically complete 'cd -' and 'cd -' with menu + # zstyle ':completion:*:*:cd:*:directory-stack' menu yes select + + # insert all expansions for expand completer + zstyle ':completion:*:expand:*' tag-order all-expansions + zstyle ':completion:*:history-words' list false + + # activate menu + zstyle ':completion:*:history-words' menu yes + + # ignore duplicate entries + zstyle ':completion:*:history-words' remove-all-dups yes + zstyle ':completion:*:history-words' stop yes + + # match uppercase from lowercase + zstyle ':completion:*' matcher-list 'm:{a-z}={A-Z}' + + # separate matches into groups + zstyle ':completion:*:matches' group 'yes' + zstyle ':completion:*' group-name '' + + if [[ "$NOMENU" -eq 0 ]] ; then + # if there are more than 5 options allow selecting from a menu + zstyle ':completion:*' menu select=5 + else + # don't use any menus at all + setopt no_auto_menu + fi + + zstyle ':completion:*:messages' format '%d' + zstyle ':completion:*:options' auto-description '%d' + + # describe options in full + zstyle ':completion:*:options' description 'yes' + + # on processes completion complete all user processes + zstyle ':completion:*:processes' command 'ps -au$USER' + + # offer indexes before parameters in subscripts + zstyle ':completion:*:*:-subscript-:*' tag-order indexes parameters + + # provide verbose completion information + zstyle ':completion:*' verbose true + + # recent (as of Dec 2007) zsh versions are able to provide descriptions + # for commands (read: 1st word in the line) that it will list for the user + # to choose from. The following disables that, because it's not exactly fast. + zstyle ':completion:*:-command-:*:' verbose false + + # set format for warnings + zstyle ':completion:*:warnings' format $'%{\e[0;31m%}No matches for:%{\e[0m%} %d' + + # define files to ignore for zcompile + zstyle ':completion:*:*:zcompile:*' ignored-patterns '(*~|*.zwc)' + zstyle ':completion:correct:' prompt 'correct to: %e' + + # Ignore completion functions for commands you don't have: + zstyle ':completion::(^approximate*):*:functions' ignored-patterns '_*' + + # Provide more processes in completion of programs like killall: + zstyle ':completion:*:processes-names' command 'ps c -u ${USER} -o command | uniq' + + # complete manual by their section + zstyle ':completion:*:manuals' separate-sections true + zstyle ':completion:*:manuals.*' insert-sections true + zstyle ':completion:*:man:*' menu yes select + + # Search path for sudo completion + zstyle ':completion:*:sudo:*' command-path /usr/local/sbin \ + /usr/local/bin \ + /usr/sbin \ + /usr/bin \ + /sbin \ + /bin \ + /usr/X11R6/bin + + # provide .. as a completion + zstyle ':completion:*' special-dirs .. + + # run rehash on completion so new installed program are found automatically: + _force_rehash() { + (( CURRENT == 1 )) && rehash + return 1 + } + + ## correction + # some people don't like the automatic correction - so run 'NOCOR=1 zsh' to deactivate it + if [[ "$NOCOR" -gt 0 ]] ; then + zstyle ':completion:*' completer _oldlist _expand _force_rehash _complete _files _ignored + setopt nocorrect + else + # try to be smart about when to use what completer... + setopt correct + zstyle -e ':completion:*' completer ' + if [[ $_last_try != "$HISTNO$BUFFER$CURSOR" ]] ; then + _last_try="$HISTNO$BUFFER$CURSOR" + reply=(_complete _match _ignored _prefix _files) + else + if [[ $words[1] == (rm|mv) ]] ; then + reply=(_complete _files) + else + reply=(_oldlist _expand _force_rehash _complete _ignored _correct _approximate _files) + fi + fi' + fi + + # command for process lists, the local web server details and host completion + zstyle ':completion:*:urls' local 'www' '/var/www/' 'public_html' + + # caching + [[ -d $ZSHDIR/cache ]] && zstyle ':completion:*' use-cache yes && \ + zstyle ':completion::complete:*' cache-path $ZSHDIR/cache/ + + # host completion + if is42 ; then + [[ -r ~/.ssh/known_hosts ]] && _ssh_hosts=(${${${${(f)"$(<$HOME/.ssh/known_hosts)"}:#[\|]*}%%\ *}%%,*}) || _ssh_hosts=() + [[ -r /etc/hosts ]] && : ${(A)_etc_hosts:=${(s: :)${(ps:\t:)${${(f)~~"$(, Bernhard Tittelbach @@ -627,69 +816,6 @@ beginning-or-end-of-somewhere() { zle -N beginning-of-somewhere beginning-or-end-of-somewhere zle -N end-of-somewhere beginning-or-end-of-somewhere - -#if [[ "$TERM" == screen ]] ; then - -## with HOME/END, move to beginning/end of line (on multiline) on first keypress -## to beginning/end of buffer on second keypress -## and to beginning/end of history on (at most) the third keypress -# terminator & non-debian xterm -bindkey '\eOH' beginning-of-somewhere # home -bindkey '\eOF' end-of-somewhere # end -# freebsd console -bindkey '\e[H' beginning-of-somewhere # home -bindkey '\e[F' end-of-somewhere # end -# xterm,gnome-terminal,quake,etc -bindkey '^[[1~' beginning-of-somewhere # home -bindkey '^[[4~' end-of-somewhere # end -# if terminal type is set to 'rxvt': -bindkey '\e[7~' beginning-of-somewhere # home -bindkey '\e[8~' end-of-somewhere # end -#fi - -bindkey '\e[A' up-line-or-search # cursor up -bindkey '\e[B' down-line-or-search # - - -## use Ctrl-left-arrow and Ctrl-right-arrow for jumping to word-beginnings on the CL -bindkey "\e[5C" forward-word -bindkey "\e[5D" backward-word -bindkey "\e[1;5C" forward-word -bindkey "\e[1;5D" backward-word -## the same for alt-left-arrow and alt-right-arrow -bindkey '^[[1;3C' forward-word -bindkey '^[[1;3D' backward-word - -# Search backward in the history for a line beginning with the current -# line up to the cursor and move the cursor to the end of the line then -zle -N history-beginning-search-backward-end history-search-end -zle -N history-beginning-search-forward-end history-search-end -#k# search history backward for entry beginning with typed text -bindkey '^xp' history-beginning-search-backward-end -#k# search history forward for entry beginning with typed text -bindkey '^xP' history-beginning-search-forward-end -#k# search history backward for entry beginning with typed text -bindkey "\e[5~" history-beginning-search-backward-end # PageUp -#k# search history forward for entry beginning with typed text -bindkey "\e[6~" history-beginning-search-forward-end # PageDown - -# bindkey -s '^l' "|less\n" # ctrl-L pipes to less -# bindkey -s '^b' " &\n" # ctrl-B runs it in the background - -# insert unicode character -# usage example: 'ctrl-x i' 00A7 'ctrl-x i' will give you an § -# See for example http://unicode.org/charts/ for unicode characters code -zrcautoload insert-unicode-char -zle -N insert-unicode-char -#k# Insert Unicode character -bindkey '^xi' insert-unicode-char - -#m# k Shift-tab Perform backwards menu completion -if [[ -n "$terminfo[kcbt]" ]]; then - bindkey "$terminfo[kcbt]" reverse-menu-complete -elif [[ -n "$terminfo[cbt]" ]]; then # required for GNU screen - bindkey "$terminfo[cbt]" reverse-menu-complete -fi - ## toggle the ,. abbreviation feature on/off # NOABBREVIATION: default abbreviation-state # 0 - enabled (default) @@ -703,10 +829,7 @@ grml_toggle_abbrev() { NOABBREVIATION=1 fi } - -#k# Toggle abbreviation expansion on/off zle -N grml_toggle_abbrev -bindkey '^xA' grml_toggle_abbrev # add a command line to the shells history without executing it commit-to-history() { @@ -714,7 +837,6 @@ commit-to-history() { zle send-break } zle -N commit-to-history -bindkey "^x^h" commit-to-history # only slash should be considered as a word separator: slash-backward-kill-word() { @@ -724,19 +846,6 @@ slash-backward-kill-word() { } zle -N slash-backward-kill-word -#k# Kill left-side word or everything up to next slash -bindkey '\ev' slash-backward-kill-word -#k# Kill left-side word or everything up to next slash -bindkey '\e^h' slash-backward-kill-word -#k# Kill left-side word or everything up to next slash -bindkey '\e^?' slash-backward-kill-word - -# use the new *-pattern-* widgets for incremental history search -if is439 ; then - bindkey '^r' history-incremental-pattern-search-backward - bindkey '^s' history-incremental-pattern-search-forward -fi - # a generic accept-line wrapper # This widget can prevent unwanted autocorrections from command-name @@ -898,7 +1007,7 @@ abk=( '....' '../../..' 'BG' '& exit' 'C' '| wc -l' - 'G' '|& grep '${grep_options:+"${grep_options[*]} "} + 'G' '|& grep '${grep_options:+"${grep_options[*]}"} 'H' '| head' 'Hl' ' --help |& less -r' #d (Display help in pager) 'L' '| less' @@ -924,118 +1033,38 @@ zleiab() { return 0 fi - matched_chars='[.-|_a-zA-Z0-9]#' - LBUFFER=${LBUFFER%%(#m)[.-|_a-zA-Z0-9]#} + LBUFFER=${LBUFFER%%(#m)[.\-+:|_a-zA-Z0-9]#} LBUFFER+=${abk[$MATCH]:-$MATCH} } -zle -N zleiab && bindkey ",." zleiab +zle -N zleiab -#f# display contents of assoc array $abk help-show-abk() { zle -M "$(print "Type ,. after these abbreviations to expand them:"; print -a -C 2 ${(kv)abk})" } -#k# Display list of abbreviations that expand when followed by ,. -zle -N help-show-abk && bindkey '^xb' help-show-abk - -# autoloading -zrcautoload zmv # who needs mmv or rename? -zrcautoload history-search-end -# we don't want to quote/espace URLs on our own... -# if autoload -U url-quote-magic ; then -# zle -N self-insert url-quote-magic -# zstyle ':url-quote-magic:*' url-metas '*?[]^()~#{}=' -# else -# print 'Notice: no url-quote-magic available :(' -# fi -alias url-quote='autoload -U url-quote-magic ; zle -N self-insert url-quote-magic' +zle -N help-show-abk -#m# k ESC-h Call \kbd{run-help} for the 1st word on the command line -alias run-help >&/dev/null && unalias run-help -for rh in run-help{,-git,-svk,-svn}; do - zrcautoload $rh -done; unset rh +# press "ctrl-e d" to insert the actual date in the form yyyy-mm-dd +insert-datestamp() { LBUFFER+=${(%):-'%D{%Y-%m-%d}'}; } +zle -N insert-datestamp -# completion system -if zrcautoload compinit ; then - compinit || print 'Notice: no compinit available :(' -else - print 'Notice: no compinit available :(' - function zstyle { } - function compdef { } -fi +# press esc-m for inserting last typed word again (thanks to caphuso!) +insert-last-typed-word() { zle insert-last-word -- 0 -1 }; +zle -N insert-last-typed-word; -is4 && zrcautoload zed # use ZLE editor to edit a file or function - -is4 && \ -for mod in complist deltochar mathfunc ; do - zmodload -i zsh/${mod} 2>/dev/null || print "Notice: no ${mod} available :(" -done - -# autoload zsh modules when they are referenced -if is4 ; then - zmodload -a zsh/stat zstat - zmodload -a zsh/zpty zpty - zmodload -ap zsh/mapfile mapfile -fi - -if is4 && zrcautoload insert-files && zle -N insert-files ; then - #k# Insert files and test globbing - bindkey "^xf" insert-files # C-x-f -fi - -bindkey ' ' magic-space # also do history expansion on space -#k# Trigger menu-complete -bindkey '\ei' menu-complete # menu completion via esc-i - -# press esc-e for editing command line in $EDITOR or $VISUAL -if is4 && zrcautoload edit-command-line && zle -N edit-command-line ; then - #k# Edit the current line in \kbd{\$EDITOR} - bindkey '\ee' edit-command-line -fi - -if is4 && [[ -n ${(k)modules[zsh/complist]} ]] ; then - #k# menu selection: pick item but stay in the menu - bindkey -M menuselect '\e^M' accept-and-menu-complete - # also use + and INSERT since it's easier to press repeatedly - bindkey -M menuselect "+" accept-and-menu-complete - bindkey -M menuselect "^[[2~" accept-and-menu-complete - - # accept a completion and try to complete again by using menu - # completion; very useful with completing directories - # by using 'undo' one's got a simple file browser - bindkey -M menuselect '^o' accept-and-infer-next-history -fi - -# press "ctrl-e d" to insert the actual date in the form yyyy-mm-dd -insert-datestamp() { LBUFFER+=${(%):-'%D{%Y-%m-%d}'}; } -zle -N insert-datestamp - -#k# Insert a timestamp on the command line (yyyy-mm-dd) -bindkey '^ed' insert-datestamp - -# press esc-m for inserting last typed word again (thanks to caphuso!) -insert-last-typed-word() { zle insert-last-word -- 0 -1 }; -zle -N insert-last-typed-word; - -#k# Insert last typed word -bindkey "\em" insert-last-typed-word - -function grml-zsh-fg() { - if (( ${#jobstates} )); then - zle .push-input - [[ -o hist_ignore_space ]] && BUFFER=' ' || BUFFER='' - BUFFER="${BUFFER}fg" - zle .accept-line - else - zle -M 'No background jobs. Doing nothing.' - fi -} -zle -N grml-zsh-fg -#k# A smart shortcut for \kbd{fg} -bindkey '^z' grml-zsh-fg +function grml-zsh-fg() { + if (( ${#jobstates} )); then + zle .push-input + [[ -o hist_ignore_space ]] && BUFFER=' ' || BUFFER='' + BUFFER="${BUFFER}fg" + zle .accept-line + else + zle -M 'No background jobs. Doing nothing.' + fi +} +zle -N grml-zsh-fg # run command line as user root via sudo: sudo-command-line() { @@ -1047,9 +1076,6 @@ sudo-command-line() { } zle -N sudo-command-line -#k# prepend the current command with "sudo" -bindkey "^os" sudo-command-line - ### jump behind the first word on the cmdline. ### useful to add options. function jump_after_first_word() { @@ -1063,14 +1089,163 @@ function jump_after_first_word() { fi } zle -N jump_after_first_word -#k# jump to after first word (for adding options) -bindkey '^x1' jump_after_first_word -# complete word from history with menu (from Book: ZSH, OpenSource-Press) -zle -C hist-complete complete-word _generic -zstyle ':completion:hist-complete:*' completer _history -#k# complete word from history with menu -bindkey "^x^x" hist-complete +#f5# Create directory under cursor or the selected area +inplaceMkDirs() { + # Press ctrl-xM to create the directory under the cursor or the selected area. + # To select an area press ctrl-@ or ctrl-space and use the cursor. + # Use case: you type "mv abc ~/testa/testb/testc/" and remember that the + # directory does not exist yet -> press ctrl-XM and problem solved + local PATHTOMKDIR + if ((REGION_ACTIVE==1)); then + local F=$MARK T=$CURSOR + if [[ $F -gt $T ]]; then + F=${CURSOR} + T=${MARK} + fi + # get marked area from buffer and eliminate whitespace + PATHTOMKDIR=${BUFFER[F+1,T]%%[[:space:]]##} + PATHTOMKDIR=${PATHTOMKDIR##[[:space:]]##} + else + local bufwords iword + bufwords=(${(z)LBUFFER}) + iword=${#bufwords} + bufwords=(${(z)BUFFER}) + PATHTOMKDIR="${(Q)bufwords[iword]}" + fi + [[ -z "${PATHTOMKDIR}" ]] && return 1 + PATHTOMKDIR=${~PATHTOMKDIR} + if [[ -e "${PATHTOMKDIR}" ]]; then + zle -M " path already exists, doing nothing" + else + zle -M "$(mkdir -p -v "${PATHTOMKDIR}")" + zle end-of-line + fi +} + +#k# mkdir -p from string under cursor or marked area +zle -N inplaceMkDirs + +#v1# set number of lines to display per page +HELP_LINES_PER_PAGE=20 +#v1# set location of help-zle cache file +HELP_ZLE_CACHE_FILE=~/.cache/zsh_help_zle_lines.zsh +# helper function for help-zle, actually generates the help text +help_zle_parse_keybindings() +{ + emulate -L zsh + setopt extendedglob + unsetopt ksharrays #indexing starts at 1 + + #v1# choose files that help-zle will parse for keybindings + ((${+HELPZLE_KEYBINDING_FILES})) || HELPZLE_KEYBINDING_FILES=( /etc/zsh/zshrc ~/.zshrc.pre ~/.zshrc ~/.zshrc.local ) + + if [[ -r $HELP_ZLE_CACHE_FILE ]]; then + local load_cache=0 + for f ($HELPZLE_KEYBINDING_FILES) [[ $f -nt $HELP_ZLE_CACHE_FILE ]] && load_cache=1 + [[ $load_cache -eq 0 ]] && . $HELP_ZLE_CACHE_FILE && return + fi + + #fill with default keybindings, possibly to be overwriten in a file later + #Note that due to zsh inconsistency on escaping assoc array keys, we encase the key in '' which we will remove later + local -A help_zle_keybindings + help_zle_keybindings['@']="set MARK" + help_zle_keybindings['xj']="vi-join lines" + help_zle_keybindings['xb']="jump to matching brace" + help_zle_keybindings['xu']="undo" + help_zle_keybindings['_']="undo" + help_zle_keybindings['xf']="find in cmdline" + help_zle_keybindings['a']="goto beginning of line" + help_zle_keybindings['e']="goto end of line" + help_zle_keybindings['t']="transpose charaters" + help_zle_keybindings['t']="transpose words" + help_zle_keybindings['s']="spellcheck word" + help_zle_keybindings['k']="backward kill buffer" + help_zle_keybindings['u']="forward kill buffer" + help_zle_keybindings['y']="insert previously killed word/string" + help_zle_keybindings["'"]="quote line" + help_zle_keybindings['"']="quote from mark to cursor" + help_zle_keybindings['']="repeat next cmd/char times (-10a -> -10 times 'a')" + help_zle_keybindings['u']="make next word Uppercase" + help_zle_keybindings['l']="make next word lowercase" + help_zle_keybindings['xd']="preview expansion under cursor" + help_zle_keybindings['q']="push current CL into background, freeing it. Restore on next CL" + help_zle_keybindings['.']="insert (and interate through) last word from prev CLs" + help_zle_keybindings[',']="complete word from newer history (consecutive hits)" + help_zle_keybindings['m']="repeat last typed word on current CL" + help_zle_keybindings['v']="insert next keypress symbol literally (e.g. for bindkey)" + help_zle_keybindings['!!:n*']="insert last n arguments of last command" + help_zle_keybindings['!!:n-']="insert arguments n..N-2 of last command (e.g. mv s s d)" + help_zle_keybindings['h']="show help/manpage for current command" + + #init global variables + unset help_zle_lines help_zle_sln + typeset -g -a help_zle_lines + typeset -g help_zle_sln=1 + + local k v + local lastkeybind_desc contents #last description starting with #k# that we found + local num_lines_elapsed=0 #number of lines between last description and keybinding + #search config files in the order they a called (and thus the order in which they overwrite keybindings) + for f in $HELPZLE_KEYBINDING_FILES; do + [[ -r "$f" ]] || continue #not readable ? skip it + contents="$(<$f)" + for cline in "${(f)contents}"; do + #zsh pattern: matches lines like: #k# .............. + if [[ "$cline" == (#s)[[:space:]]#\#k\#[[:space:]]##(#b)(*)[[:space:]]#(#e) ]]; then + lastkeybind_desc="$match[*]" + num_lines_elapsed=0 + #zsh pattern: matches lines that set a keybinding using bindkey or compdef -k + # ignores lines that are commentend out + # grabs first in '' or "" enclosed string with length between 1 and 6 characters + elif [[ "$cline" == [^#]#(bindkey|compdef -k)[[:space:]](*)(#b)(\"((?)(#c1,6))\"|\'((?)(#c1,6))\')(#B)(*) ]]; then + #description prevously found ? description not more than 2 lines away ? keybinding not empty ? + if [[ -n $lastkeybind_desc && $num_lines_elapsed -lt 2 && -n $match[1] ]]; then + #substitute keybinding string with something readable + k=${${${${${${${match[1]/\\e\^h/}/\\e\^\?/}/\\e\[5~/}/\\e\[6~/}//(\\e|\^\[)/}//\^/}/3~/} + #put keybinding in assoc array, possibly overwriting defaults or stuff found in earlier files + #Note that we are extracting the keybinding-string including the quotes (see Note at beginning) + help_zle_keybindings[${k}]=$lastkeybind_desc + fi + lastkeybind_desc="" + else + ((num_lines_elapsed++)) + fi + done + done + unset contents + #calculate length of keybinding column + local kstrlen=0 + for k (${(k)help_zle_keybindings[@]}) ((kstrlen < ${#k})) && kstrlen=${#k} + #convert the assoc array into preformated lines, which we are able to sort + for k v in ${(kv)help_zle_keybindings[@]}; do + #pad keybinding-string to kstrlen chars and remove outermost characters (i.e. the quotes) + help_zle_lines+=("${(r:kstrlen:)k[2,-2]}${v}") + done + #sort lines alphabetically + help_zle_lines=("${(i)help_zle_lines[@]}") + [[ -d ${HELP_ZLE_CACHE_FILE:h} ]] || mkdir -p "${HELP_ZLE_CACHE_FILE:h}" + echo "help_zle_lines=(${(q)help_zle_lines[@]})" >| $HELP_ZLE_CACHE_FILE + zcompile $HELP_ZLE_CACHE_FILE +} +typeset -g help_zle_sln +typeset -g -a help_zle_lines + +# Provides (partially autogenerated) help on keybindings and the zsh line editor +help-zle() +{ + emulate -L zsh + unsetopt ksharrays #indexing starts at 1 + #help lines already generated ? no ? then do it + [[ ${+functions[help_zle_parse_keybindings]} -eq 1 ]] && {help_zle_parse_keybindings && unfunction help_zle_parse_keybindings} + #already displayed all lines ? go back to the start + [[ $help_zle_sln -gt ${#help_zle_lines} ]] && help_zle_sln=1 + local sln=$help_zle_sln + #note that help_zle_sln is a global var, meaning we remember the last page we viewed + help_zle_sln=$((help_zle_sln + HELP_LINES_PER_PAGE)) + zle -M "${(F)help_zle_lines[sln,help_zle_sln-1]}" +} +zle -N help-zle ## complete word from currently visible Screen or Tmux buffer. if check_com -c screen || check_com -c tmux; then @@ -1098,24 +1273,286 @@ if check_com -c screen || check_com -c tmux; then _screen_display_wordlist[${_screen_display_wordlist[(i)$PREFIX]}]="" compadd -a _screen_display_wordlist } - #k# complete word from currently visible GNU screen buffer + #m# k CTRL-x\,\,\,S Complete word from GNU screen buffer bindkey -r "^xS" compdef -k _complete_screen_display complete-word '^xS' fi +# Load a few more functions and tie them to widgets, so they can be bound: + +function zrcautozle() { + emulate -L zsh + local fnc=$1 + zrcautoload $fnc && zle -N $fnc +} + +function zrcgotwidget() { + (( ${+widgets[$1]} )) +} + +function zrcgotkeymap() { + [[ -n ${(M)keymaps:#$1} ]] +} + +zrcautozle insert-files +zrcautozle edit-command-line +zrcautozle insert-unicode-char +if zrcautoload history-search-end; then + zle -N history-beginning-search-backward-end history-search-end + zle -N history-beginning-search-forward-end history-search-end +fi +zle -C hist-complete complete-word _generic +zstyle ':completion:hist-complete:*' completer _history + +# The actual terminal setup hooks and bindkey-calls: + +# An array to note missing features to ease diagnosis in case of problems. +typeset -ga grml_missing_features + +function zrcbindkey() { + if (( ARGC )) && zrcgotwidget ${argv[-1]}; then + bindkey "$@" + fi +} + +function bind2maps () { + local i sequence widget + local -a maps + + while [[ "$1" != "--" ]]; do + maps+=( "$1" ) + shift + done + shift + + if [[ "$1" == "-s" ]]; then + shift + sequence="$1" + else + sequence="${key[$1]}" + fi + widget="$2" + + [[ -z "$sequence" ]] && return 1 + + for i in "${maps[@]}"; do + zrcbindkey -M "$i" "$sequence" "$widget" + done +} + +if (( ${+terminfo[smkx]} )) && (( ${+terminfo[rmkx]} )); then + function zle-smkx () { + emulate -L zsh + printf '%s' ${terminfo[smkx]} + } + function zle-rmkx () { + emulate -L zsh + printf '%s' ${terminfo[rmkx]} + } + function zle-line-init () { + zle-smkx + } + function zle-line-finish () { + zle-rmkx + } + zle -N zle-line-init + zle -N zle-line-finish +else + for i in {s,r}mkx; do + (( ${+terminfo[$i]} )) || grml_missing_features+=($i) + done + unset i +fi + +typeset -A key +key=( + Home "${terminfo[khome]}" + End "${terminfo[kend]}" + Insert "${terminfo[kich1]}" + Delete "${terminfo[kdch1]}" + Up "${terminfo[kcuu1]}" + Down "${terminfo[kcud1]}" + Left "${terminfo[kcub1]}" + Right "${terminfo[kcuf1]}" + PageUp "${terminfo[kpp]}" + PageDown "${terminfo[knp]}" + BackTab "${terminfo[kcbt]}" +) + +# Guidelines for adding key bindings: +# +# - Do not add hardcoded escape sequences, to enable non standard key +# combinations such as Ctrl-Meta-Left-Cursor. They are not easily portable. +# +# - Adding Ctrl characters, such as '^b' is okay; note that '^b' and '^B' are +# the same key. +# +# - All keys from the $key[] mapping are obviously okay. +# +# - Most terminals send "ESC x" when Meta-x is pressed. Thus, sequences like +# '\ex' are allowed in here as well. + +bind2maps emacs -- Home beginning-of-somewhere +bind2maps viins vicmd -- Home vi-beginning-of-line +bind2maps emacs -- End end-of-somewhere +bind2maps viins vicmd -- End vi-end-of-line +bind2maps emacs viins -- Insert overwrite-mode +bind2maps vicmd -- Insert vi-insert +bind2maps emacs -- Delete delete-char +bind2maps viins vicmd -- Delete vi-delete-char +bind2maps emacs viins vicmd -- Up up-line-or-search +bind2maps emacs viins vicmd -- Down down-line-or-search +bind2maps emacs -- Left backward-char +bind2maps viins vicmd -- Left vi-backward-char +bind2maps emacs -- Right forward-char +bind2maps viins vicmd -- Right vi-forward-char +bind2maps viins vicmd -- Right vi-forward-char +#k# Display list of abbreviations that expand when followed by ,. +bind2maps emacs viins -- -s ',.' zleiab +bind2maps emacs viins -- -s '^xb' help-show-abk +bind2maps emacs viins -- -s '^xM' inplaceMkDirs +#k# display help for keybindings and ZLE +bind2maps emacs viins -- -s '^xz' help-zle +#k# Insert files and test globbing +bind2maps emacs viins -- -s "^xf" insert-files +#k# Edit the current line in \kbd{\$EDITOR} +bind2maps emacs viins -- -s '\ee' edit-command-line +#k# search history backward for entry beginning with typed text +bind2maps emacs viins -- -s '^xp' history-beginning-search-backward-end +#k# search history forward for entry beginning with typed text +bind2maps emacs viins -- -s '^xP' history-beginning-search-forward-end +#k# search history backward for entry beginning with typed text +bind2maps emacs viins -- PageUp history-beginning-search-backward-end +#k# search history forward for entry beginning with typed text +bind2maps emacs viins -- PageDown history-beginning-search-forward-end +#k# Toggle abbreviation expansion on/off +bind2maps emacs viins -- -s '^xA' grml_toggle_abbrev +bind2maps emacs viins -- -s "^x^h" commit-to-history +#k# Kill left-side word or everything up to next slash +bind2maps emacs viins -- -s '\ev' slash-backward-kill-word +#k# Kill left-side word or everything up to next slash +bind2maps emacs viins -- -s '\e^h' slash-backward-kill-word +#k# Kill left-side word or everything up to next slash +bind2maps emacs viins -- -s '\e^?' slash-backward-kill-word +# Do history expansion on space: +bind2maps emacs viins -- -s ' ' magic-space +#k# Trigger menu-complete +bind2maps emacs viins -- -s '\ei' menu-complete # menu completion via esc-i +#k# Insert a timestamp on the command line (yyyy-mm-dd) +bind2maps emacs viins -- -s '^ed' insert-datestamp +#k# Insert last typed word +bind2maps emacs viins -- -s "\em" insert-last-typed-word +#k# A smart shortcut for \kbd{fg} +bind2maps emacs viins -- -s '^z' grml-zsh-fg +#k# prepend the current command with "sudo" +bind2maps emacs viins -- -s "^os" sudo-command-line +#k# jump to after first word (for adding options) +bind2maps emacs viins -- -s '^x1' jump_after_first_word +#k# complete word from history with menu +bind2maps emacs viins -- -s "^x^x" hist-complete + +# insert unicode character +# usage example: 'ctrl-x i' 00A7 'ctrl-x i' will give you an § +# See for example http://unicode.org/charts/ for unicode characters code +#k# Insert Unicode character +bind2maps emacs viins -- -s '^xi' insert-unicode-char + +# use the new *-pattern-* widgets for incremental history search +if zrcgotwidget history-incremental-pattern-search-backward; then + for seq wid in '^r' history-incremental-pattern-search-backward \ + '^s' history-incremental-pattern-search-forward + do + bind2maps emacs viins vicmd -- -s $seq $wid + done +fi + +if zrcgotkeymap menuselect; then + #m# k Shift-tab Perform backwards menu completion + bind2maps menuselect -- BackTab reverse-menu-complete + + #k# menu selection: pick item but stay in the menu + bind2maps menuselect -- -s '\e^M' accept-and-menu-complete + # also use + and INSERT since it's easier to press repeatedly + bind2maps menuselect -- -s '+' accept-and-menu-complete + bind2maps menuselect -- Insert accept-and-menu-complete + + # accept a completion and try to complete again by using menu + # completion; very useful with completing directories + # by using 'undo' one's got a simple file browser + bind2maps menuselect -- -s '^o' accept-and-infer-next-history +fi + +# Finally, here are still a few hardcoded escape sequences; Special sequences +# like Ctrl- etc do suck a fair bit, because they are not +# standardised and most of the time are not available in a terminals terminfo +# entry. +# +# While we do not encourage adding bindings like these, we will keep these for +# backward compatibility. + +## use Ctrl-left-arrow and Ctrl-right-arrow for jumping to word-beginnings on +## the command line. +# URxvt sequences: +bind2maps emacs viins vicmd -- -s '\eOc' forward-word +bind2maps emacs viins vicmd -- -s '\eOd' backward-word +# These are for xterm: +bind2maps emacs viins vicmd -- -s '\e[1;5C' forward-word +bind2maps emacs viins vicmd -- -s '\e[1;5D' backward-word +## the same for alt-left-arrow and alt-right-arrow +# URxvt again: +bind2maps emacs viins vicmd -- -s '\e\e[C' forward-word +bind2maps emacs viins vicmd -- -s '\e\e[D' backward-word +# Xterm again: +bind2maps emacs viins vicmd -- -s '^[[1;3C' forward-word +bind2maps emacs viins vicmd -- -s '^[[1;3D' backward-word +# Also try ESC Left/Right: +bind2maps emacs viins vicmd -- -s '\e'${key[Right]} forward-word +bind2maps emacs viins vicmd -- -s '\e'${key[Left]} backward-word + +# autoloading + +zrcautoload zmv +zrcautoload zed + +# we don't want to quote/espace URLs on our own... +# if autoload -U url-quote-magic ; then +# zle -N self-insert url-quote-magic +# zstyle ':url-quote-magic:*' url-metas '*?[]^()~#{}=' +# else +# print 'Notice: no url-quote-magic available :(' +# fi +alias url-quote='autoload -U url-quote-magic ; zle -N self-insert url-quote-magic' + +#m# k ESC-h Call \kbd{run-help} for the 1st word on the command line +alias run-help >&/dev/null && unalias run-help +for rh in run-help{,-git,-svk,-svn}; do + zrcautoload $rh +done; unset rh + +# command not found handling + +(( ${COMMAND_NOT_FOUND} == 1 )) && +function command_not_found_handler() { + emulate -L zsh + if [[ -x ${GRML_ZSH_CNF_HANDLER} ]] ; then + ${GRML_ZSH_CNF_HANDLER} $1 + fi + return 1 +} + # history -ZSHDIR=$HOME/.zsh +ZSHDIR=${ZDOTDIR:-${HOME}/.zsh} #v# -HISTFILE=$HOME/.zsh_history +HISTFILE=${ZDOTDIR:-${HOME}}/.zsh_history isgrmlcd && HISTSIZE=500 || HISTSIZE=5000 isgrmlcd && SAVEHIST=1000 || SAVEHIST=10000 # useful for setopt append_history # dirstack handling DIRSTACKSIZE=${DIRSTACKSIZE:-20} -DIRSTACKFILE=${DIRSTACKFILE:-${HOME}/.zdirs} +DIRSTACKFILE=${DIRSTACKFILE:-${ZDOTDIR:-${HOME}}/.zdirs} if [[ -f ${DIRSTACKFILE} ]] && [[ ${#dirstack[*]} -eq 0 ]] ; then dirstack=( ${(f)"$(< $DIRSTACKFILE)"} ) @@ -1137,89 +1574,84 @@ chpwd() { if is433 ; then -CHPWD_PROFILE='default' +# chpwd_profiles(): Directory Profiles, Quickstart: +# +# In .zshrc.local: +# +# zstyle ':chpwd:profiles:/usr/src/grml(|/|/*)' profile grml +# zstyle ':chpwd:profiles:/usr/src/debian(|/|/*)' profile debian +# chpwd_profiles +# +# For details see the `grmlzshrc.5' manual page. function chpwd_profiles() { - # Say you want certain settings to be active in certain directories. - # This is what you want. - # - # zstyle ':chpwd:profiles:/usr/src/grml(|/|/*)' profile grml - # zstyle ':chpwd:profiles:/usr/src/debian(|/|/*)' profile debian - # - # When that's done and you enter a directory that matches the pattern - # in the third part of the context, a function called chpwd_profile_grml, - # for example, is called (if it exists). - # - # If no pattern matches (read: no profile is detected) the profile is - # set to 'default', which means chpwd_profile_default is attempted to - # be called. - # - # A word about the context (the ':chpwd:profiles:*' stuff in the zstyle - # command) which is used: The third part in the context is matched against - # ${PWD}. That's why using a pattern such as /foo/bar(|/|/*) makes sense. - # Because that way the profile is detected for all these values of ${PWD}: - # /foo/bar - # /foo/bar/ - # /foo/bar/baz - # So, if you want to make double damn sure a profile works in /foo/bar - # and everywhere deeper in that tree, just use (|/|/*) and be happy. - # - # The name of the detected profile will be available in a variable called - # 'profile' in your functions. You don't need to do anything, it'll just - # be there. - # - # Then there is the parameter $CHPWD_PROFILE is set to the profile, that - # was is currently active. That way you can avoid running code for a - # profile that is already active, by running code such as the following - # at the start of your function: - # - # function chpwd_profile_grml() { - # [[ ${profile} == ${CHPWD_PROFILE} ]] && return 1 - # ... - # } - # - # The initial value for $CHPWD_PROFILE is 'default'. - # - # Version requirement: - # This feature requires zsh 4.3.3 or newer. - # If you use this feature and need to know whether it is active in your - # current shell, there are several ways to do that. Here are two simple - # ways: - # - # a) If knowing if the profiles feature is active when zsh starts is - # good enough for you, you can put the following snippet into your - # .zshrc.local: - # - # (( ${+functions[chpwd_profiles]} )) && print "directory profiles active" - # - # b) If that is not good enough, and you would prefer to be notified - # whenever a profile changes, you can solve that by making sure you - # start *every* profile function you create like this: - # - # function chpwd_profile_myprofilename() { - # [[ ${profile} == ${CHPWD_PROFILE} ]] && return 1 - # print "chpwd(): Switching to profile: $profile" - # ... - # } - # - # That makes sure you only get notified if a profile is *changed*, - # not everytime you change directory, which would probably piss - # you off fairly quickly. :-) - # - # There you go. Now have fun with that. - local -x profile - - zstyle -s ":chpwd:profiles:${PWD}" profile profile || profile='default' - if (( ${+functions[chpwd_profile_$profile]} )) ; then - chpwd_profile_${profile} + local profile context + local -i reexecute + + context=":chpwd:profiles:$PWD" + zstyle -s "$context" profile profile || profile='default' + zstyle -T "$context" re-execute && reexecute=1 || reexecute=0 + + if (( ${+parameters[CHPWD_PROFILE]} == 0 )); then + typeset -g CHPWD_PROFILE + local CHPWD_PROFILES_INIT=1 + (( ${+functions[chpwd_profiles_init]} )) && chpwd_profiles_init + elif [[ $profile != $CHPWD_PROFILE ]]; then + (( ${+functions[chpwd_leave_profile_$CHPWD_PROFILE]} )) \ + && chpwd_leave_profile_${CHPWD_PROFILE} + fi + if (( reexecute )) || [[ $profile != $CHPWD_PROFILE ]]; then + (( ${+functions[chpwd_profile_$profile]} )) && chpwd_profile_${profile} fi CHPWD_PROFILE="${profile}" return 0 } + chpwd_functions=( ${chpwd_functions} chpwd_profiles ) fi # is433 +# Prompt setup for grml: + +# set colors for use in prompts (modern zshs allow for the use of %F{red}foo%f +# in prompts to get a red "foo" embedded, but it's good to keep these for +# backwards compatibility). +if zrcautoload colors && colors 2>/dev/null ; then + BLUE="%{${fg[blue]}%}" + RED="%{${fg_bold[red]}%}" + GREEN="%{${fg[green]}%}" + CYAN="%{${fg[cyan]}%}" + MAGENTA="%{${fg[magenta]}%}" + YELLOW="%{${fg[yellow]}%}" + WHITE="%{${fg[white]}%}" + NO_COLOR="%{${reset_color}%}" +else + BLUE=$'%{\e[1;34m%}' + RED=$'%{\e[1;31m%}' + GREEN=$'%{\e[1;32m%}' + CYAN=$'%{\e[1;36m%}' + WHITE=$'%{\e[1;37m%}' + MAGENTA=$'%{\e[1;35m%}' + YELLOW=$'%{\e[1;33m%}' + NO_COLOR=$'%{\e[0m%}' +fi + +# First, the easy ones: PS2..4: + +# secondary prompt, printed when the shell needs more information to complete a +# command. +PS2='\`%_> ' +# selection prompt used within a select loop. +PS3='?# ' +# the execution trace prompt (setopt xtrace). default: '+%N:%i>' +PS4='+%N:%i:%_> ' + +# Some additional features to use with our prompt: +# +# - battery status +# - debian_chroot +# - vcs_info setup and version specific fixes + # display battery status on right side of prompt via running 'BATTERY=1 zsh' if [[ $BATTERY -gt 0 ]] ; then if ! check_com -c acpi ; then @@ -1238,90 +1670,511 @@ if [[ $BATTERY -gt 0 ]] ; then else PERCENT="${PERCENT}%%" fi - fi -fi + fi +fi +} + +# set variable debian_chroot if running in a chroot with /etc/debian_chroot +if [[ -z "$debian_chroot" ]] && [[ -r /etc/debian_chroot ]] ; then + debian_chroot=$( ]] ; then + function VCS_INFO_realpath () { + setopt localoptions NO_shwordsplit chaselinks + ( builtin cd -q $1 2> /dev/null && pwd; ) + } + fi + + zstyle ':vcs_info:*' max-exports 2 + + if [[ -o restricted ]]; then + zstyle ':vcs_info:*' enable NONE + fi +fi + +# Change vcs_info formats for the grml prompt. The 2nd format sets up +# $vcs_info_msg_1_ to contain "zsh: repo-name" used to set our screen title. +# TODO: The included vcs_info() version still uses $VCS_INFO_message_N_. +# That needs to be the use of $VCS_INFO_message_N_ needs to be changed +# to $vcs_info_msg_N_ as soon as we use the included version. +if [[ "$TERM" == dumb ]] ; then + zstyle ':vcs_info:*' actionformats "(%s%)-[%b|%a] " "zsh: %r" + zstyle ':vcs_info:*' formats "(%s%)-[%b] " "zsh: %r" +else + # these are the same, just with a lot of colors: + zstyle ':vcs_info:*' actionformats "${MAGENTA}(${NO_COLOR}%s${MAGENTA})${YELLOW}-${MAGENTA}[${GREEN}%b${YELLOW}|${RED}%a${MAGENTA}]${NO_COLOR} " \ + "zsh: %r" + zstyle ':vcs_info:*' formats "${MAGENTA}(${NO_COLOR}%s${MAGENTA})${YELLOW}-${MAGENTA}[${GREEN}%b${MAGENTA}]${NO_COLOR}%} " \ + "zsh: %r" + zstyle ':vcs_info:(sv[nk]|bzr):*' branchformat "%b${RED}:${YELLOW}%r" +fi + +# Now for the fun part: The grml prompt themes in `promptsys' mode of operation + +# This actually defines three prompts: +# +# - grml +# - grml-large +# - grml-chroot +# +# They all share the same code and only differ with respect to which items they +# contain. The main source of documentation is the `prompt_grml_help' function +# below, which gets called when the user does this: prompt -h grml + +function prompt_grml_help () { + cat <<__EOF0__ + prompt grml + + This is the prompt as used by the grml-live system . It is + a rather simple one-line prompt, that by default looks something like this: + + @ [ ]% + + The prompt itself integrates with zsh's prompt themes system (as you are + witnessing right now) and is configurable to a certain degree. In + particular, these aspects are customisable: + + - The items used in the prompt (e.g. you can remove \`user' from + the list of activated items, which will cause the user name to + be omitted from the prompt string). + + - The attributes used with the items are customisable via strings + used before and after the actual item. + + The available items are: at, battery, change-root, date, grml-chroot, + history, host, jobs, newline, path, percent, rc, rc-always, sad-smiley, + shell-level, time, user, vcs + + The actual configuration is done via zsh's \`zstyle' mechanism. The + context, that is used while looking up styles is: + + ':prompt:grml::' + + Here is either \`left' or \`right', signifying whether the + style should affect the left or the right prompt. is either + \`setup' or 'items:', where \`' is one of the available items. + + The styles: + + - use-rprompt (boolean): If \`true' (the default), print a sad smiley + in $RPROMPT if the last command a returned non-successful error code. + (This in only valid if is "right"; ignored otherwise) + + - items (list): The list of items used in the prompt. If \`vcs' is + present in the list, the theme's code invokes \`vcs_info' + accordingly. Default (left): rc change-root user at host path vcs + percent; Default (right): sad-smiley + + Available styles in 'items:' are: pre, post. These are strings that + are inserted before (pre) and after (post) the item in question. Thus, the + following would cause the user name to be printed in red instead of the + default blue: + + zstyle ':prompt:grml:*:items:user' pre '%F{red}' + + Note, that the \`post' style may remain at its default value, because its + default value is '%f', which turns the foreground text attribute off (which + is exactly, what is still required with the new \`pre' value). +__EOF0__ +} + +function prompt_grml-chroot_help () { + cat <<__EOF0__ + prompt grml-chroot + + This is a variation of the grml prompt, see: prompt -h grml + + The main difference is the default value of the \`items' style. The rest + behaves exactly the same. Here are the defaults for \`grml-chroot': + + - left: grml-chroot user at host path percent + - right: (empty list) +__EOF0__ +} + +function prompt_grml-large_help () { + cat <<__EOF0__ + prompt grml-large + + This is a variation of the grml prompt, see: prompt -h grml + + The main difference is the default value of the \`items' style. In + particular, this theme uses _two_ lines instead of one with the plain + \`grml' theme. The rest behaves exactly the same. Here are the defaults + for \`grml-large': + + - left: rc jobs history shell-level change-root time date newline user + at host path vcs percent + - right: sad-smiley +__EOF0__ +} + +function grml_prompt_setup () { + emulate -L zsh + autoload -Uz vcs_info + autoload -Uz add-zsh-hook + add-zsh-hook precmd prompt_$1_precmd +} + +function prompt_grml_setup () { + grml_prompt_setup grml +} + +function prompt_grml-chroot_setup () { + grml_prompt_setup grml-chroot +} + +function prompt_grml-large_setup () { + grml_prompt_setup grml-large +} + +# These maps define default tokens and pre-/post-decoration for items to be +# used within the themes. All defaults may be customised in a context sensitive +# matter by using zsh's `zstyle' mechanism. +typeset -gA grml_prompt_pre_default \ + grml_prompt_post_default \ + grml_prompt_token_default \ + grml_prompt_token_function + +grml_prompt_pre_default=( + at '' + battery ' ' + change-root '' + date '%F{blue}' + grml-chroot '%F{red}' + history '%F{green}' + host '' + jobs '%F{cyan}' + newline '' + path '%B' + percent '' + rc '%B%F{red}' + rc-always '' + sad-smiley '' + shell-level '%F{red}' + time '%F{blue}' + user '%B%F{blue}' + vcs '' +) + +grml_prompt_post_default=( + at '' + battery '' + change-root '' + date '%f' + grml-chroot '%f ' + history '%f' + host '' + jobs '%f' + newline '' + path '%b' + percent '' + rc '%f%b' + rc-always '' + sad-smiley '' + shell-level '%f' + time '%f' + user '%f%b' + vcs '' +) + +grml_prompt_token_default=( + at '@' + battery 'PERCENT' + change-root 'debian_chroot' + date '%D{%Y-%m-%d}' + grml-chroot 'GRML_CHROOT' + history '{history#%!} ' + host '%m ' + jobs '[%j running job(s)] ' + newline $'\n' + path '%40<..<%~%<< ' + percent '%# ' + rc '%(?..%? )' + rc-always '%?' + sad-smiley '%(?..:()' + shell-level '%(3L.+ .)' + time '%D{%H:%M:%S} ' + user '%n' + vcs '0' +) + +function GRML_theme_add_token_usage () { + cat <<__EOF__ + Usage: grml_theme_add_token [-f|-i] [
 ]
+
+     is the name for the newly added token. If the \`-f' or \`-i' options
+    are used,  is the name of the function (see below for
+    details). Otherwise it is the literal token string to be used. 
 and
+     are optional.
+
+  Options:
+
+    -f    Use a function named \`' each time the token
+                    is to be expanded.
+
+    -i    Use a function named \`' to initialise the
+                    value of the token _once_ at runtime.
+
+    The functions are called with one argument: the token's new name. The
+    return value is expected in the \$REPLY parameter. The use of these
+    options is mutually exclusive.
+
+  Example:
+
+    To add a new token \`day' that expands to the current weekday in the
+    current locale in green foreground colour, use this:
+
+      grml_theme_add_token day '%D{%A}' '%F{green}' '%f'
+
+    Another example would be support for \$VIRTUAL_ENV:
+
+      function virtual_env_prompt () {
+        REPLY=\${VIRTUAL_ENV+\${VIRTUAL_ENV:t} }
+      }
+      grml_theme_add_token virtual-env -f virtual_env_prompt
+
+    After that, you will be able to use a changed \`items' style to
+    assemble your prompt.
+__EOF__
+}
+
+function grml_theme_add_token () {
+    emulate -L zsh
+    local name token pre post
+    local -i init funcall
+
+    if (( ARGC == 0 )); then
+        GRML_theme_add_token_usage
+        return 0
+    fi
+
+    init=0
+    funcall=0
+    pre=''
+    post=''
+    name=$1
+    shift
+    if [[ $1 == '-f' ]]; then
+        funcall=1
+        shift
+    elif [[ $1 == '-i' ]]; then
+        init=1
+        shift
+    fi
+
+    if (( ARGC == 0 )); then
+        printf '
+grml_theme_add_token: No token-string/function-name provided!\n\n'
+        GRML_theme_add_token_usage
+        return 1
+    fi
+    token=$1
+    shift
+    if (( ARGC != 0 && ARGC != 2 )); then
+        printf '
+grml_theme_add_token: 
 and  need to by specified _both_!\n\n'
+        GRML_theme_add_token_usage
+        return 1
+    fi
+    if (( ARGC )); then
+        pre=$1
+        post=$2
+        shift 2
+    fi
+
+    if (( ${+grml_prompt_token_default[$name]} )); then
+        printf '
+grml_theme_add_token: Token `%s'\'' exists! Giving up!\n\n' $name
+        GRML_theme_add_token_usage
+        return 2
+    fi
+    if (( init )); then
+        $token $name
+        token=$REPLY
+    fi
+    grml_prompt_pre_default[$name]=$pre
+    grml_prompt_post_default[$name]=$post
+    if (( funcall )); then
+        grml_prompt_token_function[$name]=$token
+        grml_prompt_token_default[$name]=23
+    else
+        grml_prompt_token_default[$name]=$token
+    fi
+}
+
+function grml_typeset_and_wrap () {
+    emulate -L zsh
+    local target="$1"
+    local new="$2"
+    local left="$3"
+    local right="$4"
+
+    if (( ${+parameters[$new]} )); then
+        typeset -g "${target}=${(P)target}${left}${(P)new}${right}"
+    fi
+}
+
+function grml_prompt_addto () {
+    emulate -L zsh
+    local target="$1"
+    local lr it apre apost new v
+    local -a items
+    shift
+
+    [[ $target == PS1 ]] && lr=left || lr=right
+    zstyle -a ":prompt:${grmltheme}:${lr}:setup" items items || items=( "$@" )
+    typeset -g "${target}="
+    for it in "${items[@]}"; do
+        zstyle -s ":prompt:${grmltheme}:${lr}:items:$it" pre apre \
+            || apre=${grml_prompt_pre_default[$it]}
+        zstyle -s ":prompt:grml:${grmltheme}:${lr}:$it" post apost \
+            || apost=${grml_prompt_post_default[$it]}
+        zstyle -s ":prompt:${grmltheme}:${lr}:items:$it" token new \
+            || new=${grml_prompt_token_default[$it]}
+        typeset -g "${target}=${(P)target}${apre}"
+        if (( ${+grml_prompt_token_function[$it]} )); then
+            ${grml_prompt_token_function[$it]} $it
+            typeset -g "${target}=${(P)target}${REPLY}"
+        else
+            case $it in
+            battery)
+                grml_typeset_and_wrap $target $new '' ''
+                ;;
+            change-root)
+                grml_typeset_and_wrap $target $new '(' ')'
+                ;;
+            grml-chroot)
+                if [[ -n ${(P)new} ]]; then
+                    typeset -g "${target}=${(P)target}(CHROOT)"
+                fi
+                ;;
+            vcs)
+                v="vcs_info_msg_${new}_"
+                if (( ! vcscalled )); then
+                    vcs_info
+                    vcscalled=1
+                fi
+                if (( ${+parameters[$v]} )) && [[ -n "${(P)v}" ]]; then
+                    typeset -g "${target}=${(P)target}${(P)v}"
+                fi
+                ;;
+            *) typeset -g "${target}=${(P)target}${new}" ;;
+            esac
+        fi
+        typeset -g "${target}=${(P)target}${apost}"
+    done
 }
-# set colors for use in prompts
-if zrcautoload colors && colors 2>/dev/null ; then
-    BLUE="%{${fg[blue]}%}"
-    RED="%{${fg_bold[red]}%}"
-    GREEN="%{${fg[green]}%}"
-    CYAN="%{${fg[cyan]}%}"
-    MAGENTA="%{${fg[magenta]}%}"
-    YELLOW="%{${fg[yellow]}%}"
-    WHITE="%{${fg[white]}%}"
-    NO_COLOUR="%{${reset_color}%}"
-else
-    BLUE=$'%{\e[1;34m%}'
-    RED=$'%{\e[1;31m%}'
-    GREEN=$'%{\e[1;32m%}'
-    CYAN=$'%{\e[1;36m%}'
-    WHITE=$'%{\e[1;37m%}'
-    MAGENTA=$'%{\e[1;35m%}'
-    YELLOW=$'%{\e[1;33m%}'
-    NO_COLOUR=$'%{\e[0m%}'
-fi
 
-# gather version control information for inclusion in a prompt
+function prompt_grml_precmd () {
+    emulate -L zsh
+    local grmltheme=grml
+    local -a left_items right_items
+    left_items=(rc change-root user at host path vcs percent)
+    right_items=(sad-smiley)
 
-if zrcautoload vcs_info; then
-    # `vcs_info' in zsh versions 4.3.10 and below have a broken `_realpath'
-    # function, which can cause a lot of trouble with our directory-based
-    # profiles. So:
-    if [[ ${ZSH_VERSION} == 4.3.<-10> ]] ; then
-        function VCS_INFO_realpath () {
-            setopt localoptions NO_shwordsplit chaselinks
-            ( builtin cd -q $1 2> /dev/null && pwd; )
-        }
-    fi
+    prompt_grml_precmd_worker
+}
 
-    zstyle ':vcs_info:*' max-exports 2
+function prompt_grml-chroot_precmd () {
+    emulate -L zsh
+    local grmltheme=grml-chroot
+    local -a left_items right_items
+    left_items=(grml-chroot user at host path percent)
+    right_items=()
 
-    if [[ -o restricted ]]; then
-        zstyle ':vcs_info:*' enable NONE
-    fi
-fi
+    prompt_grml_precmd_worker
+}
 
-# Change vcs_info formats for the grml prompt. The 2nd format sets up
-# $vcs_info_msg_1_ to contain "zsh: repo-name" used to set our screen title.
-# TODO: The included vcs_info() version still uses $VCS_INFO_message_N_.
-#       That needs to be the use of $VCS_INFO_message_N_ needs to be changed
-#       to $vcs_info_msg_N_ as soon as we use the included version.
-if [[ "$TERM" == dumb ]] ; then
-    zstyle ':vcs_info:*' actionformats "(%s%)-[%b|%a] " "zsh: %r"
-    zstyle ':vcs_info:*' formats       "(%s%)-[%b] "    "zsh: %r"
-else
-    # these are the same, just with a lot of colours:
-    zstyle ':vcs_info:*' actionformats "${MAGENTA}(${NO_COLOUR}%s${MAGENTA})${YELLOW}-${MAGENTA}[${GREEN}%b${YELLOW}|${RED}%a${MAGENTA}]${NO_COLOUR} " \
-                                       "zsh: %r"
-    zstyle ':vcs_info:*' formats       "${MAGENTA}(${NO_COLOUR}%s${MAGENTA})${YELLOW}-${MAGENTA}[${GREEN}%b${MAGENTA}]${NO_COLOUR}%} " \
-                                       "zsh: %r"
-    zstyle ':vcs_info:(sv[nk]|bzr):*' branchformat "%b${RED}:${YELLOW}%r"
-fi
+function prompt_grml-large_precmd () {
+    emulate -L zsh
+    local grmltheme=grml-large
+    local -a left_items right_items
+    left_items=(rc jobs history shell-level change-root time date newline
+                user at host path vcs percent)
+    right_items=(sad-smiley)
 
-# command not found handling
+    prompt_grml_precmd_worker
+}
 
-(( ${COMMAND_NOT_FOUND} == 1 )) &&
-function command_not_found_handler() {
+function prompt_grml_precmd_worker () {
     emulate -L zsh
-    if [[ -x ${GRML_ZSH_CNF_HANDLER} ]] ; then
-        ${GRML_ZSH_CNF_HANDLER} $1
+    local -i vcscalled=0
+
+    grml_prompt_addto PS1 "${left_items[@]}"
+    if zstyle -T ":prompt:${grmltheme}:right:setup" use-rprompt; then
+        grml_prompt_addto RPS1 "${right_items[@]}"
     fi
-    return 1
 }
 
-# set prompt
+grml_prompt_fallback() {
+    setopt prompt_subst
+    precmd() {
+        (( ${+functions[vcs_info]} )) && vcs_info
+    }
+
+    p0="${RED}%(?..%? )${WHITE}${debian_chroot:+($debian_chroot)}"
+    p1="${BLUE}%n${NO_COLOR}@%m %40<...<%B%~%b%<< "'${vcs_info_msg_0_}'"%# "
+    if (( EUID == 0 )); then
+        PROMPT="${BLUE}${p0}${RED}${p1}"
+    else
+        PROMPT="${RED}${p0}${BLUE}${p1}"
+    fi
+    unset p0 p1
+}
+
 if zrcautoload promptinit && promptinit 2>/dev/null ; then
-    promptinit # people should be able to use their favourite prompt
+    # Since we define the required functions in here and not in files in
+    # $fpath, we need to stick the theme's name into `$prompt_themes'
+    # ourselves, since promptinit does not pick them up otherwise.
+    prompt_themes+=( grml grml-chroot grml-large )
+    # Also, keep the array sorted...
+    prompt_themes=( "${(@on)prompt_themes}" )
 else
     print 'Notice: no promptinit available :('
+    grml_prompt_fallback
 fi
 
-setopt prompt_subst
+if is437; then
+    # The prompt themes use modern features of zsh, that require at least
+    # version 4.3.7 of the shell. Use the fallback otherwise.
+    if [[ $BATTERY -gt 0 ]]; then
+        zstyle ':prompt:grml:right:setup' items sad-smiley battery
+        add-zsh-hook precmd battery
+    fi
+    if [[ "$TERM" == dumb ]] ; then
+        zstyle ":prompt:grml(|-large|-chroot):*:items:grml-chroot" pre ''
+        zstyle ":prompt:grml(|-large|-chroot):*:items:grml-chroot" post ' '
+        for i in rc user path jobs history date time shell-level; do
+            zstyle ":prompt:grml(|-large|-chroot):*:items:$i" pre ''
+            zstyle ":prompt:grml(|-large|-chroot):*:items:$i" post ''
+        done
+        unset i
+        zstyle ':prompt:grml(|-large|-chroot):right:setup' use-rprompt false
+    elif (( EUID == 0 )); then
+        zstyle ':prompt:grml(|-large|-chroot):*:items:user' pre '%B%F{red}'
+    fi
 
-# make sure to use right prompt only when not running a command
-is41 && setopt transient_rprompt
+    # Finally enable one of the prompts.
+    if [[ -n $GRML_CHROOT ]]; then
+        prompt grml-chroot
+    elif [[ $GRMLPROMPT -gt 0 ]]; then
+        prompt grml-large
+    else
+        prompt grml
+    fi
+else
+    grml_prompt_fallback
+fi
 
+# Terminal-title wizardry
 
 function ESC_print () {
     info_print $'\ek' $'\e\\' "$@"
@@ -1340,30 +2193,7 @@ function info_print () {
     printf '%s' "${esc_end}"
 }
 
-# TODO: revise all these NO* variables and especially their documentation
-#       in zsh-help() below.
-is4 && [[ $NOPRECMD -eq 0 ]] && precmd () {
-    [[ $NOPRECMD -gt 0 ]] && return 0
-    # update VCS information
-    (( ${+functions[vcs_info]} )) && vcs_info
-
-    if [[ $TERM == screen* ]] ; then
-        if [[ -n ${vcs_info_msg_1_} ]] ; then
-            ESC_print ${vcs_info_msg_1_}
-        else
-            ESC_print "zsh"
-        fi
-    fi
-    # just use DONTSETRPROMPT=1 to be able to overwrite RPROMPT
-    if [[ ${DONTSETRPROMPT:-} -eq 0 ]] ; then
-        if [[ $BATTERY -gt 0 ]] ; then
-            # update battery (dropped into $PERCENT) information
-            battery
-            RPROMPT="%(?..:() ${PERCENT}"
-        else
-            RPROMPT="%(?..:() "
-        fi
-    fi
+function grml_reset_screen_title () {
     # adjust title of xterm
     # see http://www.faqs.org/docs/Linux-mini/Xterm-Title.html
     [[ ${NOTITLE:-} -gt 0 ]] && return 0
@@ -1374,23 +2204,33 @@ is4 && [[ $NOPRECMD -eq 0 ]] && precmd () {
     esac
 }
 
-# preexec() => a function running before every command
-is4 && [[ $NOPRECMD -eq 0 ]] && \
-preexec () {
-    [[ $NOPRECMD -gt 0 ]] && return 0
-# set hostname if not running on host with name 'grml'
+function grml_vcs_to_screen_title () {
+    if [[ $TERM == screen* ]] ; then
+        if [[ -n ${vcs_info_msg_1_} ]] ; then
+            ESC_print ${vcs_info_msg_1_}
+        else
+            ESC_print "zsh"
+        fi
+    fi
+}
+
+function grml_maintain_name () {
+    # set hostname if not running on host with name 'grml'
     if [[ -n "$HOSTNAME" ]] && [[ "$HOSTNAME" != $(hostname) ]] ; then
        NAME="@$HOSTNAME"
     fi
-# get the name of the program currently running and hostname of local machine
-# set screen window title if running in a screen
+}
+
+function grml_cmd_to_screen_title () {
+    # get the name of the program currently running and hostname of local
+    # machine set screen window title if running in a screen
     if [[ "$TERM" == screen* ]] ; then
-        # local CMD=${1[(wr)^(*=*|sudo|ssh|-*)]}       # don't use hostname
-        local CMD="${1[(wr)^(*=*|sudo|ssh|-*)]}$NAME" # use hostname
+        local CMD="${1[(wr)^(*=*|sudo|ssh|-*)]}$NAME"
         ESC_print ${CMD}
     fi
-# adjust title of xterm
-    [[ ${NOTITLE} -gt 0 ]] && return 0
+}
+
+function grml_control_xterm_title () {
     case $TERM in
         (xterm*|rxvt*)
             set_title "${(%):-"%n@%m:"}" "$1"
@@ -1398,47 +2238,17 @@ preexec () {
     esac
 }
 
-EXITCODE="%(?..%?%1v )"
-# secondary prompt, printed when the shell needs more information to complete a
-# command.
-PS2='\`%_> '
-# selection prompt used within a select loop.
-PS3='?# '
-# the execution trace prompt (setopt xtrace). default: '+%N:%i>'
-PS4='+%N:%i:%_> '
-
-# set variable debian_chroot if running in a chroot with /etc/debian_chroot
-if [[ -z "$debian_chroot" ]] && [[ -r /etc/debian_chroot ]] ; then
-    debian_chroot=$(cat /etc/debian_chroot)
-fi
-
-# don't use colors on dumb terminals (like emacs):
-if [[ "$TERM" == dumb ]] ; then
-    PROMPT="${EXITCODE}${debian_chroot:+($debian_chroot)}%n@%m %40<...<%B%~%b%<< "
-else
-    # only if $GRMLPROMPT is set (e.g. via 'GRMLPROMPT=1 zsh') use the extended
-    # prompt set variable identifying the chroot you work in (used in the
-    # prompt below)
-    if [[ $GRMLPROMPT -gt 0 ]] ; then
-        PROMPT="${RED}${EXITCODE}${CYAN}[%j running job(s)] ${GREEN}{history#%!} ${RED}%(3L.+.) ${BLUE}%* %D
-${BLUE}%n${NO_COLOUR}@%m %40<...<%B%~%b%<< "
-    else
-        # This assembles the primary prompt string
-        if (( EUID != 0 )); then
-            PROMPT="${RED}${EXITCODE}${WHITE}${debian_chroot:+($debian_chroot)}${BLUE}%n${NO_COLOUR}@%m %40<...<%B%~%b%<< "
-        else
-            PROMPT="${BLUE}${EXITCODE}${WHITE}${debian_chroot:+($debian_chroot)}${RED}%n${NO_COLOUR}@%m %40<...<%B%~%b%<< "
-        fi
+zrcautoload add-zsh-hook || add-zsh-hook () { :; }
+if [[ $NOPRECMD -eq 0 ]]; then
+    add-zsh-hook precmd grml_reset_screen_title
+    add-zsh-hook precmd grml_vcs_to_screen_title
+    add-zsh-hook preexec grml_maintain_name
+    add-zsh-hook preexec grml_cmd_to_screen_title
+    if [[ $NOTITLE -eq 0 ]]; then
+        add-zsh-hook preexec grml_control_xterm_title
     fi
 fi
 
-PROMPT="${PROMPT}"'${vcs_info_msg_0_}'"%# "
-
-# if we are inside a grml-chroot set a specific prompt theme
-if [[ -n "$GRML_CHROOT" ]] ; then
-    PROMPT="%{$fg[red]%}(CHROOT) %{$fg_bold[red]%}%n%{$fg_no_bold[white]%}@%m %40<...<%B%~%b%<< %\# "
-fi
-
 # 'hash' some often used directories
 #d# start
 hash -d deb=/var/cache/apt/archives
@@ -1473,16 +2283,16 @@ fi
 
 # do we have GNU ls with color-support?
 if [[ "$TERM" != dumb ]]; then
-    #a1# execute \kbd{@a@}:\quad ls with colors
-    alias ls='ls -b -CF '${ls_options:+"${ls_options[*]} "}
-    #a1# execute \kbd{@a@}:\quad list all files, with colors
-    alias la='ls -la '${ls_options:+"${ls_options[*]} "}
-    #a1# long colored list, without dotfiles (@a@)
-    alias ll='ls -l '${ls_options:+"${ls_options[*]} "}
-    #a1# long colored list, human readable sizes (@a@)
-    alias lh='ls -hAl '${ls_options:+"${ls_options[*]} "}
-    #a1# List files, append qualifier to filenames \\&\quad(\kbd{/} for directories, \kbd{@} for symlinks ...)
-    alias l='ls -lF '${ls_options:+"${ls_options[*]} "}
+    #a1# List files with colors (\kbd{ls -b -CF \ldots})
+    alias ls='ls -b -CF '${ls_options:+"${ls_options[*]}"}
+    #a1# List all files, with colors (\kbd{ls -la \ldots})
+    alias la='ls -la '${ls_options:+"${ls_options[*]}"}
+    #a1# List files with long colored list, without dotfiles (\kbd{ls -l \ldots})
+    alias ll='ls -l '${ls_options:+"${ls_options[*]}"}
+    #a1# List files with long colored list, human readable sizes (\kbd{ls -hAl \ldots})
+    alias lh='ls -hAl '${ls_options:+"${ls_options[*]}"}
+    #a1# List files with long colored list, append qualifier to filenames (\kbd{ls -lF \ldots})\\&\quad(\kbd{/} for directories, \kbd{@} for symlinks ...)
+    alias l='ls -lF '${ls_options:+"${ls_options[*]}"}
 else
     alias ls='ls -b -CF'
     alias la='ls -la'
@@ -1618,205 +2428,46 @@ if [[ -r /etc/debian_version ]] ; then
     salias -a up="aptitude update ; aptitude safe-upgrade"
     #a3# Execute \kbd{dpkg-buildpackage}
     alias dbp='dpkg-buildpackage'
-    #a3# Execute \kbd{grep-excuses}
-    alias ge='grep-excuses'
-
-    # get a root shell as normal user in live-cd mode:
-    if isgrmlcd && [[ $UID -ne 0 ]] ; then
-       alias su="sudo su"
-     fi
-
-    #a1# Take a look at the syslog: \kbd{\$PAGER /var/log/syslog}
-    salias llog="$PAGER /var/log/syslog"     # take a look at the syslog
-    #a1# Take a look at the syslog: \kbd{tail -f /var/log/syslog}
-    salias tlog="tail -f /var/log/syslog"    # follow the syslog
-fi
-
-# sort installed Debian-packages by size
-if check_com -c dpkg-query ; then
-    #a3# List installed Debian-packages sorted by size
-    alias debs-by-size="dpkg-query -Wf 'x \${Installed-Size} \${Package} \${Status}\n' | sed -ne '/^x  /d' -e '/^x \(.*\) install ok installed$/s//\1/p' | sort -nr"
-fi
-
-# if cdrecord is a symlink (to wodim) or isn't present at all warn:
-if [[ -L /usr/bin/cdrecord ]] || ! check_com -c cdrecord; then
-    if check_com -c wodim; then
-        cdrecord() {
-            cat <' and 'cd -' with menu
-    # zstyle ':completion:*:*:cd:*:directory-stack' menu yes select
-
-    # insert all expansions for expand completer
-    zstyle ':completion:*:expand:*'        tag-order all-expansions
-    zstyle ':completion:*:history-words'   list false
-
-    # activate menu
-    zstyle ':completion:*:history-words'   menu yes
-
-    # ignore duplicate entries
-    zstyle ':completion:*:history-words'   remove-all-dups yes
-    zstyle ':completion:*:history-words'   stop yes
-
-    # match uppercase from lowercase
-    zstyle ':completion:*'                 matcher-list 'm:{a-z}={A-Z}'
-
-    # separate matches into groups
-    zstyle ':completion:*:matches'         group 'yes'
-    zstyle ':completion:*'                 group-name ''
-
-    if [[ "$NOMENU" -eq 0 ]] ; then
-        # if there are more than 5 options allow selecting from a menu
-        zstyle ':completion:*'               menu select=5
-    else
-        # don't use any menus at all
-        setopt no_auto_menu
-    fi
-
-    zstyle ':completion:*:messages'        format '%d'
-    zstyle ':completion:*:options'         auto-description '%d'
-
-    # describe options in full
-    zstyle ':completion:*:options'         description 'yes'
-
-    # on processes completion complete all user processes
-    zstyle ':completion:*:processes'       command 'ps -au$USER'
-
-    # offer indexes before parameters in subscripts
-    zstyle ':completion:*:*:-subscript-:*' tag-order indexes parameters
-
-    # provide verbose completion information
-    zstyle ':completion:*'                 verbose true
-
-    # recent (as of Dec 2007) zsh versions are able to provide descriptions
-    # for commands (read: 1st word in the line) that it will list for the user
-    # to choose from. The following disables that, because it's not exactly fast.
-    zstyle ':completion:*:-command-:*:'    verbose false
-
-    # set format for warnings
-    zstyle ':completion:*:warnings'        format $'%{\e[0;31m%}No matches for:%{\e[0m%} %d'
-
-    # define files to ignore for zcompile
-    zstyle ':completion:*:*:zcompile:*'    ignored-patterns '(*~|*.zwc)'
-    zstyle ':completion:correct:'          prompt 'correct to: %e'
-
-    # Ignore completion functions for commands you don't have:
-    zstyle ':completion::(^approximate*):*:functions' ignored-patterns '_*'
-
-    # Provide more processes in completion of programs like killall:
-    zstyle ':completion:*:processes-names' command 'ps c -u ${USER} -o command | uniq'
-
-    # complete manual by their section
-    zstyle ':completion:*:manuals'    separate-sections true
-    zstyle ':completion:*:manuals.*'  insert-sections   true
-    zstyle ':completion:*:man:*'      menu yes select
-
-    # provide .. as a completion
-    zstyle ':completion:*' special-dirs ..
+    #a3# Execute \kbd{grep-excuses}
+    alias ge='grep-excuses'
 
-    # run rehash on completion so new installed program are found automatically:
-    _force_rehash() {
-        (( CURRENT == 1 )) && rehash
-        return 1
-    }
+    # get a root shell as normal user in live-cd mode:
+    if isgrmlcd && [[ $UID -ne 0 ]] ; then
+       alias su="sudo su"
+     fi
 
-    ## correction
-    # some people don't like the automatic correction - so run 'NOCOR=1 zsh' to deactivate it
-    if [[ "$NOCOR" -gt 0 ]] ; then
-        zstyle ':completion:*' completer _oldlist _expand _force_rehash _complete _files _ignored
-        setopt nocorrect
-    else
-        # try to be smart about when to use what completer...
-        setopt correct
-        zstyle -e ':completion:*' completer '
-            if [[ $_last_try != "$HISTNO$BUFFER$CURSOR" ]] ; then
-                _last_try="$HISTNO$BUFFER$CURSOR"
-                reply=(_complete _match _ignored _prefix _files)
-            else
-                if [[ $words[1] == (rm|mv) ]] ; then
-                    reply=(_complete _files)
-                else
-                    reply=(_oldlist _expand _force_rehash _complete _ignored _correct _approximate _files)
-                fi
-            fi'
-    fi
+    #a1# Take a look at the syslog: \kbd{\$PAGER /var/log/syslog}
+    salias llog="$PAGER /var/log/syslog"     # take a look at the syslog
+    #a1# Take a look at the syslog: \kbd{tail -f /var/log/syslog}
+    salias tlog="tail -f /var/log/syslog"    # follow the syslog
+fi
 
-    # command for process lists, the local web server details and host completion
-    zstyle ':completion:*:urls' local 'www' '/var/www/' 'public_html'
+# sort installed Debian-packages by size
+if check_com -c dpkg-query ; then
+    #a3# List installed Debian-packages sorted by size
+    alias debs-by-size="dpkg-query -Wf 'x \${Installed-Size} \${Package} \${Status}\n' | sed -ne '/^x  /d' -e '/^x \(.*\) install ok installed$/s//\1/p' | sort -nr"
+fi
 
-    # caching
-    [[ -d $ZSHDIR/cache ]] && zstyle ':completion:*' use-cache yes && \
-                            zstyle ':completion::complete:*' cache-path $ZSHDIR/cache/
+# if cdrecord is a symlink (to wodim) or isn't present at all warn:
+if [[ -L /usr/bin/cdrecord ]] || ! check_com -c cdrecord; then
+    if check_com -c wodim; then
+        cdrecord() {
+            cat <@']="set MARK"
-    help_zle_keybindings['xj']="vi-join lines"
-    help_zle_keybindings['xb']="jump to matching brace"
-    help_zle_keybindings['xu']="undo"
-    help_zle_keybindings['_']="undo"
-    help_zle_keybindings['xf']="find  in cmdline"
-    help_zle_keybindings['a']="goto beginning of line"
-    help_zle_keybindings['e']="goto end of line"
-    help_zle_keybindings['t']="transpose charaters"
-    help_zle_keybindings['t']="transpose words"
-    help_zle_keybindings['s']="spellcheck word"
-    help_zle_keybindings['k']="backward kill buffer"
-    help_zle_keybindings['u']="forward kill buffer"
-    help_zle_keybindings['y']="insert previously killed word/string"
-    help_zle_keybindings["'"]="quote line"
-    help_zle_keybindings['"']="quote from mark to cursor"
-    help_zle_keybindings['']="repeat next cmd/char  times (-10a -> -10 times 'a')"
-    help_zle_keybindings['u']="make next word Uppercase"
-    help_zle_keybindings['l']="make next word lowercase"
-    help_zle_keybindings['xd']="preview expansion under cursor"
-    help_zle_keybindings['q']="push current CL into background, freeing it. Restore on next CL"
-    help_zle_keybindings['.']="insert (and interate through) last word from prev CLs"
-    help_zle_keybindings[',']="complete word from newer history (consecutive hits)"
-    help_zle_keybindings['m']="repeat last typed word on current CL"
-    help_zle_keybindings['v']="insert next keypress symbol literally (e.g. for bindkey)"
-    help_zle_keybindings['!!:n*']="insert last n arguments of last command"
-    help_zle_keybindings['!!:n-']="insert arguments n..N-2 of last command (e.g. mv s s d)"
-    help_zle_keybindings['h']="show help/manpage for current command"
-
-    #init global variables
-    unset help_zle_lines help_zle_sln
-    typeset -g -a help_zle_lines
-    typeset -g help_zle_sln=1
-
-    local k v
-    local lastkeybind_desc contents     #last description starting with #k# that we found
-    local num_lines_elapsed=0            #number of lines between last description and keybinding
-    #search config files in the order they a called (and thus the order in which they overwrite keybindings)
-    for f in $HELPZLE_KEYBINDING_FILES; do
-        [[ -r "$f" ]] || continue   #not readable ? skip it
-        contents="$(<$f)"
-        for cline in "${(f)contents}"; do
-            #zsh pattern: matches lines like: #k# ..............
-            if [[ "$cline" == (#s)[[:space:]]#\#k\#[[:space:]]##(#b)(*)[[:space:]]#(#e) ]]; then
-                lastkeybind_desc="$match[*]"
-                num_lines_elapsed=0
-            #zsh pattern: matches lines that set a keybinding using bindkey or compdef -k
-            #             ignores lines that are commentend out
-            #             grabs first in '' or "" enclosed string with length between 1 and 6 characters
-            elif [[ "$cline" == [^#]#(bindkey|compdef -k)[[:space:]](*)(#b)(\"((?)(#c1,6))\"|\'((?)(#c1,6))\')(#B)(*)  ]]; then
-                #description prevously found ? description not more than 2 lines away ? keybinding not empty ?
-                if [[ -n $lastkeybind_desc && $num_lines_elapsed -lt 2 && -n $match[1] ]]; then
-                    #substitute keybinding string with something readable
-                    k=${${${${${${${match[1]/\\e\^h/}/\\e\^\?/}/\\e\[5~/}/\\e\[6~/}//(\\e|\^\[)/}//\^/}/3~/}
-                    #put keybinding in assoc array, possibly overwriting defaults or stuff found in earlier files
-                    #Note that we are extracting the keybinding-string including the quotes (see Note at beginning)
-                    help_zle_keybindings[${k}]=$lastkeybind_desc
-                fi
-                lastkeybind_desc=""
-            else
-              ((num_lines_elapsed++))
-            fi
-        done
-    done
-    unset contents
-    #calculate length of keybinding column
-    local kstrlen=0
-    for k (${(k)help_zle_keybindings[@]}) ((kstrlen < ${#k})) && kstrlen=${#k}
-    #convert the assoc array into preformated lines, which we are able to sort
-    for k v in ${(kv)help_zle_keybindings[@]}; do
-        #pad keybinding-string to kstrlen chars and remove outermost characters (i.e. the quotes)
-        help_zle_lines+=("${(r:kstrlen:)k[2,-2]}${v}")
-    done
-    #sort lines alphabetically
-    help_zle_lines=("${(i)help_zle_lines[@]}")
-    [[ -d ${HELP_ZLE_CACHE_FILE:h} ]] || mkdir -p "${HELP_ZLE_CACHE_FILE:h}"
-    echo "help_zle_lines=(${(q)help_zle_lines[@]})" >| $HELP_ZLE_CACHE_FILE
-    zcompile $HELP_ZLE_CACHE_FILE
-}
-typeset -g help_zle_sln
-typeset -g -a help_zle_lines
-
-#f1# Provides (partially autogenerated) help on keybindings and the zsh line editor
-help-zle()
-{
-    emulate -L zsh
-    unsetopt ksharrays  #indexing starts at 1
-    #help lines already generated ? no ? then do it
-    [[ ${+functions[help_zle_parse_keybindings]} -eq 1 ]] && {help_zle_parse_keybindings && unfunction help_zle_parse_keybindings}
-    #already displayed all lines ? go back to the start
-    [[ $help_zle_sln -gt ${#help_zle_lines} ]] && help_zle_sln=1
-    local sln=$help_zle_sln
-    #note that help_zle_sln is a global var, meaning we remember the last page we viewed
-    help_zle_sln=$((help_zle_sln + HELP_LINES_PER_PAGE))
-    zle -M "${(F)help_zle_lines[sln,help_zle_sln-1]}"
-}
-#k# display help for keybindings and ZLE (cycle pages with consecutive use)
-zle -N help-zle && bindkey '^xz' help-zle
-
 # grep for running process, like: 'any vim'
 any() {
     emulate -L zsh
@@ -2195,9 +2733,11 @@ deswap() {
 #           VIM_OPTIONS=( -p )
 #       This will cause vim to send every file given on the
 #       commandline to be send to it's own tab (needs vim7).
-vim() {
-    VIM_PLEASE_SET_TITLE='yes' command vim ${VIM_OPTIONS} "$@"
-}
+if check_com vim; then
+    vim() {
+        VIM_PLEASE_SET_TITLE='yes' command vim ${VIM_OPTIONS} "$@"
+    }
+fi
 
 # make a backup of a file
 bk() {
@@ -2297,11 +2837,12 @@ alias lsnewdir="ls -rthdl *(/om[1,10]) .*(D/om[1,10])"
 alias lsolddir="ls -rthdl *(/Om[1,10]) .*(D/Om[1,10])"
 
 # some useful aliases
-#a2# Remove current empty directory. Execute \kbd{cd ..; rmdir $OLDCWD}
+#a2# Remove current empty directory. Execute \kbd{cd ..; rmdir \$OLDCWD}
 alias rmcdir='cd ..; rmdir $OLDPWD || cd $OLDPWD'
 
 #a2# ssh with StrictHostKeyChecking=no \\&\quad and UserKnownHostsFile unset
 alias insecssh='ssh -o "StrictHostKeyChecking=no" -o "UserKnownHostsFile=/dev/null"'
+#a2# scp with StrictHostKeyChecking=no \\&\quad and UserKnownHostsFile unset
 alias insecscp='scp -o "StrictHostKeyChecking=no" -o "UserKnownHostsFile=/dev/null"'
 
 # work around non utf8 capable software in utf environment via $LANG and luit
@@ -2369,41 +2910,6 @@ cdt() {
     builtin cd "$t"
 }
 
-#f5# Create directory under cursor or the selected area
-# Press ctrl-xM to create the directory under the cursor or the selected area.
-# To select an area press ctrl-@ or ctrl-space and use the cursor.
-# Use case: you type "mv abc ~/testa/testb/testc/" and remember that the
-# directory does not exist yet -> press ctrl-XM and problem solved
-inplaceMkDirs() {
-    local PATHTOMKDIR
-    if ((REGION_ACTIVE==1)); then
-        local F=$MARK T=$CURSOR
-        if [[ $F -gt $T ]]; then
-            F=${CURSOR}
-            T=${MARK}
-        fi
-        # get marked area from buffer and eliminate whitespace
-        PATHTOMKDIR=${BUFFER[F+1,T]%%[[:space:]]##}
-        PATHTOMKDIR=${PATHTOMKDIR##[[:space:]]##}
-    else
-        local bufwords iword
-        bufwords=(${(z)LBUFFER})
-        iword=${#bufwords}
-        bufwords=(${(z)BUFFER})
-        PATHTOMKDIR="${(Q)bufwords[iword]}"
-    fi
-    [[ -z "${PATHTOMKDIR}" ]] && return 1
-    PATHTOMKDIR=${~PATHTOMKDIR}
-    if [[ -e "${PATHTOMKDIR}" ]]; then
-        zle -M " path already exists, doing nothing"
-    else
-        zle -M "$(mkdir -p -v "${PATHTOMKDIR}")"
-        zle end-of-line
-    fi
-}
-#k# mkdir -p  from string under cursor or marked area
-zle -N inplaceMkDirs && bindkey '^xM' inplaceMkDirs
-
 #f5# List files which have been accessed within the last {\it n} days, {\it n} defaults to 1
 accessed() {
     emulate -L zsh
@@ -2426,7 +2932,7 @@ check_com new || alias new=modified
 
 # use colors when GNU grep with color-support
 #a2# Execute \kbd{grep -{}-color=auto}
-(( $#grep_options > 0 )) && alias grep='grep '${grep_options:+"${grep_options[*]} "}
+(( $#grep_options > 0 )) && alias grep='grep '${grep_options:+"${grep_options[*]}"}
 
 # Translate DE<=>EN
 # 'translate' looks up fot a word in a file with language-to-language
@@ -2465,62 +2971,62 @@ simple-extract() {
     zparseopts -D -E "d=DELETE_ORIGINAL"
     for ARCHIVE in "${@}"; do
         case $ARCHIVE in
-            *.(tar.bz2|tbz2|tbz))
+            *(tar.bz2|tbz2|tbz))
                 DECOMP_CMD="tar -xvjf -"
                 USES_STDIN=true
                 USES_STDOUT=false
                 ;;
-            *.(tar.gz|tgz))
+            *(tar.gz|tgz))
                 DECOMP_CMD="tar -xvzf -"
                 USES_STDIN=true
                 USES_STDOUT=false
                 ;;
-            *.(tar.xz|txz|tar.lzma))
+            *(tar.xz|txz|tar.lzma))
                 DECOMP_CMD="tar -xvJf -"
                 USES_STDIN=true
                 USES_STDOUT=false
                 ;;
-            *.tar)
+            *tar)
                 DECOMP_CMD="tar -xvf -"
                 USES_STDIN=true
                 USES_STDOUT=false
                 ;;
-            *.rar)
+            *rar)
                 DECOMP_CMD="unrar x"
                 USES_STDIN=false
                 USES_STDOUT=false
                 ;;
-            *.lzh)
+            *lzh)
                 DECOMP_CMD="lha x"
                 USES_STDIN=false
                 USES_STDOUT=false
                 ;;
-            *.7z)
+            *7z)
                 DECOMP_CMD="7z x"
                 USES_STDIN=false
                 USES_STDOUT=false
                 ;;
-            *.(zip|jar))
+            *(zip|jar))
                 DECOMP_CMD="unzip"
                 USES_STDIN=false
                 USES_STDOUT=false
                 ;;
-            *.deb)
+            *deb)
                 DECOMP_CMD="ar -x"
                 USES_STDIN=false
                 USES_STDOUT=false
                 ;;
-            *.bz2)
+            *bz2)
                 DECOMP_CMD="bzip2 -d -c -"
                 USES_STDIN=true
                 USES_STDOUT=true
                 ;;
-            *.(gz|Z))
+            *(gz|Z))
                 DECOMP_CMD="gzip -d -c -"
                 USES_STDIN=true
                 USES_STDOUT=true
                 ;;
-            *.(xz|lzma))
+            *(xz|lzma))
                 DECOMP_CMD="xz -d -c -"
                 USES_STDIN=true
                 USES_STDOUT=true
@@ -2645,12 +3151,15 @@ xtrename() {
 # API reference: https://code.google.com/apis/urlshortener/
 function zurl() {
     emulate -L zsh
+    setopt extended_glob
+
     if [[ -z $1 ]]; then
         print "USAGE: zurl "
         return 1
     fi
 
-    local PN url prog api json data
+    local PN url prog api json contenttype item
+    local -a data
     PN=$0
     url=$1
 
@@ -2668,11 +3177,19 @@ function zurl() {
     api='https://www.googleapis.com/urlshortener/v1/url'
     contenttype="Content-Type: application/json"
     json="{\"longUrl\": \"${url}\"}"
-    data=$($prog --silent -H ${contenttype} -d ${json} $api)
-    # Match against a regex and print it
-    if [[ $data =~ '"id": "(http://goo.gl/[[:alnum:]]+)"' ]]; then
-        print $match;
-    fi
+    data=(${(f)"$($prog --silent -H ${contenttype} -d ${json} $api)"})
+    # Parse the response
+    for item in "${data[@]}"; do
+        case "$item" in
+            ' '#'"id":'*)
+                item=${item#*: \"}
+                item=${item%\",*}
+                printf '%s\n' "$item"
+                return 0
+                ;;
+        esac
+    done
+    return 1
 }
 
 #f2# Find history events by search pattern and list them by date.