bgneal@4
|
1 A newline-to-break Python-Markdown extension
|
bgneal@4
|
2 ############################################
|
bgneal@4
|
3
|
bgneal@4
|
4 :date: 2011-05-09 22:40
|
bgneal@4
|
5 :tags: Markdown, Python
|
bgneal@4
|
6 :slug: a-newline-to-break-python-markdown-extension
|
bgneal@4
|
7 :author: Brian Neal
|
bgneal@4
|
8
|
bgneal@4
|
9 When I launched a new version of my website, I decided the new forums would use
|
bgneal@4
|
10 Markdown_ instead of BBCode_ for the markup. This decision was mainly a personal
|
bgneal@4
|
11 one for aesthetic reasons. I felt that Markdown was more natural to write compared
|
bgneal@4
|
12 to the clunky square brackets of BBCode.
|
bgneal@4
|
13
|
bgneal@4
|
14 My new site is coded in Python_ using the Django_ framework. For a Markdown implementation
|
bgneal@4
|
15 I chose `Python-Markdown`_.
|
bgneal@4
|
16
|
bgneal@4
|
17 My mainly non-technical users seemed largely ambivalent to the change from
|
bgneal@4
|
18 BBCode to Markdown. This was probably because I gave them a nice Javascript editor
|
bgneal@4
|
19 (`MarkItUp!`_) which inserted the correct markup for them.
|
bgneal@4
|
20
|
bgneal@4
|
21 However, shortly after launch, one particular feature of Markdown really riled up
|
bgneal@4
|
22 some users: the default line break behavior. In strict Markdown, to create a new
|
bgneal@4
|
23 paragraph, you must insert a blank line between paragraphs. Hard returns (newlines)
|
bgneal@4
|
24 are simply ignored, just like they are in HTML. You can, however, force a break by
|
bgneal@4
|
25 ending a line with two blank spaces. This isn't very intuitive, unlike the rest of
|
bgneal@4
|
26 Markdown.
|
bgneal@4
|
27
|
bgneal@4
|
28 Now I agree the default behavior is useful if you are creating an online document, like a blog post.
|
bgneal@4
|
29 However, non-technical users really didn't understand this behavior at all in the context
|
bgneal@4
|
30 of a forum post. For example, many of my users post radio-show playlists, formatted with
|
bgneal@4
|
31 one song per line. When such a playlist was pasted into a forum post, Markdown made it
|
bgneal@4
|
32 all one giant run-together paragraph. This did not please my users. Arguably, they should
|
bgneal@4
|
33 have used a Markdown list. But it became clear teaching people the new syntax wasn't
|
bgneal@4
|
34 going to work, especially when it used to work just fine in BBCode and they had created
|
bgneal@4
|
35 their playlists in the same way for several years.
|
bgneal@4
|
36
|
bgneal@4
|
37 It turns out I am not alone in my observations (or on the receiving end of user wrath). Other,
|
bgneal@4
|
38 much larger sites, like StackOverflow_ and GitHub_, have altered their Markdown parsers
|
bgneal@4
|
39 to treat newlines as hard breaks. How can this be done with Python-Markdown?
|
bgneal@4
|
40
|
bgneal@4
|
41 It turns out this is really easy. Python-Markdown was designed with user customization
|
bgneal@4
|
42 in mind by offering an extension facility. The `extension documentation`_ is good,
|
bgneal@4
|
43 and you can find extension writing help on the friendly `mailing list`_.
|
bgneal@4
|
44
|
bgneal@4
|
45 Here is a simple extension for Python-Markdown that turns newlines into HTML <br /> tags.
|
bgneal@4
|
46
|
bgneal@4
|
47 .. sourcecode:: python
|
bgneal@4
|
48
|
bgneal@4
|
49 """
|
bgneal@4
|
50 A python-markdown extension to treat newlines as hard breaks; like
|
bgneal@4
|
51 StackOverflow and GitHub flavored Markdown do.
|
bgneal@4
|
52
|
bgneal@4
|
53 """
|
bgneal@4
|
54 import markdown
|
bgneal@4
|
55
|
bgneal@4
|
56
|
bgneal@4
|
57 BR_RE = r'\n'
|
bgneal@4
|
58
|
bgneal@4
|
59 class Nl2BrExtension(markdown.Extension):
|
bgneal@4
|
60
|
bgneal@4
|
61 def extendMarkdown(self, md, md_globals):
|
bgneal@4
|
62 br_tag = markdown.inlinepatterns.SubstituteTagPattern(BR_RE, 'br')
|
bgneal@4
|
63 md.inlinePatterns.add('nl', br_tag, '_end')
|
bgneal@4
|
64
|
bgneal@4
|
65
|
bgneal@4
|
66 def makeExtension(configs=None):
|
bgneal@4
|
67 return Nl2BrExtension(configs)
|
bgneal@4
|
68
|
bgneal@4
|
69 I saved this code in a file called ``mdx_nl2br.py`` and put it on my ``PYTHONPATH``. You can then use
|
bgneal@4
|
70 it in a Django template like this:
|
bgneal@4
|
71
|
bgneal@4
|
72 .. sourcecode:: django
|
bgneal@4
|
73
|
bgneal@4
|
74 {{ value|markdown:"nl2br" }}
|
bgneal@4
|
75
|
bgneal@4
|
76 To use the extension in Python code, something like this should do the trick:
|
bgneal@4
|
77
|
bgneal@4
|
78 .. sourcecode:: python
|
bgneal@4
|
79
|
bgneal@4
|
80 import markdown
|
bgneal@4
|
81 md = markdown.Markdown(safe_mode=True, extensions=['nl2br'])
|
bgneal@4
|
82 converted_text = md.convert(text)
|
bgneal@4
|
83
|
bgneal@4
|
84 **Update (June 21, 2011):** This extension is now being distributed with
|
bgneal@4
|
85 Python-Markdown! See `issue 13 on github`_ for the details. Thanks to Waylan
|
bgneal@4
|
86 Limberg for the help in creating the extension and for including it with
|
bgneal@4
|
87 Python-Markdown.
|
bgneal@4
|
88
|
bgneal@4
|
89
|
bgneal@4
|
90 .. _Markdown: http://daringfireball.net/projects/markdown/
|
bgneal@4
|
91 .. _BBCode: http://en.wikipedia.org/wiki/BBCode
|
bgneal@4
|
92 .. _Python: http://python.org
|
bgneal@4
|
93 .. _Django: http://djangoproject.com
|
bgneal@4
|
94 .. _MarkItUp!: http://markitup.jaysalvat.com/home/
|
bgneal@4
|
95 .. _StackOverflow: http://blog.stackoverflow.com/2009/10/markdown-one-year-later/
|
bgneal@4
|
96 .. _GitHub: http://github.github.com/github-flavored-markdown/
|
bgneal@4
|
97 .. _Python-Markdown: http://www.freewisdom.org/projects/python-markdown/
|
bgneal@4
|
98 .. _extension documentation: http://www.freewisdom.org/projects/python-markdown/Writing_Extensions
|
bgneal@4
|
99 .. _mailing list: http://lists.sourceforge.net/lists/listinfo/python-markdown-discuss
|
bgneal@4
|
100 .. _issue 13 on github: https://github.com/waylan/Python-Markdown/issues/13
|