diff options
| author | 2018-08-07 15:09:08 +0100 | |
|---|---|---|
| committer | 2018-08-07 15:09:16 +0100 | |
| commit | af54db6c136138c66cf5ca72419989525a0baa5c (patch) | |
| tree | 8519aeab8d45277c51797c7dc23aacf3b56ed1bb /pysite/rst | |
| parent | A wizard is never late, nor is he early. (diff) | |
Initial project layout for django
Diffstat (limited to 'pysite/rst')
| -rw-r--r-- | pysite/rst/__init__.py | 108 | ||||
| -rw-r--r-- | pysite/rst/directives/__init__.py | 65 | ||||
| -rw-r--r-- | pysite/rst/roles.py | 125 |
3 files changed, 0 insertions, 298 deletions
diff --git a/pysite/rst/__init__.py b/pysite/rst/__init__.py deleted file mode 100644 index e58fbe8c..00000000 --- a/pysite/rst/__init__.py +++ /dev/null @@ -1,108 +0,0 @@ -import re - -from docutils.core import publish_parts -from docutils.parsers.rst.directives import register_directive -from docutils.parsers.rst.roles import register_canonical_role - -from pysite.rst.directives import ButtonDirective -from pysite.rst.roles import fira_code_role, icon_role, page_role, url_for_role - -RST_TEMPLATE = """.. contents:: - -{0}""" - -CONTENTS_REGEX = re.compile(r"""<div class=\"contents topic\" id=\"contents\">(.*?)</div>""", re.DOTALL) -HREF_REGEX = re.compile(r"""<a class=\"reference internal\" href=\"(.*?)\".*?>(.*?)</a>""") - -TABLE_FRAGMENT = """<table class="uk-table uk-table-divider table-bordered uk-table-striped">""" - - -def render(rst: str, link_headers=True): - if link_headers: - rst = RST_TEMPLATE.format(rst) - - html = publish_parts( - source=rst, writer_name="html5", settings_overrides={ - "halt_level": 2, "syntax_highlight": "short", "initial_header_level": 3 - } - )["html_body"] - - data = { - "html": html, - "headers": [] - } - - if link_headers: - match = CONTENTS_REGEX.search(html) # Find the contents HTML - - if match: - data["html"] = html.replace(match.group(0), "") # Remove the contents from the document HTML - depth = 0 - headers = [] - current_header = {} - - group = match.group(1) - - # Sanitize the output so we can more easily parse it - group = group.replace("<li>", "<li>\n") - group = group.replace("</li>", "\n</li>") - group = group.replace("<p>", "<p>\n") - group = group.replace("</p>", "\n</p>") - - for line in group.split("\n"): - line = line.strip() # Remove excess whitespace - - if not line: # Nothing to process - continue - - if line.startswith("<li>") and depth <= 2: - # We've found a header, or the start of a header group - depth += 1 - elif line.startswith("</li>") and depth >= 0: - # That's the end of a header or header group - - if depth == 1: - # We just dealt with an entire header group, so store it - headers.append(current_header.copy()) # Store a copy, since we're clearing the dict - current_header.clear() - - depth -= 1 - elif line.startswith("<a") and depth <= 2: - # We've found an actual URL - match = HREF_REGEX.match(line) # Parse the line for the ID and header title - - if depth == 1: # Top-level header, so just store it in the current header - current_header["id"] = match.group(1) - - title = match.group(2) - - if title.startswith("<i"): # We've found an icon, which needs to have a space after it - title = title.replace("</i> ", "</i> ") - - current_header["title"] = title - else: # Second-level (or deeper) header, should be stored in a list of sub-headers - sub_headers = current_header.get("sub_headers", []) - title = match.group(2) - - if title.startswith("<i"): # We've found an icon, which needs to have a space after it - title = title.replace("</i> ", "</i> ") - - sub_headers.append({ - "id": match.group(1), - "title": title - }) - current_header["sub_headers"] = sub_headers - - data["headers"] = headers - - data["html"] = data["html"].replace("<table>", TABLE_FRAGMENT) # Style the tables properly - - return data - - -register_canonical_role("fira_code", fira_code_role) -register_canonical_role("icon", icon_role) -register_canonical_role("page", page_role) -register_canonical_role("url_for", url_for_role) - -register_directive("button", ButtonDirective) diff --git a/pysite/rst/directives/__init__.py b/pysite/rst/directives/__init__.py deleted file mode 100644 index b4359200..00000000 --- a/pysite/rst/directives/__init__.py +++ /dev/null @@ -1,65 +0,0 @@ -from docutils import nodes -from docutils.parsers.rst import Directive -from docutils.parsers.rst.directives import unchanged, unchanged_required -from flask import url_for -from jinja2 import escape - -BUTTON_TYPES = ("default", "primary", "secondary", "danger", "darkish", "darker") - -ICON_WEIGHT_TABLE = { - "light": "fal", - "regular": "far", - "solid": "fas", - "branding": "fab" -} -ICON_WEIGHTS = tuple(ICON_WEIGHT_TABLE.values()) - - -class ButtonDirective(Directive): - has_content = True - - option_spec = { - "icon": unchanged_required, - "text": unchanged_required, - "type": unchanged, - "url": unchanged, - } - - def run(self): - icon = self.options.get("icon", "") - button_type = self.options.get("type", "primary") - - text = self.options["text"] - url = self.options["url"] - - if icon: - parts = [escape(x) for x in icon.split("/")] - - if len(parts) != 2: - raise self.error("Icon specification must be in the form <type>/<name>") - elif parts: - weight = parts[0] - - if weight not in ICON_WEIGHTS: - weight = ICON_WEIGHT_TABLE.get(weight) - - if not weight: - raise self.error( - "Icon type must be one of light, regular, solid or " - "branding, or a font-awesome weight class" - ) - - icon_html = f"""<i class="uk-icon fa-fw {weight} fa-{parts[1]}"></i>""" - else: - icon_html = "" - - if button_type not in BUTTON_TYPES: - self.error(f"Button type must be one of {', '.join(BUTTON_TYPES[:-1])} or {[-1]}") - - if url.startswith("flask://"): - url = url_for(url.split("://", 1)[1]) - elif url.startswith("wiki://"): - url = url_for("wiki.page", page=url.split("://", 1)[1]) - html = f"""<a class="uk-button uk-button-{button_type}" href=\"{url}\">{icon_html} {text}</a>""" - - return [nodes.raw(html, html, format="html", **{})] diff --git a/pysite/rst/roles.py b/pysite/rst/roles.py deleted file mode 100644 index d83f07f9..00000000 --- a/pysite/rst/roles.py +++ /dev/null @@ -1,125 +0,0 @@ -from docutils import nodes -from docutils.parsers.rst.roles import set_classes -from docutils.parsers.rst.states import Inliner -from flask import url_for -from jinja2 import escape - - -def icon_role(_role: str, rawtext: str, text: str, lineno: int, inliner: Inliner, - options: dict = None, _content: dict = None): - if options is None: - options = {} - - set_classes(options) - - if "/" in text: - parts = [escape(x) for x in text.split("/")] - else: - msg = inliner.reporter.error("Icon specification must be in the form <type>/<name>", line=lineno) - prb = inliner.problematic(text, rawtext, msg) - - return [prb], [msg] - - if len(parts) != 2: - msg = inliner.reporter.error("Icon specification must be in the form <type>/<name>", line=lineno) - prb = inliner.problematic(text, rawtext, msg) - - return [prb], [msg] - else: - if parts[0] == "light": - weight = "fal" - elif parts[0] == "regular": - weight = "far" - elif parts[0] == "solid": - weight = "fas" - elif parts[0] == "branding": - weight = "fab" - else: - msg = inliner.reporter.error("Icon type must be one of light, regular, solid or branding", line=lineno) - prb = inliner.problematic(text, rawtext, msg) - - return [prb], [msg] - - html = f"""<i class="uk-icon fa-fw {weight} fa-{parts[1]}"></i>""" - - node = nodes.raw(html, html, format="html", **options) - return [node], [] - - -def url_for_role(_role: str, rawtext: str, text: str, lineno: int, inliner: Inliner, - options: dict = None, _content: dict = None): - if options is None: - options = {} - - set_classes(options) - - if "/" in text: - parts = [escape(x) for x in text.split("/")] - else: - msg = inliner.reporter.error("URL specification must be in the form <page.name>/<text>", line=lineno) - prb = inliner.problematic(text, rawtext, msg) - - return [prb], [msg] - - if len(parts) != 2: - msg = inliner.reporter.error("URL specification must be in the form <page.name>/<text>", line=lineno) - prb = inliner.problematic(text, rawtext, msg) - - return [prb], [msg] - else: - try: - url = url_for(parts[0]) - name = parts[1] - - html = f"""<a href="{url}">{name}</a>""" - - node = nodes.raw(html, html, format="html", **options) - return [node], [] - except Exception as e: - msg = inliner.reporter.error(str(e), line=lineno) - prb = inliner.problematic(text, rawtext, msg) - - return [prb], [msg] - - -def page_role(_role: str, rawtext: str, text: str, lineno: int, inliner: Inliner, - options: dict = None, _content: dict = None): - if options is None: - options = {} - - set_classes(options) - - if "/" in text: - parts = [escape(x) for x in text.rsplit("/", 1)] - else: - msg = inliner.reporter.error("Page specification must be in the form <page_slug>/<text>", line=lineno) - prb = inliner.problematic(text, rawtext, msg) - - return [prb], [msg] - - try: - url = url_for("wiki.page", page=parts[0]) - name = parts[1] - - html = f"""<a href="{url}">{name}</a>""" - - node = nodes.raw(html, html, format="html", **options) - return [node], [] - except Exception as e: - msg = inliner.reporter.error(str(e), line=lineno) - prb = inliner.problematic(text, rawtext, msg) - - return [prb], [msg] - - -def fira_code_role(_role: str, rawtext: str, text: str, lineno: int, inliner: Inliner, - options: dict = None, _content: dict = None): - if options is None: - options = {} - - set_classes(options) - - html = f"""<span class="fira-code">{text}</span>""" - node = nodes.raw(html, html, format="html", **options) - - return [node], [] |