blob: f10aee5b6d07e1c04637b14fdcf263573a8f5caf [file] [log] [blame]
diff --git a/CHANGES.rst b/CHANGES.rst
index eb204dd..3eb99a7 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -3,6 +3,14 @@ Changelog
Here is the full history of mistune.
+Version 0.7
+~~~~~~~~~~~
+
+Release date not decided.
+
+* Fix the breaking change in version 0.6 with options: **parse_inline_html** and **parse_block_html**
+
+
Version 0.6
~~~~~~~~~~~
diff --git a/README.rst b/README.rst
index 894833b..f6cb5f9 100644
--- a/README.rst
+++ b/README.rst
@@ -38,12 +38,6 @@ Installing mistune with pip::
$ pip install mistune
-If pip is not available, try easy_install::
-
- $ easy_install mistune
-
-Cython Feature
-~~~~~~~~~~~~~~
Mistune can be faster, if you compile with cython::
@@ -59,10 +53,49 @@ A simple API that render a markdown formatted text:
import mistune
- mistune.markdown('I am using **markdown**')
- # output: <p>I am using <strong>markdown</strong></p>
+ mistune.markdown('I am using **mistune markdown parser**')
+ # output: <p>I am using <strong>mistune markdown parser</strong></p>
+
+If you care about performance, it is better to re-use the Markdown instance:
+
+.. code:: python
+
+ import mistune
+
+ markdown = mistune.Markdown()
+ markdown('I am using **mistune markdown parser**')
+
+Mistune has enabled all features by default. You don't have to configure
+anything. But there are options for you to change the parser behaviors.
+
+
+Options
+-------
+
+Here is a list of all options that will affect the rendering results,
+configure them with ``mistune.Renderer``:
+
+.. code:: python
+
+ renderer = mistune.Renderer(escape=True, hard_wrap=True)
+ # use this renderer instance
+ markdown = mistune.Markdown(renderer=renderer)
+ markdown(text)
+
+* **escape**: if set to *True*, all raw html tags will be escaped.
+* **hard_wrap**: if set to *True*, it will has GFM line breaks feature.
+* **use_xhtml**: if set to *True*, all tags will be in xhtml, for example: ``<hr />``.
+* **parse_html**: parse text in block and inline level html.
+* **parse_block_html**: parse text only in block level html.
+* **parse_inline_html**: parse text only in inline level html.
+
+When using the default renderer, you can use one of the following shortcuts::
+
+ mistune.markdown(text, escape=True, hard_wrap=True)
+
+ markdown = mistune.Markdown(escape=True, hard_wrap=True)
+ markdown(text)
-Mistune has all features by default. You don't have to configure anything.
Renderer
--------
@@ -79,7 +112,7 @@ Here is an example of code highlighting:
from pygments.lexers import get_lexer_by_name
from pygments.formatters import HtmlFormatter
- class MyRenderer(mistune.Renderer):
+ class HighlightRenderer(mistune.Renderer):
def block_code(self, code, lang):
if not lang:
return '\n<pre><code>%s</code></pre>\n' % \
@@ -88,9 +121,9 @@ Here is an example of code highlighting:
formatter = HtmlFormatter()
return highlight(code, lexer, formatter)
- renderer = MyRenderer()
- md = mistune.Markdown(renderer=renderer)
- print(md.render('Some Markdown text.'))
+ renderer = HighlightRenderer()
+ markdown = mistune.Markdown(renderer=renderer)
+ print(markdown('Some code text.'))
Block Level
@@ -127,34 +160,18 @@ Here is a list of span level renderer API::
linebreak()
newline()
link(link, title, content)
- tag(html)
strikethrough(text)
text(text)
+ inline_html(text)
+Footnotes
+~~~~~~~~~
-Options
--------
-
-Here is a list of all options that will affect the rendering results:
-
-.. code:: python
-
- renderer = mistune.Renderer(escape=True)
- md = mistune.Markdown(renderer=renderer)
- md.render(text)
-
-* **escape**: if set to *True*, all raw html tags will be escaped.
-* **hard_wrap**: if set to *True*, it will has GFM line breaks feature.
-* **use_xhtml**: if set to *True*, all tags will be in xhtml, for example: ``<hr />``.
-* **parse_html**: parse text in block level html.
-
-When using the default renderer, you can use one of the following shorthands::
-
- mistune.markdown(text, escape=True)
-
- md = mistune.Markdown(escape=True)
- md.render(text)
+Here is a list of renderers related to footnotes::
+ footnote_ref(key, index)
+ footnote_item(key, text)
+ footnotes(text)
Lexers
------
@@ -172,33 +189,23 @@ It is an inline grammar, which requires custom ``InlineGrammar`` and
import copy
from mistune import Renderer, InlineGrammar, InlineLexer
- class MyRenderer(Renderer):
+ class WikiLinkRenderer(Renderer):
def wiki_link(self, alt, link):
return '<a href="%s">%s</a>' % (link, alt)
+ class WikiLinkInlineLexer(InlineLexer):
+ def enable_wiki_link(self):
+ # add wiki_link rules
+ self.rules.wiki_link = re.compile(
+ r'\[\[' # [[
+ r'([\s\S]+?\|[\s\S]+?)' # Page 2|Page 2
+ r'\]\](?!\])' # ]]
+ )
- class MyInlineGrammar(InlineGrammar):
- # it would take a while for creating the right regex
- wiki_link = re.compile(
- r'\[\[' # [[
- r'([\s\S]+?\|[\s\S]+?)' # Page 2|Page 2
- r'\]\](?!\])' # ]]
- )
-
-
- class MyInlineLexer(InlineLexer):
- default_rules = copy.copy(InlineLexer.default_rules)
-
- # Add wiki_link parser to default rules
- # you can insert it any place you like
- default_rules.insert(3, 'wiki_link')
-
- def __init__(self, renderer, rules=None, **kwargs):
- if rules is None:
- # use the inline grammar
- rules = MyInlineGrammar()
-
- super(MyInlineLexer, self).__init__(renderer, rules, **kwargs)
+ # Add wiki_link parser to default rules
+ # you can insert it some place you like
+ # but place matters, maybe 3 is not good
+ self.default_rules.insert(3, 'wiki_link')
def output_wiki_link(self, m):
text = m.group(1)
@@ -211,8 +218,10 @@ You should pass the inline lexer to ``Markdown`` parser:
.. code:: python
- renderer = MyRenderer()
- inline = MyInlineLexer(renderer)
+ renderer = WikiLinkRenderer()
+ inline = WikiLinkInlineLexer(renderer)
+ # enable the feature
+ inline.enable_wiki_link()
markdown = Markdown(renderer, inline=inline)
markdown('[[Link Text|Wiki Link]]')
@@ -220,12 +229,21 @@ It is the same with block level lexer. It would take a while to understand
the whole mechanism. But you won't do the trick a lot.
-Contribution
-------------
+Contribution & Extensions
+-------------------------
Mistune itself doesn't accept any extension. It will always be a simple one
file script.
If you want to add features, you can head over to `mistune-contrib`_.
+Here are some extensions already in `mistune-contrib`_:
+
+* Math/MathJax features
+* Highlight Code Renderer
+* TOC table of content features
+* MultiMarkdown Metadata parser
+
+Get inspired with the contrib repository.
+
.. _`mistune-contrib`: https://github.com/lepture/mistune-contrib
diff --git a/mistune.py b/mistune.py
index 316f86d..86d215e 100644
--- a/mistune.py
+++ b/mistune.py
@@ -476,6 +476,11 @@ class InlineLexer(object):
'double_emphasis', 'emphasis', 'code',
'linebreak', 'strikethrough', 'text',
]
+ inline_html_rules = [
+ 'escape', 'autolink', 'url', 'link', 'reflink',
+ 'nolink', 'double_emphasis', 'emphasis', 'code',
+ 'linebreak', 'strikethrough', 'text',
+ ]
def __init__(self, renderer, rules=None, **kwargs):
self.renderer = renderer
@@ -491,6 +496,10 @@ class InlineLexer(object):
self._in_link = False
self._in_footnote = False
+ kwargs.update(self.renderer.options)
+ _to_parse = kwargs.get('parse_html') or kwargs.get('parse_inline_html')
+ self._parse_inline_html = _to_parse
+
def __call__(self, text):
return self.output(text)
@@ -553,7 +562,15 @@ class InlineLexer(object):
return self.renderer.autolink(link, False)
def output_inline_html(self, m):
- return self.renderer.inline_html(m.group(0))
+ text = m.group(0)
+ if self._parse_inline_html:
+ if m.group(1) == 'a':
+ self._in_link = True
+ text = self.output(text, rules=self.inline_html_rules)
+ self._in_link = False
+ else:
+ text = self.output(text, rules=self.inline_html_rules)
+ return self.renderer.inline_html(text)
def output_footnote(self, m):
key = _keyify(m.group(1))
@@ -909,6 +926,10 @@ class Markdown(object):
self.footnotes = []
self.tokens = []
+ # detect if it should parse text in block html
+ _to_parse = kwargs.get('parse_html') or kwargs.get('parse_block_html')
+ self._parse_block_html = _to_parse
+
def __call__(self, text):
return self.parse(text)
@@ -1072,7 +1093,7 @@ class Markdown(object):
def output_block_html(self):
text = self.token['text']
- if self.options.get('parse_html') and not self.token.get('pre'):
+ if self._parse_block_html and not self.token.get('pre'):
text = self.inline(text)
return self.renderer.block_html(text)
diff --git a/tests/test_cases.py b/tests/test_cases.py
index 933fa4c..3853a67 100644
--- a/tests/test_cases.py
+++ b/tests/test_cases.py
@@ -99,12 +99,36 @@ def test_use_xhtml():
assert '<img src="bar" alt="foo" title="title" />' in ret
-def test_block_html():
+def test_parse_html():
ret = mistune.markdown('<div>**foo**</div>')
assert '<strong>' not in ret
ret = mistune.markdown('<div>**foo**</div>', parse_html=True)
assert '<strong>' in ret
+ ret = mistune.markdown('<span>**foo**</span>')
+ assert '<strong>' not in ret
+ ret = mistune.markdown('<span>**foo**</span>', parse_html=True)
+ assert '<strong>' in ret
+
+ ret = mistune.markdown('<span>http://example.com</span>', parse_html=True)
+ assert 'href' in ret
+ ret = mistune.markdown('<a>http://example.com</a>', parse_html=True)
+ assert 'href' not in ret
+
+
+def test_parse_inline_html():
+ ret = mistune.markdown('<div>**foo**</div>', parse_inline_html=True)
+ assert '<strong>' not in ret
+ ret = mistune.markdown('<span>**foo**</span>', parse_inline_html=True)
+ assert '<strong>' in ret
+
+
+def test_parse_block_html():
+ ret = mistune.markdown('<div>**foo**</div>', parse_block_html=True)
+ assert '<strong>' in ret
+ ret = mistune.markdown('<span>**foo**</span>', parse_block_html=True)
+ assert '<strong>' not in ret
+
def test_trigger_more_cases():
markdown = mistune.Markdown(
@@ -114,79 +138,3 @@ def test_trigger_more_cases():
)
ret = markdown.render('foo[^foo]\n\n[^foo]: foo\n\n[^foo]: bar\n')
assert 'bar' not in ret
-
-
-def test_custom_lexer():
- import copy
-
- class MyInlineGrammar(mistune.InlineGrammar):
- # it would take a while for creating the right regex
- wiki_link = re.compile(
- r'\[\[' # [[
- r'([\s\S]+?\|[\s\S]+?)' # Page 2|Page 2
- r'\]\](?!\])' # ]]
- )
-
- class MyInlineLexer(mistune.InlineLexer):
- default_rules = copy.copy(mistune.InlineLexer.default_rules)
- default_rules.insert(3, 'wiki_link')
-
- def __init__(self, renderer, rules=None, **kwargs):
- if rules is None:
- rules = MyInlineGrammar()
-
- super(MyInlineLexer, self).__init__(renderer, rules, **kwargs)
-
- def output_wiki_link(self, m):
- text = m.group(1)
- alt, link = text.split('|')
- return '<a href="%s">%s</a>' % (link, alt)
-
- markdown = mistune.Markdown(inline=MyInlineLexer)
- ret = markdown('[[Link Text|Wiki Link]]')
- assert '<a href' in ret
-
-
-def test_token_tree():
- """Tests a Renderer that returns a list from the placeholder method."""
-
- class CustomRenderer(mistune.Renderer):
- def placeholder(self):
- return []
-
- def __getattribute__(self, name):
- """Saves the arguments to each Markdown handling method."""
- found = CustomRenderer.__dict__.get(name)
- if found:
- return object.__getattribute__(self, name)
-
- def fake_method(*args, **kwargs):
- return [(name, args, kwargs)]
- return fake_method
-
- with open(os.path.join(root, 'fixtures', 'data', 'tree.md')) as f:
- content = f.read()
-
- expected = [
- ('header', ([('text', ('Title here',), {})], 2, 'Title here'), {}),
- ('paragraph', ([('text', ('Some text.',), {})],), {}),
- ('paragraph',
- ([('text', ('In two paragraphs. And then a list.',), {})],),
- {}),
- ('list',
- ([('list_item', ([('text', ('foo',), {})],), {}),
- ('list_item',
- ([('text', ('bar',), {}),
- ('list',
- ([('list_item', ([('text', ('meep',), {})],), {}),
- ('list_item', ([('text', ('stuff',), {})],), {})],
- True),
- {})],),
- {})],
- False),
- {})
- ]
-
- processor = mistune.Markdown(renderer=CustomRenderer())
- found = processor.render(content)
- assert expected == found, "Expected:\n%r\n\nFound:\n%r" % (expected, found)
diff --git a/tests/test_subclassing.py b/tests/test_subclassing.py
index 2cebfc0..f0df225 100644
--- a/tests/test_subclassing.py
+++ b/tests/test_subclassing.py
@@ -2,6 +2,7 @@
import os
import re
+import copy
import mistune
root = os.path.dirname(__file__)
@@ -73,7 +74,7 @@ class MarkdownWithMath(mistune.Markdown):
)
-class CustomRenderer(mistune.Renderer):
+class MathRenderer(mistune.Renderer):
def block_math(self, text):
return '$$%s$$' % text
@@ -92,7 +93,7 @@ def assert_data(filename):
else:
text = filename
- rv = MarkdownWithMath(renderer=CustomRenderer()).render(text)
+ rv = MarkdownWithMath(renderer=MathRenderer()).render(text)
assert text in rv
@@ -109,3 +110,82 @@ def test_markdown2html_math():
def test_math_paragraph():
# https://github.com/ipython/ipython/issues/6724
assert_data('math-paragraph.md')
+
+
+class WikiInlineGrammar(mistune.InlineGrammar):
+ # it would take a while for creating the right regex
+ wiki_link = re.compile(
+ r'\[\[' # [[
+ r'([\s\S]+?\|[\s\S]+?)' # Page 2|Page 2
+ r'\]\](?!\])' # ]]
+ )
+
+
+class WikiInlineLexer(mistune.InlineLexer):
+ default_rules = copy.copy(mistune.InlineLexer.default_rules)
+ default_rules.insert(3, 'wiki_link')
+
+ def __init__(self, renderer, rules=None, **kwargs):
+ if rules is None:
+ rules = WikiInlineGrammar()
+
+ super(WikiInlineLexer, self).__init__(renderer, rules, **kwargs)
+
+ def output_wiki_link(self, m):
+ text = m.group(1)
+ alt, link = text.split('|')
+ return '<a href="%s">%s</a>' % (link, alt)
+
+
+def test_custom_lexer():
+ markdown = mistune.Markdown(inline=WikiInlineLexer)
+ ret = markdown('[[Link Text|Wiki Link]]')
+ assert '<a href' in ret
+
+
+class TokenTreeRenderer(mistune.Renderer):
+ # options is required
+ options = {}
+
+ def placeholder(self):
+ return []
+
+ def __getattribute__(self, name):
+ """Saves the arguments to each Markdown handling method."""
+ found = TokenTreeRenderer.__dict__.get(name)
+ if found is not None:
+ return object.__getattribute__(self, name)
+
+ def fake_method(*args, **kwargs):
+ return [(name, args, kwargs)]
+ return fake_method
+
+
+def test_token_tree():
+ """Tests a Renderer that returns a list from the placeholder method."""
+ with open(os.path.join(root, 'fixtures', 'data', 'tree.md')) as f:
+ content = f.read()
+
+ expected = [
+ ('header', ([('text', ('Title here',), {})], 2, 'Title here'), {}),
+ ('paragraph', ([('text', ('Some text.',), {})],), {}),
+ ('paragraph',
+ ([('text', ('In two paragraphs. And then a list.',), {})],),
+ {}),
+ ('list',
+ ([('list_item', ([('text', ('foo',), {})],), {}),
+ ('list_item',
+ ([('text', ('bar',), {}),
+ ('list',
+ ([('list_item', ([('text', ('meep',), {})],), {}),
+ ('list_item', ([('text', ('stuff',), {})],), {})],
+ True),
+ {})],),
+ {})],
+ False),
+ {})
+ ]
+
+ processor = mistune.Markdown(renderer=TokenTreeRenderer())
+ found = processor.render(content)
+ assert expected == found, "Expected:\n%r\n\nFound:\n%r" % (expected, found)