Skip to content

Commit 32299d0

Browse files
committed
fix: wikilink re-implemented as inline parser not plugin
The problem that was brought up in #144 was that [[Wikilinks]] were half-rendered in code blocks. This is fixed. Some extra steps were necessary to make sure that [[Wikilinks|with title]] are rendered correct when in a table.
1 parent 12d3c00 commit 32299d0

File tree

4 files changed

+67
-64
lines changed

4 files changed

+67
-64
lines changed

otterwiki/plugins.py

-34
Original file line numberDiff line numberDiff line change
@@ -40,39 +40,6 @@ def page_view_htmlcontent_postprocess(self, html, page):
4040
"""
4141

4242

43-
class WikiLinkPlugin:
44-
"""This plugin preprocesses links in the [[WikiLink]] style."""
45-
46-
wiki_link_outer = re.compile(
47-
r'\[\['
48-
r'([^\]]+)'
49-
r'\]\](?!\])' # [[ # ... # ]]
50-
)
51-
wiki_link_inner = re.compile(r'([^\|]+)\|?(.*)')
52-
53-
@hookimpl
54-
def renderer_markdown_preprocess(self, md):
55-
"""
56-
Will turn
57-
[[Page]]
58-
[[Title|Link]]
59-
into
60-
[Page](/Page)
61-
[Title](/Link)
62-
"""
63-
for m in self.wiki_link_outer.finditer(md):
64-
title, link = self.wiki_link_inner.findall(m.group(1))[0]
65-
if link == '':
66-
link = title
67-
if not link.startswith("/"):
68-
link = f"/{link}"
69-
# quote link (and just in case someone encoded already: unquote)
70-
link = urllib.parse.quote(urllib.parse.unquote(link), safe="/#")
71-
md = md.replace(m.group(0), f'[{title}]({link})')
72-
73-
return md
74-
75-
7643
# pluggy doesn't by default handle chaining the output of one plugin into
7744
# another, so this is a small utility function to do this.
7845
# this utility function will chain the result of each hook into the first
@@ -88,5 +55,4 @@ def chain_hooks(hook_name, value, *args, **kwargs):
8855
# addition to the utility function above.
8956
plugin_manager = pluggy.PluginManager("otterwiki")
9057
plugin_manager.add_hookspecs(OtterWikiPluginSpec)
91-
plugin_manager.register(WikiLinkPlugin())
9258
plugin_manager.load_setuptools_entrypoints("otterwiki")

otterwiki/renderer.py

+2
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
plugin_fold,
2828
plugin_math,
2929
plugin_alerts,
30+
plugin_wikilink,
3031
)
3132
from otterwiki.plugins import chain_hooks
3233
from bs4 import BeautifulSoup
@@ -197,6 +198,7 @@ def __init__(self):
197198
plugin_fold,
198199
plugin_math,
199200
plugin_alerts,
201+
plugin_wikilink,
200202
],
201203
)
202204
self.lastword = re.compile(r"([a-zA-Z_0-9\.]+)$")

otterwiki/renderer_plugins.py

+64
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
from mistune.inline_parser import LINK_LABEL
88
from mistune.util import unikey, ESCAPE_TEXT
9+
import urllib.parse
910

1011
__all__ = ['plugin_task_lists', 'plugin_footnotes']
1112

@@ -559,6 +560,68 @@ def __call__(self, md):
559560
)
560561

561562

563+
class mistunePluginWikiLink:
564+
"""This plugin preprocesses links in the [[WikiLink]] style."""
565+
566+
PIPE_REPLACEMENT="\ufeff"
567+
568+
WIKI_LINK = (
569+
r"\[\[(([^|\]#]+)(?:#[^\]]*)?(?:(\|)([^\]]+))?)\]\]"
570+
)
571+
WIKI_LINK_RE = re.compile(WIKI_LINK)
572+
WIKI_LINK_MOD = (
573+
r"\[\[(([^"+PIPE_REPLACEMENT+r"\]#]+)(?:#[^\]]*)?(?:("+PIPE_REPLACEMENT+r")([^\]]+))?)\]\]"
574+
)
575+
WIKI_LINK_MOD_RE = re.compile(WIKI_LINK_MOD)
576+
577+
def parse_wikilink(self, inline, m, state):
578+
title, link = m.group(2), m.group(4) or ""
579+
580+
if link == '':
581+
link = title
582+
if not link.startswith("/"):
583+
link = f"/{link}"
584+
# quote link (and just in case someone encoded already: unquote)
585+
link = urllib.parse.quote(urllib.parse.unquote(link), safe="/#")
586+
587+
return "wikilink", inline.render(title, state), link
588+
589+
def render_html_wikilink(self, text, link):
590+
return '<a href="'+ link +'">' + text + '</a>'
591+
592+
def replace_wikilinks(self, match):
593+
if match.group(3) and len(match.group(3)):
594+
return "[["+match.group(2)+"%"+match.group(4)+"]]"
595+
return "[["+match.group(2)+"]]"
596+
597+
def before_parse(self, md, s, state):
598+
def replace(match):
599+
if match.group(3) and len(match.group(3)):
600+
return "[["+match.group(2)+self.PIPE_REPLACEMENT+match.group(4)+"]]"
601+
return "[["+match.group(2)+"]]"
602+
s = self.WIKI_LINK_RE.sub(replace, s)
603+
return s, state
604+
605+
def after_render(self, md, s, state):
606+
def replace(match):
607+
if match.group(3) and len(match.group(3)):
608+
return "[["+match.group(2)+"|"+match.group(4)+"]]"
609+
return "[["+match.group(2)+"]]"
610+
s = self.WIKI_LINK_MOD_RE.sub(replace, s)
611+
return s
612+
613+
def __call__(self, md):
614+
md.before_parse_hooks.append(self.before_parse)
615+
md.after_render_hooks.append(self.after_render)
616+
617+
md.inline.register_rule('wikilink', self.WIKI_LINK_MOD, self.parse_wikilink)
618+
619+
md.inline.rules.append('wikilink')
620+
621+
if md.renderer.NAME == 'html':
622+
md.renderer.register('wikilink', self.render_html_wikilink)
623+
624+
562625
plugin_task_lists = mistunePluginTaskLists()
563626
plugin_footnotes = mistunePluginFootnotes()
564627
plugin_mark = mistunePluginMark()
@@ -567,3 +630,4 @@ def __call__(self, md):
567630
plugin_fold = mistunePluginFold()
568631
plugin_math = mistunePluginMath()
569632
plugin_alerts = mistunePluginAlerts()
633+
plugin_wikilink = mistunePluginWikiLink()

tests/test_renderer.py

+1-30
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
# vim: set et ts=8 sts=4 sw=4 ai:
33

44
import pytest
5-
from otterwiki.plugins import WikiLinkPlugin
65
from otterwiki.renderer import render, clean_html
76

87

@@ -122,40 +121,12 @@ def test_wiki_link_in_table(req_ctx):
122121
[[Paul|people/Paul]]
123122
"""
124123
html, _ = render.markdown(text)
124+
print(html)
125125
assert '<td><a href="/people/John">people/John</a></td>' in html
126126
assert '<a href="/people/Paul">Paul</a>' in html
127127
assert '<td><a href="/people/Mary">Mary</a></td>' in html
128128

129129

130-
def test_preprocess_wiki_links():
131-
plugin = WikiLinkPlugin()
132-
md = plugin.renderer_markdown_preprocess(
133-
"""
134-
[[Page]]
135-
[[Title|Link]]
136-
[[Text with space|Link with space]]
137-
"""
138-
)
139-
assert '[Page](/Page)' in md
140-
assert '[Title](/Link)' in md
141-
assert '[Page](/Page)' == plugin.renderer_markdown_preprocess("[[Page]]")
142-
assert '[Title](/Link)' == plugin.renderer_markdown_preprocess("[[Title|Link]]")
143-
assert (
144-
'[Text with space](/Link%20with%20space)'
145-
== plugin.renderer_markdown_preprocess("[[Text with space|Link with space]]")
146-
)
147-
assert (
148-
'[Text with space](/Link%20with%20space)'
149-
== plugin.renderer_markdown_preprocess(
150-
"[[Text with space|Link%20with%20space]]"
151-
)
152-
)
153-
# make sure fragment identifier of the URL survived the parser
154-
assert '[Random#Title](/Random#Title)' == plugin.renderer_markdown_preprocess(
155-
"[[Random#Title]]"
156-
)
157-
158-
159130
def test_table_align():
160131
text = """
161132
| left th | center th | right th |

0 commit comments

Comments
 (0)