diff --git a/CHANGES.md b/CHANGES.md index dcfaf393..21fb4ca3 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -11,6 +11,7 @@ - [pull #710] Add emoji support (#709) - [pull #713] Fix `header-ids` extra generating duplicate ids when a suffixed id collides with another header (#661) - [pull #705] XSS fixes in links, images, and more +- [pull #720] Add `wiki-links` extra for `[[Page Name]]` style links (#221) ## python-markdown2 2.5.5 diff --git a/lib/markdown2.py b/lib/markdown2.py index 86b5d588..bf7df1ef 100755 --- a/lib/markdown2.py +++ b/lib/markdown2.py @@ -100,6 +100,9 @@ a Table of Contents for the document. (experimental) * use-file-vars: Look for an Emacs-style markdown-extras file variable to turn on Extras. +* wiki-links: Wiki-style `[[Page Name]]` links. The generated URL and CSS class + can be configured via the "base_url", "end_url", "html_class" and "build_url" + options. * wiki-tables: Google Code Wiki-style tables. See . * wavedrom: Support for generating Wavedrom digital timing diagrams @@ -4368,6 +4371,60 @@ def run(self, text: str): return FencedCodeBlocks.fenced_code_block_re.sub(self.sub, text) +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. + + The generated URL and CSS class can be tuned with the following options: + + - `base_url`: prepended to the link target (default `/`) + - `end_url`: appended to the link target (default `/`) + - `html_class`: the CSS class set on the generated `` (default + `wikilink`; pass an empty string to omit the attribute) + - `build_url`: an optional callable `build_url(label, base_url, end_url)` + returning the href, for full control over the target + + For example:: + + markdown('[[Foo Bar]]', extras={'wiki-links': { + 'base_url': '/', 'end_url': '.html', 'html_class': 'localLink'}}) + + yields ``Foo Bar``. + ''' + name = 'wiki-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) + + def build_url(self, label: str) -> str: + base_url = self.options.get('base_url', '/') + end_url = self.options.get('end_url', '/') + build_url = self.options.get('build_url') + if build_url is not None: + return build_url(label, base_url, end_url) + return '%s%s%s' % (base_url, label.replace(' ', '_'), end_url) + + def sub(self, match: re.Match[str]) -> str: + label = match.group(1).strip() + if not label: + # Nothing but whitespace between the brackets; leave it alone. + return match.group(0) + url = self.build_url(label).replace('"', '"') + html_class = self.options.get('html_class', 'wikilink') + class_str = ' class="%s"' % html_class if html_class else '' + link = '%s' % (url, class_str, label) + # Hash the finished anchor so later stages (emphasis, escaping, ...) + # don't mangle labels or URLs that contain "_" and friends. + return self.md._hash_span(link) + + def test(self, text: str) -> bool: + return '[[' in text + + class WikiTables(Extra): ''' Google Code Wiki-style tables. See @@ -4449,6 +4506,7 @@ def test(self, text): TelegramSpoiler.register() Underline.register() Wavedrom.register() +WikiLinks.register() WikiTables.register() diff --git a/test/tm-cases/wiki_links.html b/test/tm-cases/wiki_links.html new file mode 100644 index 00000000..762de41f --- /dev/null +++ b/test/tm-cases/wiki_links.html @@ -0,0 +1,16 @@ +

See the Home Page or the About section.

+ +

Wiki links work in the Middle Of A Sentence too.

+ +

An [[Inline Code]] span is left untouched, and so is a fenced block:

+ +
[[Not A Link]]
+
+ +

Labels containing an under_score, like snake_case_page, are not +turned into emphasis.

+ +

Regular Markdown links keep working alongside +wiki links.

+ +

Empty brackets like [[ ]] are ignored.

diff --git a/test/tm-cases/wiki_links.opts b/test/tm-cases/wiki_links.opts new file mode 100644 index 00000000..6eba6909 --- /dev/null +++ b/test/tm-cases/wiki_links.opts @@ -0,0 +1 @@ +{"extras": ["wiki-links"]} diff --git a/test/tm-cases/wiki_links.tags b/test/tm-cases/wiki_links.tags new file mode 100644 index 00000000..08eff55f --- /dev/null +++ b/test/tm-cases/wiki_links.tags @@ -0,0 +1 @@ +extra wiki-links diff --git a/test/tm-cases/wiki_links.text b/test/tm-cases/wiki_links.text new file mode 100644 index 00000000..6d68d9a5 --- /dev/null +++ b/test/tm-cases/wiki_links.text @@ -0,0 +1,15 @@ +See the [[Home Page]] or the [[About]] section. + +Wiki links work in the [[Middle Of A Sentence]] too. + +An `[[Inline Code]]` span is left untouched, and so is a fenced block: + + [[Not A Link]] + +Labels containing an under_score, like [[snake_case_page]], are not +turned into emphasis. + +Regular [Markdown links](https://example.com/) keep working alongside +wiki links. + +Empty brackets like [[ ]] are ignored. diff --git a/test/tm-cases/wiki_links_options.html b/test/tm-cases/wiki_links_options.html new file mode 100644 index 00000000..30ca8181 --- /dev/null +++ b/test/tm-cases/wiki_links_options.html @@ -0,0 +1,3 @@ +

The mypage link uses a custom base url, suffix and CSS class.

+ +

A Two Word target has its spaces replaced with underscores.

diff --git a/test/tm-cases/wiki_links_options.opts b/test/tm-cases/wiki_links_options.opts new file mode 100644 index 00000000..9a615bf8 --- /dev/null +++ b/test/tm-cases/wiki_links_options.opts @@ -0,0 +1 @@ +{"extras": {"wiki-links": {"base_url": "/wiki/", "end_url": ".html", "html_class": "localLink"}}} diff --git a/test/tm-cases/wiki_links_options.tags b/test/tm-cases/wiki_links_options.tags new file mode 100644 index 00000000..08eff55f --- /dev/null +++ b/test/tm-cases/wiki_links_options.tags @@ -0,0 +1 @@ +extra wiki-links diff --git a/test/tm-cases/wiki_links_options.text b/test/tm-cases/wiki_links_options.text new file mode 100644 index 00000000..a2073ded --- /dev/null +++ b/test/tm-cases/wiki_links_options.text @@ -0,0 +1,3 @@ +The [[mypage]] link uses a custom base url, suffix and CSS class. + +A [[Two Word]] target has its spaces replaced with underscores.