diff options
| -rw-r--r-- | doc/pages/changelog.asciidoc | 4 | ||||
| -rwxr-xr-x | rc/filetype/diff-parse.pl | 8 | ||||
| -rw-r--r-- | rc/tools/git.kak | 125 | ||||
| -rwxr-xr-x | rc/tools/patch-range.pl | 20 | ||||
| -rw-r--r-- | src/main.cc | 1 |
5 files changed, 140 insertions, 18 deletions
diff --git a/doc/pages/changelog.asciidoc b/doc/pages/changelog.asciidoc index 3a6804e5..f443f207 100644 --- a/doc/pages/changelog.asciidoc +++ b/doc/pages/changelog.asciidoc @@ -5,8 +5,10 @@ released versions. == Development version -* Expose env vars that are mentionned in the arguments passed to shell expansions +* Expose env vars that are mentioned in the arguments passed to shell expansions * Support for colored double underlines +* `git apply` can now operate on selected changes in the current buffer's + file (useful for quick (un)staging and reverting) == Kakoune 2024.05.18 diff --git a/rc/filetype/diff-parse.pl b/rc/filetype/diff-parse.pl index 7453b2b3..5da4e349 100755 --- a/rc/filetype/diff-parse.pl +++ b/rc/filetype/diff-parse.pl @@ -45,13 +45,15 @@ our $version = "+"; eval $begin if defined $begin; -$in_file = "$directory/$in_file" if defined $in_file; +$in_file = "$directory/$in_file" if defined $in_file && $in_file ne ""; # Outputs our $diff_line = 0; our $commit; our $file; our $file_line; +our $other_file; +our $other_file_line; our $diff_line_text; my $other_version; @@ -63,8 +65,6 @@ if ($version eq "+") { my $is_recursive_diff = 0; my $state = "header"; my $fallback_file; -my $other_file; -my $other_file_line; sub strip { my $is_recursive_diff = shift; @@ -127,7 +127,7 @@ while (<STDIN>) { $other_file_line++ if defined $other_file_line; } } - if (defined $in_file and defined $file and $file eq $in_file) { + if (defined $in_file and defined $file and ($in_file eq "" or $file eq $in_file)) { if (defined $in_file_line and defined $file_line and $file_line >= $in_file_line) { last; } diff --git a/rc/tools/git.kak b/rc/tools/git.kak index 0d837c8d..b98974d1 100644 --- a/rc/tools/git.kak +++ b/rc/tools/git.kak @@ -86,7 +86,8 @@ define-command -params 1.. \ All the optional arguments are forwarded to the git utility Available commands: add - apply - alias for "patch git apply" + apply - run "patch git apply [<arguments>]"; if buffile is + tracked, use the changes to selected lines instead blame - toggle blame annotations blame-jump - show the commit that added the line at cursor checkout @@ -192,13 +193,15 @@ define-command -params 1.. \ diff_buffer_against_rev() { rev=$1 # empty means index shift - buffile_relative=${kak_buffile#"$PWD/"} + buffile_relative=${kak_buffile#"$(git rev-parse --show-toplevel)/"} echo >${kak_command_fifo} "evaluate-commands -save-regs | %{ set-register | %{ cat >${kak_response_fifo} } execute-keys -client ${kak_client} -draft %{%<a-|><ret>} }" - git show "$rev:./${buffile_relative}" | - git diff --no-index - ${kak_response_fifo} "$@" + git show "$rev:${buffile_relative}" | + diff - ${kak_response_fifo} "$@" | + sed -e "1c--- a/$buffile_relative" \ + -e "2c+++ b/$buffile_relative" } blame_toggle() { @@ -435,7 +438,7 @@ define-command -params 1.. \ } } } - print "set-option buffer git_diff_flags $flags" + print "set-option buffer git_diff_flags $flags\n" ' ) } @@ -728,12 +731,118 @@ define-command -params 1.. \ ')" } - case "$1" in - apply) - shift + apply_selections() { + if [ -z "$(cd_bufdir >/dev/null 2>&1; git ls-files -- ":(literal)${kak_buffile}")" ]; then { enquoted="$(printf '"%s" ' "$@")" echo "require-module patch" echo "patch git apply $enquoted" + return + } fi + base_rev=HEAD + index_only=false + index=false + reverse=false + for arg; do + case "$arg" in + (--cached) index_only=true ; base_rev= ;; + (--index) index=true ;; + (--reverse|-R) reverse=true ;; + esac + done + if ! $reverse && ! $index_only; then + echo "fail %{git apply on buffer contents doesn't make sense without --reverse or --cached}" + exit + fi + cd_bufdir + num_inserted=0 + num_deleted=0 + for selection_desc in $kak_selections_desc; do { + IFS=' .,' read anchor_line _ cursor_line _ <<-EOF + $selection_desc + EOF + if [ $anchor_line -lt $cursor_line ]; then + min_line=$anchor_line + max_line=$cursor_line + else + min_line=$cursor_line + max_line=$anchor_line + fi + intended_diff='diff_buffer_against_rev "$base_rev" -u' + if $index; then { + git update-index --refresh "${kak_buffile}" >/dev/null + intended_diff='git diff --no-ext-diff HEAD -- ":(literal)${kak_buffile}"' + } elif $index_only && $reverse; then { + diff=$(eval "$intended_diff") + if [ -n "$diff" ]; then { + # Convert from buffile lines to index lines. + for line in min_line max_line; do { + if ! index_line_or_error_message=$( + eval file_line=\$$line + printf %s "$diff" | + perl "${kak_runtime}/rc/filetype/diff-parse.pl" \ + BEGIN ' + $in_file = ""; # no need to check filename, there is only one + $in_file_line = '"$file_line"'; + ' END ' + $other_file_line++ if $diff_line_text =~ m{^\+}; + $other_file_line += $in_file_line - $file_line; + print "$other_file_line\n"; + ' + ); then + echo fail "git apply: $index_line_or_error_message" + exit + fi + eval $line=$index_line_or_error_message + } done + } fi + intended_diff='git diff --no-ext-diff --cached -- ":(literal)${kak_buffile}"' + } fi + diff=$(eval "$intended_diff" | + perl "${kak_runtime}"/rc/tools/patch-range.pl -line-numbers-from-new-file \ + $min_line $max_line sh -c cat -- "$@" # forward any --reverse arg + printf .) # avoid stripping newline + diff=${diff%.} + if ! printf %s "$diff" | git apply "$@"; then + printf >&2 "git apply: error running:\n\$ git apply %s << EOF\n" "$*" + printf >&2 %s "$diff" + printf >&2 'EOF\n' + echo "fail 'git apply: failed to apply selections, see *debug* buffer'" + exit + fi + count() { + printf %s "$diff" | awk ' + BEGIN { n = 0 } + /^@@/,/^$/ { if ($0 ~ /^'"$1"'/) { n++ } } + END { print n }' + } + num_inserted=$(( $num_inserted + $(count +) )) + num_deleted=$(( $num_deleted + $(count -) )) + } done + if ! $index_only && ! $kak_modified; then + echo edit! + echo git update-diff + else + update_diff + fi + msg= + case $index_only,$reverse,$index in + (true,false,*) msg=Staged ;; + (true,true,*) msg=Unstaged ;; + (false,true,false) msg=Reverted ;; + (false,true,true) msg='Unstaged and reverted' ;; + esac + case $num_inserted,$num_deleted in + (*,0) msg="$msg $num_inserted inserted line(s)";; + (0,*) msg="$msg $num_deleted deleted line(s)";; + (*,*) msg="$msg $num_inserted inserted and $num_deleted deleted lines";; + esac + echo "echo -markup '{Information}{\\}$msg'" + } + + case "$1" in + apply) + shift + apply_selections "$@" ;; show|show-branch|log|diff|status) show_git_cmd_output "$@" diff --git a/rc/tools/patch-range.pl b/rc/tools/patch-range.pl index 2d54ab26..01611abe 100755 --- a/rc/tools/patch-range.pl +++ b/rc/tools/patch-range.pl @@ -9,6 +9,12 @@ if ($ARGV[0] eq "-print-remaining-diff") { shift @ARGV; } +my $line_number_kind = "diff"; +if ($ARGV[0] eq "-line-numbers-from-new-file") { + $line_number_kind = "new-file"; + shift @ARGV; +} + my $min_line = $ARGV[0]; shift @ARGV; my $max_line = $ARGV[0]; @@ -22,7 +28,7 @@ if (defined $ARGV[0] and $ARGV[0] =~ m{^[^-]}) { } my $reverse = grep /^(--reverse|-R)$/, @ARGV; -my $lineno = 0; +my $lineno = $line_number_kind eq "diff" ? 0 : undef; my $original = ""; my $diff_header = ""; my $wheat = ""; @@ -63,9 +69,9 @@ sub finish_hunk { } while (<STDIN>) { - ++$lineno; + ++$lineno if $line_number_kind eq "diff"; $original .= $_; - if (m{^diff}) { + if (m{^diff} || (not defined $state and m{^---})) { finish_hunk(); $state = "diff header"; $diff_header = ""; @@ -74,7 +80,8 @@ while (<STDIN>) { $signature .= $_ if $print_remaining; next; } - if (m{^@@ -\d+(?:,(\d)+)? \+\d+(?:,\d+)? @@}) { + if (m{^@@ -\d+(?:,(\d)+)? \+(\d+)(?:,\d+)? @@}) { + $lineno = $2 - 1 if $line_number_kind eq "new-file"; $hunk_remaining_lines = $1 or 1; finish_hunk(); $state = "diff hunk"; @@ -96,8 +103,11 @@ while (<STDIN>) { $signature .= $_ if $print_remaining; next; } + ++$lineno if $line_number_kind eq "new-file" && m{^[ +]}; --$hunk_remaining_lines if m{^[ -]}; - my $include = m{^ } || ($lineno >= $min_line && $lineno <= $max_line); + my $include = m{^ } || + ($lineno >= $min_line && $lineno <= $max_line) || + ($line_number_kind eq "new-file" && m{^-} && $lineno == $min_line - 1); if ($include) { $hunk_wheat .= $_; if ($print_remaining) { diff --git a/src/main.cc b/src/main.cc index 09c9b718..5eb30bc6 100644 --- a/src/main.cc +++ b/src/main.cc @@ -48,6 +48,7 @@ struct { 0, "» kak_* appearing in shell arguments will be added to the environment\n" "» {+U}double underline{} support\n" + "» {+u}git apply{} can stage/revert selected changes to current buffer\n" }, { 20240518, "» Fix tests failing on some platforms\n" |
