aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--docs/extensions/code_hilite.txt11
-rw-r--r--docs/extensions/extra.txt12
-rw-r--r--docs/extensions/fenced_code_blocks.txt25
-rw-r--r--docs/extensions/footnotes.txt29
-rw-r--r--docs/extensions/wikilinks.txt5
-rw-r--r--markdown/__init__.py2
-rw-r--r--markdown/blockprocessors.py10
-rw-r--r--markdown/extensions/codehilite.py38
-rw-r--r--markdown/extensions/extra.py12
-rw-r--r--markdown/extensions/fenced_code.py31
-rw-r--r--markdown/inlinepatterns.py5
-rw-r--r--markdown/preprocessors.py22
-rw-r--r--markdown/treeprocessors.py12
-rw-r--r--tests/basic/angle-links-and-img.html2
-rw-r--r--tests/misc/brackets-in-img-title.html9
-rw-r--r--tests/misc/brackets-in-img-title.txt12
-rw-r--r--tests/misc/escaped_chars_in_js.html12
-rw-r--r--tests/misc/escaped_chars_in_js.txt12
-rw-r--r--tests/misc/url_spaces.html4
-rw-r--r--tests/test_extensions.py84
-rw-r--r--tests/util.py8
21 files changed, 291 insertions, 66 deletions
diff --git a/docs/extensions/code_hilite.txt b/docs/extensions/code_hilite.txt
index ab09742..92f60f8 100644
--- a/docs/extensions/code_hilite.txt
+++ b/docs/extensions/code_hilite.txt
@@ -91,6 +91,17 @@ block contains and each one has a different result.
# Code goes here ...
+ Certain lines can be selected for emphasis with the colon syntax. By
+ default, emphasized lines have a yellow background. This is useful to
+ direct the reader's attention.
+
+ :::python hl_lines="1 3"
+ # This line is emphasized
+ # This line isn't
+ # This line is emphasized
+
+ (`hl_lines` is named for Pygments' "highlighted lines" option.)
+
* ###When No Language is Defined
CodeHilite is completely backward compatible so that if a code block is
diff --git a/docs/extensions/extra.txt b/docs/extensions/extra.txt
index 6140647..0507e8f 100644
--- a/docs/extensions/extra.txt
+++ b/docs/extensions/extra.txt
@@ -47,12 +47,10 @@ 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.
+The content of any raw html block 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.
@@ -70,10 +68,10 @@ This is *true* markdown text.
```
### 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.
+Nested elements are more sensitive and must be used cautiously. To avoid unexpected results:
+ * Only nest elements within block mode elements.
+ * Follow the closing tag of inner elements with a blank line.
+ * Only have one level of nesting.
#### Complex Example:
```
diff --git a/docs/extensions/fenced_code_blocks.txt b/docs/extensions/fenced_code_blocks.txt
index c54c5bd..0148c80 100644
--- a/docs/extensions/fenced_code_blocks.txt
+++ b/docs/extensions/fenced_code_blocks.txt
@@ -35,6 +35,8 @@ Fenced code blocks can have a blank line as the first and/or last line of a
code block and they can also come immediately after a list item without becoming
part of the list.
+### Language
+
In addition to PHP Extra's syntax, you can define the language of the code
block for use by syntax highlighters etc. The language will be assigned as a
class attribute of the ``<code>`` element in the output. Therefore, you should
@@ -66,6 +68,29 @@ The above will output:
[Github]: http://github.github.com/github-flavored-markdown/
+### Emphasized Lines
+
+If [Pygments][] is installed, this extension can emphasize certain lines of
+code. By default, emphasized lines have a yellow background. This is useful to
+direct the reader's attention. The lines can be specified with PHP Extra's
+syntax:
+
+ ~~~~{.python hl_lines="1 3"}
+ # This line is emphasized
+ # This line isn't
+ # This line is emphasized
+
+... or with GitHub's:
+
+ ```python hl_lines="1 3"
+ # This line is emphasized
+ # This line isn't
+ # This line is emphasized
+
+(`hl_lines` is named for Pygments' "highlighted lines" option.)
+
+[Pygments]: http://pygments.org/
+
Usage
-----
diff --git a/docs/extensions/footnotes.txt b/docs/extensions/footnotes.txt
index 1d6593c..e06344b 100644
--- a/docs/extensions/footnotes.txt
+++ b/docs/extensions/footnotes.txt
@@ -26,17 +26,30 @@ the output.
Example:
- Footnotes[^1] have a label[^label] and a definition[^!DEF].
+ Footnotes[^1] have a label[^@#$%] and the footnote's content.
- [^1]: This is a footnote
- [^label]: A footnote on "label"
- [^!DEF]: The definition of a footnote.
+ [^1]: This is a footnote's content.
+ [^@#$%]: A footnote on the label: "@#$%".
-A footnote definition may contain multiple lines, paragraphs, code blocks,
-blockquotes and most any other markdown syntax. The additional line simply
-must be indented at least an additional four spaces.
+A footnote label must start with a caret `^` and may contain any inline text
+(including spaces) between a set of square brackets `[]`. Only the first
+caret has any special meaning.
- [^1]: The first paragraph of the definition.
+A footnote's content must start with the label followed by a colon and at least
+one space. The label used to define the content must exactly match the label used
+in the body (including capitalization and whitespace). The content would then
+follow the label either on the same line or on the next line. The content may
+contain multiple lines, paragraphs, code blocks, blockquotes and most any other
+markdown syntax. The additional lines must be indented one level (four spaces or
+one tab).
+
+When working with multiple blocks, it may be helpful to start the content on a
+separate line from the label which defines the content. This way the entire block
+is indented consistently and any errors are more easily discernible by the author.
+
+
+ [^1]:
+ The first paragraph of the definition.
Paragraph two of the definition.
diff --git a/docs/extensions/wikilinks.txt b/docs/extensions/wikilinks.txt
index 5cfdb77..ca36d17 100644
--- a/docs/extensions/wikilinks.txt
+++ b/docs/extensions/wikilinks.txt
@@ -49,7 +49,7 @@ Usage
From the Python interpreter:
>>> text = "Some text with a [[WikiLink]]."
- >>> html = markdown.markdown(text, ['wikilink'])
+ >>> html = markdown.markdown(text, ['wikilinks'])
The default behavior is to point each link to the document root of the current
domain and close with a trailing slash. Additionally, each link is assigned to
@@ -91,7 +91,7 @@ could also pass in a callable which must accept three arguments (``label``,
return url
md = markdown.Markdown(
- extensions=['wikilinks'],
+ extensions=['wikilinks],
extension_configs={'wikilinks' : [('build_url', my_url_builder)]}
)
@@ -147,4 +147,3 @@ This document:
would result in the following output (notice the blank `wiki_html_class`):
<p>A <a href="http://example.com/WikiLink.html">WikiLink</a> in the first paragraph.</p>
-
diff --git a/markdown/__init__.py b/markdown/__init__.py
index 0219dc3..4943388 100644
--- a/markdown/__init__.py
+++ b/markdown/__init__.py
@@ -293,7 +293,7 @@ class Markdown(object):
# Run the tree-processors
for treeprocessor in self.treeprocessors.values():
newRoot = treeprocessor.run(root)
- if newRoot:
+ if newRoot is not None:
root = newRoot
# Serialize _properly_. Strip top-level tags.
diff --git a/markdown/blockprocessors.py b/markdown/blockprocessors.py
index 61977b4..147ff0f 100644
--- a/markdown/blockprocessors.py
+++ b/markdown/blockprocessors.py
@@ -211,7 +211,7 @@ class ListIndentProcessor(BlockProcessor):
# Step through children of tree to find matching indent level.
while indent_level > level:
child = self.lastChild(parent)
- if child and (child.tag in self.LIST_TYPES or child.tag in self.ITEM_TYPES):
+ if child is not None and (child.tag in self.LIST_TYPES or child.tag in self.ITEM_TYPES):
if child.tag in self.LIST_TYPES:
level += 1
parent = child
@@ -232,7 +232,7 @@ class CodeBlockProcessor(BlockProcessor):
sibling = self.lastChild(parent)
block = blocks.pop(0)
theRest = ''
- if sibling and sibling.tag == "pre" and len(sibling) \
+ if sibling is not None and sibling.tag == "pre" and len(sibling) \
and sibling[0].tag == "code":
# The previous block was a code block. As blank lines do not start
# new code blocks, append this block to the previous, adding back
@@ -271,7 +271,7 @@ class BlockQuoteProcessor(BlockProcessor):
block = '\n'.join([self.clean(line) for line in
block[m.start():].split('\n')])
sibling = self.lastChild(parent)
- if sibling and sibling.tag == "blockquote":
+ if sibling is not None and sibling.tag == "blockquote":
# Previous block was a blockquote so set that as this blocks parent
quote = sibling
else:
@@ -319,7 +319,7 @@ class OListProcessor(BlockProcessor):
items = self.get_items(blocks.pop(0))
sibling = self.lastChild(parent)
- if sibling and sibling.tag in self.SIBLING_TAGS:
+ if sibling is not None and sibling.tag in self.SIBLING_TAGS:
# Previous block was a list item, so set that as parent
lst = sibling
# make sure previous item is in a p- if the item has text, then it
@@ -515,7 +515,7 @@ class EmptyBlockProcessor(BlockProcessor):
# Add remaining lines to master blocks for later.
blocks.insert(0, theRest)
sibling = self.lastChild(parent)
- if sibling and sibling.tag == 'pre' and len(sibling) and sibling[0].tag == 'code':
+ if sibling is not None and sibling.tag == 'pre' and len(sibling) and sibling[0].tag == 'code':
# Last block is a codeblock. Append to preserve whitespace.
sibling[0].text = util.AtomicString('%s%s' % (sibling[0].text, filler))
diff --git a/markdown/extensions/codehilite.py b/markdown/extensions/codehilite.py
index 7a3676b..9f99518 100644
--- a/markdown/extensions/codehilite.py
+++ b/markdown/extensions/codehilite.py
@@ -31,6 +31,22 @@ try:
except ImportError:
pygments = False
+
+def parse_hl_lines(expr):
+ """Support our syntax for emphasizing certain lines of code.
+
+ expr should be like '1 2' to emphasize lines 1 and 2 of a code block.
+ Returns a list of ints, the line numbers to emphasize.
+ """
+ if not expr:
+ return []
+
+ try:
+ return map(int, expr.split())
+ except ValueError:
+ return []
+
+
# ------------------ The Main CodeHilite Class ----------------------
class CodeHilite(object):
"""
@@ -49,6 +65,8 @@ class CodeHilite(object):
* css_class: Set class name of wrapper div ('codehilite' by default).
+ * hl_lines: (List of integers) Lines to emphasize, 1-indexed.
+
Low Level Usage:
>>> code = CodeHilite()
>>> code.src = 'some text' # String or anything with a .readline attr.
@@ -59,7 +77,7 @@ class CodeHilite(object):
def __init__(self, src=None, linenums=None, guess_lang=True,
css_class="codehilite", lang=None, style='default',
- noclasses=False, tab_length=4):
+ noclasses=False, tab_length=4, hl_lines=None):
self.src = src
self.lang = lang
self.linenums = linenums
@@ -68,6 +86,7 @@ class CodeHilite(object):
self.style = style
self.noclasses = noclasses
self.tab_length = tab_length
+ self.hl_lines = hl_lines or []
def hilite(self):
"""
@@ -83,7 +102,7 @@ class CodeHilite(object):
self.src = self.src.strip('\n')
if self.lang is None:
- self._getLang()
+ self._parseHeader()
if pygments:
try:
@@ -99,7 +118,8 @@ class CodeHilite(object):
formatter = HtmlFormatter(linenos=self.linenums,
cssclass=self.css_class,
style=self.style,
- noclasses=self.noclasses)
+ noclasses=self.noclasses,
+ hl_lines=self.hl_lines)
return highlight(self.src, lexer, formatter)
else:
# just escape and build markup usable by JS highlighting libs
@@ -118,7 +138,7 @@ class CodeHilite(object):
return '<pre class="%s"><code%s>%s</code></pre>\n'% \
(self.css_class, class_str, txt)
- def _getLang(self):
+ def _parseHeader(self):
"""
Determines language of a code block from shebang line and whether said
line should be removed or left in place. If the sheband line contains a
@@ -131,6 +151,9 @@ class CodeHilite(object):
(e.i.: :::python), line numbering is left in the current state - off
by default.
+ Also parses optional list of highlight lines, like:
+
+ :::python hl_lines="1 3"
"""
import re
@@ -141,9 +164,12 @@ class CodeHilite(object):
fl = lines.pop(0)
c = re.compile(r'''
- (?:(?:^::+)|(?P<shebang>^[#]!)) # Shebang or 2 or more colons.
+ (?:(?:^::+)|(?P<shebang>^[#]!)) # Shebang or 2 or more colons
(?P<path>(?:/\w+)*[/ ])? # Zero or 1 path
(?P<lang>[\w+-]*) # The language
+ \s* # Arbitrary whitespace
+ # Optional highlight lines, single- or double-quote-delimited
+ (hl_lines=(?P<quot>"|')(?P<hl_lines>.*?)(?P=quot))?
''', re.VERBOSE)
# search first line for shebang
m = c.search(fl)
@@ -159,6 +185,8 @@ class CodeHilite(object):
if self.linenums is None and m.group('shebang'):
# Overridable and Shebang exists - use line numbers
self.linenums = True
+
+ self.hl_lines = parse_hl_lines(m.group('hl_lines'))
else:
# No match
lines.insert(0, fl)
diff --git a/markdown/extensions/extra.py b/markdown/extensions/extra.py
index dd70305..c4a0a97 100644
--- a/markdown/extensions/extra.py
+++ b/markdown/extensions/extra.py
@@ -75,8 +75,8 @@ class MarkdownInHtmlProcessor(BlockProcessor):
# 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']:
+ is_nest = self.parser.markdown.htmlStash.tag_data[i]['left_index']
+ while len(self.parser.markdown.htmlStash.tag_data) > i and is_nest:
left_child_index = \
self.parser.markdown.htmlStash.tag_data[i]['left_index']
right_child_index = \
@@ -85,11 +85,9 @@ class MarkdownInHtmlProcessor(BlockProcessor):
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
+ for i, (left_index, right_index) in enumerate(nest_index[:-1]):
+ self.run(element, block[left_index:right_index],
+ block[right_index:nest_index[i + 1][0]], True)
self.run(element, block[nest_index[-1][0]:nest_index[-1][1]], # last
block[nest_index[-1][1]:], True) # nest
diff --git a/markdown/extensions/fenced_code.py b/markdown/extensions/fenced_code.py
index c5aaa6a..39c6540 100644
--- a/markdown/extensions/fenced_code.py
+++ b/markdown/extensions/fenced_code.py
@@ -59,6 +59,20 @@ Optionally backticks instead of tildes as per how github's code block markdown i
~~~~~ # these tildes will not close the block
</code></pre>
+If the codehighlite extension and Pygments are installed, lines can be highlighted:
+
+ >>> text = '''
+ ... ```hl_lines="1 3"
+ ... line 1
+ ... line 2
+ ... line 3
+ ... ```'''
+ >>> print markdown.markdown(text, extensions=['codehilite', 'fenced_code'])
+ <pre><code><span class="hilight">line 1</span>
+ line 2
+ <span class="hilight">line 3</span>
+ </code></pre>
+
Copyright 2007-2008 [Waylan Limberg](http://achinghead.com/).
Project website: <http://packages.python.org/Markdown/extensions/fenced_code_blocks.html>
@@ -77,7 +91,7 @@ from __future__ import absolute_import
from __future__ import unicode_literals
from . import Extension
from ..preprocessors import Preprocessor
-from .codehilite import CodeHilite, CodeHiliteExtension
+from .codehilite import CodeHilite, CodeHiliteExtension, parse_hl_lines
import re
@@ -93,10 +107,14 @@ class FencedCodeExtension(Extension):
class FencedBlockPreprocessor(Preprocessor):
- FENCED_BLOCK_RE = re.compile( \
- r'(?P<fence>^(?:~{3,}|`{3,}))[ ]*(\{?\.?(?P<lang>[a-zA-Z0-9_+-]*)\}?)?[ ]*\n(?P<code>.*?)(?<=\n)(?P=fence)[ ]*$',
- re.MULTILINE|re.DOTALL
- )
+ FENCED_BLOCK_RE = re.compile(r'''
+(?P<fence>^(?:~{3,}|`{3,}))[ ]* # Opening ``` or ~~~
+(\{?\.?(?P<lang>[a-zA-Z0-9_+-]*))?[ ]* # Optional {, and lang
+# Optional highlight lines, single- or double-quote-delimited
+(hl_lines=(?P<quot>"|')(?P<hl_lines>.*?)(?P=quot))?[ ]*
+}?[ ]*\n # Optional closing }
+(?P<code>.*?)(?<=\n)
+(?P=fence)[ ]*$''', re.MULTILINE | re.DOTALL | re.VERBOSE)
CODE_WRAP = '<pre><code%s>%s</code></pre>'
LANG_TAG = ' class="%s"'
@@ -135,7 +153,8 @@ class FencedBlockPreprocessor(Preprocessor):
css_class=self.codehilite_conf['css_class'][0],
style=self.codehilite_conf['pygments_style'][0],
lang=(m.group('lang') or None),
- noclasses=self.codehilite_conf['noclasses'][0])
+ noclasses=self.codehilite_conf['noclasses'][0],
+ hl_lines=parse_hl_lines(m.group('hl_lines')))
code = highliter.hilite()
else:
diff --git a/markdown/inlinepatterns.py b/markdown/inlinepatterns.py
index de957ef..9335748 100644
--- a/markdown/inlinepatterns.py
+++ b/markdown/inlinepatterns.py
@@ -107,7 +107,7 @@ LINK_RE = NOIMG + BRK + \
r'''\(\s*(<.*?>|((?:(?:\(.*?\))|[^\(\)]))*?)\s*((['"])(.*?)\12\s*)?\)'''
# [text](url) or [text](<url>) or [text](url "title")
-IMAGE_LINK_RE = r'\!' + BRK + r'\s*\((<.*?>|([^\)]*))\)'
+IMAGE_LINK_RE = r'\!' + BRK + r'\s*\((<.*?>|([^")]+"[^"]*"|[^\)]*))\)'
# ![alttxt](http://x.com/) or ![alttxt](<http://x.com/>)
REFERENCE_RE = NOIMG + BRK+ r'\s?\[([^\]]*)\]' # [Google][3]
SHORT_REF_RE = NOIMG + r'\[([^\]]+)\]' # [Google]
@@ -231,7 +231,7 @@ class EscapePattern(Pattern):
if char in self.markdown.ESCAPED_CHARS:
return '%s%s%s' % (util.STX, ord(char), util.ETX)
else:
- return '\\%s' % char
+ return None
class SimpleTagPattern(Pattern):
@@ -344,7 +344,6 @@ class LinkPattern(Pattern):
`username:password@host:port`.
"""
- url = url.replace(' ', '%20')
if not self.markdown.safeMode:
# Return immediately bipassing parsing.
return url
diff --git a/markdown/preprocessors.py b/markdown/preprocessors.py
index c532702..1b5bc7e 100644
--- a/markdown/preprocessors.py
+++ b/markdown/preprocessors.py
@@ -154,9 +154,8 @@ class HtmlBlockPreprocessor(Preprocessor):
def _nested_markdown_in_html(self, items):
"""Find and process html child elements of the given element block."""
- i = 0
- while i < len(items):
- if self.left_tag_re.match(items[i]):
+ for i, item in enumerate(items):
+ if self.left_tag_re.match(item):
left_tag, left_index, attrs = \
self._get_left_tag(''.join(items[i:]))
right_tag, data_index = self._get_right_tag(
@@ -164,10 +163,10 @@ class HtmlBlockPreprocessor(Preprocessor):
right_listindex = \
self._stringindex_to_listindex(data_index, items[i:]) + i
if 'markdown' in attrs.keys():
+ items[i] = items[i][left_index:] # remove opening tag
placeholder = self.markdown.htmlStash.store_tag(
left_tag, attrs, i + 1, right_listindex + 1)
- items = items[:i] + [placeholder] + \
- [items[i][left_index:]] + items[i + 1:]
+ items.insert(i, placeholder)
if len(items) - right_listindex <= 1: # last nest, no tail
right_listindex -= 1
items[right_listindex] = items[right_listindex][
@@ -179,7 +178,6 @@ class HtmlBlockPreprocessor(Preprocessor):
items[i:right_listindex]))
del items[i:right_listindex]
items.insert(i, placeholder)
- i += 1
return items
def run(self, lines):
@@ -272,8 +270,12 @@ class HtmlBlockPreprocessor(Preprocessor):
if self.markdown_in_raw and 'markdown' in attrs.keys():
items[0] = items[0][left_index:]
items[-1] = items[-1][:-len(right_tag) - 2]
+ if items[len(items) - 1]: # not a newline/empty string
+ right_index = len(items) + 3
+ else:
+ right_index = len(items) + 2
new_blocks.append(self.markdown.htmlStash.store_tag(
- left_tag, attrs, 0, len(items) + 2))
+ left_tag, attrs, 0, right_index))
placeholderslen = len(self.markdown.htmlStash.tag_data)
new_blocks.extend(
self._nested_markdown_in_html(items))
@@ -290,9 +292,13 @@ class HtmlBlockPreprocessor(Preprocessor):
if self.markdown_in_raw and 'markdown' in attrs.keys():
items[0] = items[0][left_index:]
items[-1] = items[-1][:-len(right_tag) - 2]
+ if items[len(items) - 1]: # not a newline/empty string
+ right_index = len(items) + 3
+ else:
+ right_index = len(items) + 2
new_blocks.append(
self.markdown.htmlStash.store_tag(
- left_tag, attrs, 0, len(items) + 2))
+ left_tag, attrs, 0, right_index))
placeholderslen = len(self.markdown.htmlStash.tag_data)
new_blocks.extend(self._nested_markdown_in_html(items))
nests = len(self.markdown.htmlStash.tag_data) - placeholderslen
diff --git a/markdown/treeprocessors.py b/markdown/treeprocessors.py
index e6d3dc9..ef0a2aa 100644
--- a/markdown/treeprocessors.py
+++ b/markdown/treeprocessors.py
@@ -131,7 +131,7 @@ class InlineProcessor(Treeprocessor):
childResult = self.__processPlaceholders(text, subnode)
if not isText and node is not subnode:
- pos = node.getchildren().index(subnode)
+ pos = list(node).index(subnode)
node.remove(subnode)
else:
pos = 0
@@ -179,7 +179,7 @@ class InlineProcessor(Treeprocessor):
linkText(text)
if not isString(node): # it's Element
- for child in [node] + node.getchildren():
+ for child in [node] + list(node):
if child.tail:
if child.tail.strip():
self.__processElementText(node, child,False)
@@ -237,7 +237,7 @@ class InlineProcessor(Treeprocessor):
if not isString(node):
if not isinstance(node.text, util.AtomicString):
# We need to process current node too
- for child in [node] + node.getchildren():
+ for child in [node] + list(node):
if not isString(node):
if child.text:
child.text = self.__handleInline(child.text,
@@ -276,7 +276,7 @@ class InlineProcessor(Treeprocessor):
while stack:
currElement = stack.pop()
insertQueue = []
- for child in currElement.getchildren():
+ for child in currElement:
if child.text and not isinstance(child.text, util.AtomicString):
text = child.text
child.text = None
@@ -292,11 +292,11 @@ class InlineProcessor(Treeprocessor):
child.tail = dumby.text
else:
child.tail = None
- pos = currElement.getchildren().index(child) + 1
+ pos = list(currElement).index(child) + 1
tailResult.reverse()
for newChild in tailResult:
currElement.insert(pos, newChild)
- if child.getchildren():
+ if len(child):
stack.append(child)
for element, lst in insertQueue:
diff --git a/tests/basic/angle-links-and-img.html b/tests/basic/angle-links-and-img.html
index 255c299..1ca3b0b 100644
--- a/tests/basic/angle-links-and-img.html
+++ b/tests/basic/angle-links-and-img.html
@@ -1,4 +1,4 @@
-<p><a href="simple%20link" title="title">link</a>
+<p><a href="simple link" title="title">link</a>
<img alt="image" src="http://example.com/image.jpg" />
<a href="http://example.com/(()((())923)(">link</a>
<img alt="image" src="link(()))(" /></p> \ No newline at end of file
diff --git a/tests/misc/brackets-in-img-title.html b/tests/misc/brackets-in-img-title.html
new file mode 100644
index 0000000..3677139
--- /dev/null
+++ b/tests/misc/brackets-in-img-title.html
@@ -0,0 +1,9 @@
+<p><img alt="alt" src="local-img.jpg" />
+<img alt="alt" src="local-img.jpg" title="" />
+<img alt="alt" src="local-img.jpg" title="normal title" /></p>
+<p><img alt="alt" src="local-img.jpg" title="(just title in brackets)" />
+<img alt="alt" src="local-img.jpg" title="title with brackets (I think)" /></p>
+<p><img alt="alt" src="local-img.jpg" title="(" />
+<img alt="alt" src="local-img.jpg" title="(open only" />
+<img alt="alt" src="local-img.jpg" title=")" />
+<img alt="alt" src="local-img.jpg" title="close only)" /></p> \ No newline at end of file
diff --git a/tests/misc/brackets-in-img-title.txt b/tests/misc/brackets-in-img-title.txt
new file mode 100644
index 0000000..01fcd4e
--- /dev/null
+++ b/tests/misc/brackets-in-img-title.txt
@@ -0,0 +1,12 @@
+![alt](local-img.jpg)
+![alt](local-img.jpg "")
+![alt](local-img.jpg "normal title")
+
+![alt](local-img.jpg "(just title in brackets)")
+![alt](local-img.jpg "title with brackets (I think)")
+
+![alt](local-img.jpg "(")
+![alt](local-img.jpg "(open only")
+![alt](local-img.jpg ")")
+![alt](local-img.jpg "close only)")
+
diff --git a/tests/misc/escaped_chars_in_js.html b/tests/misc/escaped_chars_in_js.html
new file mode 100644
index 0000000..b821fb1
--- /dev/null
+++ b/tests/misc/escaped_chars_in_js.html
@@ -0,0 +1,12 @@
+<p><span id="e116142240">[javascript protected email address]</span>
+<script type="text/javascript">
+ var a="gqMjyw7lZCaKk6p0J3uAUYS1.dbIW2hXzDHmiVNotOPRe_Ev@c4Gs58+LBr-F9QTfxn";
+ var b=a.split("").sort().join("");
+ var c="F_-F6F_-FMe_";
+ var d="";
+ for(var e=0;e<c.length;e++)
+ d+=b.charAt(a.indexOf(c.charAt(e)));
+ document
+ .getElementById("e116142240")
+ .innerHTML="<a href=\"mailto:"+d+"\">"+d+"</a>";
+</script></p> \ No newline at end of file
diff --git a/tests/misc/escaped_chars_in_js.txt b/tests/misc/escaped_chars_in_js.txt
new file mode 100644
index 0000000..ca917c8
--- /dev/null
+++ b/tests/misc/escaped_chars_in_js.txt
@@ -0,0 +1,12 @@
+<span id="e116142240">[javascript protected email address]</span>
+<script type="text/javascript">
+ var a="gqMjyw7lZCaKk6p0J3uAUYS1.dbIW2hXzDHmiVNotOPRe_Ev@c4Gs58+LBr-F9QTfxn";
+ var b=a.split("").sort().join("");
+ var c="F_-F6F_-FMe_";
+ var d="";
+ for(var e=0;e<c.length;e++)
+ d+=b.charAt(a.indexOf(c.charAt(e)));
+ document
+ .getElementById("e116142240")
+ .innerHTML="<a href=\"mailto:"+d+"\">"+d+"</a>";
+</script>
diff --git a/tests/misc/url_spaces.html b/tests/misc/url_spaces.html
index f9c91b3..ebacb75 100644
--- a/tests/misc/url_spaces.html
+++ b/tests/misc/url_spaces.html
@@ -1,2 +1,2 @@
-<p><a href="http://wikipedia.org/wiki/Dawn%20of%20War">Dawn of War</a></p>
-<p><a href="http://wikipedia.org/wiki/Dawn%20of%20War" title="Dawn of War">Dawn of War</a></p> \ No newline at end of file
+<p><a href="http://wikipedia.org/wiki/Dawn of War">Dawn of War</a></p>
+<p><a href="http://wikipedia.org/wiki/Dawn of War" title="Dawn of War">Dawn of War</a></p> \ No newline at end of file
diff --git a/tests/test_extensions.py b/tests/test_extensions.py
index add759a..d33feec 100644
--- a/tests/test_extensions.py
+++ b/tests/test_extensions.py
@@ -126,12 +126,37 @@ class TestCodeHilite(unittest.TestCase):
'<pre class="codehilite"><code class="language-python"># A Code Comment'
'</code></pre>')
+ def testHighlightLinesWithColon(self):
+ # Test with hl_lines delimited by single or double quotes.
+ text0 = '\t:::Python hl_lines="2"\n\t#line 1\n\t#line 2\n\t#line 3'
+ text1 = "\t:::Python hl_lines='2'\n\t#line 1\n\t#line 2\n\t#line 3"
+
+ for text in (text0, text1):
+ md = markdown.Markdown(extensions=['codehilite'])
+ if self.has_pygments:
+ self.assertEqual(md.convert(text),
+ '<div class="codehilite"><pre>'
+ '<span class="c">#line 1</span>\n'
+ '<span class="hll"><span class="c">#line 2</span>\n</span>'
+ '<span class="c">#line 3</span>\n'
+ '</pre></div>')
+ else:
+ self.assertEqual(md.convert(text),
+ '<pre class="codehilite">'
+ '<code class="language-python">#line 1\n'
+ '#line 2\n'
+ '#line 3</code></pre>')
class TestFencedCode(unittest.TestCase):
""" Test fenced_code extension. """
def setUp(self):
self.md = markdown.Markdown(extensions=['fenced_code'])
+ self.has_pygments = True
+ try:
+ import pygments
+ except ImportError:
+ self.has_pygments = False
def testBasicFence(self):
""" Test Fenced Code Blocks. """
@@ -191,6 +216,65 @@ Fenced code block
'~~~~~ # these tildes will not close the block\n'
'</code></pre>')
+ def testFencedCodeWithHighlightLines(self):
+ """ Test Fenced Code with Highlighted Lines. """
+
+ text = '''
+```hl_lines="1 3"
+line 1
+line 2
+line 3
+```'''
+ md = markdown.Markdown(extensions=[
+ 'codehilite(linenums=None,guess_lang=False)',
+ 'fenced_code'])
+
+ if self.has_pygments:
+ self.assertEqual(md.convert(text),
+ '<div class="codehilite"><pre>'
+ '<span class="hll">line 1\n</span>'
+ 'line 2\n'
+ '<span class="hll">line 3\n</span>'
+ '</pre></div>')
+ else:
+ self.assertEqual(md.convert(text),
+ '<pre class="codehilite"><code>line 1\n'
+ 'line 2\n'
+ 'line 3</code></pre>')
+
+ def testFencedLanguageAndHighlightLines(self):
+ """ Test Fenced Code with Highlighted Lines. """
+
+ text0 = '''
+```.python hl_lines="1 3"
+#line 1
+#line 2
+#line 3
+```'''
+ text1 = '''
+~~~{.python hl_lines='1 3'}
+#line 1
+#line 2
+#line 3
+~~~'''
+ for text in (text0, text1):
+ md = markdown.Markdown(extensions=[
+ 'codehilite(linenums=None,guess_lang=False)',
+ 'fenced_code'])
+
+ if self.has_pygments:
+ self.assertEqual(md.convert(text),
+ '<div class="codehilite"><pre>'
+ '<span class="hll"><span class="c">#line 1</span>\n</span>'
+ '<span class="c">#line 2</span>\n'
+ '<span class="hll"><span class="c">#line 3</span>\n</span>'
+ '</pre></div>')
+ else:
+ self.assertEqual(md.convert(text),
+ '<pre class="codehilite"><code class="language-python">#line 1\n'
+ '#line 2\n'
+ '#line 3</code></pre>')
+
class TestHeaderId(unittest.TestCase):
""" Test HeaderId Extension. """
diff --git a/tests/util.py b/tests/util.py
index bbf7aea..0f416fb 100644
--- a/tests/util.py
+++ b/tests/util.py
@@ -1,16 +1,16 @@
import sys
if sys.version_info[0] == 3:
- from configparser import SafeConfigParser
+ from configparser import ConfigParser
else:
- from ConfigParser import SafeConfigParser
+ from ConfigParser import SafeConfigParser as ConfigParser
class MarkdownSyntaxError(Exception):
pass
-class CustomConfigParser(SafeConfigParser):
+class CustomConfigParser(ConfigParser):
def get(self, section, option):
- value = SafeConfigParser.get(self, section, option)
+ value = ConfigParser.get(self, section, option)
if option == 'extensions':
if len(value.strip()):
return value.split(',')