Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Replace bootstrap alert-info/warning <div>s with custom admonitions #47

Merged
merged 4 commits into from
Apr 27, 2016
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
53 changes: 51 additions & 2 deletions doc/markdown-cells.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -126,8 +126,57 @@
"\n",
"The alternative text is shown in browsers that don't support those elements. The same text is also shown in Sphinx's LaTeX output.\n",
"\n",
"> **Note:** You can also use local files for the `<audio>` and `<video>` elements, but you have to create a link to the source file somewhere, because only then are the local files copied to the HTML output directory!\n",
"You should do that anyway to make the audio/video file accessible to browsers that don't support the `<audio>` and `<video>` elements."
"<div class=\"alert alert-info\">\n",
"\n",
"**Note:** You can also use local files for the `<audio>` and `<video>` elements, but you have to create a link to the source file somewhere, because only then are the local files copied to the HTML output directory!\n",
"You should do that anyway to make the audio/video file accessible to browsers that don't support the `<audio>` and `<video>` elements.\n",
"\n",
"</div>"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Info/Warning Boxes\n",
"\n",
"<div class=\"alert alert-warning\">\n",
"\n",
"**Warning:** This is an *experimental feature*!\n",
"\n",
"Its usage will probably change in the future or it might be removed completely!\n",
"\n",
"</div>\n",
"\n",
"Until there is an info/warning extension for Markdown/CommonMark (see [this issue](https://github.com/jupyter/notebook/issues/1292)), such boxes can be created by using HTML `<div>` elements like this:\n",
"\n",
"```html\n",
"<div class=\"alert alert-info\">\n",
"\n",
"**Note:** This is a note!\n",
"\n",
"</div>\n",
"```\n",
"\n",
"For this to work reliably, you should obey the following guidelines:\n",
"\n",
"* The `class` attribute has to be either `\"alert alert-info\"` or `\"alert alert-warning\"`, other values will not be converted correctly.\n",
"* No further attributes are allowed.\n",
"* For compatibility with CommonMark, you should surround the content with empty lines above and below.\n",
"\n",
"<div class=\"alert alert-info\">\n",
"\n",
"**Note:** The text can contain further Markdown formatting.\n",
"\n",
"It is even possible to have nested boxes:\n",
"\n",
"<div class=\"alert alert-warning\">\n",
"\n",
"... but please don't *overuse* this!\n",
"\n",
"</div>\n",
"\n",
"</div>"
]
},
{
Expand Down
58 changes: 56 additions & 2 deletions nbsphinx.py
Original file line number Diff line number Diff line change
Expand Up @@ -422,8 +422,8 @@ class NotebookParser(rst.Parser):

def get_transforms(self):
"""List of transforms for documents parsed by this parser."""
return rst.Parser.get_transforms(self) + [ProcessLocalLinks,
CreateSectionLabels]
return rst.Parser.get_transforms(self) + [
ProcessLocalLinks, CreateSectionLabels, ReplaceAlertDivs]

def parse(self, inputstring, document):
"""Parse `inputstring`, write results to `document`."""
Expand Down Expand Up @@ -836,6 +836,60 @@ def apply(self):
i_still_have_to_create_the_notebook_label = False


class ReplaceAlertDivs(docutils.transforms.Transform):
"""Replace certain <div> elements with AdmonitionNode containers.

This is a quick-and-dirty work-around until a proper
Mardown/CommonMark extension for note/warning boxes is available.

"""

default_priority = 500 # Doesn't really matter

_start_re = re.compile(
r"\s*<div\s*class\s*=\s*(?P<q>\"|')([a-z\s-]*)(?P=q)\s*>\s*",
flags=re.IGNORECASE)
_class_re = re.compile(r"\s*alert\s*alert-(info|warning)\s*")
_end_re = re.compile(r"\s*</div\s*>\s*", flags=re.IGNORECASE)

def apply(self):
start_tags = []
for node in self.document.traverse(docutils.nodes.raw):
if node['format'] != 'html':
continue
start_match = self._start_re.fullmatch(node.astext())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fullmatch doesn't work for python < 3.4

if not start_match:
continue
class_match = self._class_re.fullmatch(start_match.group(2))
if not class_match:
continue
admonition_class = class_match.group(1)
if admonition_class == 'info':
admonition_class = 'note'
start_tags.append((node, admonition_class))

# Reversed order to allow nested <div> elements:
for node, admonition_class in reversed(start_tags):
content = []
for sibling in node.traverse(include_self=False, descend=False,
siblings=True, ascend=False):
end_tag = (isinstance(sibling, docutils.nodes.raw) and
sibling['format'] == 'html' and
self._end_re.fullmatch(sibling.astext()))
if end_tag:
admonition_node = AdmonitionNode(
classes=['admonition', admonition_class])
admonition_node.extend(content)
parent = node.parent
parent.replace(node, admonition_node)
for n in content:
parent.remove(n)
parent.remove(sibling)
break
else:
content.append(sibling)


def builder_inited(app):
"""Add color definitions to LaTeX preamble."""
latex_elements = app.builder.config.latex_elements
Expand Down