aboutsummaryrefslogtreecommitdiff
path: root/sphinx_ascii_theme/__init__.py
diff options
context:
space:
mode:
Diffstat (limited to 'sphinx_ascii_theme/__init__.py')
-rw-r--r--sphinx_ascii_theme/__init__.py161
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__)))