diff options
| author | Johannes Altmanninger <aclopte@gmail.com> | 2024-02-03 00:26:58 +0100 |
|---|---|---|
| committer | Maxime Coste <mawww@kakoune.org> | 2024-02-05 21:42:02 +1100 |
| commit | 4e13fbef0a8d1d6fd63b29051bb408d436bd50d5 (patch) | |
| tree | e258fd216b4430bb536ee8b10102ee8469d9461a /rc/tools | |
| parent | 24bf123503c06c44e73892aaf1ae550ae11af66e (diff) | |
rc tools git: support blame in git-diff and git-log buffers
Today we can recursively search history with "git blame-jump". However
that command has some drawbacks, mainly that it's blocking. Making
it async without any progress indicator might be confusing. Better
to run plain "git blame"[1] and press Enter.
Also it might be nice to enable recursive searches using only "git
blame" and `<ret>` (since that is bound to "git blame-jump" while
blame annotations are displayed).
Make "git blame" in git-diff/git-log buffers run "git show
$commit:$file" for the commit and file at cursor, and decorate this
blob view with blame annotations. The latter allows to use `<ret>`
and repeat.
Unfortunately this relies on a hidden option "git_blob" to keep the
commit ID and filename. Maybe we can put this metadata somewhere
else like the buffer name or contents, ideally in a way that survives
serialization.
I'd still keep "git blame-jump" because it seems faster for the common
case of tracking down a single line.
[1]: In my testing, "git blame --incremental" is not any slower than
"git blame -L123,123" at finding that line.
Diffstat (limited to 'rc/tools')
| -rw-r--r-- | rc/tools/git.kak | 88 |
1 files changed, 76 insertions, 12 deletions
diff --git a/rc/tools/git.kak b/rc/tools/git.kak index 80af81e0..f3656cec 100644 --- a/rc/tools/git.kak +++ b/rc/tools/git.kak @@ -75,6 +75,7 @@ hook -group git-show-branch-highlight global WinSetOption filetype=git-show-bran declare-option -hidden line-specs git_blame_flags declare-option -hidden str git_blame +declare-option -hidden str git_blob declare-option -hidden line-specs git_diff_flags declare-option -hidden int-list git_hunk_list @@ -168,6 +169,7 @@ define-command -params 1.. \ edit! -fifo ${output} *git* set-option buffer filetype ${filetype} $(hide_blame) + set-option buffer git_blob %{} hook -always -once buffer BufCloseFifo .* '' nop %sh{ rm -r $(dirname ${output}) } $(printf %s "${on_close_fifo}" | sed "s/'/''''/g") @@ -185,16 +187,21 @@ define-command -params 1.. \ } prepare_git_blame_args=' - contents_fifo=$(mktemp -d "${TMPDIR:-/tmp}"/kak-git.XXXXXXXX)/fifo - mkfifo ${contents_fifo} - echo >${kak_command_fifo} "evaluate-commands -save-regs | %{ - set-register | %{ - contents=\$(cat; printf .) - ( printf %s \"\${contents%.}\" >${contents_fifo} ) >/dev/null 2>&1 & - } - execute-keys -client ${kak_client} -draft %{%<a-|><ret>} - }" - set -- "$@" --contents - -- "${kak_buffile}" + if [ -n "${kak_opt_git_blob}" ]; then { + contents_fifo=/dev/null + set -- "$@" "${kak_opt_git_blob%%:*}" -- "${kak_opt_git_blob#*:}" + } else { + contents_fifo=$(mktemp -d "${TMPDIR:-/tmp}"/kak-git.XXXXXXXX)/fifo + mkfifo ${contents_fifo} + echo >${kak_command_fifo} "evaluate-commands -save-regs | %{ + set-register | %{ + contents=\$(cat; printf .) + ( printf %s \"\${contents%.}\" >${contents_fifo} ) >/dev/null 2>&1 & + } + execute-keys -client ${kak_client} -draft %{%<a-|><ret>} + }" + set -- "$@" --contents - -- "${kak_buffile}" + } fi ' blame_toggle() { @@ -205,6 +212,59 @@ define-command -params 1.. \ echo -to-file ${kak_response_fifo} 'hide_blame; exit' }" eval $(cat ${kak_response_fifo}) + if [ -z "${kak_opt_git_blob}" ] && { + [ "${kak_opt_filetype}" = git-diff ] || [ "${kak_opt_filetype}" = git-log ] + } then { + echo 'remove-highlighter window/git-blame' + printf >${kak_command_fifo} %s ' + evaluate-commands -client '${kak_client}' -draft %{ + try %{ + execute-keys <a-l><semicolon><a-?>^commit<ret><a-semicolon> + require-module diff + try %{ + diff-parse END %{ + $commit = "$commit~" if $diff_line_text =~ m{^[-]}; + printf "echo -to-file '${kak_response_fifo}' -quoting shell %s %s %d %d", + $commit, quote($file), ($file_line or 1), ('${kak_cursor_column}' - 1); + } + } catch %{ + echo -to-file '${kak_response_fifo}' -quoting shell -- %val{error} + } + } catch %{ + # Missing commit line, assume it is an uncommitted change. + echo -to-file '${kak_response_fifo}' -quoting shell -- \ + "git blame: blaming without commit line is not yet supported" + } + } + ' + n=$# + eval set -- "$(cat ${kak_response_fifo})" "$@" + if [ $# -eq $((n+1)) ]; then + echo fail -- "$(kakquote "$1")" + exit + fi + commit=$1 + file=${2#"$PWD/"} + cursor_line=$3 + cursor_column=$4 + shift 4 + # Log commit and file name because they are only echoed briefly + # and not shown elsewhere (we don't have a :messages buffer). + message="Blaming $file as of $(git rev-parse --short $commit)" + echo "echo -debug -- $(kakquote "$message")" + on_close_fifo=" + execute-keys -client ${kak_client} ${cursor_line}g<a-h>${cursor_column}l + evaluate-commands -client ${kak_client} %{ + set-option buffer git_blob $(kakquote "$commit:$file") + git blame $(for arg; do kakquote "$arg"; printf " "; done) + hook -once window NormalIdle .* %{ + execute-keys vv + echo -markup -- $(kakquote "{Information}{\\}$message. Press <ret> to jump to blamed commit") + } + } + " show_git_cmd_output show "$commit:$file" + exit + } fi eval "$prepare_git_blame_args" echo 'map window normal <ret> %{:git blame-jump<ret>}' echo 'echo -markup {Information}Press <ret> to jump to blamed commit' @@ -257,7 +317,9 @@ define-command -params 1.. \ } if (m/^author-time ([0-9]*)/) { $dates{$sha} = strftime("%F %T", localtime $1) } END { send_flags(1); }' - rm -r $(dirname $contents_fifo) + if [ "$contents_fifo" != /dev/null ]; then + rm -r $(dirname $contents_fifo) + fi ) > /dev/null 2>&1 < /dev/null & } @@ -470,7 +532,9 @@ define-command -params 1.. \ set -- eval "$prepare_git_blame_args" blame_info=$(git blame --porcelain -L"$cursor_line,$cursor_line" "$@" <${contents_fifo}) - rm -r $(dirname $contents_fifo) + if [ "$contents_fifo" != /dev/null ]; then + rm -r $(dirname $contents_fifo) + fi } fi eval "$(printf %s "$blame_info" | client=${kak_opt_docsclient:-$kak_client} \ |
