aboutsummaryrefslogtreecommitdiff
path: root/dot_config/zsh/executable_git-prompt.zsh
diff options
context:
space:
mode:
Diffstat (limited to 'dot_config/zsh/executable_git-prompt.zsh')
-rw-r--r--dot_config/zsh/executable_git-prompt.zsh408
1 files changed, 408 insertions, 0 deletions
diff --git a/dot_config/zsh/executable_git-prompt.zsh b/dot_config/zsh/executable_git-prompt.zsh
new file mode 100644
index 0000000..95c87a0
--- /dev/null
+++ b/dot_config/zsh/executable_git-prompt.zsh
@@ -0,0 +1,408 @@
+# git-prompt.zsh -- a lightweight git prompt for zsh.
+# Copyright © 2024 Wolfgang Popp
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the "Software"),
+# to deal in the Software without restriction, including without limitation
+# the rights to use, copy, modify, merge, publish, distribute, sublicense,
+# and/or sell copies of the Software, and to permit persons to whom the
+# Software is furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+# DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
+# OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+autoload -U colors && colors
+
+# Settings
+: "${ZSH_GIT_PROMPT_SHOW_UPSTREAM=""}"
+: "${ZSH_GIT_PROMPT_SHOW_STASH=""}"
+: "${ZSH_GIT_PROMPT_SHOW_TRACKING_COUNTS="1"}"
+: "${ZSH_GIT_PROMPT_SHOW_LOCAL_COUNTS="1"}"
+: "${ZSH_GIT_PROMPT_ENABLE_SECONDARY=""}"
+: "${ZSH_GIT_PROMPT_NO_ASYNC=""}"
+: "${ZSH_GIT_PROMPT_FORCE_BLANK=""}"
+: "${ZSH_GIT_PROMPT_AWK_CMD=""}"
+
+# Theming
+: "${ZSH_THEME_GIT_PROMPT_PREFIX="["}"
+: "${ZSH_THEME_GIT_PROMPT_SUFFIX="] "}"
+: "${ZSH_THEME_GIT_PROMPT_SEPARATOR="|"}"
+: "${ZSH_THEME_GIT_PROMPT_DETACHED="%{$fg_bold[cyan]%}:"}"
+: "${ZSH_THEME_GIT_PROMPT_BRANCH="%{$fg_bold[magenta]%}"}"
+: "${ZSH_THEME_GIT_PROMPT_UPSTREAM_SYMBOL="%{$fg_bold[yellow]%}⟳ "}"
+: "${ZSH_THEME_GIT_PROMPT_UPSTREAM_NO_TRACKING=""}"
+: "${ZSH_THEME_GIT_PROMPT_UPSTREAM_PREFIX="%{$fg[red]%}(%{$fg[yellow]%}"}"
+: "${ZSH_THEME_GIT_PROMPT_UPSTREAM_SUFFIX="%{$fg[red]%})"}"
+: "${ZSH_THEME_GIT_PROMPT_BEHIND="↓"}"
+: "${ZSH_THEME_GIT_PROMPT_AHEAD="↑"}"
+: "${ZSH_THEME_GIT_PROMPT_UNMERGED="%{$fg[red]%}✖"}"
+: "${ZSH_THEME_GIT_PROMPT_STAGED="%{$fg[green]%}●"}"
+: "${ZSH_THEME_GIT_PROMPT_UNSTAGED="%{$fg[red]%}✚"}"
+: "${ZSH_THEME_GIT_PROMPT_UNTRACKED="…"}"
+: "${ZSH_THEME_GIT_PROMPT_STASHED="%{$fg[blue]%}⚑"}"
+: "${ZSH_THEME_GIT_PROMPT_CLEAN="%{$fg_bold[green]%}✔"}"
+: "${ZSH_THEME_GIT_PROMPT_SECONDARY_PREFIX=""}"
+: "${ZSH_THEME_GIT_PROMPT_SECONDARY_SUFFIX=""}"
+: "${ZSH_THEME_GIT_PROMPT_TAGS_SEPARATOR=", "}"
+: "${ZSH_THEME_GIT_PROMPT_TAGS_PREFIX="🏷 "}"
+: "${ZSH_THEME_GIT_PROMPT_TAGS_SUFFIX=""}"
+: "${ZSH_THEME_GIT_PROMPT_TAG="%{$fg_bold[magenta]%}"}"
+
+# Disable promptinit if it is loaded
+(( $+functions[promptinit] )) && {promptinit; prompt off}
+
+# Allow parameter and command substitution in the prompt
+setopt PROMPT_SUBST
+
+# Override PROMPT if it does not use the gitprompt function
+[[ "$PROMPT" != *gitprompt* && "$RPROMPT" != *gitprompt* ]] \
+ && PROMPT='%B%40<..<%~ %b$(gitprompt)' \
+ && PROMPT+='%(?.%(!.%F{white}❯%F{yellow}❯%F{red}.%F{blue}❯%F{cyan}❯%F{green})❯.%F{red}❯❯❯)%f '
+
+# Find an awk implementation
+# Prefer nawk over mawk and mawk over awk
+(( $+commands[mawk] )) && : "${ZSH_GIT_PROMPT_AWK_CMD:=mawk}"
+(( $+commands[nawk] )) && : "${ZSH_GIT_PROMPT_AWK_CMD:=nawk}"
+ : "${ZSH_GIT_PROMPT_AWK_CMD:=awk}"
+
+# Use --show-stash for git versions newer than 2.35.0
+_zsh_git_prompt_git_version=$(command git version)
+if [[ "${_zsh_git_prompt_git_version:12}" == 2.<35->.<-> ]]; then
+ _zsh_git_prompt_git_cmd() {
+ GIT_OPTIONAL_LOCKS=0 command git status --show-stash --branch --porcelain=v2 2>&1 \
+ || echo "fatal: git command failed"
+ }
+else
+ _zsh_git_prompt_git_cmd() {
+ [[ -n "$ZSH_GIT_PROMPT_SHOW_STASH" ]] && (
+ c=$(command git rev-list --walk-reflogs --count refs/stash 2> /dev/null)
+ [[ -n "$c" ]] && echo "# stash $c"
+ )
+ GIT_OPTIONAL_LOCKS=0 command git status --branch --porcelain=v2 2>&1 \
+ || echo "fatal: git command failed"
+ }
+fi
+unset _zsh_git_prompt_git_version
+
+
+function _zsh_git_prompt_git_status() {
+ emulate -L zsh
+ _zsh_git_prompt_git_cmd | $ZSH_GIT_PROMPT_AWK_CMD \
+ -v PREFIX="$ZSH_THEME_GIT_PROMPT_PREFIX" \
+ -v SUFFIX="$ZSH_THEME_GIT_PROMPT_SUFFIX" \
+ -v SEPARATOR="$ZSH_THEME_GIT_PROMPT_SEPARATOR" \
+ -v DETACHED="$ZSH_THEME_GIT_PROMPT_DETACHED" \
+ -v BRANCH="$ZSH_THEME_GIT_PROMPT_BRANCH" \
+ -v UPSTREAM_TYPE="$ZSH_GIT_PROMPT_SHOW_UPSTREAM" \
+ -v SHOW_TRACKING_COUNTS="$ZSH_GIT_PROMPT_SHOW_TRACKING_COUNTS" \
+ -v SHOW_LOCAL_COUNTS="$ZSH_GIT_PROMPT_SHOW_LOCAL_COUNTS" \
+ -v UPSTREAM_SYMBOL="$ZSH_THEME_GIT_PROMPT_UPSTREAM_SYMBOL" \
+ -v UPSTREAM_NO_TRACKING="$ZSH_THEME_GIT_PROMPT_UPSTREAM_NO_TRACKING" \
+ -v UPSTREAM_PREFIX="$ZSH_THEME_GIT_PROMPT_UPSTREAM_PREFIX" \
+ -v UPSTREAM_SUFFIX="$ZSH_THEME_GIT_PROMPT_UPSTREAM_SUFFIX" \
+ -v BEHIND="$ZSH_THEME_GIT_PROMPT_BEHIND" \
+ -v AHEAD="$ZSH_THEME_GIT_PROMPT_AHEAD" \
+ -v UNMERGED="$ZSH_THEME_GIT_PROMPT_UNMERGED" \
+ -v STAGED="$ZSH_THEME_GIT_PROMPT_STAGED" \
+ -v UNSTAGED="$ZSH_THEME_GIT_PROMPT_UNSTAGED" \
+ -v UNTRACKED="$ZSH_THEME_GIT_PROMPT_UNTRACKED" \
+ -v STASHED="$ZSH_THEME_GIT_PROMPT_STASHED" \
+ -v SHOW_STASH="$ZSH_GIT_PROMPT_SHOW_STASH" \
+ -v CLEAN="$ZSH_THEME_GIT_PROMPT_CLEAN" \
+ -v RC="%{$reset_color%}" \
+ '
+ BEGIN {
+ ORS = "";
+
+ fatal = 0;
+ oid = "";
+ head = "";
+ upstream = "";
+ ahead = 0;
+ behind = 0;
+ untracked = 0;
+ unmerged = 0;
+ staged = 0;
+ unstaged = 0;
+ stashed = 0;
+ }
+
+ function prompt_element(prefix, content, suffix) {
+ print(prefix);
+ gsub("%", "%%", content);
+ print(content);
+ print(suffix);
+ print(RC);
+ }
+
+ function count_element(prefix, count, show_count) {
+ content = "";
+ if (show_count) {
+ content = count;
+ }
+ if (count > 0) {
+ prompt_element(prefix, content);
+ }
+ }
+
+ function local_element(prefix, count) {
+ count_element(prefix, count, SHOW_LOCAL_COUNTS)
+ }
+
+ function tracking_element(prefix, count) {
+ count_element(prefix, count, SHOW_TRACKING_COUNTS)
+ }
+
+ $1 == "fatal:" {
+ fatal = 1;
+ }
+
+ $2 == "branch.oid" {
+ oid = $3;
+ }
+
+ $2 == "branch.head" {
+ head = $3;
+ }
+
+ $2 == "branch.upstream" {
+ upstream = $3;
+ }
+
+ $2 == "branch.ab" {
+ ahead = $3;
+ behind = $4;
+ }
+
+ $1 == "?" {
+ ++untracked;
+ }
+
+ $1 == "u" {
+ ++unmerged;
+ }
+
+ $1 == "1" || $1 == "2" {
+ split($2, arr, "");
+ if (arr[1] != ".") {
+ ++staged;
+ }
+ if (arr[2] != ".") {
+ ++unstaged;
+ }
+ }
+
+ $2 == "stash" {
+ stashed = $3;
+ }
+
+ END {
+ if (fatal == 1) {
+ exit(1);
+ }
+
+ prompt_element(PREFIX);
+
+ if (head == "(detached)") {
+ prompt_element(DETACHED, substr(oid, 0, 7));
+ } else {
+ prompt_element(BRANCH, head);
+ }
+
+ if (upstream == "") {
+ prompt_element(UPSTREAM_NO_TRACKING);
+ } else if (UPSTREAM_TYPE == "symbol") {
+ prompt_element(UPSTREAM_SYMBOL);
+ } else if (UPSTREAM_TYPE == "full") {
+ prompt_element(UPSTREAM_PREFIX, upstream, UPSTREAM_SUFFIX);
+ }
+
+ tracking_element(BEHIND, behind * -1);
+
+ tracking_element(AHEAD, ahead * 1);
+
+ prompt_element(SEPARATOR);
+
+ local_element(UNMERGED, unmerged);
+
+ local_element(STAGED, staged);
+
+ local_element(UNSTAGED, unstaged);
+
+ local_element(UNTRACKED, untracked);
+
+ if (SHOW_STASH) {
+ local_element(STASHED, stashed);
+ }
+
+ if (unmerged == 0 && staged == 0 && unstaged == 0 && untracked == 0) {
+ prompt_element(CLEAN);
+ }
+
+ prompt_element(SUFFIX);
+ }
+ '
+}
+
+function _zsh_git_prompt_git_status_secondary() {
+ tags=$(command git tag --points-at=HEAD 2> /dev/null)
+
+ [[ -z "$tags" ]] && return
+
+ echo -n ${ZSH_THEME_GIT_PROMPT_SECONDARY_PREFIX}
+ echo -n ${ZSH_THEME_GIT_PROMPT_TAGS_PREFIX}
+
+ echo "$tags" | $ZSH_GIT_PROMPT_AWK_CMD \
+ -v SEPARATOR="$ZSH_THEME_GIT_PROMPT_TAGS_SEPARATOR" \
+ -v TAG="$ZSH_THEME_GIT_PROMPT_TAG" \
+ -v RC="%{$reset_color%}" \
+ '
+ BEGIN {
+ ORS = "";
+ }
+ {
+ if (NR != 1) {
+ print SEPARATOR;
+ print RC;
+ }
+ print TAG;
+ print $0;
+ print RC;
+ }
+ '
+
+ echo -n ${ZSH_THEME_GIT_PROMPT_TAGS_SUFFIX}
+ echo -n ${ZSH_THEME_GIT_PROMPT_SECONDARY_SUFFIX}
+}
+
+
+# The async code is taken from
+# https://github.com/zsh-users/zsh-autosuggestions/blob/master/src/async.zsh
+
+zmodload zsh/system
+
+function _zsh_git_prompt_async_request() {
+ typeset -g _ZSH_GIT_PROMPT_ASYNC_FD _ZSH_GIT_PROMPT_ASYNC_PID
+
+ # If we've got a pending request, cancel it
+ if [[ -n "$_ZSH_GIT_PROMPT_ASYNC_FD" ]] && { true <&$_ZSH_GIT_PROMPT_ASYNC_FD } 2>/dev/null;
+ then
+
+ # Close the file descriptor and remove the handler
+ exec {_ZSH_GIT_PROMPT_ASYNC_FD}<&-
+ zle -F $_ZSH_GIT_PROMPT_ASYNC_FD
+
+ # Zsh will make a new process group for the child process only if job
+ # control is enabled (MONITOR option)
+ if [[ -o MONITOR ]]; then
+ # Send the signal to the process group to kill any processes that may
+ # have been forked by the suggestion strategy
+ kill -TERM -$_ZSH_GIT_PROMPT_ASYNC_PID 2>/dev/null
+ else
+ # Kill just the child process since it wasn't placed in a new process
+ # group. If the suggestion strategy forked any child processes they may
+ # be orphaned and left behind.
+ kill -TERM $_ZSH_GIT_PROMPT_ASYNC_PID 2>/dev/null
+ fi
+ fi
+
+ # Fork a process to fetch the git status and open a pipe to read from it
+ exec {_ZSH_GIT_PROMPT_ASYNC_FD}< <(
+ # Tell parent process our pid
+ builtin echo $sysparams[pid]
+
+ _zsh_git_prompt_git_status
+ [[ -n "$ZSH_GIT_PROMPT_ENABLE_SECONDARY" ]] \
+ && builtin echo -n "##secondary##" \
+ && _zsh_git_prompt_git_status_secondary
+ )
+
+ # There's a weird bug here where ^C stops working unless we force a fork
+ # See https://github.com/zsh-users/zsh-autosuggestions/issues/364
+ command true
+
+ # Read the pid from the child process
+ read _ZSH_GIT_PROMPT_ASYNC_PID <&$_ZSH_GIT_PROMPT_ASYNC_FD
+
+ # When the fd is readable, call the response handler
+ zle -F "$_ZSH_GIT_PROMPT_ASYNC_FD" _zsh_git_prompt_callback
+}
+
+# Called when new data is ready to be read from the pipe
+# First arg will be fd ready for reading
+# Second arg will be passed in case of error
+_ZSH_GIT_PROMPT_STATUS_OUTPUT=""
+_ZSH_GIT_PROMPT_STATUS_SECONDARY_OUTPUT=""
+function _zsh_git_prompt_callback() {
+ emulate -L zsh
+ local old_primary="$_ZSH_GIT_PROMPT_STATUS_OUTPUT"
+ local old_secondary="$_ZSH_GIT_PROMPT_STATUS_SECONDARY_OUTPUT"
+ local fd_data
+ local -a output
+
+ if [[ -z "$2" || "$2" == "hup" ]]; then
+ # Read output from fd
+ fd_data="$(cat <&$1)"
+ output=( ${(s:##secondary##:)fd_data} )
+ _ZSH_GIT_PROMPT_STATUS_OUTPUT="${output[1]}"
+ _ZSH_GIT_PROMPT_STATUS_SECONDARY_OUTPUT="${output[2]}"
+
+ if [[ "$old_primary" != "$_ZSH_GIT_PROMPT_STATUS_OUTPUT" ]] \
+ || [[ "$old_secondary" != "$_ZSH_GIT_PROMPT_STATUS_SECONDARY_OUTPUT" ]] ; then
+ zle reset-prompt
+ zle -R
+ fi
+
+ # Close the fd
+ exec {1}<&-
+ fi
+
+ # Always remove the handler
+ zle -F "$1"
+
+ # Unset global FD variable to prevent closing user created FDs in the precmd hook
+ unset _ZSH_GIT_PROMPT_ASYNC_FD
+}
+
+function _zsh_git_prompt_precmd_hook() {
+ if [[ -n "$ZSH_GIT_PROMPT_FORCE_BLANK" ]]; then
+ _ZSH_GIT_PROMPT_STATUS_OUTPUT=""
+ _ZSH_GIT_PROMPT_STATUS_SECONDARY_OUTPUT=""
+ fi
+ _zsh_git_prompt_async_request
+}
+
+if (( $+commands[git] )); then
+ if [[ -z "$ZSH_GIT_PROMPT_NO_ASYNC" ]]; then
+ autoload -U add-zsh-hook \
+ && add-zsh-hook precmd _zsh_git_prompt_precmd_hook
+
+ function gitprompt() {
+ echo -n "$_ZSH_GIT_PROMPT_STATUS_OUTPUT"
+ }
+
+ function gitprompt_secondary() {
+ echo -n "$_ZSH_GIT_PROMPT_STATUS_SECONDARY_OUTPUT"
+ }
+ else
+ function gitprompt() {
+ _zsh_git_prompt_git_status
+ }
+
+ function gitprompt_secondary() {
+ [[ -n "$ZSH_GIT_PROMPT_ENABLE_SECONDARY" ]] \
+ && _zsh_git_prompt_git_status_secondary
+ }
+ fi
+else
+ function gitprompt() { }
+ function gitprompt_secondary() { }
+fi