Hey there,

First of all, thanks for all the great work on cgit. Awesome stuff!

In my setup, I like to have the markdown files in the repos rendered as
HTML instead of having them highlighted as code. Turns out it's fairly
easy to achieve using filters. I attach the necessary patches. Feel free
to commit if you think they are generally useful.

Cheers,
   Lukasz
From 85974aaa1725ba1d94fe2b2093d9e838864b7804 Mon Sep 17 00:00:00 2001
From: Lukasz Janyst <[email protected]>
Date: Thu, 8 Dec 2016 06:06:05 -0800
Subject: [PATCH 1/3] ui-tree: don't assume that a source filter must render
 code

It may be useful to render general HTML instead of just colorized
code for formats such as Markdown. Source filters may easily add
"<pre><code>" tags themselves.

Signed-off-by: Lukasz Janyst <[email protected]>
---
 ui-tree.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/ui-tree.c b/ui-tree.c
index b310242..7b9df3a 100644
--- a/ui-tree.c
+++ b/ui-tree.c
@@ -45,12 +45,12 @@ static void print_text_buffer(const char *name, char *buf, unsigned long size)
 
 	if (ctx.repo->source_filter) {
 		char *filter_arg = xstrdup(name);
-		html("<td class='lines'><pre><code>");
+		html("<td class='lines'>");
 		cgit_open_filter(ctx.repo->source_filter, filter_arg);
 		html_raw(buf, size);
 		cgit_close_filter(ctx.repo->source_filter);
 		free(filter_arg);
-		html("</code></pre></td></tr></table>\n");
+		html("</td></tr></table>\n");
 		return;
 	}
 
-- 
2.7.3

From 1cae0fb2aebea80640ba9838c19bab4035530fd1 Mon Sep 17 00:00:00 2001
From: Lukasz Janyst <[email protected]>
Date: Thu, 8 Dec 2016 06:16:07 -0800
Subject: [PATCH 2/3] ui-tree: pass full file path to source filters

This is useful when rendering files that may reference other files
in the repo and need to calculate a link to the 'plain' view. An
example of this is Markdown, where:

![text](relative/path/image.jpg)

needs to end up being:

<img alt="text" src="../../plain/root/relative/path/image.jpg" />

Filters may easily compute basename themselves.

Signed-off-by: Lukasz Janyst <[email protected]>
---
 ui-tree.c | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/ui-tree.c b/ui-tree.c
index 7b9df3a..3bdfd26 100644
--- a/ui-tree.c
+++ b/ui-tree.c
@@ -17,7 +17,7 @@ struct walk_tree_context {
 	int state;
 };
 
-static void print_text_buffer(const char *name, char *buf, unsigned long size)
+static void print_text_buffer(const char *path, char *buf, unsigned long size)
 {
 	unsigned long lineno, idx;
 	const char *numberfmt = "<a id='n%1$d' href='#n%1$d'>%1$d</a>\n";
@@ -44,7 +44,7 @@ static void print_text_buffer(const char *name, char *buf, unsigned long size)
 	}
 
 	if (ctx.repo->source_filter) {
-		char *filter_arg = xstrdup(name);
+		char *filter_arg = xstrdup(path);
 		html("<td class='lines'>");
 		cgit_open_filter(ctx.repo->source_filter, filter_arg);
 		html_raw(buf, size);
@@ -115,7 +115,7 @@ static void set_title_from_path(const char *path)
 	ctx.page.title = new_title;
 }
 
-static void print_object(const unsigned char *sha1, char *path, const char *basename, const char *rev)
+static void print_object(const unsigned char *sha1, char *path, const char *rev)
 {
 	enum object_type type;
 	char *buf;
@@ -152,7 +152,7 @@ static void print_object(const unsigned char *sha1, char *path, const char *base
 	if (buffer_is_binary(buf, size))
 		print_binary_buffer(buf, size);
 	else
-		print_text_buffer(basename, buf, size);
+		print_text_buffer(path, buf, size);
 }
 
 struct single_tree_ctx {
@@ -341,7 +341,7 @@ static int walk_tree(const unsigned char *sha1, struct strbuf *base,
 			return READ_TREE_RECURSIVE;
 		} else {
 			walk_tree_ctx->state = 2;
-			print_object(sha1, buffer.buf, pathname, walk_tree_ctx->curr_rev);
+			print_object(sha1, buffer.buf, walk_tree_ctx->curr_rev);
 			strbuf_release(&buffer);
 			return 0;
 		}
-- 
2.7.3

From 465b7cad599ac74e50620159722f12c380007c82 Mon Sep 17 00:00:00 2001
From: Lukasz Janyst <[email protected]>
Date: Thu, 8 Dec 2016 06:46:49 -0800
Subject: [PATCH 3/3] filters: add source filter highlighting code and
 rendering markdown

The filter uses pygments to highlight code file and misaka to render
Markdown ('.md' files) as HTML. The markdown renderer supports image
links, highlighting embedded code snippets and math formulas with
MathJax.

Signed-off-by: Lukasz Janyst <[email protected]>
---
 filters/syntax-highlighting-with-markdown.py | 143 +++++++++++++++++++++++++++
 1 file changed, 143 insertions(+)
 create mode 100755 filters/syntax-highlighting-with-markdown.py

diff --git a/filters/syntax-highlighting-with-markdown.py b/filters/syntax-highlighting-with-markdown.py
new file mode 100755
index 0000000..e25565c
--- /dev/null
+++ b/filters/syntax-highlighting-with-markdown.py
@@ -0,0 +1,143 @@
+#!/usr/bin/env python3
+#-------------------------------------------------------------------------------
+# The filter highlights code and renders '.md' files as html also highlighting
+# the embedded code snippets. It assumes that you have python3, pygments and
+# misaka installed.
+#-------------------------------------------------------------------------------
+
+import sys
+import os
+import io
+from pygments import highlight
+from pygments.util import ClassNotFound
+from pygments.lexers import TextLexer
+from pygments.lexers import guess_lexer
+from pygments.lexers import guess_lexer_for_filename
+from pygments.lexers import get_lexer_by_name
+from pygments.formatters import HtmlFormatter
+import misaka
+import html
+
+#-------------------------------------------------------------------------------
+# Globals
+#-------------------------------------------------------------------------------
+mathjax   = 'https://cdn.mathjax.org/mathjax/latest/MathJax.js?config=default'
+style     = 'default'
+path      = os.path.dirname(sys.argv[1])
+path_comp = len(path.split('/'))
+input     = io.TextIOWrapper(sys.stdin.buffer,  encoding='utf-8')
+output    = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')
+
+markdown_style = """
+.markdown {
+  max-width: 800px;
+}
+
+.markdown .highlighted {
+  border: solid 1px #ccc;
+  margin: 1em;
+  padding: 1em;
+}
+
+.markdown blockquote {
+  margin-left: 1em;
+  padding-left: 0.5em;
+  border-left: solid 3px #ccc;
+}
+
+.markdown em {
+  text-decoration: underline;
+}
+"""
+
+extra_highlight_style = """
+.linenodiv pre {
+  margin:        6px 0px;
+  color:         #aaa;
+  border-radius: 0;
+  border:        none;
+  border-right:  solid 1px;
+  word-wrap:     normal;
+}
+
+.linenos {
+  width:          40px;
+  vertical-align: top;
+}
+"""
+
+#-------------------------------------------------------------------------------
+def build_plain_link(link):
+    plain_link = '../'
+    for i in range(path_comp):
+        plain_link += '../'
+    plain_link += 'plain/' + path + '/' + link
+    return plain_link
+
+#-------------------------------------------------------------------------------
+def highlight_pygments(filename, data):
+    formatter = HtmlFormatter(linenos='table', lineanchors='l')
+
+    try:
+        lexer = guess_lexer_for_filename(filename, data)
+    except ClassNotFound:
+        # check if there is any shebang
+        if data[0:2] == '#!':
+            lexer = guess_lexer(data)
+        else:
+            lexer = TextLexer()
+    except TypeError:
+        lexer = TextLexer()
+
+    highlight(data, lexer, formatter, outfile=output)
+
+#-------------------------------------------------------------------------------
+class CGitRenderer(misaka.HtmlRenderer):
+    def blockcode(self, text, lang):
+        if not lang:
+            ret = '<div class="highlighted">'
+            ret += '<pre>%s</pre>'
+            ret += '</div>'
+            return ret % (html.escape(text))
+
+        lexer = get_lexer_by_name(lang, stripall = True)
+        formatter = HtmlFormatter()
+        ret = '<div class="highlighted">'
+        ret +=  highlight(text, lexer, formatter)
+        ret += '</div>'
+        return ret
+
+    def image(self, link, title='', alt=''):
+        return '<img src="%s" alt="%s"/>' % \
+               (build_plain_link(link), alt)
+
+#-------------------------------------------------------------------------------
+def render_markdown(filename, data):
+    renderer = CGitRenderer()
+    md = misaka.Markdown(renderer, extensions=('fenced-code', 'math', 'tables'))
+    output.write('<script type="text/javascript" src="%s"></script>' % (mathjax))
+    output.write('<style>')
+    output.write(markdown_style)
+    output.write('</style>')
+    output.write('<div class="markdown">')
+    output.write(md(data))
+    output.write('</div>')
+
+#-------------------------------------------------------------------------------
+def emit_highlight_style():
+    formatter = HtmlFormatter(style=style)
+
+    output.write('<style>')
+    output.write(formatter.get_style_defs('.highlight'))
+    output.write(formatter.get_style_defs('.highlighttable'))
+    output.write(extra_highlight_style)
+    output.write('</style>')
+
+data = input.read()
+filename = sys.argv[1]
+
+emit_highlight_style()
+if filename.split('.')[-1] == 'md':
+    render_markdown(filename, data)
+else:
+    highlight_pygments(filename, data)
-- 
2.7.3

_______________________________________________
CGit mailing list
[email protected]
https://lists.zx2c4.com/mailman/listinfo/cgit

Reply via email to