def test_get_toc_for(app): app.build() toctree = TocTree(app.env).get_toc_for('index', app.builder) assert_node( toctree, [ bullet_list, ( [ list_item, ( compact_paragraph, # [0][0] [ bullet_list, ( addnodes.toctree, # [0][1][0] comment, # [0][1][1] list_item) ]) ], # [0][1][2] [ list_item, ( compact_paragraph, # [1][0] [bullet_list, (addnodes.toctree, addnodes.toctree)]) ], [list_item, compact_paragraph]) ]) # [2][0] assert_node(toctree[0][0], [ compact_paragraph, reference, u"Welcome to Sphinx Tests’s documentation!" ]) assert_node(toctree[0][1][2], ([ compact_paragraph, reference, "subsection" ], [bullet_list, list_item, compact_paragraph, reference, "subsubsection" ])) assert_node(toctree[1][0], [compact_paragraph, reference, "Test for issue #1157"]) assert_node(toctree[2][0], [compact_paragraph, reference, "Indices and tables"])
def test_get_toctree_for_includehidden(app): app.build() toctree = TocTree(app.env).get_toctree_for('index', app.builder, collapse=False, includehidden=False) assert_node(toctree, [ compact_paragraph, ([caption, "Table of Contents"], bullet_list, bullet_list) ]) assert_node( toctree[1], ([list_item, ([compact_paragraph, reference, "foo"], bullet_list) ], [list_item, compact_paragraph, reference, "bar"], [list_item, compact_paragraph, reference, "http://sphinx-doc.org/"])) assert_node(toctree[1][0][1], ([list_item, compact_paragraph, reference, "quux"], [ list_item, compact_paragraph, reference, "foo.1" ], [list_item, compact_paragraph, reference, "foo.2"])) assert_node(toctree[1][0][0][0], reference, refuri="foo", secnumber=(1, )) assert_node(toctree[1][0][1][0][0][0], reference, refuri="quux", secnumber=(1, 1)) assert_node(toctree[1][0][1][1][0][0], reference, refuri="foo#foo-1", secnumber=(1, 2)) assert_node(toctree[1][0][1][2][0][0], reference, refuri="foo#foo-2", secnumber=(1, 3)) assert_node(toctree[1][1][0][0], reference, refuri="bar", secnumber=(2, )) assert_node(toctree[1][2][0][0], reference, refuri="http://sphinx-doc.org/") assert_node(toctree[2], [bullet_list, list_item, compact_paragraph, reference, "baz"])
def get_nav_object(maxdepth=None, collapse=True, **kwargs): """Return a list of nav links that can be accessed from Jinja. Parameters ---------- maxdepth: int How many layers of TocTree will be returned collapse: bool Whether to only include sub-pages of the currently-active page, instead of sub-pages of all top-level pages of the site. kwargs: key/val pairs Passed to the `TocTree.get_toctree_for` Sphinx method """ # The TocTree will contain the full site TocTree including sub-pages. # "collapse=True" collapses sub-pages of non-active TOC pages. # maxdepth controls how many TOC levels are returned toctree = TocTree(app.env).get_toctree_for( pagename, app.builder, collapse=collapse, maxdepth=maxdepth, **kwargs ) # If no toctree is defined (AKA a single-page site), skip this if toctree is None: return [] # toctree has this structure # <caption> # <bullet_list> # <list_item classes="toctree-l1"> # <list_item classes="toctree-l1"> # `list_item`s are the actual TOC links and are the only thing we want toc_items = [ item for child in toctree.children for item in child if isinstance(item, docutils.nodes.list_item) ] # Now convert our docutils nodes into dicts that Jinja can use nav = [docutils_node_to_jinja(child, only_pages=True) for child in toc_items] return nav
def test_get_toctree_for_maxdepth(app): app.build() toctree = TocTree(app.env).get_toctree_for('index', app.builder, collapse=False, maxdepth=3) assert_node(toctree, [compact_paragraph, ([caption, "Table of Contents"], bullet_list, bullet_list, bullet_list)]) assert_node(toctree[1], ([list_item, ([compact_paragraph, reference, "foo"], bullet_list)], [list_item, compact_paragraph, reference, "bar"], [list_item, compact_paragraph, reference, "http://sphinx-doc.org/"])) assert_node(toctree[1][0][1], ([list_item, compact_paragraph, reference, "quux"], [list_item, ([compact_paragraph, reference, "foo.1"], bullet_list)], [list_item, compact_paragraph, reference, "foo.2"])) assert_node(toctree[1][0][1][1][1], [bullet_list, list_item, compact_paragraph, reference, "foo.1-1"]) assert_node(toctree[1][0][0][0], reference, refuri="foo", secnumber=[1]) assert_node(toctree[1][0][1][0][0][0], reference, refuri="quux", secnumber=[1, 1]) assert_node(toctree[1][0][1][1][0][0], reference, refuri="foo#foo-1", secnumber=[1, 2]) assert_node(toctree[1][0][1][1][1][0][0][0], reference, refuri="foo#foo-1-1", secnumber=[1, 2, 1]) assert_node(toctree[1][0][1][2][0][0], reference, refuri="foo#foo-2", secnumber=[1, 3]) assert_node(toctree[1][1][0][0], reference, refuri="bar", secnumber=[2]) assert_node(toctree[1][2][0][0], reference, refuri="http://sphinx-doc.org/") assert_node(toctree[2], [bullet_list, list_item, compact_paragraph, reference, "baz"]) assert_node(toctree[3], ([list_item, compact_paragraph, reference, "Latest reference"], [list_item, compact_paragraph, reference, "Python"])) assert_node(toctree[3][0][0][0], reference, refuri="http://sphinx-doc.org/latest/") assert_node(toctree[3][1][0][0], reference, refuri="http://python.org/")
def index_link_node(app, fromdocname, refs): toc = TocTree(app.env) par_nodes = [] labels = { 'DEF': 'DĂ©finitions:', 'USE': 'Usages:', } for type in ['DEF', 'USE']: relevant_refs = [ref for ref in refs if ref['type'] == type] if not relevant_refs: continue par_node = nodes.paragraph(classes=["glossary-refs"]) par_node += nodes.strong(labels[type], labels[type]) for ref in refs: if ref['type'] != type: continue ref_node = nodes.reference('', '') docnames = toc.get_toctree_ancestors(ref['docname']) if not docnames: docnames = [ref['docname']] for i, docname in enumerate(reversed(docnames)): if i < len(docnames) - 1: for child in app.env.titles[docname].children: ref_node.append(child) ref_node.append(nodes.Text('/', '/')) else: strong_node = nodes.strong('', '') for child in app.env.titles[docname].children: strong_node.append(child) ref_node.append(strong_node) ref_node['refdocname'] = ref['docname'] ref_node['refuri'] = app.builder.get_relative_uri( fromdocname, ref['docname']) ref_node['refuri'] += "#" + ref['target_id'] par_node += ref_node par_nodes.append(par_node) return par_nodes
def index_toctree(app, pagename: str, startdepth: int, collapse: bool = True, **kwargs): """ Returns the "local" (starting at `startdepth`) TOC tree containing the current page, rendered as HTML bullet lists. This is the equivalent of `context["toctree"](**kwargs)` in sphinx templating, but using the startdepth-local instead of global TOC tree. """ # this is a variant of the function stored in `context["toctree"]`, which is # defined as `lambda **kwargs: self._get_local_toctree(pagename, **kwargs)` # with `self` being the HMTLBuilder and the `_get_local_toctree` basically # returning: # return self.render_partial(TocTree(self.env).get_toctree_for( # pagename, self, collapse, **kwargs))['fragment'] if "includehidden" not in kwargs: kwargs["includehidden"] = False if kwargs.get("maxdepth") == "": kwargs.pop("maxdepth") toctree = TocTree(app.env) ancestors = toctree.get_toctree_ancestors(pagename) try: indexname = ancestors[-startdepth] except IndexError: # eg for index.rst, but also special pages such as genindex, py-modindex, search # those pages don't have a "current" element in the toctree, so we can # directly return an emtpy string instead of using the default sphinx # toctree.get_toctree_for(pagename, app.builder, collapse, **kwargs) return "" toctree_element = _get_local_toctree_for(toctree, indexname, pagename, app.builder, collapse, **kwargs) return app.builder.render_partial(toctree_element)["fragment"]
def get_and_resolve_doctree(self, docname: str, builder: "Builder", doctree: nodes.document = None, prune_toctrees: bool = True, includehidden: bool = False) -> nodes.document: """Read the doctree from the pickle, resolve cross-references and toctrees and return it. """ if doctree is None: doctree = self.get_doctree(docname) # resolve all pending cross-references self.apply_post_transforms(doctree, docname) # now, resolve all toctree nodes for toctreenode in doctree.findall(addnodes.toctree): result = TocTree(self).resolve(docname, builder, toctreenode, prune=prune_toctrees, includehidden=includehidden) if result is None: toctreenode.replace_self([]) else: toctreenode.replace_self(result) return doctree
def resolve_toctree(self, docname: str, builder: "Builder", toctree: addnodes.toctree, prune: bool = True, maxdepth: int = 0, titles_only: bool = False, collapse: bool = False, includehidden: bool = False) -> 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. """ return TocTree(self).resolve(docname, builder, toctree, prune, maxdepth, titles_only, collapse, includehidden)
def get_and_resolve_doctree(self, docname, builder, doctree=None, prune_toctrees=True, includehidden=False): # type: (unicode, Builder, nodes.Node, bool, bool) -> nodes.Node """Read the doctree from the pickle, resolve cross-references and toctrees and return it. """ if doctree is None: doctree = self.get_doctree(docname) # resolve all pending cross-references self.apply_post_transforms(doctree, docname) # now, resolve all toctree nodes for toctreenode in doctree.traverse(addnodes.toctree): result = TocTree(self).resolve(docname, builder, toctreenode, prune=prune_toctrees, includehidden=includehidden) if result is None: toctreenode.replace_self([]) else: toctreenode.replace_self(result) return doctree
def toctree(*args, **kwargs): try: # Sphinx >= 1.6 from sphinx.environment.adapters.toctree import TocTree get_toctree_for = TocTree(app.env).get_toctree_for except ImportError: # Sphinx < 1.6 get_toctree_for = app.env.get_toctree_for toc = get_toctree_for( app.config.notfound_pagename, app.builder, collapse=kwargs.pop('collapse', False), includehidden=kwargs.pop('includehidden', False), **kwargs # not using trailing comma here makes this compatible with # Python2 syntax ) # If no TOC is found, just return ``None`` instead of failing here if not toc: return None replace_uris(app, toc, docutils.nodes.reference, 'refuri') return app.builder.render_partial(toc)['fragment']
def build_finished(app, exception): if exception is not None: return links_elem = links('') document = docutils.utils.new_document('') document['xmlns:xlink'] = "http://www.w3.org/1999/xlink" for (docname, v) in app.env.testext_refs.items(): doclink = document_ref('', **{'xmlns:href': docname, 'xmlns:role': 'http://heptet.us/linkprops/document'}) refs = v['refs'] links_elem.children.insert(0, doclink) links_elem.children.extend(refs) class TocVisitor(SphinxTranslator): def __init__(self, document, builder, docname): super().__init__(document, builder) self.new = document_toctree('', docname=docname) self._current = [self.new] def visit_bullet_list(self, node): self._current.append(toctree_list('')) def depart_bullet_list(self, node): last = self._current.pop() self._current[-1].children.append(last) def visit_list_item(self, node): self._current.append(toctree_list_item('')) def depart_list_item(self, node): last = self._current.pop() self._current[-1].children.append(last) def visit_compact_paragraph(self, node): pass def depart_compact_paragraph(self, node): pass def visit_reference(self, node): attr = {'xlink:href': node['refuri']} self._current.append(toctree_link('', **attr)) def depart_reference(self, node): last = self._current.pop() self._current[-1].children.append(last) def visit_Text(self, node): self._current.append(node.copy()) def depart_Text(self, node): last = self._current.pop() self._current[-1].children.append(last) def visit_toctree(self, node): self._current.append(node.copy()) def depart_toctree(self, node): last = self._current.pop() self._current[-1].children.append(last) def visit_caption(self, node): self._current.append(node.copy()) def depart_caption(self, node): last = self._current.pop() self._current[-1].children.append(last) def unknown_visit(self, node): self._current.append(node.copy()) def unknown_departure(self, node): last = self._current.pop() self._current[-1].children.append(last) master_doc = app.config.master_doc toctree = TocTree(app.env).get_toctree_for(master_doc, app.builder, False) if toctree: visitor = TocVisitor(document, app.builder, app.config.master_doc) visitor.new['master'] = True toctree.walkabout(visitor) document.children.append(visitor.new) # for (docname, toc) in app.env.tocs.items(): # document.children.append(nodes.container('', toctree, ids=['global-toctree'])) # toctree.walkabout(visitor) # visitor.new['master'] = docname == master_doc # document.children.append(visitor.new) # if docname == app.config.master_doc: # continue # my_toc = toc.deepcopy() # visitor = TocVisitor(document, app.builder, docname) # my_toc.walkabout(visitor) # document.children.append(visitor.new) # document.children.append(links_elem) app.builder.write_doc('_links', document)
def build_toc(node: Node, depth: int = 1) -> List[Node]: entries = [] for sectionnode in node: # find all toctree nodes in this section and add them # to the toc (just copying the toctree node which is then # resolved in self.get_and_resolve_doctree) if isinstance(sectionnode, addnodes.only): onlynode = addnodes.only(expr=sectionnode['expr']) blist = build_toc(sectionnode, depth) if blist: onlynode += blist.children # type: ignore entries.append(onlynode) continue if not isinstance(sectionnode, nodes.section): # Extension code starts here. for tabnode in traverse_in_section(sectionnode, nxt_tab_head): if tabnode.tab_toc: nodetext = [nodes.Text(tabnode)] anchorname = '#' + tabnode.label_id numentries[0] += 1 reference = nodes.reference('', '', internal=True, refuri=docname, anchorname=anchorname, *nodetext) para = addnodes.compact_paragraph( '', '', reference) item = nodes.list_item('', para) entries.append(item) # Extension code ends here. for toctreenode in traverse_in_section( sectionnode, addnodes.toctree): item = toctreenode.copy() entries.append(item) # important: do the inventory stuff TocTree(app.env).note(docname, toctreenode) continue title = sectionnode[0] # copy the contents of the section title, but without references # and unnecessary stuff visitor = SphinxContentsFilter(doctree) title.walkabout(visitor) nodetext = visitor.get_entry_text() if not numentries[0]: # for the very first toc entry, don't add an anchor # as it is the file's title anyway anchorname = '' else: anchorname = '#' + sectionnode['ids'][0] numentries[0] += 1 # make these nodes: # list_item -> compact_paragraph -> reference reference = nodes.reference('', '', internal=True, refuri=docname, anchorname=anchorname, *nodetext) para = addnodes.compact_paragraph('', '', reference) item = nodes.list_item('', para) sub_item = build_toc(sectionnode, depth + 1) item += sub_item entries.append(item) if entries: return nodes.bullet_list('', *entries) return []
def build_toc(node, depth=1): # type: (nodes.Element, int) -> nodes.bullet_list entries = [] # type: List[nodes.Element] for sectionnode in node: # find all toctree nodes in this section and add them # to the toc (just copying the toctree node which is then # resolved in self.get_and_resolve_doctree) if isinstance(sectionnode, nodes.section): title = sectionnode[0] # copy the contents of the section title, but without references # and unnecessary stuff visitor = SphinxContentsFilter(doctree) title.walkabout(visitor) nodetext = visitor.get_entry_text() if not numentries[0]: # for the very first toc entry, don't add an anchor # as it is the file's title anyway anchorname = '' else: anchorname = '#' + sectionnode['ids'][0] numentries[0] += 1 # make these nodes: # list_item -> compact_paragraph -> reference reference = nodes.reference('', '', internal=True, refuri=docname, anchorname=anchorname, *nodetext) para = addnodes.compact_paragraph('', '', reference) item = nodes.list_item('', para) # type: nodes.Element sub_item = build_toc(sectionnode, depth + 1) if sub_item: item += sub_item entries.append(item) elif isinstance(sectionnode, addnodes.only): onlynode = addnodes.only(expr=sectionnode['expr']) blist = build_toc(sectionnode, depth) if blist: onlynode += blist.children entries.append(onlynode) # ADDED elif isinstance(sectionnode, addnodes.desc): target = sectionnode.traverse(addnodes.desc_signature)[0] reference = nodes.reference( '', '', nodes.literal('', target.attributes['fullname']), internal=True, refuri=docname, anchorname='#' + target.attributes['ids'][0], ) entries.append( nodes.list_item( '', addnodes.compact_paragraph('', '', reference))) # /ADDED elif isinstance(sectionnode, nodes.Element): for toctreenode in traverse_in_section( sectionnode, addnodes.toctree): item = toctreenode.copy() entries.append(item) # important: do the inventory stuff TocTree(app.env).note(docname, toctreenode) if entries: return nodes.bullet_list('', *entries) return None
def get_doc_context(self, docname, body, metatags): doc = super().get_doc_context(docname, body, metatags) self_toctree = TocTree(self.env).get_toctree_for(docname, self, True) toctree = self.render_partial(self_toctree)['fragment'] doc['toctree'] = toctree return doc
def get_current_subtoc(self, current_page_name, start_from=None): """Return a TOC for sub-files and sub-elements of the current file. This is to provide a "contextual" navbar that shows the current page in context of all of its siblings, not just the immediate "previous" and "next". This allows a very long page with many sections to be broken into smaller pages while not losing the navigation of the overall section, with the added bonus that only the page-level bullets for the current subsection are expanded, thus making for a much shorter, "drill-down" style navigation. """ assert self.app.env is not None assert self.app.builder is not None toc_tree = TocTree(self.app.env) raw_tree = toc_tree.get_toctree_for( current_page_name, self.app.builder, True, maxdepth=-1 ) local_toc_tree = toc_tree.get_toc_for( current_page_name, self.app.builder ) if raw_tree is None: raw_tree = local_toc_tree # start with the bullets inside the doc's toc, # not the top level bullet, as we get that from the other tree if ( not local_toc_tree.children or len(local_toc_tree.children[0].children) < 2 ): local_tree = None else: local_tree = local_toc_tree.children[0].children[1] def _locate_nodes(nodes, level, outer=True): # this is a lazy way of getting at all the info in a # series of docutils nodes, with an absolute mimimal # reliance on the actual structure of the nodes. # we just look for refuris and the fact that a node # is dependent on another somehow, that's it, then we # flatten it out into a clean "tree" later. # An official Sphinx feature/extension # here would probably make much more use of direct # knowledge of the structure for elem in nodes: if hasattr(elem, "attributes"): refuri = elem.attributes.get("refuri", None) else: refuri = None name = None if refuri is not None: for index, sub_elem in enumerate(elem.children, 1): if isinstance( sub_elem, (docutils_nodes.Text, docutils_nodes.literal), ): continue else: break local_text = elem.children[0:index] name = str(local_text[0]) remainders = elem.children[index:] yield level, refuri, name, local_text else: remainders = elem.children # try to embed the item-level get_toc_for() inside # the file-level get_toctree_for(), otherwise if we # just get the full get_toctree_for(), it's enormous. if outer and refuri == "": if local_tree is not None: for ent in _locate_nodes( [local_tree], level + 1, False ): yield ent else: for ent in _locate_nodes(remainders, level + 1, outer): yield ent def _organize_nodes(nodes): """organize the nodes that we've grabbed with non-contiguous 'level' numbers into a clean hierarchy""" stack = [] levels = [] for level, refuri, name, text_nodes in nodes: if not levels or levels[-1] < level: levels.append(level) new_collection = [] if stack: stack[-1].append(new_collection) stack.append(new_collection) elif level < levels[-1]: while levels and level < levels[-1]: levels.pop(-1) if level > levels[-1]: levels.append(level) else: stack.pop(-1) stack[-1].append((refuri, name, text_nodes)) return stack def _render_nodes( stack, level=0, start_from=None, nested_element=False, parent_element=None, ): printing = False if stack: printing = ( nested_element or start_from is None or start_from in [elem[0] for elem in stack if isinstance(elem, tuple)] ) if printing: if not isinstance( parent_element, docutils_nodes.bullet_list ): new_list = docutils_nodes.bullet_list() parent_element.append(new_list) parent_element = new_list while stack: elem = stack.pop(0) as_links = not isinstance(elem, tuple) or elem[0] != "" if isinstance(elem, tuple): refuri, name, text_nodes = elem if not stack or isinstance(stack[0], tuple): if printing: list_item = docutils_nodes.list_item( classes=["selected"] if not as_links else [] ) list_item.append( self._link_node(refuri, text_nodes) if as_links else self._strong_node(refuri, text_nodes) ) parent_element.append(list_item) elif isinstance(stack[0], list): if printing: list_item = docutils_nodes.list_item( classes=["selected"] if not as_links else [] ) list_item.append( self._link_node(refuri, text_nodes) if as_links else self._strong_node(refuri, text_nodes) ) parent_element.append(list_item) else: list_item = None _render_nodes( stack[0], level=level + 1, start_from=start_from, nested_element=nested_element or printing or elem[0] == "", parent_element=list_item or parent_element, ) elif isinstance(elem, list): _render_nodes( elem, level=level + 1, start_from=start_from, nested_element=nested_element, parent_element=parent_element, ) element = docutils_nodes.bullet_list() nodes = _organize_nodes(_locate_nodes([raw_tree], 0)) _render_nodes(nodes, start_from=start_from, parent_element=element) return cast(StandaloneHTMLBuilder, self.app.builder).render_partial( element )["fragment"]
def generate_additonal_tocs(app, pagename, templatename, context, doctree): """Generate and add additional tocs to Sphinx context""" pages_list = [] content_tocs = [] glossary_tocs = [] content_toc = '' glossary_toc = '' figures_toc = bullet_list() tables_toc = bullet_list() index = app.env.config.master_doc doctree_index = app.env.get_doctree(index) for toctreenode in doctree_index.traverse(toctree): page_index = 0 while page_index < len(toctreenode['includefiles']): page_in_toc = toctreenode['includefiles'][page_index] if page_in_toc not in pages_list: pages_list.append(page_in_toc) page_index += 1 else: toctreenode['includefiles'].remove(page_in_toc) for entry in toctreenode['entries']: if page_in_toc in entry: toctreenode['entries'].remove(entry) toctree_element = TocTree(app.env).resolve(pagename, app.builder, toctreenode, includehidden=True) try: toc_caption = next(child for child in toctree_element.children if isinstance(child, caption)) toctree_element.children.remove(toc_caption) except StopIteration: pass except AttributeError: continue if 'glossary_toc' in toctreenode.parent.attributes['names']: glossary_tocs.append(toctree_element) else: content_tocs.append(toctree_element) if content_tocs: content_toc = content_tocs[0] for content_element in content_tocs[1:]: try: content_toc.extend(content_element.children) except AttributeError: continue if glossary_tocs: glossary_toc = glossary_tocs[0] for glossary_element in glossary_tocs[1:]: glossary_toc.extend(glossary_element.children) glossary_toc = glossary_toc.children[0].children[0].children[1] pages_with_fignumbers = (x for x in pages_list if x in app.env.toc_fignumbers) for page in pages_with_fignumbers: doctree_page = app.env.get_doctree(page) for figurenode in doctree_page.traverse(figure): if not figurenode.attributes['ids']: continue figure_id = figurenode.attributes['ids'][0] toc_fig_tables = app.env.toc_fignumbers[page].get('figure', {}) figure_number = toc_fig_tables.get(figure_id) if figure_number is None: continue figure_title = figurenode.children[-1].children[0] or context['t'][ 'no_description'] try: figure_text_string = u'Fig. {}.{} - {}'.format( figure_number[0], figure_number[1], figure_title) except IndexError: continue figure_text = Text(figure_text_string) figure_text.rawsource = figure_text_string figure_reference = reference() figure_reference.attributes['internal'] = True figure_reference.attributes[ 'refuri'] = app.builder.get_relative_uri( pagename, page) + '#' + figure_id figure_compact_paragraph = compact_paragraph() figure_list_item = list_item() figure_text.parent = figure_reference figure_reference.children.append(figure_text) figure_reference.parent = figure_compact_paragraph figure_compact_paragraph.children.append(figure_reference) figure_compact_paragraph.parent = figure_list_item figure_list_item.children.append(figure_compact_paragraph) figure_list_item.parent = figures_toc figures_toc.children.append(figure_list_item) for tablenode in doctree_page.traverse(table): if not tablenode.attributes['ids']: continue table_id = tablenode.attributes['ids'][0] toc_fig_tables = app.env.toc_fignumbers[page].get('table', {}) table_number = toc_fig_tables.get(table_id) if table_number is None: continue table_title = tablenode.children[0].rawsource if tablenode.children[ 0].rawsource else context['t']['no_description'] table_title = (table_title[:60] + '...') if len(table_title) > 60 else table_title table_text_string = 'Tab. ' + '.'.join( [str(n) for n in table_number]) + ' - ' + table_title table_text = Text(table_text_string) table_text.rawsource = table_text_string table_reference = reference() table_reference.attributes['internal'] = True table_reference.attributes[ 'refuri'] = app.builder.get_relative_uri(pagename, page) + '#' + table_id table_compact_paragraph = compact_paragraph() table_list_item = list_item() table_text.parent = table_reference table_reference.children.append(table_text) table_reference.parent = table_compact_paragraph table_compact_paragraph.children.append(table_reference) table_compact_paragraph.parent = table_list_item table_list_item.children.append(table_compact_paragraph) table_list_item.parent = tables_toc tables_toc.children.append(table_list_item) context['content_toc'] = app.builder.render_partial( content_toc)['fragment'] if hasattr( content_toc, 'children') and content_toc.children else None context['glossary_toc'] = app.builder.render_partial( glossary_toc)['fragment'] if hasattr( glossary_toc, 'children') and glossary_toc.children else None context['figures_toc'] = app.builder.render_partial( figures_toc)['fragment'] if hasattr( figures_toc, 'children') and figures_toc.children else None context['tables_toc'] = app.builder.render_partial( tables_toc)['fragment'] if hasattr( tables_toc, 'children') and tables_toc.children else None
def build_toc(node: nodes.Element, depth: int = 1) -> Optional[nodes.bullet_list]: """ Build the table of contents. :param node: :param depth: """ entries: List[nodes.Element] = [] item: nodes.Element toctree_plus_types = set(app.env.config.toctree_plus_types) for sectionnode in node: # find all toctree nodes in this section and add them # to the toc (just copying the toctree node which is then # resolved in self.get_and_resolve_doctree) if isinstance(sectionnode, nodes.section): title = sectionnode[0] # copy the contents of the section title, but without references # and unnecessary stuff visitor = SphinxContentsFilter(doctree) title.walkabout(visitor) nodetext = visitor.get_entry_text() if not numentries[0]: # for the very first toc entry, don't add an anchor # as it is the file's title anyway anchorname = '' else: anchorname = f'#{sectionnode["ids"][0]}' numentries[0] += 1 # make these nodes: # list_item -> compact_paragraph -> reference reference = nodes.reference( '', '', internal=True, refuri=docname, anchorname=anchorname, *nodetext, ) para = addnodes.compact_paragraph('', '', reference) item = nodes.list_item('', para) sub_item = build_toc(sectionnode, depth + 1) if sub_item: item += sub_item entries.append(item) elif isinstance(sectionnode, addnodes.desc): # Add class, function and method directives to toctree. # (doesn't currently work for method directives - are they nested?) if sectionnode.attributes["objtype"] in toctree_plus_types: attributes = sectionnode.children[0].attributes if not attributes["ids"]: # Has no anchor continue title = attributes.get("fullname", sectionnode.children[0].astext()) if sectionnode.attributes["objtype"] in {"method", "attribute"}: # TODO: remove special case title = title.split('.', 1)[-1] anchorname = f'#{attributes["ids"][0]}' reference = nodes.reference( '', '', internal=True, refuri=docname, anchorname=anchorname, *[nodes.literal(text=title)], ) para = addnodes.compact_paragraph('', '', reference) item = nodes.list_item('', para) sub_item = build_toc(sectionnode.children[1], depth + 1) if sub_item: item += sub_item entries.append(item) elif TOCTREE_PLUS_DEBUG: print(sectionnode) pprint(sectionnode.attributes["objtype"]) elif isinstance(sectionnode, addnodes.only): onlynode = addnodes.only(expr=sectionnode["expr"]) blist = build_toc(sectionnode, depth) if blist: onlynode += blist.children entries.append(onlynode) elif isinstance(sectionnode, nodes.Element): for toctreenode in traverse_in_section(sectionnode, addnodes.toctree): item = toctreenode.copy() entries.append(item) # important: do the inventory stuff TocTree(app.env).note(docname, toctreenode) if entries: return nodes.bullet_list('', *entries) else: return None
def getTocTree(self, *args, **kwargs): return TocTree(self.env).get_toctree_for(*args, **kwargs)
def _get_local_toctree(self, docname, collapse=True, **kwds): # type: (unicode, bool, Any) -> unicode if 'includehidden' not in kwds: kwds['includehidden'] = False partials = TocTree(self.env).get_toctree_for(docname, self, collapse, **kwds) return self.render_partial(partials)['fragment']
def _get_local_toctree(app, docname, collapse=True, **kwds): if 'includehidden' not in kwds: kwds['includehidden'] = False toctree = TocTree(app.env).get_toctree_for(docname, app.builder, collapse, **kwds) return toctree
def get_doc_context(self, docname, body, metatags): # type: (unicode, unicode, Dict) -> Dict[unicode, Any] """Collect items for the template context of a page.""" # find out relations print("metatags", metatags) prev = next = None parents = [] rellinks = self.globalcontext['rellinks'][:] related = self.relations.get(docname) titles = self.env.titles if related and related[2]: try: next = { 'link': self.get_relative_uri(docname, related[2]), 'title': self.render_partial(titles[related[2]])['title'] } rellinks.append((related[2], next['title'], 'N', _('next'))) except KeyError: next = None if related and related[1]: try: prev = { 'link': self.get_relative_uri(docname, related[1]), 'title': self.render_partial(titles[related[1]])['title'] } rellinks.append( (related[1], prev['title'], 'P', _('previous'))) except KeyError: # the relation is (somehow) not in the TOC tree, handle # that gracefully prev = None while related and related[0]: try: parents.append({ 'link': self.get_relative_uri(docname, related[0]), 'title': self.render_partial(titles[related[0]])['title'] }) except KeyError: pass related = self.relations.get(related[0]) if parents: # remove link to the master file; we have a generic # "back to index" link already parents.pop() parents.reverse() # title rendered as HTML title = self.env.longtitles.get(docname) title = title and self.render_partial(title)['title'] or '' # Suffix for the document source_suffix = path.splitext(self.env.doc2path(docname))[1] # the name for the copied source if self.config.html_copy_source: sourcename = docname + source_suffix if source_suffix != self.config.html_sourcelink_suffix: sourcename += self.config.html_sourcelink_suffix else: sourcename = '' # metadata for the document meta = self.env.metadata.get(docname) # local TOC and global TOC tree nav_subchapter = """<li><a href="/{subchapter}.html">{subchapter_name}</a></li>""" nav_template = """ <li class="menu-toggle-open"> <a class="deep0" href="/{chapter}.html"> <div class="al deep0-number">{chapter_number}</div> <div class="ar deep0-title">{chapter_name}</div> </a> </li> """ nav_template_subitems = """ <li class="menu-toggle-open"> <a class="deep0" href="#"> <div class="al deep0-number">{chapter_number}</div> <div class="ar deep0-title">{chapter_name}</div> </a> <ul class="deep1"> {subchapters} </ul> </li> """ result = "" for i, chapter in enumerate(self.env.toctree_includes['index']): if chapter in self.env.toctree_includes: chapter_items = self.env.toctree_includes[chapter] tmp = "" for item in chapter_items: tmp += nav_subchapter.format( subchapter=item, subchapter_name=self.env.titles[item].children[0]) result += nav_template_subitems.format( chapter_number=i + 1, chapter_name=self.env.titles[chapter].children[0], subchapters=tmp) else: result += nav_template.format( chapter=chapter, chapter_number=i + 1, chapter_name=self.env.titles[chapter].children[0]) self_toc = TocTree(self.env).get_toc_for(docname, self) toc = self.render_partial(self_toc)['fragment'] return dict( parents=parents, prev=prev, next=next, title=title, meta=meta, body=body, metatags=metatags, rellinks=rellinks, sourcename=sourcename, toc=toc, nav=result, # only display a TOC if there's more than one item to show display_toc=(self.env.toc_num_entries[docname] > 1), page_source_suffix=source_suffix, )
def _get_local_toctree(self, docname: str, collapse: bool = True, **kwds) -> str: if 'includehidden' not in kwds: kwds['includehidden'] = False return self.render_partial(TocTree(self.env).get_toctree_for( docname, self, collapse, **kwds))['fragment']
def get_doc_context(self, docname: str, body: str, metatags: str) -> Dict[str, Any]: """Collect items for the template context of a page.""" # find out relations prev = next = None parents = [] rellinks = self.globalcontext['rellinks'][:] related = self.relations.get(docname) titles = self.env.titles if related and related[2]: try: next = { 'link': self.get_relative_uri(docname, related[2]), 'title': self.render_partial(titles[related[2]])['title'] } rellinks.append((related[2], next['title'], 'N', _('next'))) except KeyError: next = None if related and related[1]: try: prev = { 'link': self.get_relative_uri(docname, related[1]), 'title': self.render_partial(titles[related[1]])['title'] } rellinks.append((related[1], prev['title'], 'P', _('previous'))) except KeyError: # the relation is (somehow) not in the TOC tree, handle # that gracefully prev = None while related and related[0]: try: parents.append( {'link': self.get_relative_uri(docname, related[0]), 'title': self.render_partial(titles[related[0]])['title']}) except KeyError: pass related = self.relations.get(related[0]) if parents: # remove link to the master file; we have a generic # "back to index" link already parents.pop() parents.reverse() # title rendered as HTML title_node = self.env.longtitles.get(docname) title = title_node and self.render_partial(title_node)['title'] or '' # Suffix for the document source_suffix = path.splitext(self.env.doc2path(docname))[1] # the name for the copied source if self.config.html_copy_source: sourcename = docname + source_suffix if source_suffix != self.config.html_sourcelink_suffix: sourcename += self.config.html_sourcelink_suffix else: sourcename = '' # metadata for the document meta = self.env.metadata.get(docname) # local TOC and global TOC tree self_toc = TocTree(self.env).get_toc_for(docname, self) toc = self.render_partial(self_toc)['fragment'] return { 'parents': parents, 'prev': prev, 'next': next, 'title': title, 'meta': meta, 'body': body, 'metatags': metatags, 'rellinks': rellinks, 'sourcename': sourcename, 'toc': toc, # only display a TOC if there's more than one item to show 'display_toc': (self.env.toc_num_entries[docname] > 1), 'page_source_suffix': source_suffix, }