From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on dcvr.yhbt.net X-Spam-Level: X-Spam-ASN: X-Spam-Status: No, score=-4.0 required=3.0 tests=ALL_TRUSTED,BAYES_00 shortcircuit=no autolearn=ham autolearn_force=no version=3.4.2 Received: from localhost (dcvr.yhbt.net [127.0.0.1]) by dcvr.yhbt.net (Postfix) with ESMTP id 35C881F9F3 for ; Mon, 27 Sep 2021 04:59:32 +0000 (UTC) From: Eric Wong To: meta@public-inbox.org Subject: [PATCH 2/2] lei rediff: add --drq and --dequote-only Date: Mon, 27 Sep 2021 04:59:31 +0000 Message-Id: <20210927045931.18609-3-e@80x24.org> In-Reply-To: <20210927045931.18609-1-e@80x24.org> References: <20210927045931.18609-1-e@80x24.org> MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit List-Id: More switches which can be useful for users who pipe from text editors. --drq can be helpful while writing patch review email replies, and perhaps --dequote-only, too. --- Documentation/lei-rediff.pod | 27 +++++++++++++++++++++ lib/PublicInbox/LEI.pm | 2 +- lib/PublicInbox/LeiRediff.pm | 47 ++++++++++++++++++++++++++++++++++++ t/solver_git.t | 40 ++++++++++++++++++++++++++++-- 4 files changed, 113 insertions(+), 3 deletions(-) diff --git a/Documentation/lei-rediff.pod b/Documentation/lei-rediff.pod index f34e946ffe52..e711de97015c 100644 --- a/Documentation/lei-rediff.pod +++ b/Documentation/lei-rediff.pod @@ -38,6 +38,33 @@ to C before piping it to L. The output of C is compatible with C if its input was a patch email. +=item --drq[=COUNT] + +De-Re-Quote. De-quote the input and re-quotes (the output). +Removes COUNT levels of C > email reply prefixes and +re-adds them upon regenerating the diff. + +This switch is intended as a convenience for running inside a +pipe-capable text editor when writing replies to a patch email. +Note: this may over-add C > prefixes if some input lines +are missing C > prefixes. + +COUNT is 1 if unspecified; in other words, C<--drq=1> and +C<--drq> are equivalent. + +It implies L unless L is specified +since text editors tend to combine stderr with stdout. + +=item --dequote-only[=COUNT] + +Like L, but does not re-add quote prefixes to the output. + +This can be useful for feeding a hunk to L +or L while writing a reply or further processing +by another diff viewer. + +Unlike L, it does NOT implies L. + =item --git-dir=DIR Specify an additional .git/ directory to scan. This option may be diff --git a/lib/PublicInbox/LEI.pm b/lib/PublicInbox/LEI.pm index 8faf74a29466..b8159cba2922 100644 --- a/lib/PublicInbox/LEI.pm +++ b/lib/PublicInbox/LEI.pm @@ -201,7 +201,7 @@ our %CMD = ( # sorted in order of importance/use: 'rediff' => [ '--stdin|LOCATION...', 'regenerate a diff with different options', 'stdin|', # /|\z/ must be first for lone dash - qw(git-dir=s@ cwd! verbose|v+ color:s no-color), + qw(git-dir=s@ cwd! verbose|v+ color:s no-color drq:1 dequote-only:1), @diff_opt, @lxs_opt, @net_opt, @c_opt ], 'add-external' => [ 'LOCATION', diff --git a/lib/PublicInbox/LeiRediff.pm b/lib/PublicInbox/LeiRediff.pm index f6960560f1a2..1e95e55ac1cc 100644 --- a/lib/PublicInbox/LeiRediff.pm +++ b/lib/PublicInbox/LeiRediff.pm @@ -134,6 +134,27 @@ EOM $pid = spawn(\@cmd, $lei->{env}, { 2 => $lei->{2}, 1 => $lei->{1} }); waitpid($pid, 0); $lei->child_error($?) if $?; # for git diff --exit-code + undef; +} + +sub wait_requote ($$$) { # OnDestroy callback + my ($lei, $pid, $old_1) = @_; + $lei->{1} = $old_1; # closes stdin of `perl -pE 's/^/> /'` + waitpid($pid, 0) == $pid or die "BUG(?) waitpid: \$!=$! \$?=$?"; + $lei->child_error($?) if $?; +} + +sub requote ($$) { + my ($lei, $pfx) = @_; + pipe(my($r, $w)) or die "pipe: $!"; + my $rdr = { 0 => $r, 1 => $lei->{1}, 2 => $lei->{2} }; + # $^X (perl) is overkill, but maybe there's a weird system w/o sed + my $pid = spawn([$^X, '-pE', "s/^/$pfx/"], $lei->{env}, $rdr); + my $old_1 = $lei->{1}; + $w->autoflush(1); + binmode $w, ':utf8'; + $lei->{1} = $w; + PublicInbox::OnDestroy->new(\&wait_requote, $lei, $pid, $old_1); } sub extract_oids { # Eml each_part callback @@ -142,6 +163,10 @@ sub extract_oids { # Eml each_part callback $self->{lei}->out($p->header_obj->as_string, "\n"); my ($s, undef) = msg_part_text($p, $p->content_type || 'text/plain'); defined $s or return; + my $rq; + if ($self->{dqre} && $s =~ s/$self->{dqre}//g) { # '> ' prefix(es) + $rq = requote($self->{lei}, $1) if $self->{lei}->{opt}->{drq}; + } my @top = split($PublicInbox::ViewDiff::EXTRACT_DIFFS, $s); undef $s; my $blobs = $self->{blobs}; # blobs to resolve @@ -191,6 +216,19 @@ sub extract_oids { # Eml each_part callback $ctxq = diff_ctxq($self, $ctxq); } +# ensure dequoted parts are available for rebuilding patches: +sub dequote_add { # Eml each_part callback + my ($ary, $self) = @_; + my ($p, undef, $idx) = @$ary; + my ($s, undef) = msg_part_text($p, $p->content_type || 'text/plain'); + defined $s or return; + if ($s =~ s/$self->{dqre}//g) { # remove '> ' prefix(es) + substr($s, 0, 0, "part-dequoted: $idx\n\n"); + utf8::encode($s); + $self->{tmp_sto}->add_eml(PublicInbox::Eml->new(\$s)); + } +} + sub input_eml_cb { # callback for all emails my ($self, $eml) = @_; { @@ -200,6 +238,7 @@ sub input_eml_cb { # callback for all emails warn @_; }; $self->{tmp_sto}->add_eml($eml); + $eml->each_part(\&dequote_add, $self) if $self->{dqre}; $self->{tmp_sto}->done; } $eml->each_part(\&extract_oids, $self, 1); @@ -207,6 +246,10 @@ sub input_eml_cb { # callback for all emails sub lei_rediff { my ($lei, @inputs) = @_; + ($lei->{opt}->{drq} && $lei->{opt}->{'dequote-only'}) and return + $lei->fail('--drq and --dequote-only are mutually exclusive'); + ($lei->{opt}->{drq} && !$lei->{opt}->{verbose}) and + $lei->{opt}->{quiet} //= 1; $lei->_lei_store(1)->write_prepare($lei); $lei->{opt}->{'in-format'} //= 'eml'; # maybe it's a non-email (code) blob from a coderepo @@ -257,6 +300,10 @@ sub ipc_atfork_child { } @{$self->{lei}->{opt}->{'git-dir'}} ]; $lei->{env}->{'psgi.errors'} = $lei->{2}; # ugh... $lei->{env}->{TMPDIR} = $self->{rdtmp}->dirname; + if (my $nr = ($lei->{opt}->{drq} || $lei->{opt}->{'dequote-only'})) { + my $re = '\s*> ' x $nr; + $self->{dqre} = qr/^($re)/ms; + } undef; } diff --git a/t/solver_git.t b/t/solver_git.t index 3c0b7f6524c6..cf450e2459e5 100644 --- a/t/solver_git.t +++ b/t/solver_git.t @@ -80,12 +80,48 @@ test_lei({tmpdir => "$tmpdir/rediff"}, sub { lei_ok(qw(rediff -q -U9 t/solve/0001-simple-mod.patch)); like($lei_out, qr!^\Q+++\E b/TODO\n@@ -103,9 \+103,11 @@!sm, 'got more context with -U9'); - lei_ok(qw(rediff -q -U9 t/solve/bare.patch)); + + my (undef, $re) = split(/\n\n/, $lei_out, 2); + $re =~ s/^/> /sgm; + substr($re, 0, 0, < \$re, %$lei_opt }); my $exp = <<'EOM'; +From: me@example.com +Subject: Re: awesome advice + +EOM + like($lei_out, qr/\Q$exp\E/, '--drq preserved header'); + + # n.b. --drq can requote the attribution line ("So-and-so wrote:"), + # but it's probably not worth preventing... + + $exp = <<'EOM'; +> --- +> TODO | 2 ++ +> Ω | 5 -- +> 1 file changed, 2 insertions(+) +> +> diff --git a/TODO b/TODO +> index 605013e4904baabecd4a0a55997aebd8e8477a8f..69df7d565d49fbaaeb0a067910f03dc22cd52bd0 100644 +> --- a/TODO +> +++ b/TODO +> @@ -96,16 +96,18 @@ all need to be considered for everything we introduce) +EOM + $exp =~ s/^>$/> /sgm; # re-add trailing white space + like($lei_out, qr/\Q$exp\E/, '--drq diffstat + context'); + + lei_ok(qw(rediff -q --abbrev=40 -U9 t/solve/bare.patch)); + $exp = <<'EOM'; diff --git a/script/public-inbox-extindex b/script/public-inbox-extindex old mode 100644 new mode 100755 -index 15ac20eb..771486c4 +index 15ac20eb871bf47697377e58a27db23102a38fca..771486c425b315bae70fd8a82d62ab0331e0a827 --- a/script/public-inbox-extindex +++ b/script/public-inbox-extindex @@ -1,13 +1,12 @@