Hello, all!
Syntax highlight is probably the most needed feature of Codestriker as
it helps code readability dramatically. Here is my implementation of
syntax highlight using Syntax::Hightligh::Engine::Kate engine (thanks
to Kannan Goundan for pointing to it).
I've used to redesign Codestriker::Http::Render.pm internal workflow a
little but in my day-to-day work It works just like before. The only
thing I haven't check in real life is LXR linking but I'm quite sure
it works too.
Codestriker is great thing to help collaborative work, great thanks to
all the team!
--
Nikita V. Borodikhin, System Administrator NIKB-RIPN BNV7-RIPE
Registered Linux user #256562 with the Linux Counter
diff -pruN codestriker-1.9.3/bin/install.pl codestriker-1.9.3_hl/bin/install.pl
--- codestriker-1.9.3/bin/install.pl 2006-08-11 03:56:56.000000000 +0400
+++ codestriker-1.9.3_hl/bin/install.pl 2007-06-22 20:37:09.000000000 +0400
@@ -114,6 +114,11 @@ my $modules = [
name => 'Authen::SASL',
version => '0',
optional => 0
+ },
+ {
+ name => 'Syntax::Hightligh::Engine::Kate',
+ version => '0',
+ optional => 1
}
];
diff -pruN codestriker-1.9.3/codestriker.conf
codestriker-1.9.3_hl/codestriker.conf
--- codestriker-1.9.3/codestriker.conf 2007-03-07 06:38:01.000000000 +0300
+++ codestriker-1.9.3_hl/codestriker.conf 2007-06-22 20:32:25.000000000
+0400
@@ -245,6 +245,15 @@ $COLOURED_MONO_MODE = 2;
$default_topic_create_mode = $COLOURED_MODE;
+# Enable syntax highlighting.
+$use_syntax_highlight = 1;
+
+# Specify manual mapping between file extension and file type for highlighting.
+# File types are Syntax::Highlight::Engine::Kate ones.
+$syntax_extension_map = {
+ #".h" => "C++",
+};
+
# The default line break viewing mode to use in the URL when viewing a
# topic. Can be either $LINE_BREAK_NORMAL_MODE or $LINE_BREAK_ASSIST_MODE.
# Using $LINE_BREAK_ASSIST_MODE indicates that extra line breaks may be
diff -pruN codestriker-1.9.3/html/codestriker.css
codestriker-1.9.3_hl/html/codestriker.css
--- codestriker-1.9.3/html/codestriker.css 2005-06-02 15:30:59.000000000
+0400
+++ codestriker-1.9.3_hl/html/codestriker.css 2007-06-21 21:26:00.000000000
+0400
@@ -144,3 +144,20 @@ A.tab {text-decoration: none}
/* Styles for error field in comment posting tooltip. */
span.hidden {display: none}
span.error {display: inline; color: red}
+
+/* Styles for syntax highlighting */
+span.dsNormal {color: black}
+span.dsKeyword {color: blue}
+span.dsDataType {color: blue}
+span.dsDecVal {color: green}
+span.dsBaseN {color: green}
+span.dsFloat {color: green}
+span.dsChar {color: green}
+span.dsString {color: green; font-style: italic}
+span.dsComment {color: gray; font-style: italic}
+span.dsOthers {color: black}
+span.dsAlert {color: red; font-style: bold}
+span.dsFunction {color: black; font-style: bold}
+span.dsRegionMarker {color: black}
+span.dsError {color: red; font-style: bold}
+
diff -pruN codestriker-1.9.3/lib/Codestriker/Http/Render.pm
codestriker-1.9.3_hl/lib/Codestriker/Http/Render.pm
--- codestriker-1.9.3/lib/Codestriker/Http/Render.pm 2006-06-14
03:38:40.000000000 +0400
+++ codestriker-1.9.3_hl/lib/Codestriker/Http/Render.pm 2007-06-22
20:33:37.000000000 +0400
@@ -14,6 +14,9 @@ use DBI;
use CGI::Carp 'fatalsToBrowser';
use HTML::Entities ();
+eval "use Syntax::Highlight::Engine::Kate;";
+my $highlight_available = !$@;
+
# Colour to use when displaying the line number that a comment is being made
# against.
my $CONTEXT_COLOUR = "red";
@@ -83,6 +86,8 @@ sub new ([EMAIL PROTECTED]@[EMAIL PROTECTED]@[EMAIL
PROTECTED]) {
$self->{old_linenumber} = 1;
$self->{new_linenumber} = 1;
+ $self->{use_highlight} = $Codestriker::use_syntax_highlight &&
$highlight_available;
+
# Get the main entry to the database
my $topic_obj = Codestriker::Model::Topic->new($self->{topic});
# Check for readonly
@@ -114,6 +119,7 @@ sub new ([EMAIL PROTECTED]@[EMAIL PROTECTED]@[EMAIL
PROTECTED]) {
$self->{diff_current_filename} = "";
$self->{diff_current_revision} = "";
$self->{diff_current_repmatch} = 0;
+ $self->{diff_current_highlighter} = 0;
# Check if the repository has an associated LXR mapping, and if so,
# setup a db connection and prepare a select statement.
@@ -188,62 +194,64 @@ sub lxr_ident($$) {
}
}
-# Parse the line and product the appropriate hyperlinks to LXR.
+# Parse the unescaped line and return array of begin position and length
+# of identifiers that can be converted to LXR links.
# Currently, this is very Java/C/C++ centric, but it will do for now.
sub lxr_data($$) {
my ($self, $data) = @_;
+ my @positions = ();
+
# Don't do anything if LXR is not enabled for this topic.
- return $data if ! defined $self->{idhashref};
+ return @positions if ! defined $self->{idhashref};
# If the line is just a comment, don't do any processing. Note this code
# isn't bullet-proof, but its good enough most of the time.
$_ = $data;
- return $data if (/^(\s| )*\/\// || /^(\s| ){0,10}\*/ ||
- /^(\s| ){0,10}\/\*/ ||
- /^(\s| )*\*\/(\s| )*$/);
+ return @positions if (/^\s*\/\// || /^\s{0,10}\*/ ||
+ /^\s{0,10}\/\*/ ||
+ /^\s*\*\/\s*$/);
# Handle package Java statements.
- if ($data =~ /^(package(\s| )+)([\w\.]+)(.*)$/) {
- return $1 . $self->lxr_ident($3) . $4;
+ if ($data =~ /^(package\s+)([\w\.]+)(.*)$/) {
+ push(@positions, length($1), length($2));
+ return @positions;
}
# Handle Java import statements.
- if ($data =~ /^(import(\s| )+)([\w\.]+)\.(\w+)((\s| )*)(.*)$/) {
- return $1 . $self->lxr_ident($3) . "." . $self->lxr_ident($4) . "$5$7";
+ if ($data =~ /^(import\s+)([\w\.]+)\.(\w+)(\s*)(.*)$/) {
+ my $startpos = length($1);
+ push(@positions, $startpos, length($2));
+ $startpos += length($2) + 1;
+ push(@positions, $startpos, length($3));
+ return @positions;
}
# Handle #include statements. Note, these aren't identifier lookups, but
# need to be mapped to http://localhost.localdomain/lxr/xxx/yyy/incfile.h
# Should include the current filename in the object for matching purposes.
# if (/^(\#\s*include\s+[\"<])(.*?)([\">].*)$/) {
-# return $1 . $self->lxr_ident($2) . $3;
+# push(@positions, length($1), length($2)
+# return @positions;
# }
# Break the string into potential identifiers, and look them up to see
# if they can be hyperlinked to an LXR lookup.
- my $idhashref = $self->{idhashref};
- my @data_tokens = split /([A-Za-z][\w]+)/, $data;
- my $newdata = "";
+ my @data_tokens = split /([_A-Za-z][\w]+)/, $data;
my $in_comment = 0;
my $eol_comment = 0;
+ my $startpos = 0;
for (my $i = 0; $i <= $#data_tokens; $i++) {
my $token = $data_tokens[$i];
- if ($token =~ /^[A-Za-z]/) {
+ if ($token =~ /^[_A-Za-z]/) {
if ($eol_comment || $in_comment) {
# Currently in a comment, don't LXRify.
- $newdata .= $token;
- } elsif ($token eq "nbsp" || $token eq "quot" || $token eq "amp" ||
- $token eq "lt" || $token eq "gt") {
- # HACK - ignore potential HTML entities. This needs to be
- # done in a smarter fashion later.
- $newdata .= $token;
} else {
- $newdata .= $self->lxr_ident($token);
+ push(@positions, $startpos, length($token));
}
} else {
- $newdata .= $token;
- $token =~ s/(\s| )//g;
+ $token =~ s/(\s*)//;
+ $startpos += length($1);
# Check if we are entering or exiting a comment.
if ($token =~ /\/\//) {
@@ -254,9 +262,11 @@ sub lxr_data($$) {
$in_comment = 1;
}
}
+
+ $startpos += length($token);
}
- return $newdata;
+ return @positions;
}
# Render a delta. If the filename has changed since the last delta, output the
@@ -300,6 +310,7 @@ sub delta ($$$$$$$$$$) {
# If it not a diff, show the entire delta (actually the file
# contents) in a single column.
$self->delta_file_header($filename, $revision, $repmatch);
+ $self->_create_highlighter($filename);
print $query->Tr($query->td(" "), $query->td(" "),"\n");
@@ -313,7 +324,8 @@ sub delta ($$$$$$$$$$) {
# Removed the delta text, where + is added to the start of each
# line. Also make sure the line is suitably escaped.
$line =~ s/^\+//;
- $line = HTML::Entities::encode($line);
+
+ $line = $self->_highlight_line($line);
my $cell = $self->render_coloured_cell($line);
my $cell_class =
@@ -326,6 +338,118 @@ sub delta ($$$$$$$$$$) {
}
}
+# Create highlighter object is possible and allowed
+sub _create_highlighter ($$) {
+ my ($self, $filename) = @_;
+
+ if ($self->{use_highlight})
+ {
+ my $hl;
+ eval "\$hl = new Syntax::Highlight::Engine::Kate();";
+
+ $hl->languageAutoSet($filename);
+
+ if ($filename =~ /.*(\.[^\.]*)$/) {
+ if (exists $Codestriker::syntax_extension_map->{$1}) {
+ $hl->language($Codestriker::syntax_extension_map->{$1});
+ }
+ }
+
+ $self->{diff_current_highlighter} = $hl;
+ }
+}
+
+# Parse line to array of pairs text-style.
+# Resulted text pieces should form the complete source line,
+# no character added, no removed
+sub _create_hltokens($$) {
+ my ($self, $line) = @_;
+
+ my @hldata;
+
+ if ($self->{diff_current_highlighter}) {
+ $self->{diff_current_highlighter}->reset();
+ @hldata = $self->{diff_current_highlighter}->highlight($line);
+ } else {
+ @hldata = [$line, "Normal"];
+ }
+
+ return @hldata;
+}
+
+# Escape entities in string and replace tabs to nbsps
+sub _htmlize ($$) {
+ my ($self, $data) = @_;
+
+ my $formatted = HTML::Entities::encode($data);
+
+ $formatted = tabadjust($self->{tabwidth}, $formatted, 1);
+ if ($self->{brmode} == $Codestriker::LINE_BREAK_ASSIST_MODE) {
+ $formatted =~ s/^(\s+)/my $sp='';for(my
$i=0;$i<length($1);$i++){$sp.=' '}$sp;/ge;
+ }
+ else {
+ $formatted =~ s/\s/ /g;
+ }
+
+ return $formatted;
+}
+
+# Highlight line and place LXR links where appropriate
+sub _highlight_line ($$) {
+ my ($self, $line) = @_;
+
+ my @lxrposes = $self->lxr_data($line);
+
+ # Ensure we always have at east one pair.
+ push (@lxrposes, length($line) + 1000, 0);
+
+ my @hltokens = $self->_create_hltokens($line);
+
+ my $result = "";
+
+ # Position in original line.
+ my $startpos = 0;
+
+ # Next identifier position and length
+ my $identpos = shift @lxrposes;
+ my $identlen = shift @lxrposes;
+
+ while (@hltokens) {
+ my $text = shift @hltokens;
+ my $class = shift @hltokens;
+
+ $result .= "<span class=\"ds$class\">";
+
+ while ($startpos + length($text) > $identpos)
+ {
+ my $relpos = $identpos - $startpos;
+
+ # We believe each identifier is completely into token.
+ my $leader = substr($text, 0, $relpos);
+ my $identifier = substr($text, $relpos, $identlen);
+
+ $result .= $self->_htmlize($leader);
+ $result .= $self->lxr_ident($identifier);
+
+ # Advance original line position.
+ $text = substr($text, $relpos + $identlen);
+ $startpos += $relpos + $identlen;
+
+ # Get next identifier pair.
+ $identpos = shift @lxrposes;
+ $identlen = shift @lxrposes;
+ }
+
+ # Process end of token.
+ $result .= $self->_htmlize($text);
+
+ $result .= "</span>";
+ $startpos += length($text);
+ }
+
+ return $result;
+}
+
# Output the header for a series of deltas for a specific file.
sub delta_file_header ($$$$) {
my ($self, $filename, $revision, $repmatch) = @_;
@@ -533,6 +657,8 @@ sub delta_text ($$$$$$$$$$$) {
my ($self, $filename, $filenumber, $revision, $old_linenumber,
$new_linenumber, $text, $repmatch, $new, $link) = @_;
+ $self->_create_highlighter($filename);
+
my $query = $self->{query};
# Split up the lines, and display them, with the appropriate links.
@@ -565,24 +691,22 @@ sub display_coloured_data ($$$$) {
my $query = $self->{query};
- # Escape the data.
- $data = HTML::Entities::encode($data);
-
my $leftline = $self->{old_linenumber};
my $rightline = $self->{new_linenumber};
if ($data =~ /^\-(.*)$/) {
# Line corresponds to something which has been removed.
- add_old_change($1, $leftline);
+ add_old_change($self->_highlight_line($1), $leftline);
$leftline++;
} elsif ($data =~ /^\+(.*)$/) {
# Line corresponds to something which has been removed.
- add_new_change($1, $rightline);
+ add_new_change($self->_highlight_line($1), $rightline);
$rightline++;
} elsif ($data =~ /^\\/) {
# A diff comment such as "No newline at end of file" - ignore it.
} else {
# Strip the first space off the diff for proper alignment.
$data =~ s/^\s//;
+ $data = $self->_highlight_line($data);
# Render the previous diff changes visually.
$self->render_changes($filenumber, $link);
@@ -625,18 +749,6 @@ sub render_coloured_cell($$)
return " ";
}
- # Replace spaces and tabs with the appropriate number of 's.
- $data = tabadjust($self->{tabwidth}, $data, 1);
- if ($self->{brmode} == $Codestriker::LINE_BREAK_ASSIST_MODE) {
- $data =~ s/^(\s+)/my $sp='';for(my
$i=0;$i<length($1);$i++){$sp.=' '}$sp;/ge;
- }
- else {
- $data =~ s/\s/ /g;
- }
-
- # Add LXR links to the output.
- $data = $self->lxr_data($data);
-
# Unconditionally add a at the start for better alignment.
return " $data";
}
@@ -1045,17 +1157,21 @@ sub display_single_filedata ($$$$$) {
my $rightline = $self->{new_linenumber};
my $max_line_length = $self->{max_line_length};
+ # Strip off all EOL symbols to display DOS-style files correctly.
+ $data =~ s/[\r\n]*$//;
+
# Handling of either an old or new view.
if ($data =~ /^\-(.*)$/o) {
# A removed line.
- $self->add_minus_monospace_line($1, $leftline++);
+ $self->add_minus_monospace_line($self->_highlight_line($1),
$leftline++);
} elsif ($data =~ /^\+(.*)$/o) {
# An added line.
- $self->add_plus_monospace_line($1, $rightline++);
+ $self->add_plus_monospace_line($self->_highlight_line($1),
$rightline++);
} else {
# An unchanged line, output it and anything pending, and remove
# the leading space for alignment reasons.
$data =~ s/^\s//;
+ $data = $self->_highlight_line($data);
$self->flush_monospaced_lines($filenumber, $max_line_length, $new,
$link);
@@ -1078,9 +1194,6 @@ sub render_monospaced_line ($$$$$$$$) {
my ($self, $filenumber, $linenumber, $new, $data, $link,
$max_line_length, $class) = @_;
- # Convert any identifier to their LXR links.
- $data = $self->lxr_data(HTML::Entities::encode($data));
-
my $prefix = "";
my $digit_width = length($linenumber);
my $max_digit_width = $self->{max_digit_width};
-------------------------------------------------------------------------
This SF.net email is sponsored by DB2 Express
Download DB2 Express C - the FREE version of DB2 express and take
control of your XML. No limits. Just data. Click to get it now.
http://sourceforge.net/powerbar/db2/
_______________________________________________
Codestriker-user mailing list
Codestriker-user@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/codestriker-user