From 8438118a871c28eebb0c3b246b6afa23cec56480 Mon Sep 17 00:00:00 2001 From: "Kenneth Benzie (Benie)" Date: Tue, 28 Nov 2017 17:31:25 +0000 Subject: [PATCH] Add git support to fresh prompt theme --- prompt_fresh_setup | 148 +++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 136 insertions(+), 12 deletions(-) diff --git a/prompt_fresh_setup b/prompt_fresh_setup index ff16af4..dd9cdc6 100644 --- a/prompt_fresh_setup +++ b/prompt_fresh_setup @@ -1,8 +1,6 @@ -visible_length() { - echo $(( ${#${(S%%)1//(\%(KF1]|)\{*\}|\%[Bbkf])}} )) -} - prompt_fresh_setup() { + compile_git_prompt + autoload -U add-zsh-hook add-zsh-hook precmd fresh_precmd @@ -23,25 +21,151 @@ prompt_fresh_setup() { # TODO: RPS1 - prompt_opts=(cr sp subst percent) + prompt_opts=(cr percent sp subst) +} + +prompt_cleanup() { + if [ -f ~/.cache/zsh/git-prompt ]; then rm ~/.cache/zsh/git-prompt; fi } fresh_precmd() { + # First get the last commands exit code before doing anything local exit_code=$? - if [[ $exit_code -ne 0 ]]; then - local result="%{%B%F{1}%}$exit_code%{%f%b%}" - fi + # Construct the time and directory portions of the prompt local time_stamp="%{%F{244}%}%D{%H:%M:%S}%{%f%}" local directory="%{%F{37}%}%~%{%f%}" - # TODO: git status + # 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 - local line="$time_stamp $directory" + 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 - local length=`visible_length "$line$result"` + # Construct the git prompt + local git="%{%F{66}%}$branch%{%f%}$ahead$behind " - print -P "$line${(l:COLUMNS - $length - 1:: :)}$result" + 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=`visible_length "$line$result"` + print -P "$line${(l:COLUMNS - $length - 1:: :)}$result" + fi +} + +visible_length() { + echo $(( ${#${(S%%)1//(\%(KF1]|)\{*\}|\%[Bbkf])}} )) +} + +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 + if [ ! -d ~/.cache/zsh ]; then mkdir -p ~/.cache/zsh; fi + if [ ! -f ~/.cache/zsh/git-prompt ]; then + cc -x c -std=gnu99 -O3 -DNDEBUG -o ~/.cache/zsh/git-prompt - << EOF +#include +#include + +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 + fi } prompt_fresh_setup "$@"