Skip to content

Commit afaaee4

Browse files
committed
adding captions to left sidebar
1 parent 8a76bc8 commit afaaee4

File tree

2 files changed

+84
-20
lines changed

2 files changed

+84
-20
lines changed

pydata_sphinx_theme/__init__.py

+78-18
Original file line numberDiff line numberDiff line change
@@ -7,22 +7,50 @@
77

88
import sphinx.builders.html
99
from sphinx.errors import ExtensionError
10+
from sphinx.environment.adapters.toctree import TocTree
11+
from sphinx import addnodes
12+
from docutils import nodes
1013

1114
from .bootstrap_html_translator import BootstrapHTML5Translator
1215
import docutils
1316

1417
__version__ = "0.1.1"
1518

1619

20+
class MyTocTree(TocTree):
21+
def get_toctree_for_subpage(
22+
self, pagename, builder, collapse=True, maxdepth=-1, **kwargs
23+
):
24+
"""Return the global TOC nodetree."""
25+
doctree = self.env.get_doctree(pagename)
26+
if pagename == "genindex":
27+
import IPython; IPython.embed()
28+
toctrees = [] # type: List[Element]
29+
if "includehidden" not in kwargs:
30+
kwargs["includehidden"] = True
31+
if "maxdepth" not in kwargs:
32+
kwargs["maxdepth"] = 0
33+
kwargs["collapse"] = collapse
34+
for toctreenode in doctree.traverse(addnodes.toctree):
35+
toctree = self.resolve(pagename, builder, toctreenode, prune=True, **kwargs)
36+
if toctree:
37+
toctrees.append(toctree)
38+
if not toctrees:
39+
return None
40+
result = toctrees[0]
41+
for toctree in toctrees[1:]:
42+
result.extend(toctree.children)
43+
return result
44+
45+
1746
def add_toctree_functions(app, pagename, templatename, context, doctree):
1847
"""Add functions so Jinja templates can add toctree objects.
1948
2049
This converts the docutils nodes into a nested dictionary that Jinja can
2150
use in our templating.
2251
"""
23-
from sphinx.environment.adapters.toctree import TocTree
2452

25-
def get_nav_object(maxdepth=None, collapse=True, **kwargs):
53+
def get_nav_object(maxdepth=None, collapse=True, subpage_caption=False, **kwargs):
2654
"""Return a list of nav links that can be accessed from Jinja.
2755
2856
Parameters
@@ -38,26 +66,45 @@ def get_nav_object(maxdepth=None, collapse=True, **kwargs):
3866
# The TocTree will contain the full site TocTree including sub-pages.
3967
# "collapse=True" collapses sub-pages of non-active TOC pages.
4068
# maxdepth controls how many TOC levels are returned
41-
toctree = TocTree(app.env).get_toctree_for(
69+
toc = MyTocTree(app.env)
70+
toctree = toc.get_toctree_for(
4271
pagename, app.builder, collapse=collapse, maxdepth=maxdepth, **kwargs
4372
)
73+
4474
# If no toctree is defined (AKA a single-page site), skip this
4575
if toctree is None:
4676
return []
4777

78+
if subpage_caption:
79+
# We only wish to show a single page's descendants, so we'll keep their captions
80+
subpage_toctree = toc.get_toctree_for_subpage(
81+
pagename, app.builder, collapse=collapse, maxdepth=maxdepth, **kwargs
82+
)
83+
if subpage_toctree is not None:
84+
# Find the current page in the top-level children
85+
for item in toctree.children:
86+
if isinstance(item, nodes.bullet_list) and item.attributes.get("iscurrent", []):
87+
# Append that pages' toctree so we get captions
88+
subpage_list = item.children[0]
89+
subpage_list.children = [subpage_list.children[0]] + subpage_toctree.children
90+
4891
# toctree has this structure
4992
# <caption>
5093
# <bullet_list>
5194
# <list_item classes="toctree-l1">
5295
# <list_item classes="toctree-l1">
5396
# `list_item`s are the actual TOC links and are the only thing we want
54-
toc_items = [item for child in toctree.children for item in child
55-
if isinstance(item, docutils.nodes.list_item)]
97+
toc_items = []
98+
for child in toctree.children:
99+
if isinstance(child, docutils.nodes.caption):
100+
toc_items.append(child)
101+
elif isinstance(child, docutils.nodes.bullet_list):
102+
for list_entry in child:
103+
if isinstance(list_entry, docutils.nodes.list_item):
104+
toc_items.append(list_entry)
56105

57106
# Now convert our docutils nodes into dicts that Jinja can use
58-
nav = [docutils_node_to_jinja(child, only_pages=True)
59-
for child in toc_items]
60-
107+
nav = [docutils_node_to_jinja(child, only_pages=True) for child in toc_items]
61108
return nav
62109

63110
def get_page_toc_object():
@@ -73,6 +120,7 @@ def get_page_toc_object():
73120
context["get_nav_object"] = get_nav_object
74121
context["get_page_toc_object"] = get_page_toc_object
75122

123+
76124
def docutils_node_to_jinja(list_item, only_pages=False):
77125
"""Convert a docutils node to a structure that can be read by Jinja.
78126
@@ -91,6 +139,12 @@ def docutils_node_to_jinja(list_item, only_pages=False):
91139
The TocTree, converted into a dictionary with key/values that work
92140
within Jinja.
93141
"""
142+
# If a caption, pass it through
143+
if isinstance(list_item, docutils.nodes.caption):
144+
nav = {"text": list_item.astext(), "type": "caption"}
145+
return nav
146+
147+
# Else, we assume it's a list item and need to parse the item content
94148
if not list_item.children:
95149
return None
96150

@@ -100,18 +154,20 @@ def docutils_node_to_jinja(list_item, only_pages=False):
100154
# <reference> <-- the thing we want
101155
reference = list_item.children[0].children[0]
102156
title = reference.astext()
103-
url = reference.attributes["refuri"]
157+
158+
url = reference.attributes.get("refuri", "")
104159
active = "current" in list_item.attributes["classes"]
105160

106161
# If we've got an anchor link, skip it if we wish
107-
if only_pages and '#' in url:
162+
if only_pages and "#" in url:
108163
return None
109164

110165
# Converting the docutils attributes into jinja-friendly objects
111166
nav = {}
112167
nav["title"] = title
113168
nav["url"] = url
114169
nav["active"] = active
170+
nav["type"] = "ref"
115171

116172
# Recursively convert children as well
117173
# If there are sub-pages for this list_item, there should be two children:
@@ -129,19 +185,23 @@ def docutils_node_to_jinja(list_item, only_pages=False):
129185

130186
# -----------------------------------------------------------------------------
131187

188+
132189
def setup_edit_url(app, pagename, templatename, context, doctree):
133190
"""Add a function that jinja can access for returning the edit URL of a page."""
191+
134192
def get_edit_url():
135193
"""Return a URL for an "edit this page" link."""
136194
required_values = ["github_user", "github_repo", "github_version"]
137195
for val in required_values:
138196
if not context.get(val):
139-
raise ExtensionError("Missing required value for `edit this page` button. "
140-
"Add %s to your `html_context` configuration" % val)
141-
142-
github_user = context['github_user']
143-
github_repo = context['github_repo']
144-
github_version = context['github_version']
197+
raise ExtensionError(
198+
"Missing required value for `edit this page` button. "
199+
"Add %s to your `html_context` configuration" % val
200+
)
201+
202+
github_user = context["github_user"]
203+
github_repo = context["github_repo"]
204+
github_version = context["github_version"]
145205
file_name = f"{pagename}{context['page_source_suffix']}"
146206

147207
# Make sure that doc_path has a path separator only if it exists (to avoid //)
@@ -153,7 +213,7 @@ def get_edit_url():
153213
url_edit = f"https://github.com/{github_user}/{github_repo}/edit/{github_version}/{doc_path}{file_name}"
154214
return url_edit
155215

156-
context['get_edit_url'] = get_edit_url
216+
context["get_edit_url"] = get_edit_url
157217

158218

159219
# -----------------------------------------------------------------------------
@@ -174,6 +234,6 @@ def setup(app):
174234
# uses a special "dirhtml" builder so we need to replace these both with
175235
# our custom HTML builder
176236
app.set_translator("readthedocs", BootstrapHTML5Translator, override=True)
177-
app.set_translator('readthedocsdirhtml', BootstrapHTML5Translator, override=True)
237+
app.set_translator("readthedocsdirhtml", BootstrapHTML5Translator, override=True)
178238
app.connect("html-page-context", setup_edit_url)
179239
app.connect("html-page-context", add_toctree_functions)

pydata_sphinx_theme/docs-sidebar.html

+6-2
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,17 @@
77
<nav class="bd-links" id="bd-docs-nav" aria-label="Main navigation">
88

99
<div class="bd-toc-item active">
10-
{% set nav = get_nav_object(maxdepth=3, collapse=True) %}
10+
{% set nav = get_nav_object(maxdepth=3, collapse=True, subpage_caption=True) %}
1111

1212
<ul class="nav bd-sidenav">
13+
<!-- top-level pages -->
1314
{% for main_nav_item in nav %}
1415
{% if main_nav_item.active %}
16+
<!-- sub-pages within each top level page -->
1517
{% for nav_item in main_nav_item.children %}
16-
{% if nav_item.children %}
18+
{% if nav_item["type"] == "caption" %}
19+
<li class="nav-caption">{{ nav_item.text }}</li>
20+
{% elif nav_item.children %}
1721

1822
<li class="{% if nav_item.active%}active{% endif %}">
1923
<a href="{{ nav_item.url }}">{{ nav_item.title }}</a>

0 commit comments

Comments
 (0)