Files
dotfiles/.config/zsh/autoload/git/glog
Julian Prein 8be1c63c18 glog: Fix coloring when scrolling
Due to the formatting placeholders sitting on the previous line, the
coloring of the topmost line disappeared when scrolling (as the escape
sequence scrolled away). This had the background that I wanted the code
that sets up the format string to be very readable and if possible very
close to the actual output. And since the colors have all different
lengths I decided to place them on the previous line to have them out of
the way.

Fix this by placing the placeholders on the same output line while still
maintaining a readable format string (code). This is done by joining the
array without placing newlines so that it can now have multiple elements
for one output line and formatting those as wished.
2024-11-01 01:55:03 +01:00

155 lines
5.5 KiB
Bash
Executable File

#!/usr/bin/env zsh
## Author: druckdev
## Created: 2020-08-28
##
## A TUI for git-log using fzf.
## Displays git-log in fzf and git-show as preview command for each commit.
# TODO: preview breaks when files were passed but they were renamed and have a
# different name at the point of this commit
# extendedglob is necessary for the expansion of the binds array
emulate -L zsh -o extendedglob
# Return if fzf is not available
if (( ! $+commands[fzf] )); then
printf "command not found: fzf" >&2
return 1
fi
# Return if not in git repo
git rev-parse || return
# One line format for fzf list view
# abbreviated commit hash (yellow), title and ref names
local formatshort='--pretty=format:%C(yellow)%h %Creset%s%C(auto)%d'
# Verbose format for the preview window on the right
local -a format=(
'--pretty=format:'
'%C(yellow)' 'Commit: ' '%H' '%n' # commit hash
'%C(auto)' ' ' '%D' '%n' # ref names (if any)
'%C(blue)' 'Author: ' '%aN <%aE>' '%n' # author mail
'%C(red)' 'AuthorDate: ' '%ad' '%n' # author date
'%C(blue)' 'Commit: ' '%cN <%cE>' '%n' # commiter mail
'%C(red)' 'CommitDate: ' '%cd' '%n' # commit date
'%C(blue)' 'Signer: ' '%GS' '%n' # signer name
'%C(green)' 'Key (%G?): ' '%GK' '%n' # pgp key used to sign
'%n'
'%C(reset)%C(bold)' ' %s%C(reset)' '%n' # subject
'%n'
'%-b' # body
'%n'
)
format="${(j::)format}"
# Ignore the graph part at the beginning, then capture the commit hash and throw
# away the rest of the line.
local commit_hash='s/^[^a-f0-9]*([a-f0-9]*).*$/\1/'
local dateshort='--date=format:%F' # year
local date="$dateshort %T %z" # year time timezone
local -A fzf_preview
read -r -d '' <<EOT
out="\$(echo -E {} | sed -E "$commit_hash")"; [[ -z "\$out" ]] ||
EOT
fzf_preview[construct]="$REPLY"
read -r -d '' <<EOT
git show "$format" "$date" --color=always --patch-with-stat "\$out"
EOT
fzf_preview[patch]="$fzf_preview[construct] { $REPLY"
# Get file arguments after (and including) `--` and wrap each in double quotes
# TODO: Display `...` behind patch-stat if the patch touched more files
# TODO: Use `-O <file>` for 'patch' command with `-- <file>` argument, so that
# the passed file is ontop although the full patch is shown
# TODO: Support -L flag as well (add `-s` for list and `-- <file>` & maybe `-W`
# for preview)
fzf_preview[files_only]="$fzf_preview[patch] ${(@)${@:${@[(ei)--]}}/(#m)*/\"$MATCH\"}"
# Use git's pager in the preview window (and with it any special highlighting
# tool, such as diff-so-fancy)
local pager
pager="$(git config --get --default="" core.pager)"
fzf_preview[patch]+="${pager:+ | }$pager; }"
fzf_preview[files_only]+="${pager:+ | }$pager; }"
read -r -d '' <<EOT
git show "$format" "$date" --color=always --stat "\$out"
EOT
fzf_preview[stat]="$fzf_preview[construct] { $REPLY; }"
# Put the commit hash into the clipboard
# (If no known clipboard tool is available, just print it)
local fzf_copy_command="$fzf_preview[construct] echo -n \"\$out\""
if [[ $OSTYPE =~ darwin ]] && (( $+commands[pbcopy] )); then
fzf_copy_command+=" | pbcopy"
elif (( $+commands[xclip] )); then
fzf_copy_command+=" | xclip -selection c"
fi
local -A binds=(
# scroll in preview window
"ctrl-alt-j" "preview-down"
"ctrl-alt-k" "preview-up"
# Copy commit hash
"ctrl-y" "execute@$fzf_copy_command@"
# Open preview "fullscreen"
# NOTE: `-+F` is there to negate a possible --quit-if-one-screen
# TODO: This assumes less to be used in core.pager
"enter" "execute@$fzf_preview[patch] | $pager -+F@"
# Preview stats
"ctrl-s" "change-preview($fzf_preview[stat])"
# Preview patch
"ctrl-p" "change-preview($fzf_preview[patch])"
# Files only
"ctrl-f" "change-preview($fzf_preview[files_only])"
)
# TODO: Make the --preview argument dependent of --stat flag (i.e.
# fzf_preview[stat] in this case). It does not really make sense to pass
# it to `git log` but can be an indicator for the preview function
local -a fzf_args=(
# Understand ansi color escape sequences.
"--ansi"
# Expand the binds array in the format "key1:value1,key2:value2".
"--bind" "${(@kj:,:)binds/(#m)*/$MATCH:$binds[$MATCH]}"
# Execute git show on the commit as preview.
"--preview" "$fzf_preview[files_only]"
# Reverse the layout so that the newest commit is at the top.
"--reverse"
# Do not sort when typing to maintain the sorting by date.
"--no-sort"
)
# The preview-window should be placed differently depending on the dimensions of
# the terminal. It should have at least 152 columns to fit a preview window on
# the right (12 hash, 50 subject, 80 patch, 10 git graph and fzf ui).
#
# ctrl-space should cycle through the preview-window positions, starting with
# hidden, then not the start position and than back the start position.
local -a tty_size
tty_size=(${=$(command stty size 2>/dev/null)})
if (( ! $? )) && (( $tty_size[2] > 152 )); then
fzf_args+=(
--preview-window=right
--bind "ctrl-space:change-preview-window(hidden|bottom|right)"
)
else
fzf_args+=(
--preview-window=down
--bind "ctrl-space:change-preview-window(hidden|right|bottom)"
)
fi
# Display the commits in the above format and pipe that into fzf if stdout is a
# terminal.
if [ -t 1 ]; then
git log "$formatshort" "$dateshort" --color=always "$@" \
| env LESS="$LESS${LESS:+ }-+F" fzf "${fzf_args[@]}"
else
git log "$formatshort" "$dateshort" --color=always "$@"
fi
return 0