From b331fe54762770c647b8f95c8f5b16004c6120da Mon Sep 17 00:00:00 2001
From: Johannes Schindelin <johannes.schindelin@gmx.de>
Date: Tue, 27 Apr 2010 21:34:44 +0200
Subject: [PATCH 1/2] gitweb: Syntax highlighting support

It requires the 'highlight' program to do all the heavy-lifting.

This is loosely based on Daniel Svensson's and Sham Chukoury's work in
gitweb-xmms2.git (it cannot be cherry-picked, as gitweb-xmms2 first forked
wildly, then not contributed back, and then went stale).

[jn: cherry picked from bc1ed6aafd9ee4937559535c66c8bddf1864bec6
 in http://repo.or.cz/w/git/dscho.git, with a few changes]

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Jakub Narebski <jnareb@gmail.com>
Acked-by: Petr Baudis <pasky@suse.cz>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 gitweb/gitweb.css  | 18 +++++++++++++
 gitweb/gitweb.perl | 65 +++++++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 82 insertions(+), 1 deletion(-)

diff --git a/gitweb/gitweb.css b/gitweb/gitweb.css
index 50067f2e0de..4132aabcdb4 100644
--- a/gitweb/gitweb.css
+++ b/gitweb/gitweb.css
@@ -572,3 +572,21 @@ span.match {
 div.binary {
 	font-style: italic;
 }
+
+/* Style definition generated by highlight 2.4.5, http://www.andre-simon.de/ */
+
+/* Highlighting theme definition: */
+
+.num    { color:#2928ff; }
+.esc    { color:#ff00ff; }
+.str    { color:#ff0000; }
+.dstr   { color:#818100; }
+.slc    { color:#838183; font-style:italic; }
+.com    { color:#838183; font-style:italic; }
+.dir    { color:#008200; }
+.sym    { color:#000000; }
+.line   { color:#555555; }
+.kwa    { color:#000000; font-weight:bold; }
+.kwb    { color:#830000; }
+.kwc    { color:#000000; font-weight:bold; }
+.kwd    { color:#010181; }
diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl
index c356e95f18f..de18ebf3019 100755
--- a/gitweb/gitweb.perl
+++ b/gitweb/gitweb.perl
@@ -227,6 +227,36 @@ our %avatar_size = (
 # Leave it undefined (or set to 'undef') to turn off load checking.
 our $maxload = 300;
 
+# syntax highlighting
+our %highlight_type = (
+	# match by basename
+	'SConstruct' => 'py',
+	'Program' => 'py',
+	'Library' => 'py',
+	'Makefile' => 'make',
+	# match by extension
+	'\.py$' => 'py', # Python
+	'\.c$' => 'c',
+	'\.h$' => 'c',
+	'\.cpp$' => 'cpp',
+	'\.cxx$' => 'cpp',
+	'\.rb$' => 'ruby',
+	'\.java$' => 'java',
+	'\.css$' => 'css',
+	'\.php3?$' => 'php',
+	'\.sh$' => 'sh', # Bash / shell script
+	'\.pl$' => 'pl', # Perl
+	'\.js$' => 'js', # JavaScript
+	'\.tex$' => 'tex', # TeX and LaTeX
+	'\.bib$' => 'bib', # BibTeX
+	'\.x?html$' => 'xml',
+	'\.xml$' => 'xml',
+	'\.awk$' => 'awk',
+	'\.bat$' => 'bat', # DOS Batch script
+	'\.ini$' => 'ini',
+	'\.spec$' => 'spec', # RPM Spec
+);
+
 # You define site-wide feature defaults here; override them with
 # $GITWEB_CONFIG as necessary.
 our %feature = (
@@ -445,6 +475,19 @@ our %feature = (
 	'javascript-actions' => {
 		'override' => 0,
 		'default' => [0]},
+
+	# Syntax highlighting support. This is based on Daniel Svensson's
+	# and Sham Chukoury's work in gitweb-xmms2.git.
+	# It requires the 'highlight' program, and therefore is disabled
+	# by default.
+
+	# To enable system wide have in $GITWEB_CONFIG
+	# $feature{'highlight'}{'default'} = [1];
+
+	'highlight' => {
+		'sub' => sub { feature_bool('highlight', @_) },
+		'override' => 0,
+		'default' => [0]},
 );
 
 sub gitweb_get_feature {
@@ -5346,6 +5389,7 @@ sub git_blob {
 	open my $fd, "-|", git_cmd(), "cat-file", "blob", $hash
 		or die_error(500, "Couldn't cat $file_name, $hash");
 	my $mimetype = blob_mimetype($fd, $file_name);
+	# use 'blob_plain' (aka 'raw') view for files that cannot be displayed
 	if ($mimetype !~ m!^(?:text/|image/(?:gif|png|jpeg)$)! && -B $fd) {
 		close $fd;
 		return git_blob_plain($mimetype);
@@ -5353,6 +5397,25 @@ sub git_blob {
 	# we can have blame only for text/* mimetype
 	$have_blame &&= ($mimetype =~ m!^text/!);
 
+	my $have_highlight = gitweb_check_feature('highlight');
+	my $syntax;
+	if ($have_highlight && defined($file_name)) {
+		my $basename = basename($file_name, '.in');
+		foreach my $regexp (keys %highlight_type) {
+			if ($basename =~ /$regexp/) {
+				$syntax = $highlight_type{$regexp};
+				last;
+			}
+		}
+
+		if ($syntax) {
+			close $fd;
+			open $fd, quote_command(git_cmd(), "cat-file", "blob", $hash)." | ".
+			          "highlight --xhtml --fragment -t 8 --syntax $syntax |"
+				or die_error(500, "Couldn't open file or run syntax highlighter");
+		}
+	}
+
 	git_header_html(undef, $expires);
 	my $formats_nav = '';
 	if (defined $hash_base && (my %co = parse_commit($hash_base))) {
@@ -5404,7 +5467,7 @@ sub git_blob {
 			$line = untabify($line);
 			printf "<div class=\"pre\"><a id=\"l%i\" href=\"" . href(-replay => 1)
 				. "#l%i\" class=\"linenr\">%4i</a> %s</div>\n",
-			       $nr, $nr, $nr, esc_html($line, -nbsp=>1);
+			       $nr, $nr, $nr, $syntax ? $line : esc_html($line, -nbsp=>1);
 		}
 	}
 	close $fd

From 592ea4173af6b445042d74b989762fd41aecdf4c Mon Sep 17 00:00:00 2001
From: Jakub Narebski <jnareb@gmail.com>
Date: Tue, 27 Apr 2010 21:34:45 +0200
Subject: [PATCH 2/2] gitweb: Refactor syntax highlighting support

This refactoring (adding guess_file_syntax and run_highlighter
subroutines) is meant to make it easier in the future to add support
for other syntax highlighing solutions, or make it smarter by not
re-running `git cat-file` second time.

Instead of looping over list of regexps (keys of %highlight_type hash),
make use of the fact that choosing syntax is based either on full
basename (%highlight_basename), or on file extension (%highlight_ext).

Add some basic test of syntax highlighting (with 'highlight' as
prerequisite) to t/t9500-gitweb-standalone-no-errors.sh test.

While at it make git_blob Perl style prettier.

Signed-off-by: Jakub Narebski <jnareb@gmail.com>
Acked-by: Petr Baudis <pasky@suse.cz>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 gitweb/gitweb.perl                     | 116 ++++++++++++++-----------
 t/t9500-gitweb-standalone-no-errors.sh |  29 +++++++
 2 files changed, 92 insertions(+), 53 deletions(-)

diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl
index de18ebf3019..7d9b6604635 100755
--- a/gitweb/gitweb.perl
+++ b/gitweb/gitweb.perl
@@ -227,36 +227,6 @@ our %avatar_size = (
 # Leave it undefined (or set to 'undef') to turn off load checking.
 our $maxload = 300;
 
-# syntax highlighting
-our %highlight_type = (
-	# match by basename
-	'SConstruct' => 'py',
-	'Program' => 'py',
-	'Library' => 'py',
-	'Makefile' => 'make',
-	# match by extension
-	'\.py$' => 'py', # Python
-	'\.c$' => 'c',
-	'\.h$' => 'c',
-	'\.cpp$' => 'cpp',
-	'\.cxx$' => 'cpp',
-	'\.rb$' => 'ruby',
-	'\.java$' => 'java',
-	'\.css$' => 'css',
-	'\.php3?$' => 'php',
-	'\.sh$' => 'sh', # Bash / shell script
-	'\.pl$' => 'pl', # Perl
-	'\.js$' => 'js', # JavaScript
-	'\.tex$' => 'tex', # TeX and LaTeX
-	'\.bib$' => 'bib', # BibTeX
-	'\.x?html$' => 'xml',
-	'\.xml$' => 'xml',
-	'\.awk$' => 'awk',
-	'\.bat$' => 'bat', # DOS Batch script
-	'\.ini$' => 'ini',
-	'\.spec$' => 'spec', # RPM Spec
-);
-
 # You define site-wide feature defaults here; override them with
 # $GITWEB_CONFIG as necessary.
 our %feature = (
@@ -478,8 +448,8 @@ our %feature = (
 
 	# Syntax highlighting support. This is based on Daniel Svensson's
 	# and Sham Chukoury's work in gitweb-xmms2.git.
-	# It requires the 'highlight' program, and therefore is disabled
-	# by default.
+	# It requires the 'highlight' program present in $PATH,
+	# and therefore is disabled by default.
 
 	# To enable system wide have in $GITWEB_CONFIG
 	# $feature{'highlight'}{'default'} = [1];
@@ -3198,6 +3168,61 @@ sub blob_contenttype {
 	return $type;
 }
 
+# guess file syntax for syntax highlighting; return undef if no highlighting
+# the name of syntax can (in the future) depend on syntax highlighter used
+sub guess_file_syntax {
+	my ($highlight, $mimetype, $file_name) = @_;
+	return undef unless ($highlight && defined $file_name);
+
+	# configuration for 'highlight' (http://www.andre-simon.de/)
+	# match by basename
+	my %highlight_basename = (
+		#'Program' => 'py',
+		#'Library' => 'py',
+		'SConstruct' => 'py', # SCons equivalent of Makefile
+		'Makefile' => 'make',
+	);
+	# match by extension
+	my %highlight_ext = (
+		# main extensions, defining name of syntax;
+		# see files in /usr/share/highlight/langDefs/ directory
+		map { $_ => $_ }
+			qw(py c cpp rb java css php sh pl js tex bib xml awk bat ini spec tcl),
+		# alternate extensions, see /etc/highlight/filetypes.conf
+		'h' => 'c',
+		map { $_ => 'cpp' } qw(cxx c++ cc),
+		map { $_ => 'php' } qw(php3 php4),
+		map { $_ => 'pl'  } qw(perl pm), # perhaps also 'cgi'
+		'mak' => 'make',
+		map { $_ => 'xml' } qw(xhtml html htm),
+	);
+
+	my $basename = basename($file_name, '.in');
+	return $highlight_basename{$basename}
+		if exists $highlight_basename{$basename};
+
+	$basename =~ /\.([^.]*)$/;
+	my $ext = $1 or return undef;
+	return $highlight_ext{$ext}
+		if exists $highlight_ext{$ext};
+
+	return undef;
+}
+
+# run highlighter and return FD of its output,
+# or return original FD if no highlighting
+sub run_highlighter {
+	my ($fd, $highlight, $syntax) = @_;
+	return $fd unless ($highlight && defined $syntax);
+
+	close $fd
+		or die_error(404, "Reading blob failed");
+	open $fd, quote_command(git_cmd(), "cat-file", "blob", $hash)." | ".
+	          "highlight --xhtml --fragment --syntax $syntax |"
+		or die_error(500, "Couldn't open file or run syntax highlighter");
+	return $fd;
+}
+
 ## ======================================================================
 ## functions printing HTML: header, footer, error page
 
@@ -5397,24 +5422,10 @@ sub git_blob {
 	# we can have blame only for text/* mimetype
 	$have_blame &&= ($mimetype =~ m!^text/!);
 
-	my $have_highlight = gitweb_check_feature('highlight');
-	my $syntax;
-	if ($have_highlight && defined($file_name)) {
-		my $basename = basename($file_name, '.in');
-		foreach my $regexp (keys %highlight_type) {
-			if ($basename =~ /$regexp/) {
-				$syntax = $highlight_type{$regexp};
-				last;
-			}
-		}
-
-		if ($syntax) {
-			close $fd;
-			open $fd, quote_command(git_cmd(), "cat-file", "blob", $hash)." | ".
-			          "highlight --xhtml --fragment -t 8 --syntax $syntax |"
-				or die_error(500, "Couldn't open file or run syntax highlighter");
-		}
-	}
+	my $highlight = gitweb_check_feature('highlight');
+	my $syntax = guess_file_syntax($highlight, $mimetype, $file_name);
+	$fd = run_highlighter($fd, $highlight, $syntax)
+		if $syntax;
 
 	git_header_html(undef, $expires);
 	my $formats_nav = '';
@@ -5465,9 +5476,8 @@ sub git_blob {
 			chomp $line;
 			$nr++;
 			$line = untabify($line);
-			printf "<div class=\"pre\"><a id=\"l%i\" href=\"" . href(-replay => 1)
-				. "#l%i\" class=\"linenr\">%4i</a> %s</div>\n",
-			       $nr, $nr, $nr, $syntax ? $line : esc_html($line, -nbsp=>1);
+			printf qq!<div class="pre"><a id="l%i" href="%s#l%i" class="linenr">%4i</a> %s</div>\n!,
+			       $nr, href(-replay => 1), $nr, $nr, $syntax ? $line : esc_html($line, -nbsp=>1);
 		}
 	}
 	close $fd
diff --git a/t/t9500-gitweb-standalone-no-errors.sh b/t/t9500-gitweb-standalone-no-errors.sh
index 63b6b060e64..4f2b9b062b0 100755
--- a/t/t9500-gitweb-standalone-no-errors.sh
+++ b/t/t9500-gitweb-standalone-no-errors.sh
@@ -647,4 +647,33 @@ test_expect_success \
 	 gitweb_run "p=.git;a=summary"'
 test_debug 'cat gitweb.log'
 
+# ----------------------------------------------------------------------
+# syntax highlighting
+
+cat >>gitweb_config.perl <<\EOF
+$feature{'highlight'}{'override'} = 1;
+EOF
+
+highlight --version >/dev/null 2>&1
+if [ $? -eq 127 ]; then
+	say "Skipping syntax highlighting test, because 'highlight' was not found"
+else
+	test_set_prereq HIGHLIGHT
+fi
+
+test_expect_success HIGHLIGHT \
+	'syntax highlighting (no highlight)' \
+	'git config gitweb.highlight yes &&
+	 gitweb_run "p=.git;a=blob;f=file"'
+test_debug 'cat gitweb.log'
+
+test_expect_success HIGHLIGHT \
+	'syntax highlighting (highlighted)' \
+	'git config gitweb.highlight yes &&
+	 echo "#!/usr/bin/sh" > test.sh &&
+	 git add test.sh &&
+	 git commit -m "Add test.sh" &&
+	 gitweb_run "p=.git;a=blob;f=test.sh"'
+test_debug 'cat gitweb.log'
+
 test_done