diff options
Diffstat (limited to 'sphinx_ascii_theme/__init__.py')
-rw-r--r-- | sphinx_ascii_theme/__init__.py | 161 |
1 files changed, 161 insertions, 0 deletions
diff --git a/sphinx_ascii_theme/__init__.py b/sphinx_ascii_theme/__init__.py new file mode 100644 index 0000000..160932e --- /dev/null +++ b/sphinx_ascii_theme/__init__.py @@ -0,0 +1,161 @@ +from os import path +import docutils + +""" +The code of the function add_toctree_functions and docutils_node_to_jinja are +on the following license: + +BSD 3-Clause License + +Copyright (c) 2018, pandas +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +It's based on the following project: + https://github.com/pandas-dev/pydata-sphinx-theme +""" + +def add_toctree_functions(app, pagename, templatename, context, doctree): + """Add functions so Jinja templates can add toctree objects. + This converts the docutils nodes into a nested dictionary that Jinja can + use in our templating. + """ + from sphinx.environment.adapters.toctree import TocTree + + 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 get_page_toc_object(): + """Return a list of within-page TOC links that can be accessed from Jinja.""" + self_toc = TocTree(app.env).get_toc_for(pagename, app.builder) + + try: + # If there's only one child, assume we have a single "title" as top header + # so start the TOC at the first item's children (AKA, level 2 headers) + if len(self_toc.children) == 1: + nav = docutils_node_to_jinja(self_toc.children[0]).get("children", []) + else: + nav = [docutils_node_to_jinja(item) for item in self_toc.children] + return nav + except Exception: + return {} + + context["get_nav_object"] = get_nav_object + context["get_page_toc_object"] = get_page_toc_object + + +def docutils_node_to_jinja(list_item, only_pages=False): + """Convert a docutils node to a structure that can be read by Jinja. + Parameters + ---------- + list_item : docutils list_item node + A parent item, potentially with children, corresponding to the level + of a TocTree. + only_pages : bool + Only include items for full pages in the output dictionary. Exclude + anchor links (TOC items with a URL that starts with #) + Returns + ------- + nav : dict + The TocTree, converted into a dictionary with key/values that work + within Jinja. + """ + if not list_item.children: + return None + + # We assume this structure of a list item: + # <list_item> + # <compact_paragraph > + # <reference> <-- the thing we want + reference = list_item.children[0].children[0] + title = reference.astext() + url = reference.attributes["refuri"] + active = "current" in list_item.attributes["classes"] + + # If we've got an anchor link, skip it if we wish + if only_pages and "#" in url: + return None + + # Converting the docutils attributes into jinja-friendly objects + nav = {} + nav["title"] = title + nav["url"] = url + nav["active"] = active + + # Recursively convert children as well + # If there are sub-pages for this list_item, there should be two children: + # a paragraph, and a bullet_list. + nav["children"] = [] + if len(list_item.children) > 1: + # The `.children` of the bullet_list has the nodes of the sub-pages. + subpage_list = list_item.children[1].children + for sub_page in subpage_list: + child_nav = docutils_node_to_jinja(sub_page, only_pages=only_pages) + if child_nav is not None: + nav["children"].append(child_nav) + return nav + +def setup(app): + app.connect("html-page-context", add_toctree_functions) + app.add_html_theme('ascii', path.abspath(path.dirname(__file__))) |