Nov

22

I said it, I did it.

I got syntax highlight working. I didn't do it during the same calendar day as when I said I'd do it, but everyone knows that your day can't actually end until you go to sleep. I'm currently in 2 hours of overtime on the 21st of November.

I figured this would be a little tricky, but I was honestly surprised that the solutions people had described in their blogs and on djangosnippets were so... complicated. Pygments as a markdown pre-processor. Ideally one would be able to just pass this through as an extension to markdown using the functionality in the existing template tag. Unfortunately, pre-processors seem to be a different sort of animal. In the end I couldn't keep things very DRY. I had to copy and alter the markdown template filter (I trimmed out a lot of the backwards compatibility stuff because I knew what version of markdown I'd be using), and (even worse), I had to copy and alter the actual markdown function.

Here's the code that I copied and modified. The only other things I needed to get this going were Pygments itself and a bunch of illegible css rules that Pygments generated for me. This is the "manni" colouring style, by the way.

from django import template
from django.conf import settings
from django.utils.encoding import smart_str, force_unicode
from django.utils.safestring import mark_safe
 
from markdown import Markdown
 
from pg_md_processor import CodeBlockPreprocessor
 
register = template.Library()
 
@register.filter
def markdown(value, arg=''):
"""
Runs Markdown over a given value, optionally using various
extensions python-markdown supports.
 
Syntax::
 
{{ value|markdown:"extension1_name,extension2_name..." }}
 
To enable safe mode, which strips raw HTML and only returns HTML
generated by actual Markdown syntax, pass "safe" as the first
extension in the list.
 
If the version of Markdown in use does not support extensions,
they will be silently ignored.
 
"""
extensions = [e for e in arg.split(",") if e]
if len(extensions) > 0 and extensions[0] == "safe":
extensions = extensions[1:]
safe_mode = True
else:
safe_mode = False
 
return mark_safe(_pygmented_markdown(force_unicode(value), extensions, safe_mode=safe_mode))
markdown.is_safe = True
 
def _pygmented_markdown(text,
extensions = [],
safe_mode = False):
 
extension_names = []
extension_configs = {}
 
for ext in extensions:
pos = ext.find("(")
if pos == -1:
extension_names.append(ext)
else:
name = ext[:pos]
extension_names.append(name)
pairs = [x.split("=") for x in ext[pos+1:-1].split(",")]
configs = [(x.strip(), y.strip()) for (x, y) in pairs]
extension_configs[name] = configs
 
md = Markdown(extensions=extension_names,
extension_configs=extension_configs,
safe_mode = safe_mode)
 
md.textPreprocessors.insert(0, CodeBlockPreprocessor())
 
return md.convert(text)

Other than stripping out all the compatibility stuff in the template filter, the only change there is from "markdown.markdown" to "_pygmented_markdown". And in _pygmented_markdown, I'm just adding one line towards the bottom:

md.textPreprocessors.insert(0, CodeBlockPreprocessor())

Seems like there ought to be a tidier way to do this.

Commenting should be set up soon. If you need to reach me. My email address is my first name "at" this blog's domain.