Skip to content
Open
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
1 change: 1 addition & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

## python-markdown2 2.5.6 (not yet released)

- Fix `wiki-links` generating nested anchors and replacing syntax inside image descriptions and HTML attributes.
- [pull #762] Speed up documents with many links by skipping the backslash-escape scan for text without a backslash
- [pull #746] Preserve query strings and fragments in `markdown-file-links` conversions, including reference-link targets.
- [pull #739] Fix `header-ids` extra putting sanitized HTML into header ids in safe mode, which allowed attribute injection (#649)
Expand Down
18 changes: 16 additions & 2 deletions lib/markdown2.py
Original file line number Diff line number Diff line change
Expand Up @@ -4482,6 +4482,8 @@ class WikiLinks(Extra):
'''
Wiki-style links. `[[Page Name]]` becomes a link to another page. This is
modelled on the wikilinks extension shipped with Python-Markdown.
Wiki syntax inside existing links, image descriptions, or HTML attributes
is left as literal text.

The generated URL and CSS class can be tuned with the following options:

Expand All @@ -4500,12 +4502,24 @@ class WikiLinks(Extra):
yields ``<a href="/Foo_Bar.html" class="localLink">Foo Bar</a>``.
'''
name = 'wiki-links'
order = (Stage.LINKS,), ()
order = (), (Stage.LINKS,)

_wiki_link_re = re.compile(r'\[\[([\w0-9_ -]+)\]\]', re.UNICODE)

def run(self, text: str) -> str:
return self._wiki_link_re.sub(self.sub, text)
# Resolve Markdown links first, then only link text outside existing
# anchors and HTML markup (including image attributes).
tokens = self.md._sorta_html_tokenize_re.split(text)
in_anchor = False
for index, token in enumerate(tokens):
if index % 2:
if re.match(r'<a(?:\s|>)', token, re.I):
in_anchor = True
elif re.match(r'</a\s*>', token, re.I):
in_anchor = False
elif not in_anchor:
tokens[index] = self._wiki_link_re.sub(self.sub, token)
return ''.join(tokens)

def build_url(self, label: str) -> str:
base_url = self.options.get('base_url', '/')
Expand Down
27 changes: 27 additions & 0 deletions test/test_markdown2.py
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,33 @@ class DirectTestCase(_MarkdownTestCase):
Python-markdown (markdown.py).
"""

def test_wiki_links_in_links_and_images(self):
cases = [
('[go [[Home]]](https://example.com)',
'<p><a href="https://example.com">go [[Home]]</a></p>\n'),
('![go [[Home]]](img.png)',
'<p><img src="img.png" alt="go [[Home]]" /></p>\n'),
('[go [[Home]]][target]\n\n[target]: https://example.com',
'<p><a href="https://example.com">go [[Home]]</a></p>\n'),
('<a href="/old">[[Home]]</a>',
'<p><a href="/old">[[Home]]</a></p>\n'),
('<A HREF="/old"><em>[[Home]]</em></A> [[Page]]',
'<p><A HREF="/old"><em>[[Home]]</em></A> <a href="/Page/" class="wikilink">Page</a></p>\n'),
('![go [[Home]]][img]\n\n[img]: pic.png',
'<p><img src="pic.png" alt="go [[Home]]" /></p>\n'),
('[![go [[Home]]](img.png)](/go)',
'<p><a href="/go"><img src="img.png" alt="go [[Home]]" /></a></p>\n'),
(r'\[[Home]] `[[Home]]` [[Page]]',
'<p>[[Home]] <code>[[Home]]</code> <a href="/Page/" class="wikilink">Page</a></p>\n'),
('<span title="[[Home]]">[[Page]]</span>',
'<p><span title="[[Home]]"><a href="/Page/" class="wikilink">Page</a></span></p>\n'),
('[go](https://example.com) [[Home]]',
'<p><a href="https://example.com">go</a> <a href="/Home/" class="wikilink">Home</a></p>\n'),
]
for source, expected in cases:
with self.subTest(source=source):
self.assertEqual(markdown2.markdown(source, extras=['wiki-links']), expected)

def test_header_ids_from_file_vars(self):
md = markdown2.Markdown(use_file_vars=True)
modeline = "<!-- -*- markdown-extras: header-ids{} -*- -->\n"
Expand Down
Loading