* Add option parsing to `prompt_fresh_setup` to detect when options are set during setup. * Add `-a` option to enable almostontop behaviour, every time a command is invoked the screen is cleared and the is prompt redrawn before the command begins using the `preexec` hook. `-a` is disabled by default. * Add `prompt_fresh_help` function to display a description of available options using `prompt -h fresh`. * Canonicalize all utility functions for the fresh prompt theme to follow the `fresh_<name>` convention. Fixes #1.
		
			
				
	
	
		
			222 lines
		
	
	
		
			6.1 KiB
		
	
	
	
		
			Bash
		
	
	
	
	
	
			
		
		
	
	
			222 lines
		
	
	
		
			6.1 KiB
		
	
	
	
		
			Bash
		
	
	
	
	
	
prompt_fresh_help() {
 | 
						||
  echo "\
 | 
						||
options:
 | 
						||
        -a      enable almostontop like behaviour"
 | 
						||
}
 | 
						||
 | 
						||
prompt_fresh_setup() {
 | 
						||
  fresh_compile_git_prompt
 | 
						||
 | 
						||
  # Parse options
 | 
						||
  local almostontop=0
 | 
						||
  while getopts 'a' opt; do
 | 
						||
    case $opt in
 | 
						||
      a) local almostontop=1 ;;
 | 
						||
      *) prompt -h fresh; return 1 ;;
 | 
						||
    esac
 | 
						||
  done
 | 
						||
 | 
						||
  autoload -U add-zsh-hook
 | 
						||
 | 
						||
  # Hook to print the first line of the "two line" prompt, this line is not
 | 
						||
  # actually part of the prompt so does not get redrawn which can sometimes
 | 
						||
  # cause lines before the prompt to disappear.
 | 
						||
  add-zsh-hook precmd fresh_line_one
 | 
						||
 | 
						||
  if [ $almostontop -eq 1 ]; then
 | 
						||
    # Hook to clear the screen then prints the prompt with previous command at
 | 
						||
    # the top of the screen.
 | 
						||
    add-zsh-hook preexec fresh_almostontop
 | 
						||
  else
 | 
						||
    add-zsh-hook -d preexec fresh_almostontop
 | 
						||
  fi
 | 
						||
 | 
						||
  if [ "$USERNAME" = "root" ]; then
 | 
						||
    local user="%{%F{9}%}%n%{%f%}"
 | 
						||
  else
 | 
						||
    local user="%{%F{35}%}%n%{%f%}"
 | 
						||
  fi
 | 
						||
 | 
						||
  if [ "$SSH_CONNECTION" != "" ]; then
 | 
						||
    local user="%{%F{35}%}$user%{%f%}@%{%F{244}%}%M%{%f%}"
 | 
						||
  fi
 | 
						||
 | 
						||
  local length=`fresh_visible_length "$user"`
 | 
						||
 | 
						||
  PS1="«$user» "
 | 
						||
  PS2="${(l:$length + 1:: :)}» "
 | 
						||
 | 
						||
  RPS1='$(fresh_rprompt)'
 | 
						||
 | 
						||
  prompt_opts=(cr percent sp subst)
 | 
						||
}
 | 
						||
 | 
						||
prompt_cleanup() {
 | 
						||
  if [ -f ~/.cache/zsh/git-prompt ]; then rm ~/.cache/zsh/git-prompt; fi
 | 
						||
}
 | 
						||
 | 
						||
fresh_line_one() {
 | 
						||
  # First get the last commands exit code before doing anything
 | 
						||
  local exit_code=$?
 | 
						||
 | 
						||
  # Construct the time and directory portions of the prompt
 | 
						||
  local time_stamp="%{%F{244}%}%D{%H:%M:%S}%{%f%}"
 | 
						||
  local directory="%{%F{37}%}%~%{%f%}"
 | 
						||
 | 
						||
  # Check we are in a git repository
 | 
						||
  if `git rev-parse --git-dir > /dev/null 2>&1`; then
 | 
						||
    # Get git branch name & exit early if not found
 | 
						||
    local branch="`git symbolic-ref HEAD 2> /dev/null`"
 | 
						||
    if [ "" = "$branch" ]; then
 | 
						||
      local branch="`git rev-parse --abbrev-ref HEAD`"
 | 
						||
      if [ "HEAD" = "$branch" ]; then
 | 
						||
        local branch="`git rev-parse --short $branch`"
 | 
						||
      fi
 | 
						||
    fi
 | 
						||
 | 
						||
    if [ "$branch" != "" ]; then
 | 
						||
      local branch=${branch#refs/heads/}
 | 
						||
      # Get the number of commits ahead/behind from the remote branch
 | 
						||
      local remote="`git config branch.$branch.remote 2> /dev/null`"
 | 
						||
      if [ "" != "remote" ]; then
 | 
						||
        local branches="refs/remotes/$remote/$branch...HEAD"
 | 
						||
        local nahead=`git rev-list --right-only $branches --count 2> /dev/null`
 | 
						||
        if [ "0" -ne "$nahead" ]; then local ahead="↑$nahead"; fi
 | 
						||
        local nbehind=`git rev-list --left-only $branches --count 2> /dev/null`
 | 
						||
        if [ "0" -ne "$nbehind" ]; then local behind="↓$nbehind"; fi
 | 
						||
      fi
 | 
						||
 | 
						||
      # Construct the git prompt
 | 
						||
      local git="%{%F{66}%}$branch%{%f%}$ahead$behind "
 | 
						||
 | 
						||
      if [ -f ~/.cache/zsh/git-prompt ]; then
 | 
						||
        # Get the change counts from git-prompt
 | 
						||
        local counts=(`~/.cache/zsh/git-prompt`)
 | 
						||
 | 
						||
        # Parse the results from git-prompt
 | 
						||
        if [ 0 -eq ${#counts[@]} ]; then # clean
 | 
						||
          local git="$git%{%B%F{2}%}✓%{%f%b%}";
 | 
						||
        else
 | 
						||
          if [ 0 != $counts[1] ]; then  # indexed
 | 
						||
            local git="$git%{%F{2}%}*$counts[1]%{%f%}"
 | 
						||
          fi
 | 
						||
          if [ 0 != $counts[2] ]; then  # modified
 | 
						||
            local git="$git%{%F{1}%}+$counts[2]%{%f%}";
 | 
						||
          fi
 | 
						||
          if [ 0 != $counts[3] ]; then  # deleted
 | 
						||
            local git="$git%{%F{1}%}-$counts[3]%{%f%}";
 | 
						||
          fi
 | 
						||
          if [ 0 != $counts[4] ]; then  # unmerged
 | 
						||
            local git="$git%{%B%F{1}%}×$counts[4]%{%f%b%}";
 | 
						||
          fi
 | 
						||
          if [ 0 != $counts[5] ]; then  # untracked
 | 
						||
            local git="$git%{%F{1}%}…%{%f%}";
 | 
						||
          fi
 | 
						||
        fi
 | 
						||
      fi
 | 
						||
    fi
 | 
						||
  fi
 | 
						||
 | 
						||
  # Construct the prompt string
 | 
						||
  local line="$time_stamp $directory $git"
 | 
						||
 | 
						||
  # Print the first line of the prompt
 | 
						||
  if [[ $exit_code -eq 0 ]]; then
 | 
						||
    print -P "$line"
 | 
						||
  else
 | 
						||
    # The last command failed, display its error code at the right
 | 
						||
    local result="%{%B%F{1}%}$exit_code%{%f%b%}"
 | 
						||
    local length=`fresh_visible_length "$line$result"`
 | 
						||
    print -P "$line${(l:COLUMNS - $length - 1:: :)}$result"
 | 
						||
  fi
 | 
						||
}
 | 
						||
 | 
						||
fresh_rprompt() {
 | 
						||
  rprompt=""
 | 
						||
  if [[ ! -z "$DOCKER_MACHINE_NAME" ]]; then
 | 
						||
    rprompt="$rprompt %{%F{3}%}$DOCKER_MACHINE_NAME%{%f%}"
 | 
						||
  fi
 | 
						||
  if [[ ! -z "$VIRTUAL_ENV" ]]; then
 | 
						||
    rprompt="$rprompt %{%F{3}%}$(basename $VIRTUAL_ENV)%{%f%}"
 | 
						||
  fi
 | 
						||
  echo $rprompt
 | 
						||
}
 | 
						||
 | 
						||
fresh_almostontop() {
 | 
						||
  clear
 | 
						||
  fresh_line_one
 | 
						||
  print -P "$PROMPT"'$1'
 | 
						||
}
 | 
						||
 | 
						||
fresh_visible_length() {
 | 
						||
  echo $(( ${#${(S%%)1//(\%(KF1]|)\{*\}|\%[Bbkf])}} ))
 | 
						||
}
 | 
						||
 | 
						||
fresh_compile_git_prompt() {
 | 
						||
  # Compile a simple C executable which parses the output of `git status
 | 
						||
  # --procelain` to greatly decrease the time taken to render the prompt
 | 
						||
  local cache=~/.cache/zsh
 | 
						||
  if [ ! -d $cache ]; then mkdir -p $cache; fi
 | 
						||
  if [ ! -f $cache/git-prompt ]; then
 | 
						||
    cc -x c -std=gnu99 -O3 -DNDEBUG -o $cache/git-prompt - 2> /dev/null << EOF
 | 
						||
#include <stdio.h>
 | 
						||
#include <stdlib.h>
 | 
						||
 | 
						||
int main() {
 | 
						||
  FILE *file = popen("git status --porcelain", "r");
 | 
						||
  if (NULL == file) { return 0; }
 | 
						||
 | 
						||
  char status[2048];
 | 
						||
  int indexed = 0, modified = 0, deleted = 0, untracked = 0, unmerged = 0;
 | 
						||
 | 
						||
  while (NULL != fgets(status, sizeof(status) - 1, file)) {
 | 
						||
    char X = status[0];
 | 
						||
    char Y = status[1];
 | 
						||
 | 
						||
    if (X == '?' && Y == '?') {
 | 
						||
      ++untracked;
 | 
						||
    } else if ((X == 'A' && (Y == 'A' || Y == 'U')) ||
 | 
						||
               (X == 'D' && (Y == 'D' || Y == 'U')) ||
 | 
						||
               (X == 'U' && (Y == 'A' || Y == 'D' || Y == 'D' || Y == 'U'))) {
 | 
						||
      ++unmerged;
 | 
						||
    } else {
 | 
						||
      switch (X) {
 | 
						||
      case ' ':
 | 
						||
        switch (Y) {
 | 
						||
          case 'M': ++modified; break;
 | 
						||
          case 'D': ++deleted; break;
 | 
						||
        } break;
 | 
						||
      case 'D': ++indexed;
 | 
						||
        switch (Y) {
 | 
						||
          case ' ': break;
 | 
						||
          case 'M': ++modified; break;
 | 
						||
        } break;
 | 
						||
      case 'M': case 'A': case 'R': case 'C': ++indexed;
 | 
						||
        switch (Y) {
 | 
						||
          case ' ': break;
 | 
						||
          case 'M': ++modified; break;
 | 
						||
          case 'D': ++deleted; break;
 | 
						||
        } break;
 | 
						||
      }
 | 
						||
    }
 | 
						||
  }
 | 
						||
 | 
						||
  pclose(file);
 | 
						||
 | 
						||
  if (indexed || modified || deleted || unmerged || untracked) {
 | 
						||
    printf("%d %d %d %d %d", indexed, modified, deleted, unmerged, untracked);
 | 
						||
  }
 | 
						||
 | 
						||
  return 0;
 | 
						||
}
 | 
						||
EOF
 | 
						||
    if [ $? -ne 0 ]; then
 | 
						||
      echo "git prompt disabled, are the system development headers installed?"
 | 
						||
    fi
 | 
						||
  fi
 | 
						||
}
 | 
						||
 | 
						||
prompt_fresh_setup "$@"
 | 
						||
 | 
						||
# vim:ft=zsh
 |