diff options
-rw-r--r-- | docs/extensions/code_hilite.txt | 11 | ||||
-rw-r--r-- | docs/extensions/extra.txt | 12 | ||||
-rw-r--r-- | docs/extensions/fenced_code_blocks.txt | 25 | ||||
-rw-r--r-- | docs/extensions/footnotes.txt | 29 | ||||
-rw-r--r-- | docs/extensions/wikilinks.txt | 5 | ||||
-rw-r--r-- | markdown/__init__.py | 2 | ||||
-rw-r--r-- | markdown/blockprocessors.py | 10 | ||||
-rw-r--r-- | markdown/extensions/codehilite.py | 38 | ||||
-rw-r--r-- | markdown/extensions/extra.py | 12 | ||||
-rw-r--r-- | markdown/extensions/fenced_code.py | 31 | ||||
-rw-r--r-- | markdown/inlinepatterns.py | 5 | ||||
-rw-r--r-- | markdown/preprocessors.py | 22 | ||||
-rw-r--r-- | markdown/treeprocessors.py | 12 | ||||
-rw-r--r-- | tests/basic/angle-links-and-img.html | 2 | ||||
-rw-r--r-- | tests/misc/brackets-in-img-title.html | 9 | ||||
-rw-r--r-- | tests/misc/brackets-in-img-title.txt | 12 | ||||
-rw-r--r-- | tests/misc/escaped_chars_in_js.html | 12 | ||||
-rw-r--r-- | tests/misc/escaped_chars_in_js.txt | 12 | ||||
-rw-r--r-- | tests/misc/url_spaces.html | 4 | ||||
-rw-r--r-- | tests/test_extensions.py | 84 | ||||
-rw-r--r-- | tests/util.py | 8 |
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(',') |