This documentation is built with sphinx-fuma.

Source code for sphinx_fuma._nav

"""Structured navigation data for the templates.

Sphinx hands themes pre-rendered HTML for the sidebar (``toctree()``) and the
page-local table of contents (``toc``). Both are nested ``<ul>`` markup with no
hook for the collapsible folders and scroll-spy list this theme needs, so the
resolved doctrees are walked here into plain dictionaries the templates render
directly.
"""

from docutils import nodes
from sphinx import addnodes

__all__ = ("breadcrumbs", "page_toc", "sidebar_tree")


def _resolve_global_toctree(app, pagename: str, maxdepth: int):
    env, builder = app.env, app.builder
    # ``titles_only`` keeps in-page headings out of the sidebar; they belong to
    # the page-local table of contents instead.
    options = {"collapse": False, "includehidden": True, "maxdepth": maxdepth, "titles_only": True}
    try:
        from sphinx.environment.adapters.toctree import global_toctree_for_doc

        return global_toctree_for_doc(env, pagename, builder, **options)
    except ImportError:  # pragma: no cover - Sphinx < 7.2 layout
        from sphinx.environment.adapters.toctree import TocTree

        return TocTree(env).get_toctree_for(pagename, builder, **options)


def _reference_of(item: nodes.Element):
    for child in item.children:
        if isinstance(child, addnodes.compact_paragraph):
            return child.next_node(nodes.reference)
        if isinstance(child, nodes.reference):
            return child
    return None


def _sublist_of(item: nodes.Element):
    for child in item.children:
        if isinstance(child, nodes.bullet_list):
            return child
    return None


def _entries(bullet_list: nodes.Element, depth: int, icons: dict[str, str]) -> list[dict]:
    entries = []
    for item in bullet_list.children:
        if not isinstance(item, nodes.list_item):
            continue
        reference = _reference_of(item)
        if reference is None:
            continue
        sublist = _sublist_of(item)
        classes = reference.get("classes", [])
        refuri = reference.get("refuri", "")
        entries.append(
            {
                "title": reference.astext(),
                "url": refuri or "#",
                # ``env.tocs`` keeps the fragment here rather than in ``refuri``.
                "anchor": reference.get("anchorname", ""),
                "external": refuri.startswith(("http://", "https://", "//")),
                "icon": icons.get(refuri, ""),
                # ``current`` marks the active page; ``active`` marks its ancestors.
                "current": "current" in classes,
                "active": "current" in item.get("classes", []),
                "depth": depth,
                "children": _entries(sublist, depth + 1, icons) if sublist is not None else [],
            }
        )
    return entries


def _icons_by_uri(app, pagename: str) -> dict[str, str]:
    """Map each document's URI (as the toctree renders it) to its ``icon`` metadata.

    Resolved toctrees carry relative URIs rather than docnames, so the lookup is
    built from the same relative-URI function the toctree resolver used.
    """
    icons = {}
    for docname, metadata in app.env.metadata.items():
        icon = str(metadata.get("icon", "")).strip()
        if icon:
            icons[app.builder.get_relative_uri(pagename, docname)] = icon
    return icons






[docs] def page_toc(app, pagename: str) -> list[dict]: """Return the page-local headings, excluding the document title.""" toc = app.env.tocs.get(pagename) if toc is None: return [] entries = _entries(toc, 1, {}) # A single top-level entry is the document title; its children are the real headings. while len(entries) == 1 and entries[0]["children"]: entries = [dict(child, depth=child["depth"] - 1) for child in entries[0]["children"]] return _flatten_toc(entries)
def _flatten_toc(entries: list[dict], depth: int = 1) -> list[dict]: """Flatten nested headings so the scroll-spy indicator can track one list.""" flat = [] for entry in entries: flat.append({"title": entry["title"], "url": entry["anchor"] or "#", "depth": depth}) flat.extend(_flatten_toc(entry["children"], depth + 1)) return flat