From 337da8d2b0b8a8a86f0aa969bba2756126d6c090 Mon Sep 17 00:00:00 2001 From: Jakub Narebski Date: Mon, 27 Feb 2012 02:55:19 +0100 Subject: [PATCH] gitweb: Introduce esc_html_match_hl and esc_html_hl_regions The esc_html_match_hl() subroutine added in this commit will be used to highlight *all* matches of given regexp, using 'match' class. Ultimately it is to be used in all match highlighting, starting with project search, which does not have it yet. It uses the esc_html_hl_regions() subroutine, which is meant to highlight in a given string a list of regions (given as a list of [ beg, end ] pairs of positions in string), using HTML element with given class. It could probably be used in other places that do highlighting of part of ready line, like highlighting of changes in a diff (diff refinement highlighting). Implementation and enhancement notes: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * Currently esc_html_hl_regions() subroutine doesn't accept any parameters, like esc_html() does. We might want for example to pass nbsp=>1 to it. It can easily be done with the following code: my %opts = grep { ref($_) ne "ARRAY" } @sel; @sel = grep { ref($_) eq "ARRAY" } @sel; This allow adding parameters after or before regions, e.g.: esc_html_hl_regions("foo bar", "mark", [ 0, 3 ], -nbsp => 1); * esc_html_hl_regions() escapes like esc_html(); if we wanted to highlight with esc_path(), we could pass subroutine reference to now named esc_gen_hl_regions(). esc_html_hl_regions("foo bar", "mark", \&esc_path, [ 0, 3 ]); Note that this way we can handle -nbsp=>1 case automatically, e.g. esc_html_hl_regions("foo bar", "mark", sub { esc_html(@_, -nbsp=>1) }, [ 0, 3 ]); * Alternate solution for highlighting region of a string would be to use the idea that strings are to be HTML-escaped, and references to scalars are HTML (like in the idea for generic committags). This would require modifying gitweb code or esc_html to get list of fragments, e.g.: esc_html(\'', 'foo', \'', ' bar', { -nbsp => 1 }); or esc_html([\'', 'foo', \'', ' bar'], -nbsp=>1); esc_html_match_hl() could be then simple wrapper around "match formatter", e.g. esc_html([ render_match_hl($str, $regexp) ], -nbsp=>1); Signed-off-by: Jakub Narebski Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 568b0775f..f8c5b6a8b 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -1715,6 +1715,47 @@ sub chop_and_escape_str { } } +# Highlight selected fragments of string, using given CSS class, +# and escape HTML. It is assumed that fragments do not overlap. +# Regions are passed as list of pairs (array references). +# +# Example: esc_html_hl_regions("foobar", "mark", [ 0, 3 ]) returns +# 'foobar' +sub esc_html_hl_regions { + my ($str, $css_class, @sel) = @_; + return esc_html($str) unless @sel; + + my $out = ''; + my $pos = 0; + + for my $s (@sel) { + $out .= esc_html(substr($str, $pos, $s->[0] - $pos)) + if ($s->[0] - $pos > 0); + $out .= $cgi->span({-class => $css_class}, + esc_html(substr($str, $s->[0], $s->[1] - $s->[0]))); + + $pos = $s->[1]; + } + $out .= esc_html(substr($str, $pos)) + if ($pos < length($str)); + + return $out; +} + +# highlight match (if any), and escape HTML +sub esc_html_match_hl { + my ($str, $regexp) = @_; + return esc_html($str) unless defined $regexp; + + my @matches; + while ($str =~ /$regexp/g) { + push @matches, [$-[0], $+[0]]; + } + return esc_html($str) unless @matches; + + return esc_html_hl_regions($str, 'match', @matches); +} + ## ---------------------------------------------------------------------- ## functions returning short strings -- 2.30.2