diff --git a/sphinx/builders/html.py b/sphinx/builders/html.py
index 3a074a1cc..5c3d9601e 100644
--- a/sphinx/builders/html.py
+++ b/sphinx/builders/html.py
@@ -45,6 +45,7 @@ from sphinx.highlighting import PygmentsBridge
from sphinx.util.console import bold, darkgreen # type: ignore
from sphinx.writers.html import HTMLWriter, HTMLTranslator, \
SmartyPantsHTMLTranslator
+from sphinx.environment.adapters.toctree import TocTree
if False:
# For type annotation
@@ -439,7 +440,7 @@ class StandaloneHTMLBuilder(Builder):
meta = self.env.metadata.get(docname)
# local TOC and global TOC tree
- self_toc = self.env.get_toc_for(docname, self)
+ self_toc = TocTree(self.env).get_toc_for(docname, self)
toc = self.render_partial(self_toc)['fragment']
return dict(
@@ -763,7 +764,7 @@ class StandaloneHTMLBuilder(Builder):
# type: (unicode, bool, Any) -> unicode
if 'includehidden' not in kwds:
kwds['includehidden'] = False
- return self.render_partial(self.env.get_toctree_for(
+ return self.render_partial(TocTree(self.env).get_toctree_for(
docname, self, collapse, **kwds))['fragment']
def get_outfilename(self, pagename):
@@ -1010,7 +1011,7 @@ class SingleFileHTMLBuilder(StandaloneHTMLBuilder):
# type: (unicode, bool, Any) -> unicode
if 'includehidden' not in kwds:
kwds['includehidden'] = False
- toctree = self.env.get_toctree_for(docname, self, collapse, **kwds)
+ toctree = TocTree(self.env).get_toctree_for(docname, self, collapse, **kwds)
self.fix_refuris(toctree)
return self.render_partial(toctree)['fragment']
@@ -1066,7 +1067,8 @@ class SingleFileHTMLBuilder(StandaloneHTMLBuilder):
def get_doc_context(self, docname, body, metatags):
# type: (unicode, unicode, Dict) -> Dict
# no relation links...
- toc = self.env.get_toctree_for(self.config.master_doc, self, False) # type: Any
+ toc = TocTree(self.env).get_toctree_for(self.config.master_doc,
+ self, False)
# if there is no toctree, toc is None
if toc:
self.fix_refuris(toc)
diff --git a/sphinx/environment/__init__.py b/sphinx/environment/__init__.py
index fd975c4ea..92a722aae 100644
--- a/sphinx/environment/__init__.py
+++ b/sphinx/environment/__init__.py
@@ -47,6 +47,7 @@ from sphinx.util.websupport import is_commentable
from sphinx.errors import SphinxError, ExtensionError
from sphinx.versioning import add_uids, merge_doctrees
from sphinx.deprecation import RemovedInSphinx20Warning
+from sphinx.environment.adapters.toctree import TocTree
from sphinx.environment.managers.indexentries import IndexEntries
from sphinx.environment.managers.toctree import Toctree
@@ -847,17 +848,26 @@ class BuildEnvironment(object):
"""Note a TOC tree directive in a document and gather information about
file relations from it.
"""
- self.toctree.note_toctree(docname, toctreenode) # type: ignore
+ warnings.warn('env.note_toctree() is deprecated. '
+ 'Use sphinx.environment.adapters.toctre.TocTree instead.',
+ RemovedInSphinx20Warning)
+ TocTree(self).note(docname, toctreenode)
def get_toc_for(self, docname, builder):
- # type: (unicode, Builder) -> addnodes.toctree
+ # type: (unicode, Builder) -> Dict[unicode, nodes.Node]
"""Return a TOC nodetree -- for use on the same page only!"""
- return self.toctree.get_toc_for(docname, builder) # type: ignore
+ warnings.warn('env.get_toc_for() is deprecated. '
+ 'Use sphinx.environment.adapters.toctre.TocTree instead.',
+ RemovedInSphinx20Warning)
+ return TocTree(self).get_toc_for(docname, builder)
def get_toctree_for(self, docname, builder, collapse, **kwds):
# type: (unicode, Builder, bool, Any) -> addnodes.toctree
"""Return the global TOC nodetree."""
- return self.toctree.get_toctree_for(docname, builder, collapse, **kwds) # type: ignore
+ warnings.warn('env.get_toctree_for() is deprecated. '
+ 'Use sphinx.environment.adapters.toctre.TocTree instead.',
+ RemovedInSphinx20Warning)
+ return TocTree(self).get_toctree_for(docname, builder, collapse, **kwds)
def get_domain(self, domainname):
# type: (unicode) -> Domain
@@ -897,9 +907,9 @@ class BuildEnvironment(object):
# now, resolve all toctree nodes
for toctreenode in doctree.traverse(addnodes.toctree):
- result = self.resolve_toctree(docname, builder, toctreenode,
- prune=prune_toctrees,
- includehidden=includehidden)
+ result = TocTree(self).resolve(docname, builder, toctreenode,
+ prune=prune_toctrees,
+ includehidden=includehidden)
if result is None:
toctreenode.replace_self([])
else:
@@ -921,9 +931,9 @@ class BuildEnvironment(object):
If *collapse* is True, all branches not containing docname will
be collapsed.
"""
- return self.toctree.resolve_toctree(docname, builder, toctree, prune, # type: ignore
- maxdepth, titles_only, collapse,
- includehidden)
+ return TocTree(self).resolve(docname, builder, toctree, prune,
+ maxdepth, titles_only, collapse,
+ includehidden)
def resolve_references(self, doctree, fromdocname, builder):
# type: (nodes.Node, unicode, Builder) -> None
diff --git a/sphinx/environment/adapters/__init__.py b/sphinx/environment/adapters/__init__.py
new file mode 100644
index 000000000..12a6fa490
--- /dev/null
+++ b/sphinx/environment/adapters/__init__.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+"""
+ sphinx.environment.adapters
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ Sphinx environment adapters
+
+ :copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS.
+ :license: BSD, see LICENSE for details.
+"""
diff --git a/sphinx/environment/adapters/toctree.py b/sphinx/environment/adapters/toctree.py
new file mode 100644
index 000000000..1ab3e229f
--- /dev/null
+++ b/sphinx/environment/adapters/toctree.py
@@ -0,0 +1,324 @@
+# -*- coding: utf-8 -*-
+"""
+ sphinx.environment.adapters.toctree
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ Toctree adapter for sphinx.environment.
+
+ :copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS.
+ :license: BSD, see LICENSE for details.
+"""
+
+from six import iteritems
+
+from docutils import nodes
+
+from sphinx import addnodes
+from sphinx.util import url_re, logging
+from sphinx.util.nodes import clean_astext, process_only_nodes
+
+if False:
+ # For type annotation
+ from typing import Any # NOQA
+ from sphinx.builders import Builder # NOQA
+ from sphinx.environment import BuildEnvironment # NOQA
+
+logger = logging.getLogger(__name__)
+
+
+class TocTree(object):
+ def __init__(self, env):
+ # type: (BuildEnvironment) -> None
+ self.env = env
+
+ def note(self, docname, toctreenode):
+ # type: (unicode, addnodes.toctree) -> None
+ """Note a TOC tree directive in a document and gather information about
+ file relations from it.
+ """
+ if toctreenode['glob']:
+ self.env.glob_toctrees.add(docname)
+ if toctreenode.get('numbered'):
+ self.env.numbered_toctrees.add(docname)
+ includefiles = toctreenode['includefiles']
+ for includefile in includefiles:
+ # note that if the included file is rebuilt, this one must be
+ # too (since the TOC of the included file could have changed)
+ self.env.files_to_rebuild.setdefault(includefile, set()).add(docname)
+ self.env.toctree_includes.setdefault(docname, []).extend(includefiles)
+
+ def resolve(self, docname, builder, toctree, prune=True, maxdepth=0,
+ titles_only=False, collapse=False, includehidden=False):
+ # type: (unicode, Builder, addnodes.toctree, bool, int, bool, bool, bool) -> nodes.Node
+ """Resolve a *toctree* node into individual bullet lists with titles
+ as items, returning None (if no containing titles are found) or
+ a new node.
+
+ If *prune* is True, the tree is pruned to *maxdepth*, or if that is 0,
+ to the value of the *maxdepth* option on the *toctree* node.
+ If *titles_only* is True, only toplevel document titles will be in the
+ resulting tree.
+ If *collapse* is True, all branches not containing docname will
+ be collapsed.
+ """
+ if toctree.get('hidden', False) and not includehidden:
+ return None
+
+ # For reading the following two helper function, it is useful to keep
+ # in mind the node structure of a toctree (using HTML-like node names
+ # for brevity):
+ #
+ #
+ # -
+ #
+ #
+ # ...
+ #
+ #
+ #
+ #
+ # The transformation is made in two passes in order to avoid
+ # interactions between marking and pruning the tree (see bug #1046).
+
+ toctree_ancestors = self.get_toctree_ancestors(docname)
+
+ def _toctree_add_classes(node, depth):
+ """Add 'toctree-l%d' and 'current' classes to the toctree."""
+ for subnode in node.children:
+ if isinstance(subnode, (addnodes.compact_paragraph,
+ nodes.list_item)):
+ # for and
, indicate the depth level and recurse
+ subnode['classes'].append('toctree-l%d' % (depth - 1))
+ _toctree_add_classes(subnode, depth)
+ elif isinstance(subnode, nodes.bullet_list):
+ # for , just recurse
+ _toctree_add_classes(subnode, depth + 1)
+ elif isinstance(subnode, nodes.reference):
+ # for , identify which entries point to the current
+ # document and therefore may not be collapsed
+ if subnode['refuri'] == docname:
+ if not subnode['anchorname']:
+ # give the whole branch a 'current' class
+ # (useful for styling it differently)
+ branchnode = subnode
+ while branchnode:
+ branchnode['classes'].append('current')
+ branchnode = branchnode.parent
+ # mark the list_item as "on current page"
+ if subnode.parent.parent.get('iscurrent'):
+ # but only if it's not already done
+ return
+ while subnode:
+ subnode['iscurrent'] = True
+ subnode = subnode.parent
+
+ def _entries_from_toctree(toctreenode, parents,
+ separate=False, subtree=False):
+ """Return TOC entries for a toctree node."""
+ refs = [(e[0], e[1]) for e in toctreenode['entries']]
+ entries = []
+ for (title, ref) in refs:
+ try:
+ refdoc = None
+ if url_re.match(ref):
+ if title is None:
+ title = ref
+ reference = nodes.reference('', '', internal=False,
+ refuri=ref, anchorname='',
+ *[nodes.Text(title)])
+ para = addnodes.compact_paragraph('', '', reference)
+ item = nodes.list_item('', para)
+ toc = nodes.bullet_list('', item)
+ elif ref == 'self':
+ # 'self' refers to the document from which this
+ # toctree originates
+ ref = toctreenode['parent']
+ if not title:
+ title = clean_astext(self.env.titles[ref])
+ reference = nodes.reference('', '', internal=True,
+ refuri=ref,
+ anchorname='',
+ *[nodes.Text(title)])
+ para = addnodes.compact_paragraph('', '', reference)
+ item = nodes.list_item('', para)
+ # don't show subitems
+ toc = nodes.bullet_list('', item)
+ else:
+ if ref in parents:
+ logger.warning('circular toctree references '
+ 'detected, ignoring: %s <- %s',
+ ref, ' <- '.join(parents),
+ location=ref)
+ continue
+ refdoc = ref
+ toc = self.env.tocs[ref].deepcopy()
+ maxdepth = self.env.metadata[ref].get('tocdepth', 0)
+ if ref not in toctree_ancestors or (prune and maxdepth > 0):
+ self._toctree_prune(toc, 2, maxdepth, collapse)
+ process_only_nodes(toc, builder.tags)
+ if title and toc.children and len(toc.children) == 1:
+ child = toc.children[0]
+ for refnode in child.traverse(nodes.reference):
+ if refnode['refuri'] == ref and \
+ not refnode['anchorname']:
+ refnode.children = [nodes.Text(title)]
+ if not toc.children:
+ # empty toc means: no titles will show up in the toctree
+ logger.warning('toctree contains reference to document %r that '
+ 'doesn\'t have a title: no link will be generated',
+ ref, location=toctreenode)
+ except KeyError:
+ # this is raised if the included file does not exist
+ logger.warning('toctree contains reference to nonexisting document %r',
+ ref, location=toctreenode)
+ else:
+ # if titles_only is given, only keep the main title and
+ # sub-toctrees
+ if titles_only:
+ # delete everything but the toplevel title(s)
+ # and toctrees
+ for toplevel in toc:
+ # nodes with length 1 don't have any children anyway
+ if len(toplevel) > 1:
+ subtrees = toplevel.traverse(addnodes.toctree)
+ if subtrees:
+ toplevel[1][:] = subtrees
+ else:
+ toplevel.pop(1)
+ # resolve all sub-toctrees
+ for subtocnode in toc.traverse(addnodes.toctree):
+ if not (subtocnode.get('hidden', False) and
+ not includehidden):
+ i = subtocnode.parent.index(subtocnode) + 1
+ for item in _entries_from_toctree(
+ subtocnode, [refdoc] + parents,
+ subtree=True):
+ subtocnode.parent.insert(i, item)
+ i += 1
+ subtocnode.parent.remove(subtocnode)
+ if separate:
+ entries.append(toc)
+ else:
+ entries.extend(toc.children)
+ if not subtree and not separate:
+ ret = nodes.bullet_list()
+ ret += entries
+ return [ret]
+ return entries
+
+ maxdepth = maxdepth or toctree.get('maxdepth', -1)
+ if not titles_only and toctree.get('titlesonly', False):
+ titles_only = True
+ if not includehidden and toctree.get('includehidden', False):
+ includehidden = True
+
+ # NOTE: previously, this was separate=True, but that leads to artificial
+ # separation when two or more toctree entries form a logical unit, so
+ # separating mode is no longer used -- it's kept here for history's sake
+ tocentries = _entries_from_toctree(toctree, [], separate=False)
+ if not tocentries:
+ return None
+
+ newnode = addnodes.compact_paragraph('', '')
+ caption = toctree.attributes.get('caption')
+ if caption:
+ caption_node = nodes.caption(caption, '', *[nodes.Text(caption)])
+ caption_node.line = toctree.line
+ caption_node.source = toctree.source
+ caption_node.rawsource = toctree['rawcaption']
+ if hasattr(toctree, 'uid'):
+ # move uid to caption_node to translate it
+ caption_node.uid = toctree.uid
+ del toctree.uid
+ newnode += caption_node
+ newnode.extend(tocentries)
+ newnode['toctree'] = True
+
+ # prune the tree to maxdepth, also set toc depth and current classes
+ _toctree_add_classes(newnode, 1)
+ self._toctree_prune(newnode, 1, prune and maxdepth or 0, collapse)
+
+ if len(newnode[-1]) == 0: # No titles found
+ return None
+
+ # set the target paths in the toctrees (they are not known at TOC
+ # generation time)
+ for refnode in newnode.traverse(nodes.reference):
+ if not url_re.match(refnode['refuri']):
+ refnode['refuri'] = builder.get_relative_uri(
+ docname, refnode['refuri']) + refnode['anchorname']
+ return newnode
+
+ def get_toctree_ancestors(self, docname):
+ # type: (unicode) -> List[unicode]
+ parent = {}
+ for p, children in iteritems(self.env.toctree_includes):
+ for child in children:
+ parent[child] = p
+ ancestors = [] # type: List[unicode]
+ d = docname
+ while d in parent and d not in ancestors:
+ ancestors.append(d)
+ d = parent[d]
+ return ancestors
+
+ def _toctree_prune(self, node, depth, maxdepth, collapse=False):
+ # type: (nodes.Node, int, int, bool) -> None
+ """Utility: Cut a TOC at a specified depth."""
+ for subnode in node.children[:]:
+ if isinstance(subnode, (addnodes.compact_paragraph,
+ nodes.list_item)):
+ # for and
- , just recurse
+ self._toctree_prune(subnode, depth, maxdepth, collapse)
+ elif isinstance(subnode, nodes.bullet_list):
+ # for
, determine if the depth is too large or if the
+ # entry is to be collapsed
+ if maxdepth > 0 and depth > maxdepth:
+ subnode.parent.replace(subnode, [])
+ else:
+ # cull sub-entries whose parents aren't 'current'
+ if (collapse and depth > 1 and
+ 'iscurrent' not in subnode.parent):
+ subnode.parent.remove(subnode)
+ else:
+ # recurse on visible children
+ self._toctree_prune(subnode, depth + 1, maxdepth, collapse)
+
+ def get_toc_for(self, docname, builder):
+ # type: (unicode, Builder) -> Dict[unicode, nodes.Node]
+ """Return a TOC nodetree -- for use on the same page only!"""
+ tocdepth = self.env.metadata[docname].get('tocdepth', 0)
+ try:
+ toc = self.env.tocs[docname].deepcopy()
+ self._toctree_prune(toc, 2, tocdepth)
+ except KeyError:
+ # the document does not exist anymore: return a dummy node that
+ # renders to nothing
+ return nodes.paragraph()
+ process_only_nodes(toc, builder.tags)
+ for node in toc.traverse(nodes.reference):
+ node['refuri'] = node['anchorname'] or '#'
+ return toc
+
+ def get_toctree_for(self, docname, builder, collapse, **kwds):
+ # type: (unicode, Builder, bool, Any) -> nodes.Node
+ """Return the global TOC nodetree."""
+ doctree = self.env.get_doctree(self.env.config.master_doc)
+ toctrees = []
+ if 'includehidden' not in kwds:
+ kwds['includehidden'] = True
+ if 'maxdepth' not in kwds:
+ kwds['maxdepth'] = 0
+ kwds['collapse'] = collapse
+ for toctreenode in doctree.traverse(addnodes.toctree):
+ toctree = self.resolve(docname, builder, toctreenode, prune=True, **kwds)
+ if toctree:
+ toctrees.append(toctree)
+ if not toctrees:
+ return None
+ result = toctrees[0]
+ for toctree in toctrees[1:]:
+ result.extend(toctree.children)
+ return result
diff --git a/sphinx/environment/managers/toctree.py b/sphinx/environment/managers/toctree.py
index 0cd011ae0..bc0d9f999 100644
--- a/sphinx/environment/managers/toctree.py
+++ b/sphinx/environment/managers/toctree.py
@@ -15,8 +15,8 @@ from docutils import nodes
from sphinx import addnodes
from sphinx.util import url_re, logging
-from sphinx.util.nodes import clean_astext, process_only_nodes
from sphinx.transforms import SphinxContentsFilter
+from sphinx.environment.adapters.toctree import TocTree as TocTreeAdapter
from sphinx.environment.managers import EnvironmentManager
if False:
@@ -149,293 +149,23 @@ class Toctree(EnvironmentManager):
"""Note a TOC tree directive in a document and gather information about
file relations from it.
"""
- if toctreenode['glob']:
- self.glob_toctrees.add(docname)
- if toctreenode.get('numbered'):
- self.numbered_toctrees.add(docname)
- includefiles = toctreenode['includefiles']
- for includefile in includefiles:
- # note that if the included file is rebuilt, this one must be
- # too (since the TOC of the included file could have changed)
- self.files_to_rebuild.setdefault(includefile, set()).add(docname)
- self.toctree_includes.setdefault(docname, []).extend(includefiles)
+ TocTreeAdapter(self.env).note(docname, toctreenode)
def get_toc_for(self, docname, builder):
- # type: (unicode, Builder) -> None
+ # type: (unicode, Builder) -> Dict[unicode, nodes.Node]
"""Return a TOC nodetree -- for use on the same page only!"""
- tocdepth = self.env.metadata[docname].get('tocdepth', 0)
- try:
- toc = self.tocs[docname].deepcopy()
- self._toctree_prune(toc, 2, tocdepth)
- except KeyError:
- # the document does not exist anymore: return a dummy node that
- # renders to nothing
- return nodes.paragraph()
- process_only_nodes(toc, builder.tags)
- for node in toc.traverse(nodes.reference):
- node['refuri'] = node['anchorname'] or '#'
- return toc
+ return TocTreeAdapter(self.env).get_toc_for(docname, builder)
def get_toctree_for(self, docname, builder, collapse, **kwds):
# type: (unicode, Builder, bool, Any) -> nodes.Node
"""Return the global TOC nodetree."""
- doctree = self.env.get_doctree(self.env.config.master_doc)
- toctrees = []
- if 'includehidden' not in kwds:
- kwds['includehidden'] = True
- if 'maxdepth' not in kwds:
- kwds['maxdepth'] = 0
- kwds['collapse'] = collapse
- for toctreenode in doctree.traverse(addnodes.toctree):
- toctree = self.env.resolve_toctree(docname, builder, toctreenode,
- prune=True, **kwds)
- if toctree:
- toctrees.append(toctree)
- if not toctrees:
- return None
- result = toctrees[0]
- for toctree in toctrees[1:]:
- result.extend(toctree.children)
- return result
+ return TocTreeAdapter(self.env).get_toctree_for(docname, builder, collapse, **kwds)
def resolve_toctree(self, docname, builder, toctree, prune=True, maxdepth=0,
titles_only=False, collapse=False, includehidden=False):
# type: (unicode, Builder, addnodes.toctree, bool, int, bool, bool, bool) -> nodes.Node
- """Resolve a *toctree* node into individual bullet lists with titles
- as items, returning None (if no containing titles are found) or
- a new node.
-
- If *prune* is True, the tree is pruned to *maxdepth*, or if that is 0,
- to the value of the *maxdepth* option on the *toctree* node.
- If *titles_only* is True, only toplevel document titles will be in the
- resulting tree.
- If *collapse* is True, all branches not containing docname will
- be collapsed.
- """
- if toctree.get('hidden', False) and not includehidden:
- return None
-
- # For reading the following two helper function, it is useful to keep
- # in mind the node structure of a toctree (using HTML-like node names
- # for brevity):
- #
- #
- # -
- #
- #
- # ...
- #
- #
- #
- #
- # The transformation is made in two passes in order to avoid
- # interactions between marking and pruning the tree (see bug #1046).
-
- toctree_ancestors = self.get_toctree_ancestors(docname)
-
- def _toctree_add_classes(node, depth):
- """Add 'toctree-l%d' and 'current' classes to the toctree."""
- for subnode in node.children:
- if isinstance(subnode, (addnodes.compact_paragraph,
- nodes.list_item)):
- # for and
- , indicate the depth level and recurse
- subnode['classes'].append('toctree-l%d' % (depth - 1))
- _toctree_add_classes(subnode, depth)
- elif isinstance(subnode, nodes.bullet_list):
- # for
, just recurse
- _toctree_add_classes(subnode, depth + 1)
- elif isinstance(subnode, nodes.reference):
- # for , identify which entries point to the current
- # document and therefore may not be collapsed
- if subnode['refuri'] == docname:
- if not subnode['anchorname']:
- # give the whole branch a 'current' class
- # (useful for styling it differently)
- branchnode = subnode
- while branchnode:
- branchnode['classes'].append('current')
- branchnode = branchnode.parent
- # mark the list_item as "on current page"
- if subnode.parent.parent.get('iscurrent'):
- # but only if it's not already done
- return
- while subnode:
- subnode['iscurrent'] = True
- subnode = subnode.parent
-
- def _entries_from_toctree(toctreenode, parents,
- separate=False, subtree=False):
- """Return TOC entries for a toctree node."""
- refs = [(e[0], e[1]) for e in toctreenode['entries']]
- entries = []
- for (title, ref) in refs:
- try:
- refdoc = None
- if url_re.match(ref):
- if title is None:
- title = ref
- reference = nodes.reference('', '', internal=False,
- refuri=ref, anchorname='',
- *[nodes.Text(title)])
- para = addnodes.compact_paragraph('', '', reference)
- item = nodes.list_item('', para)
- toc = nodes.bullet_list('', item)
- elif ref == 'self':
- # 'self' refers to the document from which this
- # toctree originates
- ref = toctreenode['parent']
- if not title:
- title = clean_astext(self.env.titles[ref])
- reference = nodes.reference('', '', internal=True,
- refuri=ref,
- anchorname='',
- *[nodes.Text(title)])
- para = addnodes.compact_paragraph('', '', reference)
- item = nodes.list_item('', para)
- # don't show subitems
- toc = nodes.bullet_list('', item)
- else:
- if ref in parents:
- logger.warning('circular toctree references '
- 'detected, ignoring: %s <- %s',
- ref, ' <- '.join(parents),
- location=ref)
- continue
- refdoc = ref
- toc = self.tocs[ref].deepcopy()
- maxdepth = self.env.metadata[ref].get('tocdepth', 0)
- if ref not in toctree_ancestors or (prune and maxdepth > 0):
- self._toctree_prune(toc, 2, maxdepth, collapse)
- process_only_nodes(toc, builder.tags)
- if title and toc.children and len(toc.children) == 1:
- child = toc.children[0]
- for refnode in child.traverse(nodes.reference):
- if refnode['refuri'] == ref and \
- not refnode['anchorname']:
- refnode.children = [nodes.Text(title)]
- if not toc.children:
- # empty toc means: no titles will show up in the toctree
- logger.warning('toctree contains reference to document %r that '
- 'doesn\'t have a title: no link will be generated',
- ref, location=toctreenode)
- except KeyError:
- # this is raised if the included file does not exist
- logger.warning('toctree contains reference to nonexisting document %r',
- ref, location=toctreenode)
- else:
- # if titles_only is given, only keep the main title and
- # sub-toctrees
- if titles_only:
- # delete everything but the toplevel title(s)
- # and toctrees
- for toplevel in toc:
- # nodes with length 1 don't have any children anyway
- if len(toplevel) > 1:
- subtrees = toplevel.traverse(addnodes.toctree)
- if subtrees:
- toplevel[1][:] = subtrees
- else:
- toplevel.pop(1)
- # resolve all sub-toctrees
- for subtocnode in toc.traverse(addnodes.toctree):
- if not (subtocnode.get('hidden', False) and
- not includehidden):
- i = subtocnode.parent.index(subtocnode) + 1
- for item in _entries_from_toctree(
- subtocnode, [refdoc] + parents,
- subtree=True):
- subtocnode.parent.insert(i, item)
- i += 1
- subtocnode.parent.remove(subtocnode)
- if separate:
- entries.append(toc)
- else:
- entries.extend(toc.children)
- if not subtree and not separate:
- ret = nodes.bullet_list()
- ret += entries
- return [ret]
- return entries
-
- maxdepth = maxdepth or toctree.get('maxdepth', -1)
- if not titles_only and toctree.get('titlesonly', False):
- titles_only = True
- if not includehidden and toctree.get('includehidden', False):
- includehidden = True
-
- # NOTE: previously, this was separate=True, but that leads to artificial
- # separation when two or more toctree entries form a logical unit, so
- # separating mode is no longer used -- it's kept here for history's sake
- tocentries = _entries_from_toctree(toctree, [], separate=False)
- if not tocentries:
- return None
-
- newnode = addnodes.compact_paragraph('', '')
- caption = toctree.attributes.get('caption')
- if caption:
- caption_node = nodes.caption(caption, '', *[nodes.Text(caption)])
- caption_node.line = toctree.line
- caption_node.source = toctree.source
- caption_node.rawsource = toctree['rawcaption']
- if hasattr(toctree, 'uid'):
- # move uid to caption_node to translate it
- caption_node.uid = toctree.uid
- del toctree.uid
- newnode += caption_node
- newnode.extend(tocentries)
- newnode['toctree'] = True
-
- # prune the tree to maxdepth, also set toc depth and current classes
- _toctree_add_classes(newnode, 1)
- self._toctree_prune(newnode, 1, prune and maxdepth or 0, collapse)
-
- if len(newnode[-1]) == 0: # No titles found
- return None
-
- # set the target paths in the toctrees (they are not known at TOC
- # generation time)
- for refnode in newnode.traverse(nodes.reference):
- if not url_re.match(refnode['refuri']):
- refnode['refuri'] = builder.get_relative_uri(
- docname, refnode['refuri']) + refnode['anchorname']
- return newnode
-
- def get_toctree_ancestors(self, docname):
- # type: (unicode) -> List[unicode]
- parent = {}
- for p, children in iteritems(self.toctree_includes):
- for child in children:
- parent[child] = p
- ancestors = [] # type: List[unicode]
- d = docname
- while d in parent and d not in ancestors:
- ancestors.append(d)
- d = parent[d]
- return ancestors
-
- def _toctree_prune(self, node, depth, maxdepth, collapse=False):
- # type: (nodes.Node, int, int, bool) -> None
- """Utility: Cut a TOC at a specified depth."""
- for subnode in node.children[:]:
- if isinstance(subnode, (addnodes.compact_paragraph,
- nodes.list_item)):
- # for and
- , just recurse
- self._toctree_prune(subnode, depth, maxdepth, collapse)
- elif isinstance(subnode, nodes.bullet_list):
- # for
, determine if the depth is too large or if the
- # entry is to be collapsed
- if maxdepth > 0 and depth > maxdepth:
- subnode.parent.replace(subnode, [])
- else:
- # cull sub-entries whose parents aren't 'current'
- if (collapse and depth > 1 and
- 'iscurrent' not in subnode.parent):
- subnode.parent.remove(subnode)
- else:
- # recurse on visible children
- self._toctree_prune(subnode, depth + 1, maxdepth, collapse)
+ return TocTreeAdapter(self.env).resolve(docname, builder, toctree, prune, maxdepth,
+ titles_only, collapse, includehidden)
def assign_section_numbers(self):
# type: () -> List[unicode]
diff --git a/sphinx/ext/autosummary/__init__.py b/sphinx/ext/autosummary/__init__.py
index 4995c0261..8d521a74d 100644
--- a/sphinx/ext/autosummary/__init__.py
+++ b/sphinx/ext/autosummary/__init__.py
@@ -69,6 +69,7 @@ from docutils import nodes
import sphinx
from sphinx import addnodes
+from sphinx.environment.adapters.toctree import TocTree
from sphinx.util import import_object, rst, logging
from sphinx.pycode import ModuleAnalyzer, PycodeError
from sphinx.ext.autodoc import Options
@@ -104,7 +105,7 @@ def process_autosummary_toc(app, doctree):
try:
if (isinstance(subnode, autosummary_toc) and
isinstance(subnode[0], addnodes.toctree)):
- env.note_toctree(env.docname, subnode[0])
+ TocTree(env).note(env.docname, subnode[0])
continue
except IndexError:
continue