From: Jakub Narebski <hidden> Date: 2016-06-15 22:52:56
This patch series, though independent, is best applied on top of
"[PATCH 0/2] gitweb: Project search improvements" series, or in other
words on top of 'bl/gitweb-project-filter' branch.
First two patches in this series are about speeding up project search
(and perhaps in the future also project pagination). Those two could
be squashed together.
Next two patches are about making it more visible what are we
searching for, or rather what was matched (important especially with
regexp match). The last patch is speculative patch about showing
match using shortened description.
Jakub Narebski (5):
gitweb: Option for filling only specified info in
fill_project_list_info
gitweb: Faster project search
gitweb: Highlight matched part of project name when searching
projects
gitweb: Highlight matched part of project description when searching
projects
gitweb: Highlight matched part of shortened project description
gitweb/gitweb.perl | 122 ++++++++++++++++++++++++++++++++++++++++++++++------
1 files changed, 108 insertions(+), 14 deletions(-)
--
1.7.9
From: Jakub Narebski <hidden> Date: 2016-06-15 22:52:56
Introduce project_info_needs_filling($pr, $key[, @fill_only]), which
is now used in place of simple 'defined $pr->{$key}' to check if
specific slot in project needs to be filled.
This is in preparation of future lazy filling of project info in
project search and pagination of sorted list of projects. The only
functional change is that fill_project_list_info() now checks if 'age'
is already filled before running git_get_last_activity().
Signed-off-by: Jakub Narebski <redacted>
---
This could have been squashed with the next commit, but this way it is
pure refactoring that shouldn't change gitweb behavior.
Adding project_info_needs_filling() subroutine could have been split
into separate commit, but it would be subroutine without use...
gitweb/gitweb.perl | 41 +++++++++++++++++++++++++++++++----------
1 files changed, 31 insertions(+), 10 deletions(-)
@@ -5185,35 +5185,56 @@ sub git_project_search_form {print"</div>\n";}+# entry for given $key doesn't need filling if either $key already exists+# in $project_info hash, or we are interested only in subset of keys+# and given key is not among @fill_only.+subproject_info_needs_filling{+my($project_info,$key,@fill_only)=@_;++if(!@fill_only||# we are interested in everything+grep{$keyeq$_}@fill_only){# or key is in @fill_only+# check if key is already filled+return!exists$project_info->{$key};+}+# uninteresting key, outside @fill_only+return0;+}+# fills project list info (age, description, owner, category, forks)# for each project in the list, removing invalid projects from-# returned list+# returned list, or fill only specified info (removing invalid projects+# only when filling 'age').+## NOTE: modifies $projlist, but does not remove entries from itsubfill_project_list_info{-my$projlist=shift;+my($projlist,@fill_only)=@_;my@projects;my$show_ctags=gitweb_check_feature('ctags');PROJECT:foreachmy$pr(@$projlist){-my(@activity)=git_get_last_activity($pr->{'path'});-unless(@activity){-nextPROJECT;+if(project_info_needs_filling($pr,'age',@fill_only)){+my(@activity)=git_get_last_activity($pr->{'path'});+unless(@activity){+nextPROJECT;+}+($pr->{'age'},$pr->{'age_string'})=@activity;}-($pr->{'age'},$pr->{'age_string'})=@activity;-if(!defined$pr->{'descr'}){+if(project_info_needs_filling($pr,'descr',@fill_only)){my$descr=git_get_project_description($pr->{'path'})||"";$descr=to_utf8($descr);$pr->{'descr_long'}=$descr;$pr->{'descr'}=chop_str($descr,$projects_list_description_width,5);}-if(!defined$pr->{'owner'}){+if(project_info_needs_filling($pr,'owner',@fill_only)){$pr->{'owner'}=git_get_project_owner("$pr->{'path'}")||"";}-if($show_ctags){+if($show_ctags&&+project_info_needs_filling($pr,'ctags',@fill_only)){$pr->{'ctags'}=git_get_project_ctags($pr->{'path'});}-if($projects_list_group_categories&&!defined$pr->{'category'}){+if($projects_list_group_categories&&+project_info_needs_filling($pr,'category',@fill_only)){my$cat=git_get_project_category($pr->{'path'})||$project_list_default_category;$pr->{'category'}=to_utf8($cat);
From: Jakub Narebski <hidden> Date: 2016-06-15 22:52:56
Previous commit make gitweb use esc_html_match_hl() to mark match in
the _whole_ description of a project when searching projects.
This commit makes gitweb highlight match in _shortened_ description,
based on match in whole description, using esc_html_match_hl_chopped()
subroutine (with some code duplication with esc_html_match_hl()).
If match is in shortened part, then trailing "... " is highlighted.
Signed-off-by: Jakub Narebski <redacted>
---
This is an RFC because of code duplication between esc_html_match_hl()
and esc_html_match_hl_chopped().
gitweb/gitweb.perl | 41 ++++++++++++++++++++++++++++++++++++++++-
1 files changed, 40 insertions(+), 1 deletions(-)
@@ -1739,6 +1739,44 @@ sub esc_html_match_hl {return$out;}+# highlight match (if any) of shortened string, and escape HTML+subesc_html_match_hl_chopped{+my($str,$chopped,$regexp)=@_;+returnesc_html($chopped)unlessdefined$regexp;+returnesc_html_match_hl($str,$regexp)if($streq$chopped);++my@matches;+while($str=~ /$regexp/g){+push@matches,[$-[0],$+[0]];+}+returnesc_html($chopped)unless@matches;++my$tail="... ";+$chopped=~s/\Q$tail\E$//;# see chop_str+my$len=length($chopped);+my$out='';+my$pos=0;+formy$m(@matches){+if($m->[0]>$len){+$tail=$cgi->span({-class=>'match'},$tail);+last;+}+$out.=esc_html(substr$str,$pos,$m->[0]-$pos);+$out.=$cgi->span({-class=>'match'},+esc_html(substr$chopped,$m->[0],+($m->[1]>$len?$len:$m->[1])-$m->[0]));+if($m->[1]>$len){+$tail=$cgi->span({-class=>'match'},$tail);+$pos=$len;+last;+}+$pos=$m->[1];+}+$out.=esc_html(substr($chopped,$pos)).$tail;++return$out;+}+## ----------------------------------------------------------------------## functions returning short strings
@@ -5372,7 +5410,8 @@ sub git_project_list_rows {"<td>".$cgi->a({-href=>href(project=>$pr->{'path'},action=>"summary"),-class=>"list",-title=>$pr->{'descr_long'}},$search_regexp-?esc_html_match_hl($pr->{'descr_long'},$search_regexp)+?esc_html_match_hl_chopped($pr->{'descr_long'},+$pr->{'descr'},$search_regexp):esc_html($pr->{'descr'}))."</td>\n"."<td><i>".chop_and_escape_str($pr->{'owner'},15)."</i></td>\n";
From: Jakub Narebski <hidden> Date: 2016-06-15 22:52:56
Use esc_html_match_hl() from previous commit to mark match in the
_whole_ description when searching projects.
Currently, with this commit, when searching projects there is always
shown full description of a project, and not a shortened one (like for
ordinary projects list view), even if the match is on project name and
not project description.
Showing full description when there is match on it is useful to avoid
situation where match is in shortened, invisible part... well, perhaps
that could be solved (showing shortened description), but it would
require some extra code.
Signed-off-by: Jakub Narebski <redacted>
---
The part about showing match using shortened description no longer
applies after the following patch... though it is an RFC for now.
gitweb/gitweb.perl | 5 ++++-
1 files changed, 4 insertions(+), 1 deletions(-)
From: Jakub Narebski <hidden> Date: 2016-06-15 22:52:56
Use newly introduced esc_html_match_hl() to escape HTML and mark match
with span element with 'match' class. Currently only 'path' part
(i.e. project name) is highlighted; match might be on the project
description.
The code makes use of the fact that defined $search_regexp means that
there was search going on.
Signed-off-by: Jakub Narebski <redacted>
---
Introducing esc_html_match_hl() could have been split into a separate
commit, but it would be subroutine without any use.
gitweb/gitweb.perl | 28 +++++++++++++++++++++++++++-
1 files changed, 27 insertions(+), 1 deletions(-)
@@ -1715,6 +1715,30 @@ sub chop_and_escape_str {}}+# highlight match (if any), and escape HTML+subesc_html_match_hl{+my($str,$regexp)=@_;+returnesc_html($str)unlessdefined$regexp;++my@matches;+while($str=~ /$regexp/g){+push@matches,[$-[0],$+[0]];+}+returnesc_html($str)unless@matches;++my$out='';+my$pos=0;+formy$m(@matches){+$out.=esc_html(substr$str,$pos,$m->[0]-$pos);+$out.=$cgi->span({-class=>'match'},+esc_html(substr$str,$m->[0],$m->[1]-$m->[0]));+$pos=$m->[1];+}+$out.=esc_html(substr$str,$pos);++return$out;+}+## ----------------------------------------------------------------------## functions returning short strings
@@ -5342,7 +5366,9 @@ sub git_project_list_rows {print"</td>\n";}print"<td>".$cgi->a({-href=>href(project=>$pr->{'path'},action=>"summary"),--class=>"list"},esc_html($pr->{'path'}))."</td>\n".+-class=>"list"},+esc_html_match_hl($pr->{'path'},$search_regexp)).+"</td>\n"."<td>".$cgi->a({-href=>href(project=>$pr->{'path'},action=>"summary"),-class=>"list",-title=>$pr->{'descr_long'}},esc_html($pr->{'descr'}))."</td>\n".
From: Jakub Narebski <hidden> Date: 2016-06-15 22:52:56
Before searching by some field the information we search for must be
filled in. For this fill_project_list_info() was enhanced in previous
commit to take additional parameters which part of projects info to
fill. This way we can limit doing expensive calculations (like
running git-for-each-ref to get 'age' / "Last changed" info) only to
projects which we will show as search results.
With this commit the number of git commands used to generate search
results is 2*<matched projects> + 1, and depends on number of matched
projects rather than number of all projects (all repositories).
Note: this is 'git for-each-ref' to find last activity, and 'git config'
for each project, and 'git --version' once.
Signed-off-by: Jakub Narebski <redacted>
---
search_projects_list() now pre-fills required parts of project info by
itself, so running fill_project_list_info() before calling it is no
longer necessary and actually you should not do it.
gitweb/gitweb.perl | 9 +++++++--
1 files changed, 7 insertions(+), 2 deletions(-)
@@ -2989,6 +2989,10 @@ sub search_projects_list {return@$projlistunless($tagfilter||$searchtext);+# searching projects require filling to be run before it;+fill_project_list_info($projlist,+$tagfilter?'ctags':(),+$searchtext?('path','descr'):());my@projects;PROJECT:foreachmy$pr(@$projlist){
@@ -5370,12 +5374,13 @@ sub git_project_list_body {# filtering out forks before filling info allows to do less work@projects=filter_forks_from_projects_list(\@projects)if($check_forks);-@projects=fill_project_list_info(\@projects);-# searching projects require filling to be run before it+# search_projects_list pre-fills required info@projects=search_projects_list(\@projects,'searchtext'=>$searchtext,'tagfilter'=>$tagfilter)if($tagfilter||$searchtext);+# fill the rest+@projects=fill_project_list_info(\@projects);$order||=$default_projects_order;$from=0unlessdefined$from;
From: Jakub Narebski <hidden> Date: 2016-06-15 22:52:56
Previous commit make gitweb use esc_html_match_hl() to mark match in
the _whole_ description of a project when searching projects.
This commit makes gitweb highlight match in _shortened_ description,
based on match in whole description, using esc_html_match_hl_chopped()
subroutine.
If match is in removed (chopped) part, even partially, then trailing
"... " is highlighted.
Signed-off-by: Jakub Narebski <redacted>
---
This version removes code duplication from v1.
It is still marked as RFC, because I am not sure if it is right way
to highlight match in shortened string, or if we better use full string,
or full string if match is in chopped part.
gitweb/gitweb.perl | 40 ++++++++++++++++++++++++++++++++++++++--
1 files changed, 38 insertions(+), 2 deletions(-)
@@ -1720,11 +1720,46 @@ sub esc_html_match_hl {my($str,$regexp)=@_;returnesc_html($str)unlessdefined$regexp;+returnesc_html_match_hl_chopped($str,undef,$regexp);+}+++# highlight match (if any) of shortened string, and escape HTML+subesc_html_match_hl_chopped{+my($str,$chopped,$regexp)=@_;+returnesc_html($chopped)unlessdefined$regexp;+my@matches;while($str=~ /$regexp/g){push@matches,[$-[0],$+[0]];}-returnesc_html($str)unless@matches;+returnesc_html(defined$chopped?$chopped:$str)unless@matches;++# filter matches so that we mark chopped string, if it is present+if(defined$chopped){+my$tail="... ";# see chop_str+unless($chopped=~s/\Q$tail\E$//){+$tail='';+}+my$chop_len=length($chopped);+my$tail_len=length($tail);+my@filtered;++formy$m(@matches){+if($m->[0]>$chop_len){+push@filtered,[$chop_len,$chop_len+$tail_len]if($tail_len>0);+last;+}elsif($m->[1]>$chop_len){+push@filtered,[$m->[0],$chop_len+$tail_len];+last;+}+push@filtered,$m;+}++# further operations are on chopped string+$str=$chopped.$tail;+@matches=@filtered;+}my$out='';my$pos=0;
@@ -5372,7 +5407,8 @@ sub git_project_list_rows {"<td>".$cgi->a({-href=>href(project=>$pr->{'path'},action=>"summary"),-class=>"list",-title=>$pr->{'descr_long'}},$search_regexp-?esc_html_match_hl($pr->{'descr_long'},$search_regexp)+?esc_html_match_hl_chopped($pr->{'descr_long'},+$pr->{'descr'},$search_regexp):esc_html($pr->{'descr'}))."</td>\n"."<td><i>".chop_and_escape_str($pr->{'owner'},15)."</i></td>\n";