Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Override generation of id & href attributes for API documentation #1208

Merged
merged 3 commits into from
Apr 25, 2023
Merged
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
71 changes: 71 additions & 0 deletions src/pydata_sphinx_theme/translator.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
"""A custom Sphinx HTML Translator for Bootstrap layout."""

import types
from functools import partial
from packaging.version import Version

import sphinx
from packaging.version import Version
from sphinx.application import Sphinx
from sphinx.ext.autosummary import autosummary_table
from docutils.nodes import Element
from sphinx.util import logging

logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -59,6 +62,74 @@ def visit_table(self, node):
tag = self.starttag(node, "table", CLASS=" ".join(classes), **atts)
self.body.append(tag)

def visit_section(self, node):
"""Handle section nodes, parsing ``ids`` to replace dots with underscores.

This will modify the ``id`` of HTML ``<section>`` tags, e.g. where Python modules are documented.
Replacing dots with underscores allows the tags to be recognized as navigation targets by ScrollSpy.
"""
if "ids" in node:
node["ids"] = [id_.replace(".", "_") for id_ in node["ids"]]
super().visit_section(node)

def visit_desc_signature(self, node):
"""Handle function & method signature nodes, parsing ``ids`` to replace dots with underscores.

This will modify the ``id`` attribute of HTML ``<dt>`` & ``<dd>`` tags, where Python functions are documented.
Replacing dots with underscores allows the tags to be recognized as navigation targets by ScrollSpy.
"""
if "ids" in node:
ids = node["ids"]
for i, id_ in enumerate(ids):
ids[i] = id_.replace(".", "_")
super().visit_desc_signature(node)

def visit_reference(self, node):
"""Handle reference nodes, parsing ``refuri`` and ``anchorname`` attributes to replace dots with underscores.

This will modify the ``href`` attribute of internal HTML ``<a>`` tags, e.g. the sidebar navigation links.
"""
try:
# We are only interested in internal anchor references
internal, anchorname = node["internal"], node["anchorname"]
if internal and anchorname.startswith("#") and "." in anchorname:
# Get the root node of the current document
document = self.builder.env.get_doctree(self.builder.current_docname)

# Get the target anchor ID
target_id = anchorname.lstrip("#")
sanitized_id = target_id.replace(".", "_")
# Update the node `href`
node["refuri"] = node["anchorname"] = "#" + sanitized_id

# Define a search condition to find the target node by ID
def find_target(search_id, node):
return (
isinstance(node, Element)
and ("ids" in node)
and (search_id in node["ids"])
)

# NOTE: Replacing with underscores creates the possibility for conflicting references
# We should check for these and warn the user if any are found
if any(document.traverse(condition=partial(find_target, sanitized_id))):
logger.warning(
f'Sanitized reference "{sanitized_id}" for "{target_id}" conflicts with an existing reference!'
)

# Find nodes with the given ID (there should only be one)
targets = document.traverse(condition=partial(find_target, target_id))
# Replace dots with underscores in the target node ID
for target in targets:
# NOTE: By itself, modifying the target `ids` here seems to be insufficient, however it helps
# ensure that the reference `refuri` and target `ids` remain consistent during the build process
target["ids"] = [
sanitized_id if id_ == target_id else id_
for id_ in target["ids"]
]
except KeyError:
pass
super().visit_reference(node)

def setup_translators(app: Sphinx):
"""Add bootstrap HTML functionality if we are using an HTML translator.
Expand Down