zsh/prompt_fresh_setup
Kenneth Benzie (Benie) df819f58a8 Update fresh prompt
* When a command fails, don't print the error code at the end of the
  first line but directly after the git stats.
* Remove `cr` from `prompt_opts` since there is no longer a new line in
  `PS1`, combined with above, this result in no new lines being inserted
  when the terminal becomes narrower e.g. a horizontal tmux split.
* Remove `fresh_visible_length` function and instead use the length of
  string variables e.g. `${#userhost}`.
2018-08-30 21:04:05 +01:00

217 lines
6.0 KiB
Bash
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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 [ "$USER" = "root" ]; then
local user="%{%F{1}%}%n%{%f%}"
else
local user="%{%F{35}%}%n%{%f%}"
fi
local userhost=$USER
if [ "$SSH_CONNECTION" != "" ]; then
local user="$user@%{%F{244}%}%M%{%f%}"
local userhost="$userhost@`hostname`"
fi
PS1="«$user» "
PS2="«${(l:${#userhost}:: :)}» "
RPS1='$(fresh_rprompt)'
prompt_opts=(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%}"
[[ -n $SANDBOX_HOME ]] && \
local directory="%{%F{3}%}$SANDBOX_NAME${PWD#$SANDBOX_HOME}%{%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
# Print the first line of the prompt
if [[ $exit_code -ne 0 ]]; then
# The last command failed, display its error code at the right
local result=" %{%B%F{1}%}$exit_code%{%f%b%}"
fi
# Construct the prompt string
local line="$time_stamp $directory $git$result"
print -P "$line"
}
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_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