summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohannes Altmanninger <aclopte@gmail.com>2024-02-03 00:26:55 +0100
committerMaxime Coste <mawww@kakoune.org>2024-02-05 21:42:02 +1100
commit36efbf4cbfe70c7fb8ca8ac6171cd5d8b134fdc0 (patch)
tree00256a5903208e6be492b812dcd1f6651e54394f
parentce7ceb1cf000aeef1c3f371d33df3c5660e16a6d (diff)
rc filetype diff: extract diff parsing from diff-jump
Most diff consumers we've written only care about the "final" state after parsing through a diff. Let's extract the diff parsing part, for reuse in several new commands. In future we should try to use this (or better, a diff-parsing library) for patch-range.pl. We'd add a callback argument that is invoked once perl hunk (or line). Unfortunately I haven't found that library for Perl yet.
-rwxr-xr-xrc/filetype/diff-parse.pl127
-rw-r--r--rc/filetype/diff.kak154
2 files changed, 179 insertions, 102 deletions
diff --git a/rc/filetype/diff-parse.pl b/rc/filetype/diff-parse.pl
new file mode 100755
index 00000000..f7858eb7
--- /dev/null
+++ b/rc/filetype/diff-parse.pl
@@ -0,0 +1,127 @@
+#!/usr/bin/env perl
+
+use warnings;
+
+sub quote {
+ my $token = shift;
+ $token =~ s/'/''/g;
+ return "'$token'";
+}
+sub fail {
+ my $reason = shift;
+ print "set-register e fail " . quote("diff-parse.pl: $reason");
+ exit;
+}
+
+my $begin;
+my $end;
+
+while (defined $ARGV[0]) {
+ if ($ARGV[0] eq "--") {
+ shift;
+ last;
+ }
+ if ($ARGV[0] =~ m{^(BEGIN|END)$}) {
+ if (not defined $ARGV[1]) {
+ fail "missing argument to $ARGV[0]";
+ }
+ if ($ARGV[0] eq "BEGIN") {
+ $begin = $ARGV[1];
+ } else {
+ $end = $ARGV[1];
+ }
+ shift, shift;
+ next;
+ }
+ fail "unknown argument: $ARGV[0]";
+}
+
+# Inputs
+our $directory = $ENV{PWD};
+our $strip;
+our $version = "+";
+
+eval $begin if defined $begin;
+
+# Outputs
+our $file;
+our $file_line;
+our $diff_line_text;
+
+my $other_version;
+if ($version eq "+") {
+ $other_version = "-";
+} else {
+ $other_version = "+";
+}
+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;
+ my $f = shift;
+
+ my $effective_strip;
+ if (defined $strip) {
+ $effective_strip = $strip;
+ } else {
+ # A "diff -r" or "git diff" adds "diff" lines to
+ # the output. If no such line is present, we have
+ # a plain diff between files (not directories), so
+ # there should be no need to strip the directory.
+ $effective_strip = $is_recursive_diff ? 1 : 0;
+ }
+
+ if ($f !~ m{^/}) {
+ $f =~ s,^([^/]+/+){$effective_strip},, or fail "directory prefix underflow";
+ $f = "$directory/$f";
+ }
+ return $f;
+}
+
+while (<STDIN>) {
+ s/^(> )*//g;
+ $diff_line_text = $_;
+ if (m{^diff\b}) {
+ $state = "header";
+ $is_recursive_diff = 1;
+ if (m{^diff -\S* (\S+) (\S+)$}) {
+ $fallback_file = strip $is_recursive_diff, ($version eq "+" ? $2 : $1);
+ }
+ next;
+ }
+ if ($state eq "header") {
+ if (m{^[$version]{3} ([^\t\n]+)}) {
+ $file = strip $is_recursive_diff, $1;
+ next;
+ }
+ if (m{^[$other_version]{3} ([^\t\n]+)}) {
+ $other_file = strip $is_recursive_diff, $1;
+ next;
+ }
+ }
+ if (m{^@@ -(\d+)(?:,\d+)? \+(\d+)(?:,\d+)? @@}) {
+ $state = "contents";
+ $file_line = ($version eq "+" ? $2 : $1) - 1;
+ $other_file_line = ($version eq "+" ? $1 : $2) - 1;
+ } else {
+ my $iscontext = m{^[ ]};
+ if (m{^[ $version]}) {
+ $file_line++ if defined $file_line;
+ }
+ if (m{^[ $other_version]}) {
+ $other_file_line++ if defined $other_file_line;
+ }
+ }
+}
+if (not defined $file) {
+ $file = ($fallback_file or $other_file);
+}
+if (not defined $file) {
+ fail "missing diff header";
+}
+
+eval $end if defined $end;
diff --git a/rc/filetype/diff.kak b/rc/filetype/diff.kak
index 6d7ba695..530bf308 100644
--- a/rc/filetype/diff.kak
+++ b/rc/filetype/diff.kak
@@ -29,7 +29,7 @@ define-command diff-jump -params .. -docstring %{
- jump to the old file instead of the new file
-<num> strip <num> leading directory components, like -p<num> in patch(1). Defaults to 1 if there is a 'diff' line (as printed by 'diff -r'), or 0 otherwise.
} %{
- evaluate-commands -draft -save-regs ac| %{
+ evaluate-commands -draft -save-regs c| %{
# Save the column because we will move the cursor.
set-register c %val{cursor_column}
# If there is a "diff" line, we don't need to look further back.
@@ -41,120 +41,70 @@ define-command diff-jump -params .. -docstring %{
# or content.
execute-keys Gk
}
- set-register a %arg{@}
- set-register | %{
- [ -n "$kak_reg_a" ] && eval set -- "$kak_quoted_reg_a"
- cmd=$(column=$kak_reg_c perl -we '
- sub quote {
- $SQ = "'\''";
- $token = shift;
- $token =~ s/$SQ/$SQ$SQ/g;
- return "$SQ$token$SQ";
- }
- sub fail {
- $reason = shift;
- print "fail " . quote("diff-jump: $reason");
- exit;
- }
- $version = "+", $other_version = "-";
- $strip = undef;
- $directory = $ENV{PWD};
- $seen_ddash = 0;
- foreach (@ARGV) {
- if ($seen_ddash or !m{^-}) {
- $directory = $_;
- } elsif ($_ eq "-") {
- $version = "-", $other_version = "+";
- } elsif (m{^-(\d+)$}) {
- $strip = $1;
- } elsif ($_ eq "--") {
- $seen_ddash = 1;
- } else {
- fail "unknown option: $_";
- }
+ diff-parse BEGIN %{
+ my $seen_ddash = 0;
+ foreach (@ARGV) {
+ if ($seen_ddash or !m{^-}) {
+ $directory = $_;
+ } elsif ($_ eq "-") {
+ $version = "-", $other_version = "+";
+ } elsif (m{^-(\d+)$}) {
+ $strip = $1;
+ } elsif ($_ eq "--") {
+ $seen_ddash = 1;
+ } else {
+ fail "unknown option: $_";
}
- $have_diff_line = 0;
- $state = "header";
- while (<STDIN>) {
- s/^(> )*//g;
- $last_line = $_;
- if (m{^diff\b}) {
- $state = "header";
- $have_diff_line = 1;
- if (m{^diff -\S* (\S+) (\S+)$}) {
- $fallback_file = $version eq "+" ? $2 : $1;
- }
- next;
- }
- if ($state eq "header") {
- if (m{^[$version]{3} ([^\t\n]+)}) {
- $file = $1;
- next;
- }
- if (m{^[$other_version]{3} ([^\t\n]+)}) {
- $fallback_file = $1;
+ }
+ } END %exp{
+ my $file_column;
+ if (not defined $file_line) {
+ $file_line = "";
+ $file_column = "";
+ } else {
+ my $diff_column = %reg{c};
+ $file_column = $diff_column - 1; # Account for [ +-] diff prefix.
+ # If the cursor was on a hunk header, go to the section header if possible.
+ if ($diff_line_text =~ m{^(@@ -\d+(?:,\d+)? \+\d+(?:,\d+) @@ )([^\n]*)}) {
+ my $hunk_header_prefix = $1;
+ my $hunk_header_from_userdiff = $2;
+ open FILE, "<", $file or fail "failed to open file: $!: $file";
+ my @lines = <FILE>;
+ for (my $i = $file_line - 1; $i >= 0 and $i < scalar @lines; $i--) {
+ if ($lines[$i] !~ m{\Q$hunk_header_from_userdiff}) {
next;
}
- }
- if (m{^@@ -(\d+)(?:,\d+)? \+(\d+)(?:,\d+)? @@}) {
- $state = "contents";
- $line = ($version eq "+" ? $2 : $1) - 1;
- } elsif (m{^[ $version]}) {
- $line++ if defined $line;
- }
- }
- if (not defined $file) {
- $file = $fallback_file;
- }
- if (not defined $file) {
- fail "missing diff header";
- }
- if (not defined $strip) {
- # A "diff -r" or "git diff" adds "diff" lines to
- # the output. If no such line is present, we have
- # a plain diff between files (not directories), so
- # there should be no need to strip the directory.
- $strip = $have_diff_line ? 1 : 0;
- }
- if ($file !~ m{^/}) {
- $file =~ s,^([^/]+/+){$strip},, or fail "directory prefix underflow";
- $file = "$directory/$file";
- }
-
- if (defined $line) {
- $column = $ENV{column} - 1; # Account for [ +-] diff prefix.
- # If the cursor was on a hunk header, go to the section header if possible.
- if ($last_line =~ m{^(@@ -\d+(?:,\d+)? \+\d+(?:,\d+) @@ )([^\n]*)}) {
- $hunk_header_prefix = $1;
- $hunk_header_from_userdiff = $2;
- open FILE, "<", $file or fail "failed to open file: $!: $file";
- @lines = <FILE>;
- for (my $i = $line - 1; $i >= 0 && $i < scalar @lines; $i--) {
- if ($lines[$i] !~ m{\Q$hunk_header_from_userdiff}) {
- next;
- }
- $line = $i + 1;
- # Re-add 1 because the @@ line does not have a [ +-] diff prefix.
- $column = $column + 1 - length $hunk_header_prefix;
- last;
- }
+ $file_line = $i + 1;
+ # Re-add 1 because the @@ line does not have a [ +-] diff prefix.
+ $file_column = $diff_column + 1 - length $hunk_header_prefix;
+ last;
}
}
-
- printf "edit -existing -- %s $line $column", quote($file);
- ' -- "$@")
- echo "set-register c $cmd" >"$kak_command_fifo"
- }
- execute-keys <a-|><ret>
+ }
+ printf "set-register c %%s $file_line $file_column\n", quote($file);
+ } -- %arg{@}
evaluate-commands -client %val{client} %{
evaluate-commands -try-client %opt{jumpclient} %{
- %reg{c}
+ edit -existing -- %reg{c}
}
}
}
}
complete-command diff-jump file
+define-command -hidden diff-parse -params 2.. %{
+ evaluate-commands -save-regs ae %{
+ set-register a %arg{@}
+ set-register e nop
+ set-register | %{
+ eval set -- "$kak_quoted_reg_a"
+ perl "${kak_runtime}/rc/filetype/diff-parse.pl" "$@" >"$kak_command_fifo"
+ }
+ execute-keys <a-|><ret>
+ %reg{e}
+ }
+}
+
ยง
define-command \