Source code for swole.widgets.markdown

import dominate
from dominate.tags import div, code, pre
from markdown_it import MarkdownIt
from markdown_it.renderer import RendererHTML
from markdown_it.common.utils import escapeHtml

from swole.widgets.base import Widget


class DominateRenderer(RendererHTML):
    """ Custom renderer for markdown-it parser. It render Markdown using
    Dominate tags.
    """
    def _render(self, tokens, options, env):
        """ Recursive function parsing each token into the right Dominate tag.
        """
        pending_tags = []
        pending_content = [[]]
        for t, token in enumerate(tokens):
            if token.type == "fence":       # Special case
                pending_content[-1].append(self.fence(tokens, t, options, env))
            elif token.tag != "":
                if not token.nesting:       # Directly append to content
                    c = [token.content] if token.content else []
                    tag = getattr(dominate.tags, token.tag)
                    tag = tag(*c) if token.attrs is None else tag(*c, **token.attrs)
                    pending_content[-1].append(tag)
                elif len(pending_tags) > 0 and pending_tags[-1] == token.tag:       # Closing tag
                    t = pending_tags.pop()
                    c = pending_content.pop()
                    tag = getattr(dominate.tags, t)
                    tag = tag(c) if token.attrs is None else tag(c, **token.attrs)
                    pending_content[-1].append(tag)
                else:       # Opening tag
                    if token.tag == "p" and len(pending_tags) > 0 and pending_tags[-1] == "li":
                        continue

                    pending_tags.append(token.tag)
                    pending_content.append([])
            elif token.children is not None:
                assert len(token.children) > 0
                pending_content[-1].extend(self._render(token.children, options, env))
            else:
                if not token.hidden:
                    pending_content[-1].append(escapeHtml(token.content))

        assert len(pending_tags) == 0, pending_tags
        assert len(pending_content) == 1, pending_content

        return pending_content[-1]

    def render(self, tokens, options, env):
        """ Takes token stream and generates Dominate HTML.
        Check the default implementation at :
        https://github.com/executablebooks/markdown-it-py/blob/master/markdown_it/renderer.py
        """
        content = self._render(tokens, options, env)

        d = div()
        d.add(*content)
        return d

    def fence(self, tokens, idx, options, env):
        token = tokens[idx]
        # TODO : Later, add correct highlights for languages
        # info = unescapeAll(token.info).strip() if token.info else ""
        # langName = ""

        # if info:
        #     langName = info.split()[0]

        highlighted = escapeHtml(token.content)

        if token.attrs:
            return pre(code(highlighted), **token.attr)
        else:
            return pre(code(highlighted))


[docs]class Markdown(Widget): """ A general widget to write Markdown. """ def __init__(self, content="", **kwargs): """ Arguments: content (`str`, optional): Markdown content. Defaults to empty string. """ super().__init__(**kwargs) self.content = content def html(self): md = MarkdownIt(renderer_cls=DominateRenderer).enable('table') html = md.render(self.content) html.attributes["id"] = self.id return html def get(self): return self.content def set(self, x): self.content = x