From ca0a28d52d43609cd6cabf5af085260032e5afa9 Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Thu, 17 Jan 2019 11:51:34 +0000 Subject: view: wire up diff and vcs viewers with solver --- lib/PublicInbox/ViewDiff.pm | 147 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 147 insertions(+) create mode 100644 lib/PublicInbox/ViewDiff.pm (limited to 'lib/PublicInbox/ViewDiff.pm') diff --git a/lib/PublicInbox/ViewDiff.pm b/lib/PublicInbox/ViewDiff.pm new file mode 100644 index 00000000..ee450fa7 --- /dev/null +++ b/lib/PublicInbox/ViewDiff.pm @@ -0,0 +1,147 @@ +# Copyright (C) 2019 all contributors +# License: AGPL-3.0+ +# +# used by PublicInbox::View +package PublicInbox::ViewDiff; +use strict; +use warnings; +use base qw(Exporter); +our @EXPORT_OK = qw(flush_diff); + +use PublicInbox::Hval qw(ascii_html); +use PublicInbox::Git qw(git_unquote); + +sub DSTATE_INIT () { 0 } +sub DSTATE_STAT () { 1 } # TODO +sub DSTATE_HEAD () { 2 } # /^diff --git /, /^index /, /^--- /, /^\+\+\+ / +sub DSTATE_HUNK () { 3 } # /^@@ / +sub DSTATE_CTX () { 4 } # /^ / +sub DSTATE_ADD () { 5 } # /^\+/ +sub DSTATE_DEL () { 6 } # /^\-/ + +my $OID_NULL = '0{7,40}'; +my $OID_BLOB = '[a-f0-9]{7,40}'; +my $PATH_A = '"?a/.+|/dev/null'; +my $PATH_B = '"?b/.+|/dev/null'; + +sub to_html ($$) { + $_[0]->linkify_1($_[1]); + $_[0]->linkify_2(ascii_html($_[1])); +} + +# link to line numbers in blobs +sub diff_hunk ($$$$) { + my ($dctx, $spfx, $ca, $cb) = @_; + my $oid_a = $dctx->{oid_a}; + my $oid_b = $dctx->{oid_b}; + + (defined($oid_a) && defined($oid_b)) or return "@@ $ca $cb @@"; + + my ($n) = ($ca =~ /^-(\d+)/); + $n = defined($n) ? do { ++$n; "#n$n" } : ''; + + my $rv = qq(@@ $ca); + + ($n) = ($cb =~ /^\+(\d+)/); + $n = defined($n) ? do { ++$n; "#n$n" } : ''; + + $rv .= qq( $cb @@); +} + +sub flush_diff ($$$$) { + my ($dst, $spfx, $linkify, $diff) = @_; + my $state = DSTATE_INIT; + my $dctx; # {}, keys: oid_a, oid_b, path_a, path_b + + foreach my $s (@$diff) { + if ($s =~ /^ /) { + if ($state == DSTATE_HUNK || $state == DSTATE_ADD || + $state == DSTATE_DEL || $state == DSTATE_HEAD) { + $$dst .= ""; + $state = DSTATE_CTX; + } + $$dst .= to_html($linkify, $s); + } elsif ($s =~ /^-- $/) { # email signature begins + if ($state != DSTATE_INIT) { + $state = DSTATE_INIT; + $$dst .= ''; + } + $$dst .= $s; + } elsif ($s =~ m!^diff --git ($PATH_A) ($PATH_B)$!x) { + if ($state != DSTATE_HEAD) { + my ($pa, $pb) = ($1, $2); + $$dst .= '' if $state != DSTATE_INIT; + $$dst .= ""; + $state = DSTATE_HEAD; + $pa = (split('/', git_unquote($pa), 2))[1]; + $pb = (split('/', git_unquote($pb), 2))[1]; + $dctx = { path_a => $pa, path_b => $pb }; + } + $$dst .= to_html($linkify, $s); + } elsif ($s =~ s/^(index $OID_NULL\.\.)($OID_BLOB)\b//o) { + $$dst .= qq($1$2); + $$dst .= to_html($linkify, $s) ; + } elsif ($s =~ s/^index ($OID_NULL)(\.\.$OID_BLOB)\b//o) { + $$dst .= 'index '; + $$dst .= qq($1$2); + $$dst .= to_html($linkify, $s); + } elsif ($s =~ /^index ($OID_BLOB)\.\.($OID_BLOB)/o) { + $dctx->{oid_a} = $1; + $dctx->{oid_b} = $2; + $$dst .= to_html($linkify, $s); + } elsif ($s =~ s/^@@ (\S+) (\S+) @@//) { + my ($ca, $cb) = ($1, $2); + if ($state == DSTATE_HEAD || $state == DSTATE_CTX || + $state == DSTATE_ADD || $state == DSTATE_DEL) { + $$dst .= ""; + $state = DSTATE_HUNK; + $$dst .= diff_hunk($dctx, $spfx, $ca, $cb); + } else { + $$dst .= to_html($linkify, "@@ $ca $cb @@"); + } + $$dst .= to_html($linkify, $s); + } elsif ($s =~ m!^--- $PATH_A!) { + if ($state == DSTATE_INIT) { # color only (no oid link) + $state = DSTATE_HEAD; + $$dst .= ""; + } + $$dst .= to_html($linkify, $s); + } elsif ($s =~ m!^\+{3} $PATH_B!) { + if ($state == DSTATE_INIT) { # color only (no oid link) + $state = DSTATE_HEAD; + $$dst .= ""; + } + $$dst .= to_html($linkify, $s); + } elsif ($s =~ /^\+/) { + if ($state != DSTATE_ADD && $state != DSTATE_INIT) { + $$dst .= ""; + $state = DSTATE_ADD; + } + $$dst .= to_html($linkify, $s); + } elsif ($s =~ /^-/) { + if ($state != DSTATE_DEL && $state != DSTATE_INIT) { + $$dst .= ""; + $state = DSTATE_DEL; + } + $$dst .= to_html($linkify, $s); + # ignore the following lines in headers: + } elsif ($s =~ /^(?:dis)similarity index/ || + $s =~ /^(?:old|new) mode/ || + $s =~ /^(?:deleted|new) file mode/ || + $s =~ /^(?:copy|rename) (?:from|to) / || + $s =~ /^(?:dis)?similarity index /) { + $$dst .= to_html($linkify, $s); + } else { + if ($state != DSTATE_INIT) { + $$dst .= ''; + $state = DSTATE_INIT; + } + $$dst .= to_html($linkify, $s); + } + } + @$diff = (); + $$dst .= '' if $state != DSTATE_INIT; + undef; +} + +1; -- cgit v1.2.3-24-ge0c7