Skip to content

Commit

Permalink
feat: alerts are now supported by the markdown renderer
Browse files Browse the repository at this point in the history
- added [!NOTE], [!TIP], [!IMPORTANT], [!WARNING] and [!CAUTION]
- updated /help/syntax
- added tests
  • Loading branch information
redimp committed Sep 10, 2024
1 parent 268aeca commit 1a88ab8
Show file tree
Hide file tree
Showing 5 changed files with 145 additions and 4 deletions.
2 changes: 2 additions & 0 deletions otterwiki/renderer.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
plugin_spoiler,
plugin_fold,
plugin_math,
plugin_alerts,
)
from otterwiki.plugins import chain_hooks
from bs4 import BeautifulSoup
Expand Down Expand Up @@ -185,6 +186,7 @@ def __init__(self):
plugin_spoiler,
plugin_fold,
plugin_math,
plugin_alerts,
],
)
self.lastword = re.compile(r"([a-zA-Z_0-9\.]+)$")
Expand Down
58 changes: 55 additions & 3 deletions otterwiki/renderer_plugins.py
Original file line number Diff line number Diff line change
Expand Up @@ -470,9 +470,6 @@ def __call__(self, md):


class mistunePluginMath:
# too aggressive
# MATH_BLOCK = re.compile(r'(\${1,2})((?:\\.|[\s\S])*)\1')
# MATH_BLOCK = re.compile(r'\${1,2}[^]*?[^\\]\${1,2}')
MATH_BLOCK = re.compile(r'(\${2})((?:\\.|.)*)\${2}')
MATH_INLINE_PATTERN = (
r'\$(?=[^\s\$])(' r'(?:\\\$|[^\$])*' r'(?:' + ESCAPE_TEXT + r'|[^\s\$]))\$'
Expand Down Expand Up @@ -508,10 +505,65 @@ def __call__(self, md):
md.renderer.register('math_inline', self.render_html_inline)


class mistunePluginAlerts:
TYPE_ICONS = {
'NOTE': '<i class="fas fa-info-circle"></i>',
'TIP': '<i class="far fa-lightbulb"></i>',
'IMPORTANT': '<i class="far fa-comment-alt"></i>',
'WARNING': '<i class="fas fa-exclamation-triangle"></i>',
'CAUTION': '<i class="fas fa-exclamation-circle"></i>',
}
TYPES_WITH_PIPES = "|".join(TYPE_ICONS.keys())
ALERT_LEADING = re.compile(r'^ *\>(\s*\[!(' + TYPES_WITH_PIPES + r')\])?', flags=re.MULTILINE)
ALERT_BLOCK = re.compile(r'(?: {0,3}>\s*\[!(' + TYPES_WITH_PIPES + r')\][^\n]*(?:\n|$))( {0,3}>[^\n]*(?:\n|$))+', flags=re.I)

def parse_alert_block(self, block, m, state):
text = m.group(0)
type = m.group(1).upper()

# we are searching for the complete bock, so we have to remove
# syntax from the beginning of each line>
text = self.ALERT_LEADING.sub('', text)

text = text.strip()

children = block.parse(text, state)
if not isinstance(children, list):
children = [children]

return {
"type": "alert_block",
"text": text,
"params": (type,),
"children": children,
}

def render_html_alert_block(self, text, type):
text = text.strip()
return f'<div class="quote-alert quote-alert-{type.lower()}"><div class="quote-alert-header">{self.TYPE_ICONS[type]} {type.capitalize()}</div><p>{text}</p></div>\n'

def __call__(self, md):
md.block.register_rule(
'alert_block', self.ALERT_BLOCK, self.parse_alert_block
)

index = md.block.rules.index('block_quote')
if index != -1:
md.block.rules.insert(index, 'alert_block')
else:
md.block.rules.append('alert_block')

if md.renderer.NAME == "html":
md.renderer.register(
"alert_block", self.render_html_alert_block
)


plugin_task_lists = mistunePluginTaskLists()
plugin_footnotes = mistunePluginFootnotes()
plugin_mark = mistunePluginMark()
plugin_fancy_blocks = mistunePluginFancyBlocks()
plugin_spoiler = mistunePluginSpoiler()
plugin_fold = mistunePluginFold()
plugin_math = mistunePluginMath()
plugin_alerts = mistunePluginAlerts()
32 changes: 32 additions & 0 deletions otterwiki/static/css/otterwiki.css
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,38 @@ pre,
background-color: #263238;
}

.quote-alert {
border-left: .3rem solid;
padding: 0.5rem 2rem;
margin: 1rem 0em;
background-color: rgba(0, 0, 0, 0.05);
}
.quote-alert p {
margin-block-start: .5rem;
margin-block-end: .5rem;
}
/* quote alert header */
.quote-alert-header i {
padding: 0 .5rem 0 0;
font-size: 1.6rem;
}
/* quote alert header */
.quote-alert-note .quote-alert-header { color: var(--primary-color); }
.quote-alert-tip .quote-alert-header { color: var(--green-color-dark); }
.dark-mode .quote-alert-tip .quote-alert-header { color: var(--green-color); }
.quote-alert-important .quote-alert-header { color: var(--indigo-color); }
.quote-alert-warning .quote-alert-header { color: var(--yellow-color-dark); }
.dark-mode .quote-alert-warning .quote-alert-header { color: var(--yellow-color); }
.quote-alert-caution .quote-alert-header { color: var(--danger-color); }
/* quote alert border */
.quote-alert-note { border-color: var(--primary-color); }
.quote-alert-tip { border-color: var(--green-color-dark); }
.dark-mode .quote-alert-tip { border-color: var(--green-color); }
.quote-alert-warning { border-color: var(--yellow-color-dark); }
.dark-mode .quote-alert-warning { border-color: var(--yellow-color); }
.quote-alert-caution { border-color: var(--danger-color); }
.quote-alert-important { border-color: var(--indigo-color); }

.revision-small {
font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
text-decoration: none;
Expand Down
22 changes: 22 additions & 0 deletions otterwiki/templates/snippets/syntax.html
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,10 @@ <h4>{{ name }}</h4>

{{ heading("Blocks") }}

These special blocks are not part of the markdown standard, but
are helpful for structuring content.

<h4>Fancy blocks</h4>
<div class="row">
<div class="col-md-6 col-sm-12 p-5">
<pre>
Expand All @@ -315,6 +319,7 @@ <h4>{{ name }}</h4>
</div> {# row #}
Block styles available: <span class="alert alert-primary text-monospace" style="padding: 0.2rem">info</span>, <span class="alert alert-success text-monospace" style="padding: 0.2rem">success</span>, <span class="alert alert-secondary text-monospace" style="padding: 0.2rem">warning</span>, <span class="alert alert-danger text-monospace" style="padding: 0.2rem">danger</span> and <span class="alert text-monospace" style="padding: 0.2rem">none</span>.

<h4>Spoiler blocks</h4>
<div class="row my-20">
<div class="col-md-6 col-sm-12">
<pre>
Expand All @@ -329,6 +334,7 @@ <h4>{{ name }}</h4>
</div>
</div>

<h4>Folded blocks</h4>
<div class="row my-20 h-100">
<div class="col-md-6 col-sm-12">
<pre>
Expand All @@ -345,6 +351,22 @@ <h4>{{ name }}</h4>
</div>
</div>

<h4>Alerts</h4>
<div class="row">
<div class="col-md-6 col-sm-12 p-5">
<pre>
> [!NOTE]
> Useful for highlighting special
> information in your code.
</pre>
</div>
<div class="col-md-6 col-sm-12 p-5">
<div class="quote-alert quote-alert-note" style="margin:0;"><div class="quote-alert-header"><i class="fas fa-info-circle"></i> Note</div><p>Useful for highlighting speacial information in your code.</p></div>
</div>
</div> {# row #}
Alerts available: <code>[!NOTE]</code>, <code>[!TIP]</code>, <code>[!IMPORTANT]</code>, <code>[!WARNING]</code> and <code>[!CAUTION]</code>.


{{ heading("Remark") }}
There's actually a lot more to Markdown than this. See the official
<a href="http://daringfireball.net/projects/markdown/basics">introduction</a> and
Expand Down
35 changes: 34 additions & 1 deletion tests/test_renderer.py
Original file line number Diff line number Diff line change
Expand Up @@ -402,7 +402,6 @@ def test_fancy_blocks():
assert "<strong>red alert</strong>" in html
assert 'class="alert alert-danger' in html
assert 'role="alert"' in html
print(html)

md = """:::
# Head of the block
Expand Down Expand Up @@ -436,3 +435,37 @@ def test_fold():
assert '>Headline is used as summary</summary>' in html
# check fold
assert "<p>with the details folded.</p>" in html

def test_all_alert_types():
from otterwiki.renderer_plugins import mistunePluginAlerts
for type, icon in mistunePluginAlerts.TYPE_ICONS.items():
md = f"> [!{type}]\n>text\n>text\n"
html, _ = render.markdown(md)
assert 'text\ntext' in html
assert icon in html

def test_alerts():
md = """random paragraph 1.
>[!TIP]
>Note content
random paragraph 2."""
html, _ = render.markdown(md)
# easier testing
html = html.replace("\n", "")
# check content
assert 'Note content' in html
assert 'Tip</div>'
# make sure nothing is lost
assert '<p>random paragraph 1.</p>' in html
assert '<p>random paragraph 2.</p>' in html

def test_mistunePluginAlerts():
from otterwiki.renderer_plugins import mistunePluginAlerts
for type, icon in mistunePluginAlerts.TYPE_ICONS.items():
# check regexp
md = f">[!{type}]\n>Note content\n>content\n"
m = mistunePluginAlerts.ALERT_BLOCK.search(md)
assert m is not None
assert m.group(1) == type

0 comments on commit 1a88ab8

Please sign in to comment.