Skip to content

Commit d924acf

Browse files
brechtmAA-Turner
andauthored
Make toctree accept special docnames (#10673)
The `.. toctree::` directive now supports the reserved special docnames 'genindex', 'modindex', and 'search'. Co-authored-by: Adam Turner <9087854+aa-turner@users.noreply.github.com>
1 parent c8e5199 commit d924acf

File tree

8 files changed

+62
-4
lines changed

8 files changed

+62
-4
lines changed

CHANGES

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,10 @@ Features added
1818
* #10755: linkcheck: Check the source URL of raw directives that use the ``url``
1919
option.
2020
* #10781: Allow :rst:role:`ref` role to be used with definitions and fields.
21-
* #10717: HTML Search: Increase priority for full title and
21+
* #10717: HTML Search: Increase priority for full title and
2222
subtitle matches in search results
2323
* #10718: HTML Search: Save search result score to the HTML element for debugging
24+
* #10673: Make toctree accept 'genindex', 'modindex' and 'search' docnames
2425

2526
Bugs fixed
2627
----------

sphinx/directives/other.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,10 +77,11 @@ def run(self) -> List[Node]:
7777
return ret
7878

7979
def parse_content(self, toctree: addnodes.toctree) -> List[Node]:
80+
generated_docnames = frozenset(self.env.domains['std'].initial_data['labels'].keys())
8081
suffixes = self.config.source_suffix
8182

8283
# glob target documents
83-
all_docnames = self.env.found_docs.copy()
84+
all_docnames = self.env.found_docs.copy() | generated_docnames
8485
all_docnames.remove(self.env.docname) # remove current document
8586

8687
ret: List[Node] = []
@@ -95,6 +96,9 @@ def parse_content(self, toctree: addnodes.toctree) -> List[Node]:
9596
patname = docname_join(self.env.docname, entry)
9697
docnames = sorted(patfilter(all_docnames, patname))
9798
for docname in docnames:
99+
if docname in generated_docnames:
100+
# don't include generated documents in globs
101+
continue
98102
all_docnames.remove(docname) # don't include it again
99103
toctree['entries'].append((None, docname))
100104
toctree['includefiles'].append(docname)
@@ -118,7 +122,7 @@ def parse_content(self, toctree: addnodes.toctree) -> List[Node]:
118122
docname = docname_join(self.env.docname, docname)
119123
if url_re.match(ref) or ref == 'self':
120124
toctree['entries'].append((title, ref))
121-
elif docname not in self.env.found_docs:
125+
elif docname not in self.env.found_docs | generated_docnames:
122126
if excluded(self.env.doc2path(docname, False)):
123127
message = __('toctree contains reference to excluded document %r')
124128
subtype = 'excluded'

sphinx/environment/adapters/toctree.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
"""Toctree adapter for sphinx.environment."""
22

3-
from typing import TYPE_CHECKING, Any, Iterable, List, Optional, cast
3+
from typing import TYPE_CHECKING, Any, Dict, Iterable, List, Optional, Tuple, cast
44

55
from docutils import nodes
66
from docutils.nodes import Element, Node
@@ -54,6 +54,7 @@ def resolve(self, docname: str, builder: "Builder", toctree: addnodes.toctree,
5454
"""
5555
if toctree.get('hidden', False) and not includehidden:
5656
return None
57+
generated_docnames: Dict[str, Tuple[str, str, str]] = self.env.domains['std'].initial_data['labels'].copy() # NoQA: E501
5758

5859
# For reading the following two helper function, it is useful to keep
5960
# in mind the node structure of a toctree (using HTML-like node names
@@ -139,6 +140,16 @@ def _entries_from_toctree(toctreenode: addnodes.toctree, parents: List[str],
139140
item = nodes.list_item('', para)
140141
# don't show subitems
141142
toc = nodes.bullet_list('', item)
143+
elif ref in generated_docnames:
144+
docname, _, sectionname = generated_docnames[ref]
145+
if not title:
146+
title = sectionname
147+
reference = nodes.reference('', title, internal=True,
148+
refuri=docname, anchorname='')
149+
para = addnodes.compact_paragraph('', '', reference)
150+
item = nodes.list_item('', para)
151+
# don't show subitems
152+
toc = nodes.bullet_list('', item)
142153
else:
143154
if ref in parents:
144155
logger.warning(__('circular toctree references '

sphinx/environment/collectors/toctree.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,7 @@ def _walk_toctree(toctreenode: addnodes.toctree, depth: int) -> None:
201201

202202
def assign_figure_numbers(self, env: BuildEnvironment) -> List[str]:
203203
"""Assign a figure number to each figure under a numbered toctree."""
204+
generated_docnames = frozenset(env.domains['std'].initial_data['labels'].keys())
204205

205206
rewrite_needed = []
206207

@@ -248,6 +249,7 @@ def register_fignumber(docname: str, secnum: Tuple[int, ...],
248249
fignumbers[figure_id] = get_next_fignumber(figtype, secnum)
249250

250251
def _walk_doctree(docname: str, doctree: Element, secnum: Tuple[int, ...]) -> None:
252+
nonlocal generated_docnames
251253
for subnode in doctree.children:
252254
if isinstance(subnode, nodes.section):
253255
next_secnum = get_section_number(docname, subnode)
@@ -260,6 +262,9 @@ def _walk_doctree(docname: str, doctree: Element, secnum: Tuple[int, ...]) -> No
260262
if url_re.match(subdocname) or subdocname == 'self':
261263
# don't mess with those
262264
continue
265+
if subdocname in generated_docnames:
266+
# or these
267+
continue
263268

264269
_walk_doc(subdocname, secnum)
265270
elif isinstance(subnode, nodes.Element):

tests/roots/test-toctree-index/conf.py

Whitespace-only changes.
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
foo
2+
===
3+
4+
:index:`word`
5+
6+
.. py:module:: pymodule
7+
8+
.. py:function:: Timer.repeat(repeat=3, number=1000000)
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
test-toctree-index
2+
==================
3+
4+
.. toctree::
5+
6+
foo
7+
8+
9+
.. toctree::
10+
:caption: Indices
11+
12+
genindex
13+
modindex
14+
search
15+

tests/test_environment_toctree.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -346,3 +346,17 @@ def test_get_toctree_for_includehidden(app):
346346

347347
assert_node(toctree[2],
348348
[bullet_list, list_item, compact_paragraph, reference, "baz"])
349+
350+
351+
@pytest.mark.sphinx('xml', testroot='toctree-index')
352+
def test_toctree_index(app):
353+
app.build()
354+
toctree = app.env.tocs['index']
355+
assert_node(toctree,
356+
[bullet_list, ([list_item, (compact_paragraph, # [0][0]
357+
[bullet_list, (addnodes.toctree, # [0][1][0]
358+
addnodes.toctree)])])]) # [0][1][1]
359+
assert_node(toctree[0][1][1], addnodes.toctree,
360+
caption="Indices", glob=False, hidden=False,
361+
titlesonly=False, maxdepth=-1, numbered=0,
362+
entries=[(None, 'genindex'), (None, 'modindex'), (None, 'search')])

0 commit comments

Comments
 (0)