-# TODO: Explain what some of this does..
+bindkey -v
-bindkey -e
-bindkey -s '\el' "ls\n"
-bindkey '\ew' kill-region
-bindkey '^r' history-incremental-search-backward
-bindkey "^[[5~" up-line-or-history
-bindkey "^[[6~" down-line-or-history
+bindkey '^N' history-search-forward
+bindkey '^P' history-search-backward
+bindkey ' ' magic-space # also do history expansion on space
-# make search up and down work, so partially type and hit up/down to find relevant stuff
-bindkey '^[[A' up-line-or-search # up arrow
-bindkey '^[[B' down-line-or-search # down arrow
-
-bindkey "^[[H" beginning-of-line
-bindkey "^[[1~" beginning-of-line
-bindkey "^[OH" beginning-of-line
-bindkey "\e[7~" beginning-of-line
-bindkey "^[[F" end-of-line
-bindkey "^[[4~" end-of-line
-bindkey "\e[8~" end-of-line
-bindkey "^[OF" end-of-line
-bindkey ' ' magic-space # also do history expansion on space
-
-bindkey "^[[1;5C" forward-word
-bindkey "^[[1;5D" backward-word
+bindkey -s '^[l' '\11ls\r' # show dir content
bindkey '^[[Z' reverse-menu-complete
+bindkey -M vicmd 'v' edit-command-line
+
+# delay after ESC (in 0.01s)
+export KEYTIMEOUT=40
+
+# emacs bindings
+bindkey '^A' beginning-of-line
+bindkey '^B' backward-char
+bindkey '^E' end-of-line
+bindkey '^F' edit-command-line
+bindkey '^G' list-expand
+bindkey '^O' accept-line-and-down-history
+bindkey '^Q' push-input
+bindkey '^?' backward-delete-char
+bindkey '^H' backward-delete-char
+bindkey '^W' backward-kill-word
+bindkey '^X=' what-cursor-position
+bindkey '^[.' insert-last-word
+bindkey '^[?' which-command
+bindkey '^[A' accept-and-hold
+bindkey '^[a' accept-and-hold
+bindkey '^[C' capitalize-word
+bindkey '^[c' capitalize-word
+bindkey '^[H' run-help
+bindkey '^[h' run-help
+bindkey '^[T' transpose-words
+bindkey '^[t' transpose-words
+
+# make search up and down work, so partially type and hit up/down to find relevant stuff
+bindkey '^[[A' up-line-or-search # up arrow
+bindkey '^[[B' down-line-or-search # down arrow
+bindkey '^[[5~' up-line-or-history # PgUp
+bindkey '^[[6~' down-line-or-history # PgDown
+
+bindkey '^[OA' up-line-or-history
+bindkey '^[OB' down-line-or-history
+bindkey '^[OC' forward-char
+bindkey '^[OD' backward-char
+bindkey '^[OF' end-of-line
+bindkey '^[OH' beginning-of-line
+
+bindkey '\e[c' forward-word
+bindkey '\e[d' backward-word
+
# Make the delete key (or Fn + Delete on the Mac) work instead of outputting a ~
bindkey '^?' backward-delete-char
-bindkey "^[[3~" delete-char
-bindkey "^[3;5~" delete-char
-bindkey "\e[3~" delete-char
+bindkey '^[[3~' delete-char
+bindkey '^[3;5~' delete-char
+bindkey '\e[3~' delete-char
+++ /dev/null
-# get the name of the branch we are on
-function git_prompt_info() {
- ref=$(git symbolic-ref HEAD 2> /dev/null) || \
- ref=$(git rev-parse --short HEAD 2> /dev/null) || return
- echo "$ZSH_THEME_GIT_PROMPT_PREFIX${ref#refs/heads/}$(parse_git_dirty)$ZSH_THEME_GIT_PROMPT_SUFFIX"
-}
-
-
-# Checks if working tree is dirty
-parse_git_dirty() {
- if [[ -n $(git status -s 2> /dev/null) ]]; then
- echo "$ZSH_THEME_GIT_PROMPT_DIRTY"
- else
- echo "$ZSH_THEME_GIT_PROMPT_CLEAN"
- fi
-}
-
-# get the difference between the local and remote branches
-git_remote_status() {
- remote=${$(git rev-parse --verify ${hook_com[branch]}@{upstream} --symbolic-full-name 2>/dev/null)/refs\/remotes\/}
- if [[ -n ${remote} ]] ; then
- ahead=$(git rev-list ${hook_com[branch]}@{upstream}..HEAD 2>/dev/null | wc -l)
- behind=$(git rev-list HEAD..${hook_com[branch]}@{upstream} 2>/dev/null | wc -l)
-
- if [ $ahead -eq 0 ] && [ $behind -gt 0 ]
- then
- echo "$ZSH_THEME_GIT_PROMPT_BEHIND_REMOTE"
- elif [ $ahead -gt 0 ] && [ $behind -eq 0 ]
- then
- echo "$ZSH_THEME_GIT_PROMPT_AHEAD_REMOTE"
- elif [ $ahead -gt 0 ] && [ $behind -gt 0 ]
- then
- echo "$ZSH_THEME_GIT_PROMPT_DIVERGED_REMOTE"
- fi
- fi
-}
-
-# Checks if there are commits ahead from remote
-function git_prompt_ahead() {
- if $(echo "$(git log origin/$(current_branch)..HEAD 2> /dev/null)" | grep '^commit' &> /dev/null); then
- echo "$ZSH_THEME_GIT_PROMPT_AHEAD"
- fi
-}
-
-# Formats prompt string for current git commit short SHA
-function git_prompt_short_sha() {
- SHA=$(git rev-parse --short HEAD 2> /dev/null) && echo "$ZSH_THEME_GIT_PROMPT_SHA_BEFORE$SHA$ZSH_THEME_GIT_PROMPT_SHA_AFTER"
-}
-
-# Formats prompt string for current git commit long SHA
-function git_prompt_long_sha() {
- SHA=$(git rev-parse HEAD 2> /dev/null) && echo "$ZSH_THEME_GIT_PROMPT_SHA_BEFORE$SHA$ZSH_THEME_GIT_PROMPT_SHA_AFTER"
-}
-
-# Get the status of the working tree
-git_prompt_status() {
- INDEX=$(git status --porcelain -b 2> /dev/null)
- STATUS=""
- if $(echo "$INDEX" | grep '^?? ' &> /dev/null); then
- STATUS="$ZSH_THEME_GIT_PROMPT_UNTRACKED$STATUS"
- fi
- if $(echo "$INDEX" | grep '^A ' &> /dev/null); then
- STATUS="$ZSH_THEME_GIT_PROMPT_ADDED$STATUS"
- elif $(echo "$INDEX" | grep '^M ' &> /dev/null); then
- STATUS="$ZSH_THEME_GIT_PROMPT_ADDED$STATUS"
- fi
- if $(echo "$INDEX" | grep '^ M ' &> /dev/null); then
- STATUS="$ZSH_THEME_GIT_PROMPT_MODIFIED$STATUS"
- elif $(echo "$INDEX" | grep '^AM ' &> /dev/null); then
- STATUS="$ZSH_THEME_GIT_PROMPT_MODIFIED$STATUS"
- elif $(echo "$INDEX" | grep '^ T ' &> /dev/null); then
- STATUS="$ZSH_THEME_GIT_PROMPT_MODIFIED$STATUS"
- fi
- if $(echo "$INDEX" | grep '^R ' &> /dev/null); then
- STATUS="$ZSH_THEME_GIT_PROMPT_RENAMED$STATUS"
- fi
- if $(echo "$INDEX" | grep '^ D ' &> /dev/null); then
- STATUS="$ZSH_THEME_GIT_PROMPT_DELETED$STATUS"
- elif $(echo "$INDEX" | grep '^D ' &> /dev/null); then
- STATUS="$ZSH_THEME_GIT_PROMPT_DELETED$STATUS"
- elif $(echo "$INDEX" | grep '^AD ' &> /dev/null); then
- STATUS="$ZSH_THEME_GIT_PROMPT_DELETED$STATUS"
- fi
- if $(git rev-parse --verify refs/stash >/dev/null 2>&1); then
- STATUS="$ZSH_THEME_GIT_PROMPT_STASHED$STATUS"
- fi
- if $(echo "$INDEX" | grep '^UU ' &> /dev/null); then
- STATUS="$ZSH_THEME_GIT_PROMPT_UNMERGED$STATUS"
- fi
- if $(echo "$INDEX" | grep '^## .*ahead' &> /dev/null); then
- STATUS="$ZSH_THEME_GIT_PROMPT_AHEAD$STATUS"
- fi
- if $(echo "$INDEX" | grep '^## .*behind' &> /dev/null); then
- STATUS="$ZSH_THEME_GIT_PROMPT_BEHIND$STATUS"
- fi
- if $(echo "$INDEX" | grep '^## .*diverged' &> /dev/null); then
- STATUS="$ZSH_THEME_GIT_PROMPT_DIVERGED$STATUS"
- fi
- echo $STATUS
-}
--- /dev/null
+hostcolor=green
+[[ $(hostname) == "tardis" ]] && hostcolor=red
+
+precmd() {
+ PROMPT='%T %n@%{$fg[$hostcolor]%}%m%{$reset_color%}%-0>..>$(git_prompt_status)%>>
+%(?..%{$fg_bold[white]%}%?%{$reset_color%})$(vi_prompt_info)%{%(!.$fg[red]❰.$fg[green]❱)%1G%} '
+ RPROMPT='%{$fg[green]%}%~%{$reset_color%}'
+}
+
+vi_prompt_info() {
+ local vicmd="$fg_bold[green]❰$reset_color%1G"
+ local viins="$fg_bold[blue]❱$reset_color%1G"
+ printf '%s' "%{${${KEYMAP/vicmd/$vicmd}/(main|viins)/$viins}%}"
+}
+
+function zle-line-init zle-line-finish zle-keymap-select {
+ zle reset-prompt
+ zle -R
+}
+
+zle -N zle-line-init
+zle -N zle-line-finish
+zle -N zle-keymap-select
+
+# reset zle on resize
+TRAPWINCH() {
+ zle && { zle reset-prompt; zle -R }
+}
+
+# Get the status of the working tree
+git_prompt_status() {
+ local branch ahead behind
+ local added deleted modified renamed unmerged untracked dirty
+ # Use porcelain status for easy parsing.
+ local status_cmd="git status --porcelain -b"
+
+ # Get current status.
+ while IFS=$'\n' read line; do
+ if [[ "$line" == \#\#\ * ]]; then
+ [[ "$line" =~ '## ([^.]*)\.\.\.(.*)' ]] && branch=$match[1]
+ [[ "$line" =~ 'ahead ([0-9]+)' ]] && ahead=$match[1]
+ [[ "$line" =~ 'behind ([0-9]+)' ]] && behind=$match[1]
+ else
+ # Count added, deleted, modified, renamed, unmerged, untracked, dirty.
+ # T (type change) is undocumented, see http://git.io/FnpMGw.
+ # index
+ [[ "$line" == M[\ MTD]\ * ]] && (( updated++ ))
+ [[ "$line" == [AC][\ MTD]\ * ]] && (( added++ ))
+ [[ "$line" == D[\ MT]\ * ]] && (( deleted++ ))
+ [[ "$line" == R[\ MTD]\ * ]] && (( renamed++ ))
+
+ # work tree
+ [[ "$line" == [\ MARCT]M\ * ]] && (( modified++ ))
+ [[ "$line" == [\ MARCT]D\ * ]] && (( deleted_wt++ ))
+ [[ "$line" == \?\?\ * ]] && (( untracked++ ))
+
+ # morge conflicts
+ [[ "$line" == (AA|DD|U?|?U)\ * ]] && (( unmerged++ ))
+ fi
+ done < <(${(z)status_cmd} 2> /dev/null)
+
+ local git_status=" %{$fg[yellow]%}"
+
+ # Format branch
+ if [[ -n $branch ]]; then
+ git_status+="$branch"
+ else
+ git_status+="$(git rev-parse --short HEAD 2> /dev/null)"
+ [[ $? -ne 0 ]] && return
+ fi
+
+ # Format upstream
+ local upstream_str
+ (( ahead > 0 )) && upstream_str+="%{$fg[blue]%} >$ahead"
+ (( behind > 0 )) && upstream_str+="%{$fg[blue]%} <$behind"
+ git_status+="$upstream_str"
+
+ # Format stashed
+ stashed=$(git stash list | wc -l)
+ if (( stashed > 0 )) then
+ stashed_str+="%{$fg_bold[cyan]%} ⋎$stashed%{$reset_color%}"
+ fi
+ git_status+="$stashed_str"
+
+ # Format index
+ local index_str
+ (( updated > 0 )) && index_str+="%{$fg[green]%} *$updated"
+ (( added > 0 )) && index_str+="%{$fg[green]%} +$added"
+ (( deleted > 0 )) && index_str+="%{$fg[green]%} -$deleted"
+ (( renamed > 0 )) && index_str+="%{$fg[green]%} ≈$renamed"
+ git_status+="$index_str"
+
+ # Format working tree
+ local wt_str
+ (( modified > 0 )) && wt_str+="%{$fg[red]%} *$modified"
+ (( deleted_wt > 0 )) && wt_str+="%{$fg[red]%} -$deleted_wt"
+ (( untracked > 0 )) && wt_str+="%{$fg[red]%} +$untracked"
+ (( unmerged > 0 )) && wt_str+="%{$fg[magenta]%} ♒$unmerged"
+ git_status+="$wt_str"
+
+ git_status+="%{$reset_color%}"
+
+ echo $git_status
+}
+
+function print_if_fits() {
+ local zero length
+
+ zero='%([BSUbfksu]|([FB]|){*})'
+ length=${#${(S%%)1//$~zero/}}
+ echo "%-$length(l.$1.)"
+}
+++ /dev/null
-[[ $(hostname) == "tardis" ]] && hostcolor=red
-[[ $(hostname) == "shada" ]] && hostcolor=green
-PROMPT='%(?..%{$fg_bold[white]%}[%?])%{$reset_color%}[%D{%k:%M} %n@%{$fg[$hostcolor]%}%m%{$reset_color%}$(git_prompt_info)]%# '
-RPROMPT='%{$fg[green]%}%~%{$reset_color%}'
-
-ZSH_THEME_GIT_PROMPT_PREFIX=" %{$fg[yellow]%}<"
-ZSH_THEME_GIT_PROMPT_SUFFIX=">%{$reset_color%}"
-ZSH_THEME_GIT_PROMPT_DIRTY="*"
-ZSH_THEME_GIT_PROMPT_CLEAN=""