Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

Add html rendering framework for blobs #47

Open
wants to merge 5 commits into from

2 participants

@seveas

Mostly because I want to render pod. But it's pretty generic as demonstrated by the relative ease with which I added a markdown renderer in the 3rd commit.

seveas added some commits
@seveas seveas Syntax higlighting improvements
- Support all languages Syntax::Highlight::Engine::Kate can detect
- Detect filetype based on #! lines in scripts
- Consider .t files to be perl code
a8f5701
@seveas seveas Have linenumbers in blob displays
Steal some code from the blob template and adapt the syntax highlighting
code to cope with it.
18add14
@seveas seveas Add a basic framework for rendering blobs as html
And more specifically, add the hardcoded renderers for pod.
5aee9cf
@seveas seveas Don't render garbage when no HTML renderer is found 2b0c14e
@seveas seveas And add a markdown renderer
Mostly to show how easy it is to add renderers now
cb6ab93
@doherty

.mkdn is another common filename extension for Markdown. You might check which ones Github accepts, and do the same thing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Jul 29, 2012
  1. @seveas

    Syntax higlighting improvements

    seveas authored
    - Support all languages Syntax::Highlight::Engine::Kate can detect
    - Detect filetype based on #! lines in scripts
    - Consider .t files to be perl code
  2. @seveas

    Have linenumbers in blob displays

    seveas authored
    Steal some code from the blob template and adapt the syntax highlighting
    code to cope with it.
  3. @seveas

    Add a basic framework for rendering blobs as html

    seveas authored
    And more specifically, add the hardcoded renderers for pod.
  4. @seveas
  5. @seveas

    And add a markdown renderer

    seveas authored
    Mostly to show how easy it is to add renderers now
This page is out of date. Refresh to see the latest.
View
1  Makefile.PL
@@ -103,6 +103,7 @@ requires 'List::MoreUtils';
requires 'Path::Class' => '0.17';
requires 'Syntax::Highlight::Engine::Kate';
requires 'Sys::Hostname';
+requires 'Text::Markdown';
requires_external_bin 'git';
View
58 lib/Gitalist/ContentMangler/Resolver/Default.pm
@@ -1,12 +1,60 @@
use MooseX::Declare;
+use Syntax::Highlight::Engine::Kate ();
+
+our @interpreters = (
+ 'awk',
+ 'bash',
+ 'ksh',
+ 'make',
+ 'node',
+ 'perl',
+ 'prolog',
+ 'python',
+ 'ruby',
+ 'sh',
+ 'tcl',
+);
+
+our %interpretersx = (
+ 'awk' => 'AWK',
+ 'ksh' => 'Bash',
+ 'make' => 'Makefile',
+ 'node' => 'Javascript',
+ 'sh' => 'Bash',
+);
class Gitalist::ContentMangler::Resolver::Default with Gitalist::ContentMangler::Resolver {
method resolve ($data) {
- # This should be pulled out of $self->config
- my $language;
- $language = 'Perl' if $data->{filename} =~ /\.p[lm]$/i;
- $language = 'Diff' if $data->{action} eq 'diff_fancy';
+ if($data->{action} eq 'diff_fancy') {
+ return 'Gitalist::ContentMangler::Transformer::SyntaxHighlight' => {language => 'Diff', css => 'Diff'};
+ }
+ my $kate = Syntax::Highlight::Engine::Kate->new();
+ # Detect .t files as perl code
+ $kate->extensions->{'*.t'} = ['Perl'];
+ my $language = $kate->languagePropose($data->{filename}) || $kate->languagePropose(lc $data->{filename});
+ if(!$language && exists($data->{blob})) {
+ my $interp = substr(${$data->{blob}}, 0, 256);
+ if($interp =~ /^#!(?:\S*\/)?([^\s\/]+)/) {
+ my $interp = $1;
+
+ for my $interpreter (@interpreters) {
+ if($interp =~ /$interpreter/) {
+ $language = $interpretersx{$interpreter} || ucfirst $interpreter;
+ last;
+ }
+ }
+ }
+ }
+ if($data->{action} eq 'html') {
+ if(($language || '') eq 'Perl' || $data->{filename} =~ /\.pod$/) {
+ return 'Gitalist::ContentMangler::Transformer::RenderPod' => {};
+ }
+ if($data->{filename} =~ /\.md$/) {
+ return 'Gitalist::ContentMangler::Transformer::RenderMarkdown' => {};
+ }
+ return 'Gitalist::ContentMangler::Transformer::NoRenderer' => {};
+ }
return unless $language;
- return 'Gitalist::ContentMangler::Transformer::SyntaxHighlight' => {language => $language, css => $language};
+ return 'Gitalist::ContentMangler::Transformer::SyntaxHighlight' => {language => $language, css => 'Code'};
}
}
View
12 lib/Gitalist/ContentMangler/Transformer/NoRenderer.pm
@@ -0,0 +1,12 @@
+use MooseX::Declare;
+
+class Gitalist::ContentMangler::Transformer::NoRenderer {
+ method transform($c, $config) {
+ $c->stash(
+ mangled => 1,
+ );
+ for (grep $_, $c->stash->{blobs} ? @{$c->stash->{blobs}} : $c->stash->{blob}) {
+ $_ = "No Renderer defined for this file format";
+ }
+ }
+}
View
13 lib/Gitalist/ContentMangler/Transformer/RenderMarkdown.pm
@@ -0,0 +1,13 @@
+use MooseX::Declare;
+
+class Gitalist::ContentMangler::Transformer::RenderMarkdown {
+ method transform($c, $config) {
+ $c->stash(
+ syntax_css => $c->uri_for("/static/css/syntax/Pod.css"),
+ mangled => 1,
+ );
+ for (grep $_, $c->stash->{blobs} ? @{$c->stash->{blobs}} : $c->stash->{blob}) {
+ $_ = $c->view('MarkdownRenderer')->render($c, $_, $config);
+ }
+ }
+}
View
13 lib/Gitalist/ContentMangler/Transformer/RenderPod.pm
@@ -0,0 +1,13 @@
+use MooseX::Declare;
+
+class Gitalist::ContentMangler::Transformer::RenderPod {
+ method transform($c, $config) {
+ $c->stash(
+ syntax_css => $c->uri_for("/static/css/syntax/Pod.css"),
+ mangled => 1,
+ );
+ for (grep $_, $c->stash->{blobs} ? @{$c->stash->{blobs}} : $c->stash->{blob}) {
+ $_ = $c->view('PodRenderer')->render($c, $_, $config);
+ }
+ }
+}
View
5 lib/Gitalist/Controller/Fragment/Ref.pm
@@ -93,6 +93,11 @@ after blob => sub {
$c->forward('Model::ContentMangler');
};
+after html => sub {
+ my ( $self, $c ) = @_;
+ $c->forward('Model::ContentMangler');
+};
+
after history => sub {
my ($self, $c) = @_;
my $repository = $c->stash->{Repository};
View
1  lib/Gitalist/Model/ContentMangler.pm
@@ -48,6 +48,7 @@ sub process {
my($transformer, $config) = $self->resolve({
filename => $c->stash->{filename} || '',
+ blob => \$c->stash->{blob},
config => Gitalist->config->{'Model::ContentMangler'},
action => $c->action->name,
});
View
5 lib/Gitalist/URIStructure/Ref.pm
@@ -86,6 +86,11 @@ sub blob : Chained('find') Does('FilenameArgs') Args() {
sub blame : Chained('find') Does('FilenameArgs') Args() {}
+sub html : Chained('find') Does('FilenameArgs') Args() {
+ my ($self, $c) = @_;
+ $c->forward('find_blob');
+}
+
sub history : Chained('find') Does('FilenameArgs') Args() {}
1;
View
19 lib/Gitalist/View/MarkdownRenderer.pm
@@ -0,0 +1,19 @@
+package Gitalist::View::MarkdownRenderer;
+use Moose;
+use namespace::autoclean;
+
+extends 'Catalyst::View';
+
+use Text::Markdown qw(markdown);
+
+sub proces {
+ my($self, $c) = @_;
+
+ $c->res->body($self->render($c, $c->res->body, $c->stash));
+}
+
+sub render {
+ my ($self, $c, $blob, $args) = @_;
+
+ sprintf '<div class="pod">%s</div>', markdown($blob);
+}
View
26 lib/Gitalist/View/PodRenderer.pm
@@ -0,0 +1,26 @@
+package Gitalist::View::PodRenderer;
+use Moose;
+use namespace::autoclean;
+
+extends 'Catalyst::View';
+
+use Pod::Simple::HTML;
+
+sub proces {
+ my($self, $c) = @_;
+
+ $c->res->body($self->render($c, $c->res->body, $c->stash));
+}
+
+sub render {
+ my ($self, $c, $blob, $args) = @_;
+
+ my $pod = Pod::Simple::HTML->new();
+ my $ret;
+ $pod->html_header_before_title('<div class="pod"><span style="display: none">');
+ $pod->html_header_after_title("</span>");
+ $pod->html_footer('</div>');
+ $pod->output_string(\$ret);
+ $pod->parse_string_document($blob);
+ return $ret;
+}
View
27 lib/Gitalist/View/SyntaxHighlight.pm
@@ -5,7 +5,6 @@ use namespace::autoclean;
extends 'Catalyst::View';
use Syntax::Highlight::Engine::Kate ();
-use Syntax::Highlight::Engine::Kate::Perl ();
use HTML::Entities qw(encode_entities);
@@ -19,7 +18,7 @@ sub render {
my ($self, $c, $blob, $args) = @_;
# Don't bother with anything over 64kb, it'll be tragically slow.
- return encode_entities $blob if length $blob > 8192;
+ return encode_entities $blob if length $blob > 65536;
my $lang = $args->{language};
@@ -53,6 +52,30 @@ sub render {
);
my $hltxt = $hl->highlightText($blob);
+
+ # Line numbering breaks <span class="Other">#define foo\nbar</span>
+ # So let's fix that by closing all spans at end-of-line and opening
+ # new ones on the next, if needed.
+
+ my @lines = split(/\n/, $hltxt);
+ my $last_class = undef;
+ map {
+ unless($_ =~ s/^<\/span>//) {
+ if($last_class) {
+ $_ = "<span class=\"$last_class\">" . $_;
+ }
+ }
+ $last_class = undef;
+ if($_ =~ /<span class="(.*?)">(?!.*<\/span>)/) {
+ $last_class = $1;
+ }
+ if($_ !~ /<\/span>$/) {
+ $_ .= "</span>";
+ }
+ $_;
+ } @lines;
+
+ $hltxt = join("\n", @lines);
$hltxt =~ s/([^[:ascii:]])/encode_entities($1)/eg;
$hltxt;
};
View
11 root/fragment/ref/blob.tt2
@@ -3,7 +3,14 @@
[%- ELSIF is_binary -%]
<div class='blob'>This is a binary file which won't render natively on the web, but you can get it here all the same: <a href="[% c.uri_for_action('/ref/raw', c.req.captures, filename) %]" title="[% filename %]">[% filename %]</a></div>
[%- ELSE -%]
-[%- INCLUDE inc/syntax_highlight_css.tt2 -%]
[%- USE UTF8Decode -%]
-<pre class='blob'>[% IF mangled; blob; ELSE; blob | utf8_decode | html; END; %]</pre>
+[%- IF mangled; INCLUDE inc/syntax_highlight_css.tt2; END -%]
+[% FOR line IN blob.split("\n") %]
+ <tr class=''>
+ <td nowrap class='lineno' id='l[% loop.index+1 %]'><tt><a href='[% c.uri_for_action('/ref/blame', [Repository.name, info.commit.sha1], filename.to_path ) %]#l[% lopo.index %]'>[% loop.index+1 %]</a></tt></td>
+ <td nowrap class='data'><pre>[%-
+ IF mangled; line; ELSE; line | utf8_decode | html; END
+ -%]</pre></td>
+ </tr>
+[% END %]
[%- END -%]
View
2  root/fragment/ref/html.tt2
@@ -0,0 +1,2 @@
+[%- IF mangled; INCLUDE inc/syntax_highlight_css.tt2; END -%]
+[% blob %]
View
7 root/ref/blob.tt2
@@ -6,7 +6,12 @@
</div>
[% END %]
+<div id="blob">
+<table class="listing">
+<tbody>
[% subinclude('/fragment/ref/blob', c.req.captures, c.req.args.to_path) %]
-
+</tbody>
+</table>
+</div>
View
3  root/ref/html.tt2
@@ -0,0 +1,3 @@
+<h3 class="header">[% INCLUDE 'nav/path.tt2' %]</h3>
+
+[% subinclude('/fragment/ref/html', c.req.captures, c.req.args.to_path) %]
View
30 root/static/css/core.css
@@ -493,21 +493,21 @@ BUT the final width needs to be set with javascript based on the parent element
/* /blame */
-#blame pre, #blame tt {
+#blame pre, #blame tt, #blob pre, #blob tt {
margin: 0;
font-size: 12px;
}
#blame .commit-info {
}
-#blame .lineno {
+#blame .lineno, #blob .lineno {
text-align: right;
padding: 0 8px;
}
-#blame a {
+#blame a, #blob a {
text-decoration: none;
}
-#blame {
+#blame, #blob {
overflow-x: scroll;
}
#blame tr.alt {
@@ -516,37 +516,21 @@ BUT the final width needs to be set with javascript based on the parent element
#blame tbody tr:hover {
background-color: #fefeaa;
}
-#blame td {
+#blame td, #blob td {
vertical-align:middle;
padding: 3px;
}
-#blame td.lineno {
+#blame td.lineno, #blob td.lineno {
background-color: #eee;
}
#blame td.date, #blame td.author, #blame td.commit-info {
}
-#blame tbody td.data {
+#blame tbody td.data, #blob tbody td.data {
padding-left: 5px;
background-color: #333;
color: #ddd;
}
-/* /blob */
-pre.blob {
- background-color: #333;
- color: #ddd;
- border-left: solid 3px #c33;
- padding: 5px;
- padding-left: 15px;
- margin: 20px 15px 20px;
- overflow:auto;
- font-size:12px;
-}
-div.blob {
- text-align: center;
- margin: 30px;
-}
-
/* /blobdiff etc */
.commit-message {
View
0  root/static/css/syntax/Perl.css → root/static/css/syntax/Code.css
File renamed without changes
View
31 root/static/css/syntax/Pod.css
@@ -0,0 +1,31 @@
+div.pod {
+ margin-left: 1em;
+ margin-right: 1em;
+}
+
+div.pod *, div.pod * a:hover {
+ color: #666;
+}
+
+div.pod h1 {
+ padding : 0px;
+}
+
+div.pod h2 {
+ margin: 0px;
+ font-size: 1.4em;
+}
+
+div.pod h1 a, div.pod h2 a, div.pod h3 a, div.pod h4 a, div.pod h5 a, div.pod h6 a {
+ text-decoration: none;
+}
+
+div.pod p, div.pod dl, div.pod ul {
+ padding-left: 40px;
+ padding-right: 40px;
+}
+
+div.pod dl dd, div.pod ul li, div.pod dl p, div.pod ul p {
+ padding-left: 0px;
+ padding-right: 0px;
+}
View
0  root/static/css/syntax/pod.css
No changes.
View
1  root/wrapper.tt2
@@ -104,6 +104,7 @@
"raw" => 1,
"blame" => 1,
"history" => 1,
+ "html" => 1,
};
SET action_name = c.action.name
Something went wrong with that request. Please try again.