diff options
author | thing1 <thing1@seacrossedlovers.xyz> | 2025-01-28 09:14:32 +0000 |
---|---|---|
committer | thing1 <thing1@seacrossedlovers.xyz> | 2025-01-28 09:14:32 +0000 |
commit | 904cec3c4a329cf89fc3219d359239910d61f3f6 (patch) | |
tree | 8d113899921dfbaca0e77c49ab5fc827362d1091 /autoload/filetype/diff-parse.pl |
Diffstat (limited to 'autoload/filetype/diff-parse.pl')
-rw-r--r-- | autoload/filetype/diff-parse.pl | 143 |
1 files changed, 143 insertions, 0 deletions
diff --git a/autoload/filetype/diff-parse.pl b/autoload/filetype/diff-parse.pl new file mode 100644 index 0000000..203e9b1 --- /dev/null +++ b/autoload/filetype/diff-parse.pl @@ -0,0 +1,143 @@ +#!/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 $in_file; +our $in_file_line; +our $version = "+"; + +eval $begin if defined $begin; + +$in_file = "$directory/$in_file" if defined $in_file; + +# Outputs +our $diff_line = 0; +our $commit; +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>) { + $diff_line++; + s/^(> )*//g; + $diff_line_text = $_; + if (m{^commit (\w+)}) { + $commit = $1; + next; + } + 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 (defined $in_file and defined $file and $file eq $in_file) { + if (defined $in_file_line and defined $file_line and $file_line >= $in_file_line) { + last; + } + } +} +if (not defined $file) { + $file = ($fallback_file or $other_file); +} +if (not defined $file) { + fail "missing diff header"; +} + +eval $end if defined $end; |