Merge remote-tracking branch 'krobelus/patch-selected-lines'

This commit is contained in:
Maxime Coste 2023-11-05 11:09:41 +11:00
commit c0788f370b
3 changed files with 168 additions and 1 deletions

View File

@ -61,6 +61,7 @@ define-command -params 1.. \
All the optional arguments are forwarded to the git utility
Available commands:
add
apply (alias for "patch git apply")
rm
reset
blame
@ -81,11 +82,12 @@ define-command -params 1.. \
grep
} -shell-script-candidates %{
if [ $kak_token_to_complete -eq 0 ]; then
printf "add\nrm\nreset\nblame\ncommit\ncheckout\ndiff\nhide-blame\nhide-diff\nlog\nnext-hunk\nprev-hunk\nshow\nshow-branch\nshow-diff\ninit\nstatus\nupdate-diff\ngrep\n"
printf "add\napply\nrm\nreset\nblame\ncommit\ncheckout\ndiff\nhide-blame\nhide-diff\nlog\nnext-hunk\nprev-hunk\nshow\nshow-branch\nshow-diff\ninit\nstatus\nupdate-diff\ngrep\n"
else
case "$1" in
commit) printf -- "--amend\n--no-edit\n--all\n--reset-author\n--fixup\n--squash\n"; git ls-files -m ;;
add) git ls-files -dmo --exclude-standard ;;
apply) printf -- "--reverse\n--cached\n--index\n" ;;
rm|grep) git ls-files -c ;;
esac
fi
@ -326,6 +328,12 @@ define-command -params 1.. \
}
case "$1" in
apply)
shift
enquoted="$(printf '"%s" ' "$@")"
echo "require-module patch"
echo "patch git apply $enquoted"
;;
show|show-branch|log|diff|status)
show_git_cmd_output "$@"
;;

93
rc/tools/patch-range.pl Executable file
View File

@ -0,0 +1,93 @@
#!/usr/bin/env perl
use strict;
use warnings;
my $min_line = $ARGV[0];
shift @ARGV;
my $max_line = $ARGV[0];
shift @ARGV;
my $patch_cmd;
if (defined $ARGV[0] and $ARGV[0] =~ m{^[^-]}) {
$patch_cmd = "@ARGV";
} else {
$patch_cmd = "patch @ARGV";
}
my $reverse = grep /^(--reverse|-R)$/, @ARGV;
my $lineno = 0;
my $original = "";
my $wheat = "";
my $chaff = "";
my $state = undef;
my $hunk_wheat = undef;
my $hunk_chaff = undef;
my $hunk_header = undef;
sub compute_hunk_header {
my $original_header = shift;
my $hunk = shift;
my $old_lines = 0;
my $new_lines = 0;
for (split /\n/, $hunk) {
$old_lines++ if m{^[ -]};
$new_lines++ if m{^[ +]};
}
my $updated_header = $original_header =~ s/^@@ -(\d+),\d+\s+\+(\d+),\d+ @@(.*)/@@ -$1,$old_lines +$2,$new_lines @\@$3/mr;
return $updated_header;
}
sub finish_hunk {
return unless defined $hunk_header;
if ($hunk_wheat =~ m{^[-+]}m) {
$wheat .= (compute_hunk_header $hunk_header, $hunk_wheat). $hunk_wheat;
}
$chaff .= (compute_hunk_header $hunk_header, $hunk_chaff) . $hunk_chaff;
$hunk_header = undef;
}
while (<STDIN>) {
++$lineno;
$original .= $_;
if (m{^diff}) {
finish_hunk();
$state = "diff header";
}
if (m{^@@}) {
finish_hunk();
$state = "diff hunk";
$hunk_header = $_;
$hunk_wheat = "";
$hunk_chaff = "";
next;
}
if ($state eq "diff header") {
$wheat .= $_;
$chaff .= $_;
next;
}
my $include = m{^ } || ($lineno >= $min_line && $lineno <= $max_line);
if ($include) {
$hunk_wheat .= $_;
$hunk_chaff .= $_ if m{^ };
if ($reverse ? m{^[-]} : m{^\+}) {
$hunk_chaff .= " " . substr $_, 1;
}
} else {
if ($reverse ? m{^\+} : m{^-}) {
$hunk_wheat .= " " . substr $_, 1;
}
$hunk_chaff .= $_;
}
}
finish_hunk();
open PATCH_COMMAND, "|-", "$patch_cmd 1>&2" or die "patch-range.pl: error running '$patch_cmd': $!";
print PATCH_COMMAND $wheat;
if (not close PATCH_COMMAND) {
print $original;
print STDERR "patch-range.pl: error running:\n" . "\$ $patch_cmd << EOF\n$wheat" . "EOF\n";
exit 1;
}
print $chaff;

66
rc/tools/patch.kak Normal file
View File

@ -0,0 +1,66 @@
define-command patch -params .. -docstring %{
patch [<arguments>]: apply selections in diff to a file
Given some selections within a unified diff, apply the changed lines in
each selection by piping them to "patch <arguments> 1>&2"
(or "<arguments> 1>&2" if <arguments> starts with a non-option argument).
If successful, the in-buffer diff will be updated to reflect the applied
changes.
For selections that contain no newline, the entire enclosing diff hunk
is applied (unless the cursor is inside a diff header, in which case
the entire diff is applied).
To revert changes, <arguments> must contain "--reverse" or "-R".
} %exp{
evaluate-commands -draft -itersel -save-regs aefs|^ %%{
set-register f %val{source}
%{
try %{
execute-keys <a-k>\n<ret>
} catch %{
# The selection contains no newline.
execute-keys -save-regs '' Z
execute-keys <a-l><semicolon><a-?>^diff<ret>
try %{
execute-keys <a-k>^@@<ret>
# If the cursor is in a diff hunk, stage the entire hunk.
execute-keys z
execute-keys /.*?(?:(?=\n@@)|(?=\ndiff)|(?=\n\n)|\z)<ret>x<semicolon><a-?>^@@<ret>
} catch %{
# If the cursor is in a diff header, stage the entire diff.
execute-keys <a-semicolon>?.*?(?:(?=\ndiff)|(?=\n\n)|\z)<ret>
}
}
# We want to apply only the selected lines. Remember them.
execute-keys <a-:>
set-register s %val{selection_desc}
# Select forward until the end of the last hunk.
execute-keys H?.*?(?:(?=\n@@)|(?=\ndiff)|(?=\n\n)|\z)<ret>x
# Select backward to the beginning of the first hunk's diff header.
execute-keys <a-semicolon><a-L><a-?>^diff<ret>
# Move cursor to the beginning so we know the diff's offset within the buffer.
execute-keys <a-:><a-semicolon>
set-register a %arg{@}
set-register e nop
set-register | %{
# The selected range to apply.
IFS=' .,' read min_line _ max_line _ <<-EOF
$kak_reg_s
EOF
min_line=$((min_line - kak_cursor_line + 1))
max_line=$((max_line - kak_cursor_line + 1))
# Since registers are never empty, we get an empty arg even if
# there were no args. This does no harm because we pass it to
# a shell where it expands to nothing.
eval set -- $kak_quoted_reg_a
"${kak_reg_f%/*}/patch-range.pl" $min_line $max_line "$@" ||
echo >$kak_command_fifo "set-register e fail 'patch: failed to apply selections, see *debug* buffer'"
}
execute-keys |<ret>
%reg{e}
}
}
}
provide-module patch %§§