summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWaylan Limberg <waylan@gmail.com>2013-02-10 11:47:43 -0800
committerWaylan Limberg <waylan@gmail.com>2013-02-10 11:47:43 -0800
commit5eebfc371fbb92f0b7364c582ee21cfe35e61a74 (patch)
treecac5cc1548af1d8025de4d21f24e8e472d167feb
parent41cc055580d63ffb7eb2bbb6c88e121727d91d06 (diff)
parentf78dcbedf94baa17392dafd5bb08c47d2a57ba74 (diff)
downloadpython-markdown-5eebfc371fbb92f0b7364c582ee21cfe35e61a74.tar.gz
Merge pull request #140 from slig/admonition
Admonitions: Initial version. This is still considered beta and subject to change. Thanks for all the work @slig.
-rw-r--r--docs/extensions/admonition.txt75
-rw-r--r--docs/extensions/code_hilite.txt46
-rw-r--r--docs/extensions/index.txt26
-rw-r--r--docs/extensions/smart_strong.txt6
-rw-r--r--markdown/extensions/admonition.py117
-rw-r--r--tests/extensions/admonition.html30
-rw-r--r--tests/extensions/admonition.txt28
-rw-r--r--tests/extensions/test.cfg3
-rw-r--r--tests/test_extensions.py35
9 files changed, 318 insertions, 48 deletions
diff --git a/docs/extensions/admonition.txt b/docs/extensions/admonition.txt
new file mode 100644
index 0000000..21a16f4
--- /dev/null
+++ b/docs/extensions/admonition.txt
@@ -0,0 +1,75 @@
+title: Admonition
+prev_title: Smart Strong Extension
+prev_url: smart_strong.html
+next_title: CodeHilite Extension
+next_url: code_hilite.html
+
+Admonition
+==========
+
+Summary
+-------
+
+This extension adds [rST-style][rST] admonitions to Markdown documents.
+
+This extension is included in the standard Markdown library.
+
+[rST]: http://docutils.sourceforge.net/docs/ref/rst/directives.html#specific-admonitions
+
+Syntax
+------
+
+Admonitions are created using the following syntax:
+
+ !!! type "optional explicit title within double quotes"
+ Any number of other indented markdown elements.
+
+ This is the second paragraph.
+
+`type` will be used as the CSS classname and as default title. It must be a
+single word. So, for instance:
+
+ !!! note
+ You should note that the title will be automatically capitalized.
+
+will render:
+
+ <div class="admonition note">
+ <p class="admonition-title">Note</p>
+ <p>You should note that the title will be automatically capitalized.</p>
+ </div>
+
+Optionally, you can use custom titles. For instance:
+
+ !!! danger "Don't try this at home"
+ ...
+
+will render:
+
+ <div class="admonition danger">
+ <p class="admonition-title">Don't try this at home</p>
+ <p>...</p>
+ </div>
+
+If you don't want a title, use a blank string `""`:
+
+ !!! important ""
+ This is a admonition box without a title.
+
+results in:
+
+ <div class="admonition important">
+ <p>This is a admonition box without a title.</p>
+ </div>
+
+
+rST suggests the following `types`, but you're free to use whatever you want:
+ attention, caution, danger, error, hint, important, note, tip, warning.
+
+Styling
+-------
+
+There is no CSS included as part of this extension. Look up the default
+[Sphinx][sphinx] theme if you need inspiration.
+
+[sphinx]: http://sphinx.pocoo.org/ \ No newline at end of file
diff --git a/docs/extensions/code_hilite.txt b/docs/extensions/code_hilite.txt
index 55a1a00..fbf05b3 100644
--- a/docs/extensions/code_hilite.txt
+++ b/docs/extensions/code_hilite.txt
@@ -1,6 +1,6 @@
title: CodeHilite Extension
-prev_title: Smart Strong Extension
-prev_url: smart_strong.html
+prev_title: Admonition Extension
+prev_url: admonition.html
next_title: HTML Tidy Extension
next_url: html_tidy.html
@@ -10,7 +10,7 @@ CodeHilite
Summary
-------
-The CodeHilite Extension adds code/syntax highlighting to standard
+The CodeHilite Extension adds code/syntax highlighting to standard
Python-Markdown code blocks using [Pygments][].
[Pygments]: http://pygments.org/
@@ -20,32 +20,32 @@ This extension is included in the Markdown library.
Setup
-----
-You will also need to [download][dl] and install the Pygments package on your
+You will also need to [download][dl] and install the Pygments package on your
`PYTHONPATH`. You will need to determine the appropriate CSS classes and create
-appropriate rules for them, which are either defined in or linked from the
-header of your HTML templates. See the excellent [documentation][] for more
-details. If no language is defined, Pygments will attempt to guess the
+appropriate rules for them, which are either defined in or linked from the
+header of your HTML templates. See the excellent [documentation][] for more
+details. If no language is defined, Pygments will attempt to guess the
language. When that fails, the code block will display as un-highlighted code.
[dl]: http://pygments.org/download/
[documentation]: http://pygments.org/docs
-**Note:** The css and/or javascript is not included as part of this extension
+**Note:** The css and/or javascript is not included as part of this extension
but shall always be provided by the end user.
Syntax
------
-The CodeHilite Extension follows the same [syntax][] as regular Markdown code
-blocks, with one exception. The hiliter needs to know what language to use for
-the code block. There are three ways to tell the hiliter what language the code
+The CodeHilite Extension follows the same [syntax][] as regular Markdown code
+blocks, with one exception. The hiliter needs to know what language to use for
+the code block. There are three ways to tell the hiliter what language the code
block contains and each one has a different result.
[syntax]: http://daringfireball.net/projects/markdown/syntax#precode
###SheBang (with path)
-If the first line of the codeblock contains a shebang, the language is derived
+If the first line of the codeblock contains a shebang, the language is derived
from that and line numbers are used.
#!/usr/bin/python
@@ -59,8 +59,8 @@ Will result in:
###SheBang (no path)
-If the first line contains a shebang, but the shebang line does not contain a
-path (a single `/` or even a space), then that line is removed from the code
+If the first line contains a shebang, but the shebang line does not contain a
+path (a single `/` or even a space), then that line is removed from the code
block before processing. Line numbers are used.
#!python
@@ -72,8 +72,8 @@ Will result in:
####Colons
-If the first line begins with three or more colons, the text following the
-colons identifies the language. The first line is removed from the code block
+If the first line begins with three or more colons, the text following the
+colons identifies the language. The first line is removed from the code block
before processing and line numbers are not used.
:::python
@@ -85,10 +85,10 @@ Will result in:
###When No Language is Defined
-CodeHilite is completely backward compatible so that if a code block is
-encountered that does not define a language, the block is simple wrapped in
-`<pre>` tags and output. Note: one exception would be that the Pygments
-highlighting engine will try to guess the language. Upon failure, the same
+CodeHilite is completely backward compatible so that if a code block is
+encountered that does not define a language, the block is simple wrapped in
+`<pre>` tags and output. Note: one exception would be that the Pygments
+highlighting engine will try to guess the language. Upon failure, the same
behavior will happen as described here.
# Code goes here ...
@@ -109,11 +109,11 @@ From the Python interpreter:
>>> html = markdown.markdown(text, ['codehilite'])
-If you want every code block to have line numbers, even when using colons
-(`:::`) for language identification, the setting `force_linenos` is available
+If you want every code block to have line numbers, even when using colons
+(`:::`) for language identification, the setting `force_linenos` is available
to do so.
- >>> html = markdown.markdown(text,
+ >>> html = markdown.markdown(text,
... ['codehilite(force_linenos=True)']
... )
diff --git a/docs/extensions/index.txt b/docs/extensions/index.txt
index a51f797..c9ee005 100644
--- a/docs/extensions/index.txt
+++ b/docs/extensions/index.txt
@@ -8,16 +8,16 @@ next_url: extra.html
Available Extensions
====================
-Python Markdown offers a flexible extension mechanism, which makes it possible
-to change and/or extend the behavior of the parser without having to edit the
-actual source files.
+Python Markdown offers a flexible extension mechanism, which makes it possible
+to change and/or extend the behavior of the parser without having to edit the
+actual source files.
To use an extension, pass it's name to markdown with the `extensions` keyword.
-See the [Library Reference](../reference.html#extensions) for more details.
+See the [Library Reference](../reference.html#extensions) for more details.
markdown.markdown(some_text, extensions=['footnotes', 'nl2br'])
-From the command line, specify an extension with the `-x` option. See the
+From the command line, specify an extension with the `-x` option. See the
[Command Line docs](../cli.html) or use the `--help` option for more details.
python -m markdown -x footnotes -x tables input.txt > output.html
@@ -26,9 +26,9 @@ Officially Supported Extensions
-------------------------------
The extensions listed below are included with (at least) the most recent release
-and are officially supported by Python-Markdown. Any documentation is
-maintained here and all bug reports should be made to the project. If you
-have a typical install of Python-Markdown, these extensions are already
+and are officially supported by Python-Markdown. Any documentation is
+maintained here and all bug reports should be made to the project. If you
+have a typical install of Python-Markdown, these extensions are already
available to you.
### Markdown Extra
@@ -63,6 +63,7 @@ but come in the standard Python-Markdown library.
Extension | Name in Python-Markdown
--------- | -----------------------
+[Admonition][] | `admonition`
[CodeHilite][] | `codehilite`
[HTML Tidy][] | `html_tidy`
[HeaderId] | `headerid`
@@ -73,6 +74,7 @@ Extension | Name in Python-Markdown
[Table of Contents] | `toc`
[WikiLinks] | `wikilinks`
+[Admonition]: admonition.html
[CodeHilite]: code_hilite.html
[HTML Tidy]: html_tidy.html
[HeaderId]: header_id.html
@@ -87,11 +89,11 @@ Third Party Extensions
----------------------
Various individuals and/or organizations have developed extensions which they
-have made available to the public. A [list of third party
+have made available to the public. A [list of third party
extensions](https://github.com/waylan/Python-Markdown/wiki/Third-Party-Extensions)
-is maintained on the wiki for your convenience. The Python-Markdown team
-offers no official support for these extensions. Please see the developer of
+is maintained on the wiki for your convenience. The Python-Markdown team
+offers no official support for these extensions. Please see the developer of
each extension for support.
-If you would like to write your own extensions, see the
+If you would like to write your own extensions, see the
[Extensions API](api.html) for details.
diff --git a/docs/extensions/smart_strong.txt b/docs/extensions/smart_strong.txt
index 8de574f..fd3bae6 100644
--- a/docs/extensions/smart_strong.txt
+++ b/docs/extensions/smart_strong.txt
@@ -1,8 +1,8 @@
title: Smart Strong Extension
prev_title: Tables Extension
prev_url: tables.html
-next_title: CodeHilite Extension
-next_url: code_hilite.html
+next_title: Admonition Extension
+next_url: admonition.html
Smart_Strong
============
@@ -11,7 +11,7 @@ Summary
-------
The Markdown Smart_Strong Extension adds smarter handling of double underscores
-within words. This does for double underscores what
+within words. This does for double underscores what
[smart_emphasis](../reference.html#smart_emphasis) does for single underscores.
The Smart_Strong Extension is included in the standard Markdown library.
diff --git a/markdown/extensions/admonition.py b/markdown/extensions/admonition.py
new file mode 100644
index 0000000..798befb
--- /dev/null
+++ b/markdown/extensions/admonition.py
@@ -0,0 +1,117 @@
+#!/usr/bin/env python
+
+"""
+Admonition extension for Python-Markdown
+========================================
+
+Adds rST-style admonitions. Inspired by [rST][] feature with the same name.
+
+The syntax is (followed by an indented block with the contents):
+ !!! [type] [optional explicit title]
+
+Where `type` is used as a CSS class name of the div. If not present, `title`
+defaults to the capitalized `type`, so "note" -> "Note".
+
+rST suggests the following `types`, but you're free to use whatever you want:
+ attention, caution, danger, error, hint, important, note, tip, warning
+
+
+A simple example:
+ !!! note
+ This is the first line inside the box.
+
+Outputs:
+ <div class="admonition note">
+ <p class="admonition-title">Note</p>
+ <p>This is the first line inside the box</p>
+ </div>
+
+You can also specify the title and CSS class of the admonition:
+ !!! custom "Did you know?"
+ Another line here.
+
+Outputs:
+ <div class="admonition custom">
+ <p class="admonition-title">Did you know?</p>
+ <p>Another line here.</p>
+ </div>
+
+[rST]: http://docutils.sourceforge.net/docs/ref/rst/directives.html#specific-admonitions
+
+By [Tiago Serafim](http://www.tiagoserafim.com/).
+
+"""
+
+import re
+import markdown
+from markdown.util import etree
+
+
+class AdmonitionExtension(markdown.Extension):
+ """ Admonition extension for Python-Markdown. """
+
+ def extendMarkdown(self, md, md_globals):
+ """ Add Admonition to Markdown instance. """
+ md.registerExtension(self)
+
+ md.parser.blockprocessors.add('admonition',
+ AdmonitionProcessor(md.parser),
+ '_begin')
+
+
+class AdmonitionProcessor(markdown.blockprocessors.BlockProcessor):
+
+ CLASSNAME = 'admonition'
+ CLASSNAME_TITLE = 'admonition-title'
+ RE = re.compile(r'(?:^|\n)!!!\ ?([\w\-]+)(?:\ "(.*?)")?')
+
+ def test(self, parent, block):
+ sibling = self.lastChild(parent)
+ return self.RE.search(block) or \
+ (block.startswith(' ' * self.tab_length) and sibling and \
+ sibling.get('class', '').find(self.CLASSNAME) != -1)
+
+ def run(self, parent, blocks):
+ sibling = self.lastChild(parent)
+ block = blocks.pop(0)
+ m = self.RE.search(block)
+
+ if m:
+ block = block[m.end() + 1:] # removes the first line
+
+ block, theRest = self.detab(block)
+
+ if m:
+ klass, title = self.get_class_and_title(m)
+ div = etree.SubElement(parent, 'div')
+ div.set('class', u'%s %s' % (self.CLASSNAME, klass))
+ if title:
+ p = etree.SubElement(div, 'p')
+ p.text = title
+ p.set('class', self.CLASSNAME_TITLE)
+ else:
+ div = sibling
+
+ self.parser.parseChunk(div, block)
+
+ if theRest:
+ # This block contained unindented line(s) after the first indented
+ # line. Insert these lines as the first block of the master blocks
+ # list for future processing.
+ blocks.insert(0, theRest)
+
+ def get_class_and_title(self, match):
+ klass, title = match.group(1), match.group(2)
+ if title is None:
+ # no title was provided, use the capitalized classname as title
+ # e.g.: `!!! note` will render `<p class="admonition-title">Note</p>`
+ title = klass.capitalize()
+ elif title == '':
+ # an explicit blank title should not be rendered
+ # e.g.: `!!! warning ""` will *not* render `p` with a title
+ title = None
+ return klass, title
+
+
+def makeExtension(configs={}):
+ return AdmonitionExtension(configs=configs)
diff --git a/tests/extensions/admonition.html b/tests/extensions/admonition.html
new file mode 100644
index 0000000..437cac6
--- /dev/null
+++ b/tests/extensions/admonition.html
@@ -0,0 +1,30 @@
+<p>Some text</p>
+<div class="admonition note">
+<p class="admonition-title">Note</p>
+<p>A normal paragraph here</p>
+<ol>
+<li>first</li>
+<li>second</li>
+</ol>
+<blockquote>
+<p>Some important quote</p>
+<p>another paragraph in the quote</p>
+</blockquote>
+<pre><code>int main() {
+ // insert some code
+}
+</code></pre>
+</div>
+<p>More text and stuff.</p>
+<div class="admonition note">
+<p class="admonition-title">Did you know?</p>
+<p>You can customize the title of the admonition</p>
+</div>
+<div class="admonition mycustomcssclass">
+<p class="admonition-title">And now...</p>
+<p>For something completely different.</p>
+<p>You can also use a custom CSS class name.</p>
+</div>
+<div class="admonition tip">
+<p>An explicitly empty string prevents the title from being rendered.</p>
+</div> \ No newline at end of file
diff --git a/tests/extensions/admonition.txt b/tests/extensions/admonition.txt
new file mode 100644
index 0000000..fd18bd6
--- /dev/null
+++ b/tests/extensions/admonition.txt
@@ -0,0 +1,28 @@
+Some text
+
+!!! note
+ A normal paragraph here
+
+ 1. first
+ 2. second
+
+ > Some important quote
+
+ > another paragraph in the quote
+
+ int main() {
+ // insert some code
+ }
+
+More text and stuff.
+
+!!! note "Did you know?"
+ You can customize the title of the admonition
+
+!!! mycustomcssclass "And now..."
+ For something completely different.
+
+ You can also use a custom CSS class name.
+
+!!! tip ""
+ An explicitly empty string prevents the title from being rendered.
diff --git a/tests/extensions/test.cfg b/tests/extensions/test.cfg
index d642bd8..4efd837 100644
--- a/tests/extensions/test.cfg
+++ b/tests/extensions/test.cfg
@@ -32,3 +32,6 @@ extensions=sane_lists
[nl2br_w_attr_list]
extensions=nl2br,attr_list
+
+[admonition]
+extensions=admonition
diff --git a/tests/test_extensions.py b/tests/test_extensions.py
index 7dab60a..fd77e5e 100644
--- a/tests/test_extensions.py
+++ b/tests/test_extensions.py
@@ -2,7 +2,7 @@
Python-Markdown Extension Regression Tests
==========================================
-A collection of regression tests to confirm that the included extensions
+A collection of regression tests to confirm that the included extensions
continue to work as advertised. This used to be accomplished by doctests.
"""
@@ -60,7 +60,7 @@ class TestFencedCode(unittest.TestCase):
def testBasicFence(self):
""" Test Fenced Code Blocks. """
- text = '''
+ text = '''
A paragraph before a fenced code block:
~~~
@@ -124,7 +124,7 @@ class TestHeaderId(unittest.TestCase):
def testBasicHeaderId(self):
""" Test Basic HeaderID """
-
+
text = "# Some Header #"
self.assertEqual(self.md.convert(text),
'<h1 id="some-header">Some Header</h1>')
@@ -202,8 +202,8 @@ The body. This is paragraph one.'''
self.assertEqual(self.md.convert(text),
'<p>The body. This is paragraph one.</p>')
self.assertEqual(self.md.Meta,
- {'author': ['Waylan Limberg', 'John Doe'],
- 'blank_data': [''],
+ {'author': ['Waylan Limberg', 'John Doe'],
+ 'blank_data': [''],
'title': ['A Test Doc.']})
def testMissingMetaData(self):
@@ -239,18 +239,18 @@ class TestWikiLinks(unittest.TestCase):
def testSimpleSettings(self):
""" Test Simple Settings. """
- self.assertEqual(markdown.markdown(self.text,
+ self.assertEqual(markdown.markdown(self.text,
['wikilinks(base_url=/wiki/,end_url=.html,html_class=foo)']),
'<p>Some text with a '
'<a class="foo" href="/wiki/WikiLink.html">WikiLink</a>.</p>')
-
+
def testComplexSettings(self):
""" Test Complex Settings. """
md = markdown.Markdown(
- extensions = ['wikilinks'],
+ extensions = ['wikilinks'],
extension_configs = {'wikilinks': [
- ('base_url', 'http://example.com/'),
+ ('base_url', 'http://example.com/'),
('end_url', '.html'),
('html_class', '') ]},
safe_mode = True)
@@ -281,8 +281,23 @@ Some text with a [[WikiLink]]."""
def my_url_builder(label, base, end):
return '/bar/'
- md = markdown.Markdown(extensions=['wikilinks'],
+ md = markdown.Markdown(extensions=['wikilinks'],
extension_configs={'wikilinks' : [('build_url', my_url_builder)]})
self.assertEqual(md.convert('[[foo]]'),
'<p><a class="wikilink" href="/bar/">foo</a></p>')
+class TestAdmonition(unittest.TestCase):
+ """ Test Admonition Extension. """
+
+ def setUp(self):
+ self.md = markdown.Markdown(extensions=['admonition'])
+
+ def testRE(self):
+ RE = self.md.parser.blockprocessors['admonition'].RE
+ tests = [
+ ('!!! note', ('note', None)),
+ ('!!! note "Please Note"', ('note', 'Please Note')),
+ ('!!! note ""', ('note', '')),
+ ]
+ for test, expected in tests:
+ self.assertEqual(RE.match(test).groups(), expected)