From 5b4f6356b1e5a188ac3cad65e134895319bbb64f Mon Sep 17 00:00:00 2001 From: Simon Willison Date: Sat, 1 Jul 2023 21:50:50 -0700 Subject: [PATCH] New {% toc %} tag, closes #145 --- plugins/toc_template_tag.py | 77 ++++++++++++++++++++ templates/pages/tutorials/data-analysis.html | 4 + 2 files changed, 81 insertions(+) create mode 100644 plugins/toc_template_tag.py diff --git a/plugins/toc_template_tag.py b/plugins/toc_template_tag.py new file mode 100644 index 0000000..a9a79aa --- /dev/null +++ b/plugins/toc_template_tag.py @@ -0,0 +1,77 @@ +from bs4 import BeautifulSoup as Soup +from datasette import hookimpl +from jinja2 import nodes +from jinja2.exceptions import TemplateSyntaxError +from jinja2.ext import Extension + + +@hookimpl +def prepare_jinja2_environment(env): + env.add_extension(TableOfContentsExtension) + + +class TableOfContentsExtension(Extension): + tags = set(["toc"]) + + def __init__(self, environment): + super(TableOfContentsExtension, self).__init__(environment) + + def parse(self, parser): + # We need this for reporting errors + lineno = next(parser.stream).lineno + body = parser.parse_statements(["name:endtoc"], drop_needle=True) + return nodes.CallBlock( + self.call_method("_render_toc"), + [], + [], + body, + ).set_lineno(lineno) + + async def _render_toc(self, caller): + inner_html = await caller() + soup = Soup(inner_html, "html5lib") + # Find all the headings, add IDs to them + headings = soup.select("h1,h2,h3,h4,h5,h6") + issued_ids = set() + menu_links = [] + for heading in headings: + text = "-".join(strip_special_chars(heading.text.lower()).split()) + suffix = 0 + while True: + if suffix: + id = "{}-{}".format(text, suffix) + else: + id = text + if id not in issued_ids: + break + suffix += 1 + heading["id"] = id + issued_ids.add(id) + menu_links.append((int(heading.name[1:]), heading.text, id)) + + tree = build_tree(menu_links) + return tree_to_html(tree) + "\n\n" + str(soup) + + +def strip_special_chars(text): + return "".join(c for c in text if c.isalnum() or c in " -") + + +def build_tree(links): + nodes = {1: []} + for depth, name, link in links: + node = (depth, name, link, []) + nodes[depth - 1].append(node) + nodes[depth] = node[3] + return nodes[1] + + +def tree_to_html(tree): + html = "" + return html diff --git a/templates/pages/tutorials/data-analysis.html b/templates/pages/tutorials/data-analysis.html index a75cb5c..a648ceb 100644 --- a/templates/pages/tutorials/data-analysis.html +++ b/templates/pages/tutorials/data-analysis.html @@ -105,6 +105,8 @@ +{% toc %} + {% markdown extensions="fenced_code codehilite" extra_tags="span div" extra_attrs="span:class div:class" %} ## What you'll need @@ -574,6 +576,8 @@ {% endmarkdown %} +{% endtoc %} + {% include "_more_tutorials.html" %} {% endblock %}