Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
96 changes: 96 additions & 0 deletions docs/extensions/extra.txt
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,99 @@ therefore, not part of Python-Markdown Extra. If you really would
like Extra to include additional extensions, we suggest creating
your own clone of Extra under a different name
(see the [Extension API](api.html)).

Markdown Inside HTML Blocks
---------------------------

Unlike the other Extra features, this feature is build into the markdown core and is turned on when `extra` is enabled.

The content of any block-level element can be Markdown-formatted simply by adding a `markdown` attribute to the opening tag. The markdown attribute will be stripped from the output, but all other attributes will be preserved.

If the markdown value is set to `1` (recommended) or any value other than `span` or `block`, the default behavior will be executed: `p`,`h[1-6]`,`li`,`dd`,`dt`,`td`,`th`,`legend`, and `address` elements skip block parsing while others do not. If the default is overrident by a value of `span`, *block parsing will be skipped* regardless of tag. If the default is overriden by a value of `block`, *block parsing will occur* regardless of tag.

*An opening tag with the markdown attribute must start immediately on a line following a blank line.*

#### Simple Example:
```
This is *true* markdown text.

<div markdown="1">
This is *true* markdown text.
</div>
```
#### Result:
```
<p>This is <em>true</em> markdown text.</p>
<div>
<p>This is <em>true</em> markdown text.</p>
</div>
```

### Nested Markdown Inside HTML BLocks
Nested elements are more sensitive and must be used cautiously. Violation of the following will lead to unexpected behavior or unhandled exceptions.
* Only block mode elements may have further elements nested within them.
* The closing tag of inner elements must be followed by a blank line.
* More than one level of nesting is not supported (i.e., elements nested within elements nested within elements). This feature is not an alternative to templating.

#### Complex Example:
```
<div markdown="1" name="Example">

The text of the `Example` element.

<div markdown="1" name="DefaultBlockMode">
This text gets wrapped in `p` tags.
</div>

The tail of the `DefaultBlockMode` subelement.

<p markdown="1" name="DefaultSpanMode">
This text *is not* wrapped in additional `p` tags.
</p>

The tail of the `DefaultSpanMode` subelement.

<div markdown="span" name="SpanModeOverride">
This `div` block is not wrapped in paragraph tags.
Note: Subelements are not required to have tail text.
</div>

<p markdown="block" name="BlockModeOverride">
This `p` block *is* foolishly wrapped in further paragraph tags.
</p>

The tail of the `BlockModeOverride` subelement.

<div name="RawHtml">
Raw html blocks may also be nested.
</div>

</div>

This text is after the markdown in html.
```
#### Result:
```
<div name="Example">
<p>The text of the <code>Example</code> element.</p>
<div name="DefaultBlockMode">
<p>This text gets wrapped in <code>p</code> tags.</p>
</div>
<p>The tail of the <code>DefaultBlockMode</code> subelement.</p>
<p name="DefaultSpanMode">
This text <em>is not</em> wrapped in additional <code>p</code> tags.</p>
<p>The tail of the <code>DefaultSpanMode</code> subelement.</p>
<div name="SpanModeOverride">
This <code>div</code> block is not wrapped in paragraph tags.
Note: Subelements are not required to have tail text.</div>
<p name="BlockModeOverride">
<p>This <code>p</code> block <em>is</em> foolishly wrapped in further paragraph tags.</p>
</p>
<p>The tail of the <code>BlockModeOverride</code> subelement.</p>
<div name="RawHtml">
Raw html blocks may also be nested.
</div>

</div>
<p>This text is after the markdown in html.</p>
```
93 changes: 85 additions & 8 deletions markdown/extensions/extra.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,29 +6,32 @@
[PHP Markdown Extra](http://michelf.com/projects/php-markdown/extra/).

Note that each of the individual extensions still need to be available
on your PYTHONPATH. This extension simply wraps them all up as a
on your PYTHONPATH. This extension simply wraps them all up as a
convenience so that only one extension needs to be listed when
initiating Markdown. See the documentation for each individual
extension for specifics about that extension.

In the event that one or more of the supported extensions are not
available for import, Markdown will issue a warning and simply continue
without that extension.
In the event that one or more of the supported extensions are not
available for import, Markdown will issue a warning and simply continue
without that extension.

There may be additional extensions that are distributed with
There may be additional extensions that are distributed with
Python-Markdown that are not included here in Extra. Those extensions
are not part of PHP Markdown Extra, and therefore, not part of
Python-Markdown Extra. If you really would like Extra to include
additional extensions, we suggest creating your own clone of Extra
under a differant name. You could also edit the `extensions` global
variable defined below, but be aware that such changes may be lost
under a differant name. You could also edit the `extensions` global
variable defined below, but be aware that such changes may be lost
when you upgrade to any future version of Python-Markdown.

"""

from __future__ import absolute_import
from __future__ import unicode_literals
from . import Extension
from ..blockprocessors import BlockProcessor
from .. import util
import re

extensions = ['smart_strong',
'fenced_code',
Expand All @@ -38,7 +41,7 @@
'tables',
'abbr',
]


class ExtraExtension(Extension):
""" Add various extensions to Markdown class."""
Expand All @@ -49,6 +52,80 @@ def extendMarkdown(self, md, md_globals):
if not md.safeMode:
# Turn on processing of markdown text within raw html
md.preprocessors['html_block'].markdown_in_raw = True
md.parser.blockprocessors.add('markdown_block',
MarkdownInHtmlProcessor(md.parser),
'_begin')
md.parser.blockprocessors.tag_counter = -1
md.parser.blockprocessors.contain_span_tags = re.compile(
r'^(p|h[1-6]|li|dd|dt|td|th|legend|address)$', re.IGNORECASE)


def makeExtension(configs={}):
return ExtraExtension(configs=dict(configs))


class MarkdownInHtmlProcessor(BlockProcessor):
"""Process Markdown Inside HTML Blocks."""
def test(self, parent, block):
return block == util.TAG_PLACEHOLDER % \
str(self.parser.blockprocessors.tag_counter + 1)

def _process_nests(self, element, block):
"""Process the element's child elements in self.run."""
# Build list of indexes of each nest within the parent element.
nest_index = [] # a list of tuples: (left index, right index)
i = self.parser.blockprocessors.tag_counter + 1
while len(self.parser.markdown.htmlStash.tag_data) > i and self.\
parser.markdown.htmlStash.tag_data[i]['left_index']:
left_child_index = \
self.parser.markdown.htmlStash.tag_data[i]['left_index']
right_child_index = \
self.parser.markdown.htmlStash.tag_data[i]['right_index']
nest_index.append((left_child_index - 1, right_child_index))
i += 1

# Create each nest subelement.
i = 0
for n in nest_index[:-1]:
self.run(element, block[n[0]:n[1]],
block[n[1]:nest_index[i + 1][0]], True)
i += 1
self.run(element, block[nest_index[-1][0]:nest_index[-1][1]], # last
block[nest_index[-1][1]:], True) # nest

def run(self, parent, blocks, tail=None, nest=False):
self.parser.blockprocessors.tag_counter += 1
tag_data = self.parser.markdown.htmlStash.tag_data[
self.parser.blockprocessors.tag_counter]

# Create Element
markdown_value = tag_data['attrs'].pop('markdown')
element = util.etree.SubElement(parent, tag_data['tag'],
tag_data['attrs'])

# Slice Off Block
if nest:
self.parser.parseBlocks(parent, tail) # Process Tail
block = blocks[1:]
else: # includes nests since a third level of nesting isn't supported
block = blocks[tag_data['left_index'] + 1:
tag_data['right_index']]
del blocks[:tag_data['right_index']]

# Process Text
if (self.parser.blockprocessors.contain_span_tags.match( # Span Mode
tag_data['tag']) and markdown_value != 'block') or \
markdown_value == 'span':
element.text = '\n'.join(block)
else: # Block Mode
i = self.parser.blockprocessors.tag_counter + 1
if len(self.parser.markdown.htmlStash.tag_data) > i and self.\
parser.markdown.htmlStash.tag_data[i]['left_index']:
first_subelement_index = self.parser.markdown.htmlStash.\
tag_data[i]['left_index'] - 1
self.parser.parseBlocks(
element, block[:first_subelement_index])
if not nest:
block = self._process_nests(element, block)
else:
self.parser.parseBlocks(element, block)
Loading