Markdown With Syntax Highlighting In Django

Written by Martin Geber on October 27, 2007 at 9:30 p.m. Filed unter: DjangoPython.  1 Comment. Trackback URL.

Contents

These days I wrote my first Django project (you are currently browsing it). Of course I wanted to post entries into this blog with code snippets. To make the reading experiance better for my readers I decided to use a syntax highlighter. You find thousands of them around the WWW.

The first try: syntaxhighlighter with tiny_mce

I decided to use syntaxhighlighter, which is purly JavaScript-based, so I didn't need to anything. (I was glad, because I was, and still am a Django and Python newbee.) The only thing I had to do was to use either of this to enable my syntax highlighting:

<!-- use a textarea, where I place the code -->
<textarea name="code" class="c#" cols="60" rows="10">
    // Python-code goes here
</textarea>

<!-- or use this (HTML-invalid -->
<pre name="code" class="python">
    // Python-code goes here
</pre>

As the second possibility syntaxhighlighter provides is invalid HTML, and I want my pages to be XHTML1.1-valid, I wanted to use the first possibility.

To write even more comfortable I installed tiny_mce according to this tutorial.

But after writing my first entry I was completely sure that I have to change the way I highlight my syntax. The problem is that tiny_mce has problems with textareas, what forced me to edit the HTML directly, here is a little snipped of code I had to deal with:

<textarea class="html" name="code">
&lt;!-- result, when {{ object.title }} was &quot;My test entry&quot; --&gt; 
&lt;html&gt; 
&nbsp;&nbsp;&nbsp; &lt;head&gt; 
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &lt;title&gt;Breadcrumps Location Bar&lt;/title&gt; 
&nbsp;&nbsp;&nbsp; &lt;/head&gt; 
&nbsp;&nbsp;&nbsp; &lt;body&gt; 
&lt;div id=&quot;breadcrumbs&quot;&gt; 
&lt;a href=&quot;/&quot;&gt;Home&lt;/a&gt; &amp;raquo; &lt;a href=&quot;/application/&quot;&gt;Application&lt;/a&gt; &amp;raquo; My Test Entry 
&lt;/div&gt; 
&nbsp;&nbsp;&nbsp; &lt;/body&gt; 
&lt;/html&gt;
</textarea>

I guess it is obvious that it is close to impossible to write more complex code using the tiny_mce in combination with syntaxhighlighter.

The new way: Markdown and Pygens

The tiny_mce ever was a tool I loved. I still use it on my Emma Watson-website to enable people to write nice-styled news without knowing HTML or learning some markup. And somehow, I never wanted to learn markups as well.

Python gives you the ability with some helpers to create great documentations, which use reStructuredText. This markup doesn't differ too much from Markdown.

I guess this is why I decided to not just ban syntaxhighlighter from my blog but also tiny_mce. I want to learn markdown.

There is another point, why this isn't a bad idea: Markdown in combination with its extension CodeHilite enables codeblock highlighting using GNU Enscript, dp.SyntaxHighlighter or Pygments.

The definition of what I wanted to achieve i clear now, I guess.
I wanted to replace tiny_mce and syntaxhighlighter. And add instead markdown with CodeHilite into my weblog.

How to install Markdown and CodeHilite

First of all install Pygments and Markdown:

:~# easy_install Pygments
:~# easy_install markdown

Now CodeHilite must be installed. Just put the file, which can be downloaded from the homepage into your PYTHONPATH (most commonly /usr/lib/python2.4/site-packages/). If you are not sure how to do it on your server, here is a small explanation: First copy the source code, than go onto your server (SSH):

:~# cd /usr/lib/python2.4/site-packages/
:~# tough touch mdx_codehilite.py
:~# vi mdx_codehilite.py
# Insert copyed code here by pressing [Ins] and click the right mouse button
# Press [Ctrl]+[C] now and type ":w" then ":q"

That's it. Now everything is ready to use markdown in your Django Project.

Enabling Django Templates to use Markdown with CodeHilite

From now on it is easy. First create a Python module into one of your templatetags-dir (I usually use project/utils/templatetags, make sure utils is in your INSTALLED_APPS-attribute of your settings.py).

I created a filterlib.py into that directory. Into this file write the following Python code:

from django import template
from django.template.defaultfilters import stringfilter

from django.conf import settings

register = template.Library()

@register.filter(name='markdown')
@stringfilter
def markdown(value, arg=''):
    """
    Filter to create HTML out of Markdown, using custom extensions.

    The diffrence between this filter and the django-internal markdown
    filter (located in ``django/contrib/markup/templatetags/markup.py``)
    is that this filter enables extensions to be load.

    Usage::

        {{ object.text|markdown }}
        {{ object.text|markdown:"save" }}
        {{ object.text|markdown:"codehilite" }}
        {{ object.text|markdown:"save,codehilite" }}

    This code is taken from
    http://www.freewisdom.org/projects/python-markdown/Django
    """
    try:
        import markdown
    except ImportError:
        if settings.DEBUG:
            raise (template.TemplateSyntaxError,
                   "Error in {% markdown %} filter: "
                   + "The markdown library isn't installed.")
        else :
            from django.utils.html import escape, linebreaks
            return linebreaks(escape(value))
    else:
        extensions=arg.split(",")
        if len(extensions) > 0 and extensions[0] == "safe" :
            extensions = extensions[1:]
            safe_mode = True
        else :
            safe_mode = False
        return markdown.markdown(value, extensions, safe_mode=safe_mode)

(As it is noticed in the Docstring, this code was taken from http://www.freewisdom.org/projects/python-markdown/Django. They suggest to overwrite the original definition of markdown, I strongly recommand you not to do that for various reason, which doesn't belog here.)

This filter enables us now to load the CodeHilite into Markdown. See the magic:

<!-- we suggest object.text is filled with the following text -->
Hello World
===========

I'm the first Django-Markup test.

*  **Lorem ipsum** dolor sit amet, consectetuer adipiscing elit.
*  Nulla lacinia, mi eget congue aliquet, augue lorem vulputate mi, et
   ultricies enim ipsum vitae eros.
*  Nunc feugiat gravida *urna*.

> Sed tincidunt nunc non velit.
> Maecenas non felis.

<!-- your django template -->
{% load filterlib %}
<html>
    <head>Test</head>
    <body>
        {{ object.text|markdown:"codehilite" }}
    </body>
</html>

<!-- this will be produced -->
<html>
    <head>Test</head>
    <body>
        <h1>Hello World</h1>
        <p>I'm the first Django-Markup test.</p>
        <ul>
            <li><strong>Lorem ipsum</strong> dolor sit amet, consectetuer adipiscing elit.</li>
            <li>Nulla lacinia, mi eget congue aliquet, augue lorem vulputate mi, et
            ultricies enim ipsum vitae eros.</li>
            <li>Nunc feugiat gravida <em>urna</em>.</li>
        </ul>
        <blockquote>
            Sed tincidunt nunc non velit.
            Maecenas non felis.
        </blockquote>
    </body>
</html>

Congratulations! You can use Markdown now.

How to use Markdown and CodeHilite

Now you wonder how you can syntax highlight your source codes. Here the syntax of Markdown is limited, which is why we installed the CodeHilite. Markdown's syntax for code is:

   Introducing text:
   
        class Entry(models.Model):
            pass

To add syntax highlighting you just have to do this:

   Introducing text:
   
        :::python
        class Entry(models.Model):
            pass

This code will now be syntax-highlighted, in case you already have loaded a Syntax CSS into your HTML-page. As I love the look Jeff Croft gave his syntax-blocks, I used his as a starting point. Check my CSS to see which classes are available:
http://www.martin-geber.com/res/css/syntax.css
Please Notice: The class syntax is called codehilite by default, I changed this in the module mdx_codehilite.py.

Source code definition

As shown above ::: will introduce the language, which is used below. In case you want line numbers use #! instead.

Which languages are supported?
You can find a full list at Pygments, here is a small selection (web-and-django-related):

Yes, isn't that cool? You are are able to markup django-template code now perfectly.

What else?

Markdown is quite a good tool to allow users to markup their texts without allowing HTML. So just put it into your show_comments.html template the following line:

{% load filterlib %}
{{ comment.comment|striptags|markdown }}

In this example users aren't able to post source codes, which are highlighted. In case you'd add :"codehilite", the problem is that users can use Python and PHP highlighing, HTML would just be deleted. I'm not sure if there is a way to allow HTML within the code-block and delete all other tags. In case you have an answer to that, let me know.

Comments

The comments also include all Trackbacks.

#1

Paul commented, on April 13, 2008 at 5:17 a.m.:

Cool site! Very insightful! Now off to make my own Django-based site!

 
Post a Comment


Or

Your Way: Home » Thoughts » 2007 » October » Saturday, 27 » Markdown With Syntax Highlighting In Django