gitweb: Refactor generation of shortlog, tags and heads body

Add git_shortlog_body, git_tags_body and git_heads_body to generate
table with shortlog, tags and heads respectively in git_summary and
git_shortlog, git_tags, git_heads respectively.

Better support for lightweight tags in git_read_refs; currently only
lightweight tag pointing to tag object is not resolved fully.

Shortlog, tags and heads body tables have proper class now (we could
use id instead of class).

Add support for showing full comment on mouseover to tags list when
comment is shortened, similar to how full title of commit was/is
shown on mouseover when title was shortened.  Changed layout of tags
table to better show lightweight tags.

Add showing which branch (head) is current branch (current head),
using "current_head" class (we could use id instead).

Corrected "</table\n>" and hit_header_div instead of git_header_div.

Signed-off-by: Jakub Narebski <jnareb@gmail.com>
Signed-off-by: Junio C Hamano <junkio@cox.net>
diff --git a/gitweb/gitweb.cgi b/gitweb/gitweb.cgi
index 2f17318..c741e73 100755
--- a/gitweb/gitweb.cgi
+++ b/gitweb/gitweb.cgi
@@ -1102,6 +1102,10 @@
 			$ref_item{'refid'} = $ref_id;
 			$ref_item{'epoch'} = $co{'committer_epoch'};
 			$ref_item{'age'} = $co{'age_string'};
+		} else {
+			$ref_item{'reftype'} = $type;
+			$ref_item{'name'} = $ref_file;
+			$ref_item{'refid'} = $ref_id;
 		}
 
 		push @reflist, \%ref_item;
@@ -1111,6 +1115,156 @@
 	return \@reflist;
 }
 
+sub git_shortlog_body {
+	# uses global variable $project
+	my ($revlist, $from, $to, $refs, $extra) = @_;
+	$from = 0 unless defined $from;
+	$to = $#{$revlist} if (!defined $to || $#{$revlist} < $to);
+
+	print "<table class=\"shortlog\" cellspacing=\"0\">\n";
+	my $alternate = 0;
+	for (my $i = $from; $i <= $to; $i++) {
+		my $commit = $revlist->[$i];
+		#my $ref = defined $refs ? git_get_referencing($refs, $commit) : '';
+		my $ref = git_get_referencing($refs, $commit);
+		my %co = git_read_commit($commit);
+		my %ad = date_str($co{'author_epoch'});
+		if ($alternate) {
+			print "<tr class=\"dark\">\n";
+		} else {
+			print "<tr class=\"light\">\n";
+		}
+		$alternate ^= 1;
+		# git_summary() used print "<td><i>$co{'age_string'}</i></td>\n" .
+		print "<td title=\"$co{'age_string_age'}\"><i>$co{'age_string_date'}</i></td>\n" .
+		      "<td><i>" . esc_html(chop_str($co{'author_name'}, 10)) . "</i></td>\n" .
+		      "<td>";
+		if (length($co{'title_short'}) < length($co{'title'})) {
+			print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$commit"),
+			               -class => "list", -title => "$co{'title'}"},
+			      "<b>" . esc_html($co{'title_short'}) . "$ref</b>");
+		} else {
+			print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$commit"),
+			               -class => "list"},
+			      "<b>" . esc_html($co{'title'}) . "$ref</b>");
+		}
+		print "</td>\n" .
+		      "<td class=\"link\">" .
+		      $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$commit")}, "commit") . " | " .
+		      $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commitdiff;h=$commit")}, "commitdiff") .
+		      "</td>\n" .
+		      "</tr>\n";
+	}
+	if (defined $extra) {
+		print "<tr>\n" .
+		      "<td colspan=\"4\">$extra</td>\n" .
+		      "</tr>\n";
+	}
+	print "</table>\n";
+}
+
+sub git_tags_body {
+	# uses global variable $project
+	my ($taglist, $from, $to, $extra) = @_;
+	$from = 0 unless defined $from;
+	$to = $#{$taglist} if (!defined $to || $#{$taglist} < $to);
+
+	print "<table class=\"tags\" cellspacing=\"0\">\n";
+	my $alternate = 0;
+	for (my $i = $from; $i <= $to; $i++) {
+		my $entry = $taglist->[$i];
+		my %tag = %$entry;
+		my $comment_lines = $tag{'comment'};
+		my $comment = shift @$comment_lines;
+		my $comment_short;
+		if (defined $comment) {
+			$comment_short = chop_str($comment, 30, 5);
+		}
+		if ($alternate) {
+			print "<tr class=\"dark\">\n";
+		} else {
+			print "<tr class=\"light\">\n";
+		}
+		$alternate ^= 1;
+		print "<td><i>$tag{'age'}</i></td>\n" .
+		      "<td>" .
+		      $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=$tag{'reftype'};h=$tag{'refid'}"),
+		               -class => "list"}, "<b>" . esc_html($tag{'name'}) . "</b>") .
+		      "</td>\n" .
+		      "<td>";
+		if (defined $comment) {
+			if (length($comment_short) < length($comment)) {
+				print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=tag;h=$tag{'id'}"),
+				               -class => "list", -title => $comment}, $comment_short);
+			} else {
+				print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=tag;h=$tag{'id'}"),
+				               -class => "list"}, $comment);
+			}
+		}
+		print "</td>\n" .
+		      "<td class=\"selflink\">";
+		if ($tag{'type'} eq "tag") {
+			print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=tag;h=$tag{'id'}")}, "tag");
+		} else {
+			print "&nbsp;";
+		}
+		print "</td>\n" .
+		      "<td class=\"link\">" . " | " .
+		      $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=$tag{'reftype'};h=$tag{'refid'}")}, $tag{'reftype'});
+		if ($tag{'reftype'} eq "commit") {
+			print " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=shortlog;h=$tag{'name'}")}, "shortlog") .
+			      " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=log;h=$tag{'refid'}")}, "log");
+		} elsif ($tag{'reftype'} eq "blob") {
+			print " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob_plain;h=$tag{'refid'}")}, "raw");
+		}
+		print "</td>\n" .
+		      "</tr>";
+	}
+	if (defined $extra) {
+		print "<tr>\n" .
+		      "<td colspan=\"5\">$extra</td>\n" .
+		      "</tr>\n";
+	}
+	print "</table>\n";
+}
+
+sub git_heads_body {
+	# uses global variable $project
+	my ($taglist, $head, $from, $to, $extra) = @_;
+	$from = 0 unless defined $from;
+	$to = $#{$taglist} if (!defined $to || $#{$taglist} < $to);
+
+	print "<table class=\"heads\" cellspacing=\"0\">\n";
+	my $alternate = 0;
+	for (my $i = $from; $i <= $to; $i++) {
+		my $entry = $taglist->[$i];
+		my %tag = %$entry;
+		my $curr = $tag{'id'} eq $head;
+		if ($alternate) {
+			print "<tr class=\"dark\">\n";
+		} else {
+			print "<tr class=\"light\">\n";
+		}
+		$alternate ^= 1;
+		print "<td><i>$tag{'age'}</i></td>\n" .
+		      ($tag{'id'} eq $head ? "<td class=\"current_head\">" : "<td>") .
+		      $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=shortlog;h=$tag{'name'}"),
+		               -class => "list"}, "<b>" . esc_html($tag{'name'}) . "</b>") .
+		      "</td>\n" .
+		      "<td class=\"link\">" .
+		      $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=shortlog;h=$tag{'name'}")}, "shortlog") . " | " .
+		      $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=log;h=$tag{'name'}")}, "log") .
+		      "</td>\n" .
+		      "</tr>";
+	}
+	if (defined $extra) {
+		print "<tr>\n" .
+		      "<td colspan=\"3\">$extra</td>\n" .
+		      "</tr>\n";
+	}
+	print "</table>\n";
+}
+
 sub git_summary {
 	my $descr = git_read_description($project) || "none";
 	my $head = git_read_head($project);
@@ -1139,138 +1293,36 @@
 	my $refs = read_info_ref();
 	git_header_html();
 	git_page_nav('summary','', $head);
+
 	print "<div class=\"title\">&nbsp;</div>\n";
 	print "<table cellspacing=\"0\">\n" .
 	      "<tr><td>description</td><td>" . esc_html($descr) . "</td></tr>\n" .
 	      "<tr><td>owner</td><td>$owner</td></tr>\n" .
 	      "<tr><td>last change</td><td>$cd{'rfc2822'}</td></tr>\n" .
 	      "</table>\n";
+
 	open my $fd, "-|", $GIT, "rev-list", "--max-count=17", git_read_head($project)
 		or die_error(undef, "Open git-rev-list failed.");
 	my @revlist = map { chomp; $_ } <$fd>;
 	close $fd;
 	git_header_div('shortlog');
-	my $i = 16;
-	print "<table cellspacing=\"0\">\n";
-	my $alternate = 0;
-	foreach my $commit (@revlist) {
-		my %co = git_read_commit($commit);
-		my %ad = date_str($co{'author_epoch'});
-		if ($alternate) {
-			print "<tr class=\"dark\">\n";
-		} else {
-			print "<tr class=\"light\">\n";
-		}
-		$alternate ^= 1;
-		if ($i-- > 0) {
-			my $ref = git_get_referencing($refs, $commit);
-			print "<td><i>$co{'age_string'}</i></td>\n" .
-			      "<td><i>" . esc_html(chop_str($co{'author_name'}, 10)) . "</i></td>\n" .
-			      "<td>";
-			if (length($co{'title_short'}) < length($co{'title'})) {
-				print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$commit"), -class => "list", -title => "$co{'title'}"},
-				      "<b>" . esc_html($co{'title_short'}) . "$ref</b>");
-			} else {
-				print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$commit"), -class => "list"},
-				      "<b>" . esc_html($co{'title'}) . "$ref</b>");
-			}
-			print "</td>\n" .
-			      "<td class=\"link\">" .
-			      $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$commit")}, "commit") .
-			      " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commitdiff;h=$commit")}, "commitdiff") .
-			      "</td>\n" .
-			      "</tr>";
-		} else {
-			print "<td>" . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=shortlog")}, "...") . "</td>\n" .
-			"</tr>";
-			last;
-		}
-	}
-	print "</table\n>";
+	git_shortlog_body(\@revlist, 0, 15, $refs,
+	                  $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=shortlog")}, "..."));
 
 	my $taglist = git_read_refs("refs/tags");
 	if (defined @$taglist) {
 		git_header_div('tags');
-		my $i = 16;
-		print "<table cellspacing=\"0\">\n";
-		my $alternate = 0;
-		foreach my $entry (@$taglist) {
-			my %tag = %$entry;
-			my $comment_lines = $tag{'comment'};
-			my $comment = shift @$comment_lines;
-			if (defined($comment)) {
-				$comment = chop_str($comment, 30, 5);
-			}
-			if ($alternate) {
-				print "<tr class=\"dark\">\n";
-			} else {
-				print "<tr class=\"light\">\n";
-			}
-			$alternate ^= 1;
-			if ($i-- > 0) {
-				print "<td><i>$tag{'age'}</i></td>\n" .
-				      "<td>" .
-				      $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=$tag{'reftype'};h=$tag{'refid'}"), -class => "list"},
-				      "<b>" . esc_html($tag{'name'}) . "</b>") .
-				      "</td>\n" .
-				      "<td>";
-				if (defined($comment)) {
-					print $cgi->a({-class => "list", -href => "$my_uri?" . esc_param("p=$project;a=tag;h=$tag{'id'}")}, esc_html($comment));
-				}
-				print "</td>\n" .
-				      "<td class=\"link\">";
-				if ($tag{'type'} eq "tag") {
-					print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=tag;h=$tag{'id'}")}, "tag") . " | ";
-				}
-				print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=$tag{'reftype'};h=$tag{'refid'}")}, $tag{'reftype'});
-				if ($tag{'reftype'} eq "commit") {
-					print " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=shortlog;h=$tag{'name'}")}, "shortlog") .
-					      " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=log;h=$tag{'refid'}")}, "log");
-				}
-				print "</td>\n" .
-				      "</tr>";
-			} else {
-				print "<td>" . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=tags")}, "...") . "</td>\n" .
-				"</tr>";
-				last;
-			}
-		}
-		print "</table\n>";
+		git_tags_body($taglist, 0, 15,
+		              $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=tags")}, "..."));
 	}
 
 	my $headlist = git_read_refs("refs/heads");
 	if (defined @$headlist) {
 		git_header_div('heads');
-		my $i = 16;
-		print "<table cellspacing=\"0\">\n";
-		my $alternate = 0;
-		foreach my $entry (@$headlist) {
-			my %tag = %$entry;
-			if ($alternate) {
-				print "<tr class=\"dark\">\n";
-			} else {
-				print "<tr class=\"light\">\n";
-			}
-			$alternate ^= 1;
-			if ($i-- > 0) {
-				print "<td><i>$tag{'age'}</i></td>\n" .
-				      "<td>" .
-				      $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=shortlog;h=$tag{'name'}"), -class => "list"},
-				      "<b>" . esc_html($tag{'name'}) . "</b>") .
-				      "</td>\n" .
-				      "<td class=\"link\">" .
-				      $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=shortlog;h=$tag{'name'}")}, "shortlog") .
-				      " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=log;h=$tag{'name'}")}, "log") .
-				      "</td>\n" .
-				      "</tr>";
-			} else {
-				print "<td>" . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=heads")}, "...") . "</td>\n" .
-				"</tr>";
-				last;
-			}
-		}
-		print "</table\n>";
+		git_heads_body($taglist, $head, 0, 15,
+		               $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=heads")}, "..."));
 	}
+
 	git_footer_html();
 }
 
@@ -1472,48 +1524,11 @@
 	git_header_html();
 	git_page_nav('','', $head,undef,$head);
 	git_header_div('summary', $project);
-	print "<table cellspacing=\"0\">\n";
 
 	my $taglist = git_read_refs("refs/tags");
-	my $alternate = 0;
 	if (defined @$taglist) {
-		foreach my $entry (@$taglist) {
-			my %tag = %$entry;
-			my $comment_lines = $tag{'comment'};
-			my $comment = shift @$comment_lines;
-			if (defined($comment)) {
-				$comment = chop_str($comment, 30, 5);
-			}
-			if ($alternate) {
-				print "<tr class=\"dark\">\n";
-			} else {
-				print "<tr class=\"light\">\n";
-			}
-			$alternate ^= 1;
-			print "<td><i>$tag{'age'}</i></td>\n" .
-			      "<td>" .
-			      $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=$tag{'reftype'};h=$tag{'refid'}"), -class => "list"},
-			      "<b>" . esc_html($tag{'name'}) . "</b>") .
-			      "</td>\n" .
-			      "<td>";
-			if (defined($comment)) {
-				print $cgi->a({-class => "list", -href => "$my_uri?" . esc_param("p=$project;a=tag;h=$tag{'id'}")}, $comment);
-			}
-			print "</td>\n" .
-			      "<td class=\"link\">";
-			if ($tag{'type'} eq "tag") {
-				print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=tag;h=$tag{'id'}")}, "tag") . " | ";
-			}
-			print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=$tag{'reftype'};h=$tag{'refid'}")}, $tag{'reftype'});
-			if ($tag{'reftype'} eq "commit") {
-				print " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=shortlog;h=$tag{'name'}")}, "shortlog") .
-				      " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=log;h=$tag{'refid'}")}, "log");
-			}
-			print "</td>\n" .
-			      "</tr>";
-		}
+		git_tags_body($taglist);
 	}
-	print "</table\n>";
 	git_footer_html();
 }
 
@@ -1521,32 +1536,13 @@
 	my $head = git_read_head($project);
 	git_header_html();
 	git_page_nav('','', $head,undef,$head);
-	hit_header_div('summary', $project);
-	print "<table cellspacing=\"0\">\n";
+	git_header_div('summary', $project);
 
 	my $taglist = git_read_refs("refs/heads");
 	my $alternate = 0;
 	if (defined @$taglist) {
-		foreach my $entry (@$taglist) {
-			my %tag = %$entry;
-			if ($alternate) {
-				print "<tr class=\"dark\">\n";
-			} else {
-				print "<tr class=\"light\">\n";
-			}
-			$alternate ^= 1;
-			print "<td><i>$tag{'age'}</i></td>\n" .
-			      "<td>" .
-			      $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=shortlog;h=$tag{'name'}"), -class => "list"}, "<b>" . esc_html($tag{'name'}) . "</b>") .
-			      "</td>\n" .
-			      "<td class=\"link\">" .
-			      $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=shortlog;h=$tag{'name'}")}, "shortlog") .
-			      " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=log;h=$tag{'name'}")}, "log") .
-			      "</td>\n" .
-			      "</tr>";
-		}
+		git_heads_body($taglist, $head);
 	}
-	print "</table\n>";
 	git_footer_html();
 }
 
@@ -2544,48 +2540,19 @@
 	close $fd;
 
 	my $paging_nav = git_get_paging_nav('shortlog', $hash, $head, $page, $#revlist);
+	my $next_link = '';
+	if ($#revlist >= (100 * ($page+1)-1)) {
+		$next_link =
+			$cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=shortlog;h=$hash;pg=" . ($page+1)),
+			         -title => "Alt-n"}, "next");
+	}
+
 
 	git_header_html();
 	git_page_nav('shortlog','', $hash,$hash,$hash, $paging_nav);
 	git_header_div('summary', $project);
 
-	print "<table cellspacing=\"0\">\n";
-	my $alternate = 0;
-	for (my $i = ($page * 100); $i <= $#revlist; $i++) {
-		my $commit = $revlist[$i];
-		my $ref = git_get_referencing($refs, $commit);
-		my %co = git_read_commit($commit);
-		my %ad = date_str($co{'author_epoch'});
-		if ($alternate) {
-			print "<tr class=\"dark\">\n";
-		} else {
-			print "<tr class=\"light\">\n";
-		}
-		$alternate ^= 1;
-		print "<td title=\"$co{'age_string_age'}\"><i>$co{'age_string_date'}</i></td>\n" .
-		      "<td><i>" . esc_html(chop_str($co{'author_name'}, 10)) . "</i></td>\n" .
-		      "<td>";
-		if (length($co{'title_short'}) < length($co{'title'})) {
-			print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$commit"), -class => "list", -title => "$co{'title'}"},
-			      "<b>" . esc_html($co{'title_short'}) . "$ref</b>");
-		} else {
-			print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$commit"), -class => "list"},
-			      "<b>" . esc_html($co{'title_short'}) . "$ref</b>");
-		}
-		print "</td>\n" .
-		      "<td class=\"link\">" .
-		      $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$commit")}, "commit") .
-		      " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commitdiff;h=$commit")}, "commitdiff") .
-		      "</td>\n" .
-		      "</tr>";
-	}
-	if ($#revlist >= (100 * ($page+1)-1)) {
-		print "<tr>\n" .
-		      "<td>" .
-		      $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=shortlog;h=$hash;pg=" . ($page+1)), -title => "Alt-n"}, "next") .
-		      "</td>\n" .
-		      "</tr>\n";
-	}
-	print "</table\n>";
+	git_shortlog_body(\@revlist, ($page * 100), $#revlist, $refs, $next_link);
+
 	git_footer_html();
 }