Ported from GitHub Flavored Markdown
Usage:
from pygfm import gfm
import markdown
html_text = markdown(gfm(markdown_text))Note: This filter should be run on a markdown-formatted string BEFORE the markdown filter itself.
Ported from GitHub Flavored Markdown
Usage:
from pygfm import gfm
import markdown
html_text = markdown(gfm(markdown_text))Note: This filter should be run on a markdown-formatted string BEFORE the markdown filter itself.
| """ | |
| Python Github Flavoured Markdown | |
| Ported from http://github.github.com/github-flavored-markdown/ | |
| Usage: | |
| html_text = markdown(gfm(markdown_text)) | |
| Note: This filter should be run on the markdown-formatted string BEFORE the markdown filter itself. | |
| """ | |
| import re | |
| from hashlib import md5 | |
| def gfm(text): | |
| # Extract pre blocks. | |
| extractions = {} | |
| def pre_extraction_callback(matchobj): | |
| digest = md5(matchobj.group(0)).hexdigest() | |
| extractions[digest] = matchobj.group(0) | |
| return "{gfm-extraction-%s}" % digest | |
| pattern = re.compile(r'<pre>.*?</pre>', re.MULTILINE | re.DOTALL) | |
| text = re.sub(pattern, pre_extraction_callback, text) | |
| # Prevent foo_bar_baz from ending up with an italic word in the middle. | |
| def italic_callback(matchobj): | |
| s = matchobj.group(0) | |
| if list(s).count('_') >= 2: | |
| return s.replace('_', '\_') | |
| return s | |
| text = re.sub(r'^(?! {4}|\t)\w+_\w+_\w[\w_]*', italic_callback, text) | |
| # In very clear cases, let newlines become <br /> tags. | |
| def newline_callback(matchobj): | |
| if len(matchobj.group(1)) == 1: | |
| return matchobj.group(0).rstrip() + ' \n' | |
| else: | |
| return matchobj.group(0) | |
| pattern = re.compile(r'^[\w\<][^\n]*(\n+)', re.MULTILINE) | |
| text = re.sub(pattern, newline_callback, text) | |
| # Insert pre block extractions. | |
| def pre_insert_callback(matchobj): | |
| return '\n\n' + extractions[matchobj.group(1)] | |
| text = re.sub(r'{gfm-extraction-([0-9a-f]{32})\}', pre_insert_callback, text) | |
| return text | |
| # Test suite. | |
| try: | |
| from nose.tools import assert_equal | |
| except ImportError: | |
| def assert_equal(a, b): | |
| assert a == b, '%r != %r' % (a, b) | |
| def test_single_underscores(): | |
| """Don't touch single underscores inside words.""" | |
| assert_equal( | |
| gfm('foo_bar'), | |
| 'foo_bar', | |
| ) | |
| def test_underscores_code_blocks(): | |
| """Don't touch underscores in code blocks.""" | |
| assert_equal( | |
| gfm(' foo_bar_baz'), | |
| ' foo_bar_baz', | |
| ) | |
| def test_underscores_pre_blocks(): | |
| """Don't touch underscores in pre blocks.""" | |
| assert_equal( | |
| gfm('<pre>\nfoo_bar_baz\n</pre>'), | |
| '\n\n<pre>\nfoo_bar_baz\n</pre>', | |
| ) | |
| def test_pre_block_pre_text(): | |
| """Don't treat pre blocks with pre-text differently.""" | |
| a = '\n\n<pre>\nthis is `a\\_test` and this\\_too\n</pre>' | |
| b = 'hmm<pre>\nthis is `a\\_test` and this\\_too\n</pre>' | |
| assert_equal( | |
| gfm(a)[2:], | |
| gfm(b)[3:], | |
| ) | |
| def test_two_underscores(): | |
| """Escape two or more underscores inside words.""" | |
| assert_equal( | |
| gfm('foo_bar_baz'), | |
| 'foo\\_bar\\_baz', | |
| ) | |
| def test_newlines_simple(): | |
| """Turn newlines into br tags in simple cases.""" | |
| assert_equal( | |
| gfm('foo\nbar'), | |
| 'foo \nbar', | |
| ) | |
| def test_newlines_group(): | |
| """Convert newlines in all groups.""" | |
| assert_equal( | |
| gfm('apple\npear\norange\n\nruby\npython\nerlang'), | |
| 'apple \npear \norange\n\nruby \npython \nerlang', | |
| ) | |
| def test_newlines_long_group(): | |
| """Convert newlines in even long groups.""" | |
| assert_equal( | |
| gfm('apple\npear\norange\nbanana\n\nruby\npython\nerlang'), | |
| 'apple \npear \norange \nbanana\n\nruby \npython \nerlang', | |
| ) | |
| def test_newlines_list(): | |
| """Don't convert newlines in lists.""" | |
| assert_equal( | |
| gfm('# foo\n# bar'), | |
| '# foo\n# bar', | |
| ) | |
| assert_equal( | |
| gfm('* foo\n* bar'), | |
| '* foo\n* bar', | |
| ) |