7
7
8
8
import sphinx .builders .html
9
9
from sphinx .errors import ExtensionError
10
+ from sphinx .environment .adapters .toctree import TocTree
11
+ from sphinx import addnodes
12
+ from docutils import nodes
10
13
11
14
from .bootstrap_html_translator import BootstrapHTML5Translator
12
15
import docutils
13
16
14
17
__version__ = "0.1.1"
15
18
16
19
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
+ toctrees = [] # type: List[Element]
27
+ if "includehidden" not in kwargs :
28
+ kwargs ["includehidden" ] = True
29
+ if "maxdepth" not in kwargs :
30
+ kwargs ["maxdepth" ] = 0
31
+ kwargs ["collapse" ] = collapse
32
+ for toctreenode in doctree .traverse (addnodes .toctree ):
33
+ toctree = self .resolve (pagename , builder , toctreenode , prune = True , ** kwargs )
34
+ if toctree :
35
+ toctrees .append (toctree )
36
+ if not toctrees :
37
+ return None
38
+ result = toctrees [0 ]
39
+ for toctree in toctrees [1 :]:
40
+ result .extend (toctree .children )
41
+ return result
42
+
43
+
17
44
def add_toctree_functions (app , pagename , templatename , context , doctree ):
18
45
"""Add functions so Jinja templates can add toctree objects.
19
46
20
47
This converts the docutils nodes into a nested dictionary that Jinja can
21
48
use in our templating.
22
49
"""
23
- from sphinx .environment .adapters .toctree import TocTree
24
50
25
- def get_nav_object (maxdepth = None , collapse = True , ** kwargs ):
51
+ def get_nav_object (maxdepth = None , collapse = True , subpage_caption = False , ** kwargs ):
26
52
"""Return a list of nav links that can be accessed from Jinja.
27
53
28
54
Parameters
@@ -38,26 +64,46 @@ def get_nav_object(maxdepth=None, collapse=True, **kwargs):
38
64
# The TocTree will contain the full site TocTree including sub-pages.
39
65
# "collapse=True" collapses sub-pages of non-active TOC pages.
40
66
# maxdepth controls how many TOC levels are returned
41
- toctree = TocTree (app .env ).get_toctree_for (
67
+ toc = MyTocTree (app .env )
68
+ toctree = toc .get_toctree_for (
42
69
pagename , app .builder , collapse = collapse , maxdepth = maxdepth , ** kwargs
43
70
)
71
+
44
72
# If no toctree is defined (AKA a single-page site), skip this
45
73
if toctree is None :
46
74
return []
47
75
76
+ if subpage_caption :
77
+ # We only wish to show a single page's descendants, so we'll keep their captions
78
+ subpage_toctree = toc .get_toctree_for_subpage (
79
+ pagename , app .builder , collapse = collapse , maxdepth = maxdepth , ** kwargs
80
+ )
81
+ if subpage_toctree is not None :
82
+ # Find the current page in the top-level children
83
+ for item in toctree .children :
84
+ if isinstance (item , nodes .bullet_list ) and item .attributes .get ("iscurrent" , []):
85
+ # Append that pages' toctree so we get captions
86
+ subpage_list = item .children [0 ]
87
+ subpage_list .children = [subpage_list .children [0 ]] + subpage_toctree .children
88
+
89
+
48
90
# toctree has this structure
49
91
# <caption>
50
92
# <bullet_list>
51
93
# <list_item classes="toctree-l1">
52
94
# <list_item classes="toctree-l1">
53
95
# `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 )]
96
+ toc_items = []
97
+ for child in toctree .children :
98
+ if isinstance (child , docutils .nodes .caption ):
99
+ toc_items .append (child )
100
+ elif isinstance (child , docutils .nodes .bullet_list ):
101
+ for list_entry in child :
102
+ if isinstance (list_entry , docutils .nodes .list_item ):
103
+ toc_items .append (list_entry )
56
104
57
105
# 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
-
106
+ nav = [docutils_node_to_jinja (child , only_pages = True ) for child in toc_items ]
61
107
return nav
62
108
63
109
def get_page_toc_object ():
@@ -73,6 +119,7 @@ def get_page_toc_object():
73
119
context ["get_nav_object" ] = get_nav_object
74
120
context ["get_page_toc_object" ] = get_page_toc_object
75
121
122
+
76
123
def docutils_node_to_jinja (list_item , only_pages = False ):
77
124
"""Convert a docutils node to a structure that can be read by Jinja.
78
125
@@ -91,6 +138,12 @@ def docutils_node_to_jinja(list_item, only_pages=False):
91
138
The TocTree, converted into a dictionary with key/values that work
92
139
within Jinja.
93
140
"""
141
+ # If a caption, pass it through
142
+ if isinstance (list_item , docutils .nodes .caption ):
143
+ nav = {"text" : list_item .astext (), "type" : "caption" }
144
+ return nav
145
+
146
+ # Else, we assume it's a list item and need to parse the item content
94
147
if not list_item .children :
95
148
return None
96
149
@@ -100,18 +153,20 @@ def docutils_node_to_jinja(list_item, only_pages=False):
100
153
# <reference> <-- the thing we want
101
154
reference = list_item .children [0 ].children [0 ]
102
155
title = reference .astext ()
103
- url = reference .attributes ["refuri" ]
156
+
157
+ url = reference .attributes .get ("refuri" , "" )
104
158
active = "current" in list_item .attributes ["classes" ]
105
159
106
160
# If we've got an anchor link, skip it if we wish
107
- if only_pages and '#' in url :
161
+ if only_pages and "#" in url :
108
162
return None
109
163
110
164
# Converting the docutils attributes into jinja-friendly objects
111
165
nav = {}
112
166
nav ["title" ] = title
113
167
nav ["url" ] = url
114
168
nav ["active" ] = active
169
+ nav ["type" ] = "ref"
115
170
116
171
# Recursively convert children as well
117
172
# If there are sub-pages for this list_item, there should be two children:
@@ -129,19 +184,23 @@ def docutils_node_to_jinja(list_item, only_pages=False):
129
184
130
185
# -----------------------------------------------------------------------------
131
186
187
+
132
188
def setup_edit_url (app , pagename , templatename , context , doctree ):
133
189
"""Add a function that jinja can access for returning the edit URL of a page."""
190
+
134
191
def get_edit_url ():
135
192
"""Return a URL for an "edit this page" link."""
136
193
required_values = ["github_user" , "github_repo" , "github_version" ]
137
194
for val in required_values :
138
195
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' ]
196
+ raise ExtensionError (
197
+ "Missing required value for `edit this page` button. "
198
+ "Add %s to your `html_context` configuration" % val
199
+ )
200
+
201
+ github_user = context ["github_user" ]
202
+ github_repo = context ["github_repo" ]
203
+ github_version = context ["github_version" ]
145
204
file_name = f"{ pagename } { context ['page_source_suffix' ]} "
146
205
147
206
# Make sure that doc_path has a path separator only if it exists (to avoid //)
@@ -153,7 +212,7 @@ def get_edit_url():
153
212
url_edit = f"https://github.com/{ github_user } /{ github_repo } /edit/{ github_version } /{ doc_path } { file_name } "
154
213
return url_edit
155
214
156
- context [' get_edit_url' ] = get_edit_url
215
+ context [" get_edit_url" ] = get_edit_url
157
216
158
217
159
218
# -----------------------------------------------------------------------------
@@ -174,6 +233,6 @@ def setup(app):
174
233
# uses a special "dirhtml" builder so we need to replace these both with
175
234
# our custom HTML builder
176
235
app .set_translator ("readthedocs" , BootstrapHTML5Translator , override = True )
177
- app .set_translator (' readthedocsdirhtml' , BootstrapHTML5Translator , override = True )
236
+ app .set_translator (" readthedocsdirhtml" , BootstrapHTML5Translator , override = True )
178
237
app .connect ("html-page-context" , setup_edit_url )
179
238
app .connect ("html-page-context" , add_toctree_functions )
0 commit comments