Subject: [RFC/PATCH] gitweb: Simplify git_project_list_body

From: Jakub Narebski <[email-masked]>
Date: Thu, 1 May 2008 12:20:57 +0200

Extract filling project list info, i.e. adding age, description, owner
and forks information, to fill_project_list_info() subroutine.  This
is preparation for smart pagination and smart searching (if possible,
only calculate info for those projects which are shown).

Extract generating table header cell one can sort by into
print_sort_th_str() and print_sort_th_num() subroutines, and
print_sort_th() driver subroutine.  This avoids repetition, and should
make further improvements (like JavaScript client-side sorting) easier.

Signed-off-by: Jakub Narebski <[email-masked]>
---
This is an RFC patch, first patch of series meant to improve generating
list of projects, trying to reduce both amount of work server has to do
to generate the page, and the size of the page to reduce amount of data
send and amount of rendering web browser (client) has to do.

An RFC because:
 * I don't quote like naming of print_sort_th*() subroutines, but
   I could not think of a better name
 * I wonder if there is a better way to pass arguments, and better way
   to deal with numeric sorting (age) and lexical soering (the rest).

I'd like for someone with better Perl knowledge than me examine this
patch.

What is planned:
 * Adding description to project_list format, so everything except age
   (last changed) can be read by parsing single file, making gitweb
   performance better
 * Paginate projects list in such way so the information such as age
   is gathered and filled only for projects which are shown, if possible
   (it is not possible for example if worting by age)
 * Add searching for projects, by project name (project path),
   description, and owner.  Make gitweb use search page if number of
   projects is greater than some configurable number, as a front page

What is planned in the future
 * Add project's categories support.

 gitweb/gitweb.perl |  108 ++++++++++++++++++++++++++++-----------------------
 1 files changed, 59 insertions(+), 49 deletions(-)

diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl
index 2facf2d..6a28dca 100755
--- a/gitweb/gitweb.perl
+++ b/gitweb/gitweb.perl
@@ -3548,21 +3548,23 @@ sub git_patchset_body {
 
 # . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
 
-sub git_project_list_body {
-	my ($projlist, $order, $from, $to, $extra, $no_header) = @_;
-
-	my ($check_forks) = gitweb_check_feature('forks');
-
+# fills project list info (age, description, owner, forks) for each
+# project in the list, removing invalid projects from returned list
+sub fill_project_list_info {
+	my ($projlist, $check_forks) = @_;
 	my @projects;
+
+ PROJECT:
 	foreach my $pr (@$projlist) {
-		my (@aa) = git_get_last_activity($pr->{'path'});
-		unless (@aa) {
-			next;
+		my (@activity) = git_get_last_activity($pr->{'path'});
+		unless (@activity) {
+			next PROJECT;
 		}
-		($pr->{'age'}, $pr->{'age_string'}) = @aa;
+		($pr->{'age'}, $pr->{'age_string'}) = @activity;
 		if (!defined $pr->{'descr'}) {
 			my $descr = git_get_project_description($pr->{'path'}) || "";
-			$pr->{'descr_long'} = to_utf8($descr);
+			$descr = to_utf8($descr);
+			$pr->{'descr_long'} = $descr;
 			$pr->{'descr'} = chop_str($descr, $projects_list_description_width, 5);
 		}
 		if (!defined $pr->{'owner'}) {
@@ -3574,14 +3576,50 @@ sub git_project_list_body {
 			    ($pname !~ /\/$/) &&
 			    (-d "$projectroot/$pname")) {
 				$pr->{'forks'} = "-d $projectroot/$pname";
-			}
-			else {
+			}	else {
 				$pr->{'forks'} = 0;
 			}
 		}
 		push @projects, $pr;
 	}
 
+	return @projects;
+}
+
+sub print_sort_th {
+	my ($str_sort, $name, $order, $key, $header, $projlist) = @_;
+	$key    ||= $name;
+	$header ||= ucfirst($name);
+
+	if ($order eq $name) {
+		if ($str_sort) {
+			@$projlist = sort {$a->{$key} cmp $b->{$key}} @$projlist;
+		} else {
+			@$projlist = sort {$a->{$key} <=> $b->{$key}} @$projlist;
+		}
+		print "<th>$header</th>\n";
+	} else {
+		print "<th>" .
+		      $cgi->a({-href => href(project=>undef, order=>$name),
+		               -class => "header"}, $header) .
+		      "</th>\n";
+	}
+}
+
+sub print_sort_th_str {
+	print_sort_th(1, @_);
+}
+
+sub print_sort_th_num {
+	print_sort_th(0, @_);
+}
+
+sub git_project_list_body {
+	my ($projlist, $order, $from, $to, $extra, $no_header) = @_;
+
+	my ($check_forks) = gitweb_check_feature('forks');
+	my @projects = fill_project_list_info($projlist, $check_forks);
+
 	$order ||= $default_projects_order;
 	$from = 0 unless defined $from;
 	$to = $#projects if (!defined $to || $#projects < $to);
@@ -3592,43 +3630,15 @@ sub git_project_list_body {
 		if ($check_forks) {
 			print "<th></th>\n";
 		}
-		if ($order eq "project") {
-			@projects = sort {$a->{'path'} cmp $b->{'path'}} @projects;
-			print "<th>Project</th>\n";
-		} else {
-			print "<th>" .
-			      $cgi->a({-href => href(project=>undef, order=>'project'),
-			               -class => "header"}, "Project") .
-			      "</th>\n";
-		}
-		if ($order eq "descr") {
-			@projects = sort {$a->{'descr'} cmp $b->{'descr'}} @projects;
-			print "<th>Description</th>\n";
-		} else {
-			print "<th>" .
-			      $cgi->a({-href => href(project=>undef, order=>'descr'),
-			               -class => "header"}, "Description") .
-			      "</th>\n";
-		}
-		if ($order eq "owner") {
-			@projects = sort {$a->{'owner'} cmp $b->{'owner'}} @projects;
-			print "<th>Owner</th>\n";
-		} else {
-			print "<th>" .
-			      $cgi->a({-href => href(project=>undef, order=>'owner'),
-			               -class => "header"}, "Owner") .
-			      "</th>\n";
-		}
-		if ($order eq "age") {
-			@projects = sort {$a->{'age'} <=> $b->{'age'}} @projects;
-			print "<th>Last Change</th>\n";
-		} else {
-			print "<th>" .
-			      $cgi->a({-href => href(project=>undef, order=>'age'),
-			               -class => "header"}, "Last Change") .
-			      "</th>\n";
-		}
-		print "<th></th>\n" .
+		print_sort_th_str('project', $order, 'path',
+		                  'Project', \@projects);
+		print_sort_th_str('descr', $order, 'descr_long',
+		                  'Description', \@projects);
+		print_sort_th_str('owner', $order, 'owner',
+		                  'Owner', \@projects);
+		print_sort_th_num('age', $order, 'age',
+		                  'Last Change', \@projects);
+		print "<th></th>\n" . # for links
 		      "</tr>\n";
 	}
 	my $alternate = 1;
-- 
1.5.5