from docutils import nodes from sphinx import addnodes from sphinx.util import logging from sphinx.util.nodes import clean_astext from sphinx.util.docutils import SphinxDirective import copy logger = logging.getLogger(__name__) class TableofContents(SphinxDirective): def run(self): """ returns an array of nodes for the tableofcontents directive declaration """ ret = [] wrappernode = nodes.compound(classes=["tableofcontents-wrapper"]) self.add_name(wrappernode) depth = 0 globaltoc = self._process_toc_dict(copy.deepcopy(self.config.globaltoc)) # remove master_doc from the dict if "file" in globaltoc and globaltoc["file"] == self.config.master_doc: del globaltoc["file"] wncopy = wrappernode.deepcopy() self._has_toc_yaml(wncopy, globaltoc, depth) ret.append(wncopy) return ret def _process_toc_dict(self, globaltoc): """ Filters globaltoc to take children of the current page only """ for key, val in globaltoc.items(): if key == "file": if val == self.env.docname: del globaltoc[key] return globaltoc if key == "sections": for item in val: if "file" in item and item["file"] == self.env.docname: del item["file"] return item elif "sections" in item: globaltoc = self._process_toc_dict(item) return globaltoc def _has_toc_yaml(self, subnode, tocdict, depth): """ constructs toc nodes from globaltoc dict """ depth += 1 for key, val in tocdict.items(): if key == "header": header = self._handle_toc_header(subnode, val, depth) subnode["classes"].append("ls-none") subnode.append(header) if key in ["file", "url"]: if "title" in tocdict: title = tocdict["title"] else: if val not in self.env.titles: continue title = clean_astext(self.env.titles[val]) if key == "url": if "http" in val: val = val internal = False else: # since "file" key will be anyways for each "url" key continue else: val = "/" + val + self.env.app.builder.out_suffix internal = True reference = nodes.reference( "", "", internal=internal, refuri=val, anchorname="", *[nodes.Text(title)] ) para = addnodes.compact_paragraph("", "", reference) item = nodes.list_item("", para) item["classes"].append("tableofcontents-l%d" % (depth)) subnode.append(item) if key == "sections": sectionlist = nodes.bullet_list().deepcopy() sectionheader = None headerlist = None for item in val: if "header" in item: if headerlist: sectionlist["classes"].append("ls-none") sectionlist.append(sectionheader) sectionlist.append(headerlist) headerlist = nodes.bullet_list().deepcopy() sectionheader = self._handle_toc_header( sectionlist, item["header"], depth ) else: if headerlist: self._has_toc_yaml(headerlist, item, depth) else: self._has_toc_yaml(sectionlist, item, depth) # handling for last header in the section if headerlist: sectionlist["classes"].append("ls-none") sectionlist.append(sectionheader) sectionlist.append(headerlist) subnode.append(sectionlist) def _handle_toc_header(self, subnode, val, depth): """ Constructs node for the headers in globaltoc """ if val in self.env.titles: title = clean_astext(self.env.titles[val]) val = "/" + val + self.env.app.builder.out_suffix reference = nodes.reference( "", "", internal=False, refuri=val, anchorname="", *[nodes.Text(title)] ) para = addnodes.compact_paragraph("", "", reference) else: para = addnodes.compact_paragraph("", "", nodes.Text(val)) item = nodes.list_item("", para) item["classes"].append("fs-1-2") return item