diff options
Diffstat (limited to 'pysite')
| -rw-r--r-- | pysite/rst/__init__.py | 70 | ||||
| -rw-r--r-- | pysite/views/wiki/edit.py | 5 | ||||
| -rw-r--r-- | pysite/views/wiki/render.py | 4 | ||||
| -rw-r--r-- | pysite/views/ws/rst.py | 2 | 
4 files changed, 76 insertions, 5 deletions
| diff --git a/pysite/rst/__init__.py b/pysite/rst/__init__.py index e0fc973e..0c069615 100644 --- a/pysite/rst/__init__.py +++ b/pysite/rst/__init__.py @@ -1,15 +1,83 @@  # coding=utf-8 +import re +  from docutils.core import publish_parts  from docutils.parsers.rst.roles import register_canonical_role  from pysite.rst.roles import 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>""") +  def render(rst: str): -    return publish_parts( +    rst = RST_TEMPLATE.format(rst) +    html = publish_parts(          source=rst, writer_name="html5", settings_overrides={"halt_level": 2, "syntax_highlight": "short"}      )["html_body"] +    data = { +        "html": html, +        "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) +                    current_header["title"] = match.group(2) +                else:  # Second-level (or deeper) header, should be stored in a list of sub-headers under the current +                    sub_headers = current_header.get("sub_headers", []) +                    sub_headers.append({ +                        "id": match.group(1), +                        "title": match.group(2) +                    }) +                    current_header["sub_headers"] = sub_headers + +        data["headers"] = headers +    return data +  register_canonical_role("icon", icon_role)  register_canonical_role("page", page_role) diff --git a/pysite/views/wiki/edit.py b/pysite/views/wiki/edit.py index a111f9ce..a2e98e20 100644 --- a/pysite/views/wiki/edit.py +++ b/pysite/views/wiki/edit.py @@ -35,11 +35,14 @@ class EditView(RouteView, DBMixin):      @csrf      def post(self, page):          rst = request.form["rst"] +        rendered = render(rst) +          obj = {              "slug": page,              "title": request.form["title"],              "rst": rst, -            "html": render(rst) +            "html": rendered["html"], +            "headers": rendered["headers"]          }          self.db.insert( diff --git a/pysite/views/wiki/render.py b/pysite/views/wiki/render.py index aa365c4a..9d3e8cc3 100644 --- a/pysite/views/wiki/render.py +++ b/pysite/views/wiki/render.py @@ -30,7 +30,7 @@ class RenderView(APIView):          data = data[0]["data"]          try: -            html = render(data) +            html = render(data)["html"]              return jsonify({"data": html})          except SystemMessage as e: @@ -51,7 +51,7 @@ class RenderView(APIView):                  if match:                      data["error_lines"].append(                          { -                            "row": int(match.group(1)) - 1, +                            "row": int(match.group(1)) - 3,                              "column": 0,                              "type": "error",                              "text": match.group(2) diff --git a/pysite/views/ws/rst.py b/pysite/views/ws/rst.py index 19c4129b..24bdb3ca 100644 --- a/pysite/views/ws/rst.py +++ b/pysite/views/ws/rst.py @@ -23,7 +23,7 @@ class RSTWebsocket(WS):          self.log.debug(f"RST | Message: {message}")          try: -            data = render(message) +            data = render(message)["html"]          except Exception as e:              self.log.exception("Parsing error")              data = str(e) | 
