# vim:ft=zsh ts=2 sw=2 sts=2 # # agnoster's Theme - https://gist.github.com/3712874 # A Powerline-inspired theme for ZSH # # # README # # In order for this theme to render correctly, you will need a # [Powerline-patched font](https://gist.github.com/1595572). # # In addition, I recommend the # [Solarized theme](https://github.com/altercation/solarized/) and, if you're # using it on Mac OS X, [iTerm 2](http://www.iterm2.com/) over Terminal.app - # it has significantly better color fidelity. # # # Goals # # The aim of this theme is to only show you *relevant* information. Like most # prompts, it will only show git information when in a git working directory. # However, it goes a step further: everything from the current user and # hostname to whether the last call exited with an error to whether background # jobs are running in this shell will all be displayed automatically when # appropriate. ### Segment drawing # A few utility functions to make it easy and re-usable to draw segmented prompts CURRENT_BG='NONE' if [[ -z "$PRIMARY_FG" ]]; then PRIMARY_FG=black fi # Characters SEGMENT_SEPARATOR="\ue0b0" PLUSMINUS="\u00b1" BRANCH="\ue0a0" DETACHED="\u27a6" CROSS="\u2718" LIGHTNING="\u26a1" GEAR="\u2699" # Begin a segment # Takes two arguments, background and foreground. Both can be omitted, # rendering default background/foreground. prompt_segment() { local bg fg [[ -n $1 ]] && bg="%K{$1}" || bg="%k" [[ -n $2 ]] && fg="%F{$2}" || fg="%f" if [[ $CURRENT_BG != 'NONE' && $1 != $CURRENT_BG ]]; then print -n "%{$bg%F{$CURRENT_BG}%}$SEGMENT_SEPARATOR%{$fg%}" else print -n "%{$bg%}%{$fg%}" fi CURRENT_BG=$1 [[ -n $3 ]] && print -n $3 } # End the prompt, closing any open segments prompt_end() { if [[ -n $CURRENT_BG ]]; then print -n "%{%k%F{$CURRENT_BG}%}$SEGMENT_SEPARATOR" else print -n "%{%k%}" fi print -n "%{%f%}" CURRENT_BG='' } ### Prompt components # Each component will draw itself, and hide itself if no information needs to be shown # Context: user@hostname (who am I and where am I) prompt_context() { local user=`whoami` if [[ "$user" != "$DEFAULT_USER" || -n "$SSH_CONNECTION" ]]; then prompt_segment $PRIMARY_FG default " %(!.%{%F{yellow}%}.)$user@%m " fi } # Git: branch/detached head, dirty status prompt_git() { #«»±˖˗‑‐‒ ━ ✚‐↔←↑↓→↭⇎⇔⋆━◂▸◄►◆☀★☗☊✔✖❮❯⚑⚙ local PL_BRANCH_CHAR () { local LC_ALL="" LC_CTYPE="en_US.UTF-8" PL_BRANCH_CHAR=$'\ue0a0' # } local ref dirty mode repo_path clean has_upstream local modified untracked added deleted tagged stashed local ready_commit git_status bgclr fgclr local commits_diff commits_ahead commits_behind has_diverged to_push to_pull repo_path=$(git rev-parse --git-dir 2>/dev/null) if $(git rev-parse --is-inside-work-tree >/dev/null 2>&1); then dirty=$(parse_git_dirty) git_status=$(git status --porcelain 2> /dev/null) ref=$(git symbolic-ref HEAD 2> /dev/null) || ref="➦ $(git rev-parse --short HEAD 2> /dev/null)" if [[ -n $dirty ]]; then clean='' bgclr='yellow' fgclr='magenta' else clean=' ✔' bgclr='green' fgclr='white' fi local upstream=$(git rev-parse --symbolic-full-name --abbrev-ref @{upstream} 2> /dev/null) if [[ -n "${upstream}" && "${upstream}" != "@{upstream}" ]]; then has_upstream=true; fi local current_commit_hash=$(git rev-parse HEAD 2> /dev/null) local number_of_untracked_files=$(\grep -c "^??" <<< "${git_status}") # if [[ $number_of_untracked_files -gt 0 ]]; then untracked=" $number_of_untracked_files◆"; fi if [[ $number_of_untracked_files -gt 0 ]]; then untracked=" $number_of_untracked_files☀"; fi local number_added=$(\grep -c "^A" <<< "${git_status}") if [[ $number_added -gt 0 ]]; then added=" $number_added✚"; fi local number_modified=$(\grep -c "^.M" <<< "${git_status}") if [[ $number_modified -gt 0 ]]; then modified=" $number_modified●" bgclr='red' fgclr='white' fi local number_added_modified=$(\grep -c "^M" <<< "${git_status}") local number_added_renamed=$(\grep -c "^R" <<< "${git_status}") if [[ $number_modified -gt 0 && $number_added_modified -gt 0 ]]; then modified="$modified$((number_added_modified+number_added_renamed))±" elif [[ $number_added_modified -gt 0 ]]; then modified=" ●$((number_added_modified+number_added_renamed))±" fi local number_deleted=$(\grep -c "^.D" <<< "${git_status}") if [[ $number_deleted -gt 0 ]]; then deleted=" $number_deleted‒" bgclr='red' fgclr='white' fi local number_added_deleted=$(\grep -c "^D" <<< "${git_status}") if [[ $number_deleted -gt 0 && $number_added_deleted -gt 0 ]]; then deleted="$deleted$number_added_deleted±" elif [[ $number_added_deleted -gt 0 ]]; then deleted=" ‒$number_added_deleted±" fi local tag_at_current_commit=$(git describe --exact-match --tags $current_commit_hash 2> /dev/null) if [[ -n $tag_at_current_commit ]]; then tagged=" ☗$tag_at_current_commit "; fi local number_of_stashes="$(git stash list -n1 2> /dev/null | wc -l)" if [[ $number_of_stashes -gt 0 ]]; then stashed=" ${number_of_stashes##*( )}⚙" bgclr='magenta' fgclr='white' fi if [[ $number_added -gt 0 || $number_added_modified -gt 0 || $number_added_deleted -gt 0 ]]; then ready_commit=' ⚑'; fi local upstream_prompt='' if [[ $has_upstream == true ]]; then commits_diff="$(git log --pretty=oneline --topo-order --left-right ${current_commit_hash}...${upstream} 2> /dev/null)" commits_ahead=$(\grep -c "^<" <<< "$commits_diff") commits_behind=$(\grep -c "^>" <<< "$commits_diff") upstream_prompt="$(git rev-parse --symbolic-full-name --abbrev-ref @{upstream} 2> /dev/null)" upstream_prompt=$(sed -e 's/\/.*$/ ☊ /g' <<< "$upstream_prompt") fi has_diverged=false if [[ $commits_ahead -gt 0 && $commits_behind -gt 0 ]]; then has_diverged=true; fi if [[ $has_diverged == false && $commits_ahead -gt 0 ]]; then if [[ $bgclr == 'red' || $bgclr == 'magenta' ]] then to_push=" $fg_bold[white]↑$commits_ahead$fg_bold[$fgclr]" else to_push=" $fg_bold[black]↑$commits_ahead$fg_bold[$fgclr]" fi fi if [[ $has_diverged == false && $commits_behind -gt 0 ]]; then to_pull=" $fg_bold[magenta]↓$commits_behind$fg_bold[$fgclr]"; fi if [[ -e "${repo_path}/BISECT_LOG" ]]; then mode=" <B>" elif [[ -e "${repo_path}/MERGE_HEAD" ]]; then mode=" >M<" elif [[ -e "${repo_path}/rebase" || -e "${repo_path}/rebase-apply" || -e "${repo_path}/rebase-merge" || -e "${repo_path}/../.dotest" ]]; then mode=" >R>" fi prompt_segment $bgclr $fgclr echo -n " %{$fg_bold[$fgclr]%}${ref/refs\/heads\//$PL_BRANCH_CHAR $upstream_prompt}${mode}$to_push$to_pull$clean$tagged$stashed$untracked$modified$deleted$added$ready_commit%{$fg_no_bold[$fgclr]%} " fi } # Dir: current working directory prompt_dir() { prompt_segment blue $PRIMARY_FG ' %~ ' } # Status: # - was there an error # - am I root # - are there background jobs? prompt_status() { local symbols symbols=() [[ $RETVAL -ne 0 ]] && symbols+="%{%F{red}%}$CROSS" [[ $UID -eq 0 ]] && symbols+="%{%F{yellow}%}$LIGHTNING" [[ $(jobs -l | wc -l) -gt 0 ]] && symbols+="%{%F{cyan}%}$GEAR" [[ -n "$symbols" ]] && prompt_segment $PRIMARY_FG default " $symbols " } # Display current virtual environment prompt_virtualenv() { if [[ -n $VIRTUAL_ENV ]]; then color=cyan prompt_segment $color $PRIMARY_FG print -Pn " $(basename $VIRTUAL_ENV) " fi } ## Main prompt prompt_agnoster_main() { RETVAL=$? CURRENT_BG='NONE' prompt_status prompt_context prompt_virtualenv prompt_dir prompt_git prompt_end } prompt_agnoster_precmd() { vcs_info PROMPT='%{%f%b%k%}$(prompt_agnoster_main) ' } prompt_agnoster_setup() { autoload -Uz add-zsh-hook autoload -Uz vcs_info prompt_opts=(cr subst percent) add-zsh-hook precmd prompt_agnoster_precmd zstyle ':vcs_info:*' enable git zstyle ':vcs_info:*' check-for-changes false zstyle ':vcs_info:git*' formats '%b' zstyle ':vcs_info:git*' actionformats '%b (%a)' } prompt_agnoster_setup "$@"