Skip to content

Commit

Permalink
Add include_rubrics_in_toc object description option
Browse files Browse the repository at this point in the history
Fixes #126
  • Loading branch information
mhostetter committed Jul 22, 2022
1 parent 00c6810 commit 3a3fa74
Show file tree
Hide file tree
Showing 3 changed files with 81 additions and 1 deletion.
25 changes: 24 additions & 1 deletion docs/apidoc/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ The following options can be customized for each object type using
.. objconf:: include_fields_in_toc

Indicates whether to include fields, like "Parameters", "Returns", "Raises",
etc. in the table of contents.
etc., in the table of contents.

For an example, see: :cpp:expr:`synopses_ex::Foo` and note the ``Template
Parameters``, ``Parameters``, and ``Returns`` headings shown in the
Expand All @@ -135,6 +135,29 @@ The following options can be customized for each object type using
("py:.*", dict(include_fields_in_toc=False)),
]
.. objconf:: include_rubrics_in_toc

Indicates whether to include rubrics in object descriptions, like "Notes", "References", "Examples",
etc., in the table of contents.

.. note::

Traditionally, rubrics are not intended to be included in the table of contents. However with
:objconf:`include_fields_in_toc`, rubric-like fields may be included in the TOC. Including
other rubrics from the object description in the TOC is provided for visual consistency.

.. admonition:: Example
:class: example

To include object description rubrics in the table of contents for all
``py`` domain objects, add the following to :file:`conf.py`:

.. code-block:: python
object_description_options = [
("py:.*", dict(include_rubrics_in_toc=True)),
]
Other options described elsewhere include:

- :objconf:`wrap_signatures_with_css`
Expand Down
34 changes: 34 additions & 0 deletions sphinx_immaterial/apidoc/apidoc_formatting.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,10 @@ def depart_field_name(self, node: docutils.nodes.Element) -> None:
self.add_permalink_ref(node, _("Permalink to this headline"))
super().depart_field_name(node)

def depart_rubric(self, node: docutils.nodes.Element) -> None:
self.add_permalink_ref(node, _("Permalink to this headline"))
super().depart_rubric(node)

def depart_term(self, node: docutils.nodes.Element) -> None:
if "ids" in node.attributes:
self.add_permalink_ref(node, _("Permalink to this definition"))
Expand Down Expand Up @@ -229,6 +233,32 @@ def run(self: sphinx.directives.ObjectDescription) -> List[docutils.nodes.Node]:
sphinx.directives.ObjectDescription.run = run


def _monkey_patch_object_description_to_include_rubrics_in_toc():
orig_run = sphinx.directives.ObjectDescription.run

def run(self: sphinx.directives.ObjectDescription) -> List[docutils.nodes.Node]:
nodes = orig_run(self)

options = get_object_description_options(self.env, self.domain, self.objtype)
if not options["include_rubrics_in_toc"]:
return nodes

obj_desc = nodes[-1]
obj_content = obj_desc[-1]
for child in obj_content:
if not isinstance(child, docutils.nodes.rubric):
continue
rubric = cast(docutils.nodes.rubric, child)
if rubric["ids"]:
continue
rubric_id = docutils.nodes.make_id(rubric.astext())
rubric["ids"].append(rubric_id)

return nodes

sphinx.directives.ObjectDescription.run = run


def format_object_description_tooltip(
env: sphinx.environment.BuildEnvironment,
options: ObjectDescriptionOptions,
Expand Down Expand Up @@ -449,6 +479,7 @@ def setup(app: sphinx.application.Sphinx):

app.connect("object-description-transform", _wrap_signatures, priority=1000)
_monkey_patch_object_description_to_include_fields_in_toc()
_monkey_patch_object_description_to_include_rubrics_in_toc()

add_object_description_option(
app, "wrap_signatures_with_css", type_constraint=bool, default=True
Expand All @@ -462,6 +493,9 @@ def setup(app: sphinx.application.Sphinx):
add_object_description_option(
app, "include_fields_in_toc", type_constraint=bool, default=True
)
add_object_description_option(
app, "include_rubrics_in_toc", type_constraint=bool, default=False
)
add_object_description_option(
app,
"generate_synopses",
Expand Down
23 changes: 23 additions & 0 deletions sphinx_immaterial/apidoc/object_toc.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,22 @@ def _make_section_from_field(
section += titlenode
return section

def _make_section_from_rubric(
source: docutils.nodes.rubric,
) -> Optional[docutils.nodes.section]:
rubric = cast(docutils.nodes.rubric, source)
ids = rubric["ids"]
if not ids:
# Not indexed
return None
section = docutils.nodes.section()
section["ids"] = ids
title = rubric.astext()
# Sphinx uses the first child of the section node as the title.
titlenode = docutils.nodes.comment(title, title)
section += titlenode
return section

def _make_section_from_term(
source: docutils.nodes.term,
) -> Optional[docutils.nodes.section]:
Expand Down Expand Up @@ -143,6 +159,13 @@ def _collect(
if new_node is not None:
target += new_node
target = new_node
elif isinstance(source, docutils.nodes.rubric):
# Rubric. Try to create synthetic section.
new_node = _make_section_from_rubric(source)
if new_node is not None:
target += new_node
# Rubrics cannot contain sub-sections
return
elif isinstance(source, docutils.nodes.term) and source.get("toc_title"):
# Term with toc title. Try to create synthetic section.
new_node = _make_section_from_term(source)
Expand Down

0 comments on commit 3a3fa74

Please sign in to comment.