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"])
Example #3
0
    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/")
Example #5
0
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
Example #6
0
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"]
Example #7
0
    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
Example #8
0
    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)
Example #9
0
    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
Example #10
0
    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']
Example #11
0
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)
Example #12
0
 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 []
Example #13
0
        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
Example #15
0
    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"]
Example #16
0
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
Example #17
0
		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
Example #18
0
 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']
Example #20
0
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
Example #21
0
    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,
        )
Example #22
0
 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']
Example #23
0
    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,
        }