From 3b6a066a2ecdf5fd8b4a2e01bc509683e59806a7 Mon Sep 17 00:00:00 2001 From: Matthias Bussonnier Date: Tue, 23 Apr 2024 11:49:46 +0200 Subject: [PATCH] DRAFT: Refactor navbar generating function. On top of #1771, should fix #1773 (once fininished) This try to split the function into two parts: one that wrangle the data, one that generate html. The generating the data part should be cacheable, while the other can be made more flexible to be reusable in different part of the theme (typically generating or not dropdowns in the sidebar or navbar). --- src/pydata_sphinx_theme/toctree.py | 69 ++++++++++++++++++++++++++---- 1 file changed, 60 insertions(+), 9 deletions(-) diff --git a/src/pydata_sphinx_theme/toctree.py b/src/pydata_sphinx_theme/toctree.py index b5220da5b..f20e80cd8 100644 --- a/src/pydata_sphinx_theme/toctree.py +++ b/src/pydata_sphinx_theme/toctree.py @@ -1,7 +1,9 @@ """Methods to build the toctree used in the html pages.""" +from dataclasses import dataclass from functools import cache from itertools import count +from textwrap import dedent from typing import Iterator, List, Tuple, Union from urllib.parse import urlparse @@ -51,6 +53,16 @@ def _get_ancestor_pagename(app: Sphinx, pagename: str, startdepth: int) -> str: return out, toctree +@dataclass +class LinkInfo: + """Dataclass to generate toctree data.""" + + is_current: bool + href: str + title: str + is_external: bool + + def add_toctree_functions( app: Sphinx, pagename: str, templatename: str, context, doctree ) -> None: @@ -129,14 +141,14 @@ def generate_header_nav_before_dropdown( # NOTE: `env.toctree_includes` is a dict mapping pagenames to any (possibly # hidden) TocTree directives on that page (i.e., the "child" pages nested # under `pagename`). - active_header_page = [ - *_get_toctree_ancestors(app.env.toctree_includes, pagename) - ] + header_pages = [*_get_toctree_ancestors(app.env.toctree_includes, pagename)] else: - active_header_page = toctree.get_toctree_ancestors(pagename) - if active_header_page: + header_pages = toctree.get_toctree_ancestors(pagename) + if header_pages: # The final list item will be the top-most ancestor - active_header_page = active_header_page[-1] + active_header_page = header_pages[-1] + else: + active_header_page = None # NOTE: `env.tocs` is a dict mapping pagenames to hierarchical bullet-lists # ("nodetrees" in Sphinx parlance) of in-page headings (including `toctree::` @@ -145,6 +157,8 @@ def generate_header_nav_before_dropdown( root_toc = app.env.tocs[app.config.root_doc] links_html = [] + links_data = [] + # Iterate through each node in the root document toc. # Grab the toctree pages and find the relative link + title. for toc in traverse_or_findall(root_toc, TocTreeNodeClass): @@ -175,28 +189,65 @@ def generate_header_nav_before_dropdown( link_status = "nav-external" if is_absolute else "nav-internal" link_href = page if is_absolute else context["pathto"](page) + links_data.append( + LinkInfo( + is_current=(page == active_header_page), + href=link_href, + title=title, + is_external=is_absolute, + ) + ) + # create the html output links_html.append( - f""" + dedent( + f""" """ + ) ) # Add external links defined in configuration as sibling list items for external_link in context["theme_external_links"]: + links_data.append( + LinkInfo( + is_current=False, + href=external_link["url"], + title=external_link["name"], + is_external=True, + ) + ) links_html.append( - f""" - """ + ) + ) + lhtml = [] + for link in links_data: + lhtml.append( + dedent( + f""" + + """ + ) ) + for a, b, d in zip(links_html, lhtml, links_data): + assert a == b, (a, b, d) + assert links_html == lhtml, (links_html, lhtml) # The first links will always be visible links_solo = links_html[:n_links_before_dropdown]