11
11
from sphinx .util import logging
12
12
from sphinx .util .nodes import _make_id
13
13
14
- from .directive import LOG_PREFIX
14
+ from .directive import LOG_PREFIX , INLINE_TAB_DOCNAMES
15
15
from .nodes import TabContainer
16
16
from .sectiondata import SectionData
17
17
from .tab_id import TabId
@@ -33,9 +33,10 @@ def env_purge_doc(app: Sphinx, _: BuildEnvironment, docname: str) -> None:
33
33
:param _: The Sphinx BuildEnvironment object; unused
34
34
:param docname: The name of the document to purge
35
35
"""
36
- if hasattr (app .env , "sphinx_tabs" ):
37
- if docname in app .env .sphinx_tabs :
38
- app .env .sphinx_tabs .pop (docname )
36
+ if hasattr (app .env , INLINE_TAB_DOCNAMES ):
37
+ tab_docnames : list [str ] = getattr (app .env , INLINE_TAB_DOCNAMES )
38
+ if docname in tab_docnames :
39
+ tab_docnames .remove (docname )
39
40
40
41
41
42
def env_merge_info (
@@ -50,73 +51,15 @@ def env_merge_info(
50
51
:param docnames: A list of the document names to merge
51
52
:param other: The Sphinx BuildEnvironment from the reader worker
52
53
"""
53
- if not hasattr (app .env , "sphinx_tabs" ):
54
- app .env .sphinx_tabs = {}
55
- if hasattr (other , "sphinx_tabs" ):
54
+ if not hasattr (app .env , INLINE_TAB_DOCNAMES ):
55
+ setattr (app .env , INLINE_TAB_DOCNAMES , [])
56
+ tab_docnames : list [str ] = getattr (app .env , INLINE_TAB_DOCNAMES )
57
+ if hasattr (other , INLINE_TAB_DOCNAMES ):
58
+ other_tab_docnames : list [str ] = getattr (other , INLINE_TAB_DOCNAMES )
56
59
for docname in docnames :
57
- if docname in other .sphinx_tabs :
58
- if docname not in app .env .sphinx_tabs :
59
- app .env .sphinx_tabs [docname ] = []
60
- if (
61
- len (app .env .sphinx_tabs [docname ]) > 0
62
- and len (other .sphinx_tabs [docname ]) == 0
63
- ):
64
- logger .warning (
65
- f"{ LOG_PREFIX } env_merge_info: { docname } ; not overwriting app.env with empty list from other"
66
- )
67
- continue
68
- app .env .sphinx_tabs [docname ] = other .sphinx_tabs [docname ].copy ()
69
-
70
-
71
- def merge_toctrees (original_toc : nodes .list_item , tab_based_toc : nodes .list_item ) -> nodes .list_item :
72
- """
73
- Merge the original toctree with the tab-based TOC to preserve both child pages and tab content headings.
74
-
75
- :param original_toc: The original toctree (includes child pages)
76
- :param tab_based_toc: The tab-based TOC (includes tab content headings)
77
- :return: A merged toctree containing both types of content
78
- """
79
- # If the tab-based TOC is empty, return the original TOC
80
- if not tab_based_toc or not hasattr (tab_based_toc , 'children' ) or not tab_based_toc .children :
81
- return original_toc
82
-
83
- # If the original TOC is empty, return the tab-based TOC
84
- if not original_toc or not hasattr (original_toc , 'children' ) or not original_toc .children :
85
- return tab_based_toc
86
-
87
- # Create a new list item to hold the merged content
88
- merged_toc = nodes .list_item ()
89
- merged_bullet_list = nodes .bullet_list ()
90
-
91
- # Add the tab-based TOC content first (this includes the tab headings)
92
- if hasattr (tab_based_toc , 'children' ) and tab_based_toc .children :
93
- for child in tab_based_toc .children :
94
- if isinstance (child , nodes .bullet_list ):
95
- # Extract items from the tab-based bullet list
96
- for item in child .children :
97
- if isinstance (item , nodes .list_item ):
98
- merged_bullet_list .append (item )
99
- else :
100
- # Direct child - add it as a list item
101
- merged_bullet_list .append (child )
102
-
103
- # Add the original TOC content (this includes child pages)
104
- if hasattr (original_toc , 'children' ) and original_toc .children :
105
- for child in original_toc .children :
106
- if isinstance (child , nodes .bullet_list ):
107
- # Extract items from the original bullet list
108
- for item in child .children :
109
- if isinstance (item , nodes .list_item ):
110
- merged_bullet_list .append (item )
111
- else :
112
- # Direct child - add it as a list item
113
- merged_bullet_list .append (child )
114
-
115
- # Only add the bullet list if it has items
116
- if merged_bullet_list .children :
117
- merged_toc .append (merged_bullet_list )
118
-
119
- return merged_toc
60
+ if docname in other_tab_docnames :
61
+ if docname not in tab_docnames :
62
+ tab_docnames .append (docname )
120
63
121
64
122
65
def doctree_read (app : Sphinx , doctree : nodes .document ):
@@ -126,43 +69,69 @@ def doctree_read(app: Sphinx, doctree: nodes.document):
126
69
:param app: The Sphinx Application instance
127
70
:param doctree: The parsed document tree; unused
128
71
"""
129
- if not hasattr (app .env , "sphinx_tabs" ):
130
- app .env .sphinx_tabs = {}
131
- if (
132
- app .env .docname in app .env .sphinx_tabs
133
- and len (app .env .sphinx_tabs [app .env .docname ]) > 0
134
- ):
72
+ if not hasattr (app .env , INLINE_TAB_DOCNAMES ):
73
+ setattr (app .env , INLINE_TAB_DOCNAMES , [])
74
+ tab_docnames : list [str ] = getattr (app .env , INLINE_TAB_DOCNAMES )
75
+ if app .env .docname in tab_docnames :
135
76
logger .debug (f"{ LOG_PREFIX } doctree_read: { app .env .docname } has tabs" )
136
77
137
- # Store the original toctree structure before replacing it
138
- original_toc = None
139
- if (len (app .env .tocs [app .env .docname ][0 ]) > 1 and
140
- app .env .tocs [app .env .docname ][0 ][1 ] is not None ):
141
- original_toc = app .env .tocs [app .env .docname ][0 ][1 ]
142
- logger .debug (f"{ LOG_PREFIX } doctree_read({ app .env .docname } ): preserving original toctree" )
143
-
144
78
# Generate the tab-based TOC (includes headings from within tabs)
145
- tab_based_toc : nodes .list_item = sectiondata_to_toc (
79
+ updated_tocs : nodes .list_item = sectiondata_to_toc (
146
80
app .env .docname ,
147
81
collect_sections (app .env , doctree , app .env .docname , doctree ),
148
82
)
149
-
150
- # Merge the original toctree with the tab-based TOC
151
- if original_toc is not None :
152
- logger .debug (f"{ LOG_PREFIX } doctree_read({ app .env .docname } ): merging original toctree with tab-based TOC" )
153
- updated_tocs = merge_toctrees (original_toc , tab_based_toc )
154
- else :
155
- logger .debug (f"{ LOG_PREFIX } doctree_read({ app .env .docname } ): using tab-based TOC only" )
156
- updated_tocs = tab_based_toc
157
83
158
84
logger .debug (
159
85
f"{ LOG_PREFIX } doctree_read({ app .env .docname } ): updated_tocs[0][1]={ updated_tocs } "
160
86
)
161
87
if len (app .env .tocs [app .env .docname ][0 ]) == 1 :
162
88
app .env .tocs [app .env .docname ][0 ].append (updated_tocs )
163
89
else :
164
- app .env .tocs [app .env .docname ][0 ][1 ] = updated_tocs
165
- app .env .sphinx_tabs [app .env .docname ].clear ()
90
+ # SAFE FIX: Check if the original structure has toctree nodes
91
+ # If it does, leave it completely unchanged to preserve left navigation
92
+ original_toc_item = app .env .tocs [app .env .docname ][0 ][1 ]
93
+ has_toctree = False
94
+
95
+ if hasattr (original_toc_item , 'children' ) and original_toc_item .children :
96
+ for child in original_toc_item .children :
97
+ if isinstance (child , addnodes .toctree ):
98
+ has_toctree = True
99
+ break
100
+
101
+ if has_toctree :
102
+ # Merge tab headings into the existing right-pane TOC while preserving
103
+ # the original toctree (left navigation) structure.
104
+ try :
105
+ # Find the bullet list in the original toc item (holds section entries)
106
+ original_bullets = None
107
+ for child in getattr (original_toc_item , "children" , []) or []:
108
+ if isinstance (child , nodes .bullet_list ):
109
+ original_bullets = child
110
+ break
111
+
112
+ # Find the bullet list produced by our updated tab-aware TOC
113
+ updated_bullets = None
114
+ for child in getattr (updated_tocs , "children" , []) or []:
115
+ if isinstance (child , nodes .bullet_list ):
116
+ updated_bullets = child
117
+ break
118
+
119
+ # If we have tab headings to merge
120
+ if updated_bullets is not None :
121
+ if original_bullets is None :
122
+ # No existing bullets: attach updated list directly
123
+ original_toc_item .append (updated_bullets )
124
+ else :
125
+ # Append each new tab heading item to existing bullets
126
+ for li in list (updated_bullets .children ):
127
+ original_bullets .append (li )
128
+ except Exception as e :
129
+ logger .warning (
130
+ f"{ LOG_PREFIX } doctree_read({ app .env .docname } ): failed merging tab headings into TOC: { e } "
131
+ )
132
+ else :
133
+ # Only apply tab modifications if no toctree nodes are present
134
+ app .env .tocs [app .env .docname ][0 ][1 ] = updated_tocs
166
135
167
136
168
137
def html_page_context (
@@ -182,8 +151,9 @@ def html_page_context(
182
151
:param doctree:
183
152
:return:
184
153
"""
185
- if hasattr (app .env , "sphinx_tabs" ):
186
- if pagename in app .env .sphinx_tabs :
154
+ if hasattr (app .env , INLINE_TAB_DOCNAMES ):
155
+ tab_docnames : list [str ] = getattr (app .env , INLINE_TAB_DOCNAMES )
156
+ if pagename in tab_docnames :
187
157
logger .debug (
188
158
f"{ LOG_PREFIX } html_page_context: { pagename } ; context.keys()={ context .keys ()} "
189
159
)
0 commit comments