Currently it's not easy to capture an unbounded number of items in a committag phrase to hyperlink individually. For example, I would like to match and hyperlinking each bug ID individually in these situations: [#1234, #1235] Resolves-bug: 1234, 1235 bugs 1234, 1235, and 1236 Match Bugzilla bug IDs with two regexes instead of one. The first is a pre-filter that allows easy matching of multiple bug IDs and contextual queues like "bugs ___ and ___" in a phrase, and the second easily picks out the individual big IDs for hyperlinking. Signed-off-by: Marcel M. Cary <marcel@xxxxxxxxxxxxxxxx> --- gitweb/gitweb.perl | 68 +++++++++++++++++++++++++++++------------ t/t9502-gitweb-committags.sh | 69 ++++++++++++++++++++++++++++++++++++++--- 2 files changed, 112 insertions(+), 25 deletions(-) diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 2d72202..032b1c5 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -262,14 +262,31 @@ our %committags = ( 'override' => 0, 'sub' => \&hyperlink_committag, }, - # Link mentions of bug IDs to bugzilla + # Link mentions of bugs to bugzilla, allowing for separate outer + # and inner regexes (see unit test for example) 'bugzilla' => { 'options' => { - 'pattern' => qr/bug\s+(\d+)/, + 'pattern' => qr/(?i:bugs?):?\s+ + [#]?\d+(?:(?:,\s*|,?\sand\s|,?\sn?or\s|\s+) + [#]?\d+\b)*/x, + 'innerpattern' => qr/#?(\d+)/, 'url' => 'http://bugzilla.example.com/show_bug.cgi?id=', }, 'override' => 0, - 'sub' => \&hyperlink_committag, + 'sub' => sub { + my ($opts, @match) = @_; + if ($opts->{'innerpattern'}) { + my @message_fragments = (); + push_or_append_replacements(\@message_fragments, + $opts->{'innerpattern'}, + $match[0], sub { + return hyperlink_committag($opts, @_); + }); + return @message_fragments; + } else { + return hyperlink_committag(@_); + } + }, }, # Link URLs 'url' => { @@ -1626,23 +1643,10 @@ COMMITTAG: next PART; } - my $oldpos = 0; - - MATCH: - while ($fragment =~ m/$pattern/gc) { - my ($prepos, $postpos) = ($-[0], $+[0]); - my $repl = $sub->($opts, $&, $1); - $repl = "" if (!defined $repl); - - my $pre = substr($fragment, $oldpos, $prepos - $oldpos); - push_or_append(\@new_message_fragments, $pre); - push_or_append(\@new_message_fragments, $repl); - - $oldpos = $postpos; - } # end while [regexp matches] - - my $rest = substr($fragment, $oldpos); - push_or_append(\@new_message_fragments, $rest); + push_or_append_replacements(\@new_message_fragments, + $pattern, $fragment, sub { + $sub->($opts, @_); + }); } # end foreach (@message_fragments) @@ -1672,6 +1676,30 @@ sub hyperlink_committag { esc_html($match[0], -nbsp=>1)); } +# Find $pattern in string $fragment, and push_or_append the parts +# between matches and the result of calling $sub with matched text to +# $new_fragments. +sub push_or_append_replacements { + my ($new_fragments, $pattern, $fragment, $sub) = @_; + + my $oldpos = 0; + +MATCH: + while ($fragment =~ m/$pattern/gc) { + my ($prepos, $postpos) = ($-[0], $+[0]); + + my @repl = $sub->($&, $1); + + my $pre = substr($fragment, $oldpos, $prepos - $oldpos); + push_or_append($new_fragments, $pre); + push_or_append($new_fragments, @repl); + + $oldpos = $postpos; + } # end while [regexp matches] + + my $rest = substr($fragment, $oldpos); + push_or_append($new_fragments, $rest); +} sub push_or_append (\@@) { my $fragments = shift; diff --git a/t/t9502-gitweb-committags.sh b/t/t9502-gitweb-committags.sh index f86cb3d..718e763 100755 --- a/t/t9502-gitweb-committags.sh +++ b/t/t9502-gitweb-committags.sh @@ -52,7 +52,7 @@ echo '$feature{"committags"}{"override"} = 1;' >> gitweb_config.perl test_expect_success 'bugzilla: enabled' ' gitweb_run "p=.git;a=commit;h=HEAD" && grep -F -q \ - "Fixes <a class=\"text\" href=\"http://bugzilla.example.com/show_bug.cgi?id=1234\">bug 1234</a> involving" \ + "Fixes bug <a class=\"text\" href=\"http://bugzilla.example.com/show_bug.cgi?id=1234\">1234</a> involving" \ gitweb.output ' test_debug 'cat gitweb.log' @@ -62,7 +62,7 @@ git config gitweb.committag.bugzilla.url 'http://bts.example.com?bug=' test_expect_success 'bugzilla: url overridden but not permitted' ' gitweb_run "p=.git;a=commit;h=HEAD" && grep -F -q \ - "Fixes <a class=\"text\" href=\"http://bugzilla.example.com/show_bug.cgi?id=1234\">bug 1234</a> involving" \ + "Fixes bug <a class=\"text\" href=\"http://bugzilla.example.com/show_bug.cgi?id=1234\">1234</a> involving" \ gitweb.output ' test_debug 'cat gitweb.log' @@ -72,12 +72,13 @@ echo '$committags{"bugzilla"}{"override"} = 1;' >> gitweb_config.perl test_expect_success 'bugzilla: url overridden' ' gitweb_run "p=.git;a=commit;h=HEAD" && grep -F -q \ - "Fixes <a class=\"text\" href=\"http://bts.example.com?bug=1234\">bug 1234</a> involving" \ + "Fixes bug <a class=\"text\" href=\"http://bts.example.com?bug=1234\">1234</a> involving" \ gitweb.output ' test_debug 'cat gitweb.log' test_debug 'grep 1234 gitweb.output' +git config gitweb.committag.bugzilla.innerpattern '' git config gitweb.committag.bugzilla.pattern 'Fixes bug (\d+)' test_expect_success 'bugzilla: pattern overridden' ' gitweb_run "p=.git;a=commit;h=HEAD" && @@ -87,17 +88,75 @@ test_expect_success 'bugzilla: pattern overridden' ' ' test_debug 'cat gitweb.log' test_debug 'grep 1234 gitweb.output' -git config --unset gitweb.committag.bugzilla.pattern +git config --unset gitweb.committag.bugzilla.innerpattern +git config --unset gitweb.committag.bugzilla.pattern test_expect_success 'bugzilla: affects log view too' ' gitweb_run "p=.git;a=log" && grep -F -q \ - "<a class=\"text\" href=\"http://bts.example.com?bug=1234\">bug 1234</a>" \ + "<a class=\"text\" href=\"http://bts.example.com?bug=1234\">1234</a>" \ gitweb.output ' test_debug 'cat gitweb.log' test_debug 'grep 1234 gitweb.output' +echo more_bugzilla > file.txt +git add file.txt +git commit -q -F - file.txt <<END +[#123,#45] This commit fixes two bugs involving bar and baz. +END +git config gitweb.committag.bugzilla.pattern '^\[#\d+(, ?#\d+)\]' +git config gitweb.committag.bugzilla.innerpattern '#(\d+)' +git config gitweb.committag.bugzilla.url 'http://bugs/' +test_expect_success 'bugzilla: override everything, use fancier url format' ' + gitweb_run "p=.git;a=commit;h=HEAD" && + grep -F -q \ + "[<a class=\"text\" href=\"http://bugs/123\">#123</a>,<a class=\"text\" href=\"http://bugs/45\">#45</a>]" \ + gitweb.output +' +test_debug 'cat gitweb.log' +test_debug 'grep 123 gitweb.output' + +echo even_more_bugzilla > file.txt +git add file.txt +git commit -q -F - file.txt <<END +Fix memory leak in confabulator from bug 123. + +Based on history from bugs 223, 224, and 225, +fix bug 323 or 324. + +Bug: 423,424,425,426,427,428,429,430,431,432,435 +Resolves-bugs: #523 #524 +END +git config --unset gitweb.committag.bugzilla.pattern +git config --unset gitweb.committag.bugzilla.innerpattern +git config --unset gitweb.committag.bugzilla.url +gitweb_run "p=.git;a=commit;h=HEAD" +test_expect_success 'bugzilla: fancy defaults: match one bug' ' + grep -q "from bug <a[^>]*>123</a>." gitweb.output +' +test_expect_success 'bugzilla: fancy defaults: comma-separated list' ' + grep -q \ + "bugs <a[^>]*>223</a>, <a[^>]*>224</a>, and <a[^>]*>225</a>," \ + gitweb.output +' +test_expect_success 'bugzilla: fancy defaults: or-pair' ' + grep -q "bug <a[^>]*>323</a> or <a[^>]*>324</a>." \ + gitweb.output +' +test_expect_success 'bugzilla: fancy defaults: comma-separated, caps, >10' ' + grep -q \ + "Bug: <a[^>]*>423</a>,<a[^>]*>424</a>,.*,<a[^>]*>435</a>" \ + gitweb.output +' +test_expect_success 'bugzilla: fancy defaults: space-separated with hash' ' + grep -q -e \ + "-bugs: <a[^>]*>#523</a> <a[^>]*>#524</a>" \ + gitweb.output +' +test_debug 'cat gitweb.log' +test_debug 'grep 23 gitweb.output' + # ---------------------------------------------------------------------- # url linking # -- 1.6.4.4 -- To unsubscribe from this list: send the line "unsubscribe git" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html