@@ -118,6 +118,7 @@ def __getattr__(self, name: str):
118
118
"current_node" ,
119
119
"reporter" ,
120
120
"language_module_rst" ,
121
+ "_heading_offset" ,
121
122
"_level_to_section" ,
122
123
):
123
124
raise AttributeError (
@@ -142,6 +143,7 @@ def setup_render(
142
143
self .language_module_rst : ModuleType = get_language_rst (
143
144
self .document .settings .language_code
144
145
)
146
+ self ._heading_offset : int = 0
145
147
# a mapping of heading levels to its currently associated node
146
148
self ._level_to_section : dict [int , nodes .document | nodes .section ] = {
147
149
0 : self .document
@@ -324,13 +326,15 @@ def nested_render_text(
324
326
lineno : int ,
325
327
inline : bool = False ,
326
328
temp_root_node : None | nodes .Element = None ,
329
+ heading_offset : int = 0 ,
327
330
) -> None :
328
331
"""Render unparsed text (appending to the current node).
329
332
330
333
:param text: the text to render
331
334
:param lineno: the starting line number of the text, within the full source
332
335
:param inline: whether the text is inline or block
333
336
:param temp_root_node: If set, allow sections to be created as children of this node
337
+ :param heading_offset: offset heading levels by this amount
334
338
"""
335
339
tokens = (
336
340
self .md .parseInline (text , self .md_env )
@@ -347,22 +351,27 @@ def nested_render_text(
347
351
if token .map :
348
352
token .map = [token .map [0 ] + lineno , token .map [1 ] + lineno ]
349
353
350
- if temp_root_node is None :
351
- self ._render_tokens (tokens )
352
- else :
353
- # we need to temporarily set the root node,
354
- # and we also want to restore the level_to_section mapping at the end
355
- current_level_to_section = {
356
- i : node for i , node in self ._level_to_section .items ()
357
- }
358
- current_root_node = self .md_env .get ("temp_root_node" , None )
359
- try :
354
+ @contextmanager
355
+ def _restore ():
356
+ current_heading_offset = self ._heading_offset
357
+ self ._heading_offset = heading_offset
358
+ if temp_root_node is not None :
359
+ # we need to temporarily set the root node,
360
+ # and we also want to restore the level_to_section mapping at the end
361
+ current_level_to_section = {
362
+ i : node for i , node in self ._level_to_section .items ()
363
+ }
364
+ current_root_node = self .md_env .get ("temp_root_node" , None )
360
365
self .md_env ["temp_root_node" ] = temp_root_node
361
- self ._render_tokens (tokens )
362
- finally :
366
+ yield
367
+ self ._heading_offset = current_heading_offset
368
+ if temp_root_node is not None :
363
369
self .md_env ["temp_root_node" ] = current_root_node
364
370
self ._level_to_section = current_level_to_section
365
371
372
+ with _restore ():
373
+ self ._render_tokens (tokens )
374
+
366
375
@contextmanager
367
376
def current_node_context (
368
377
self , node : nodes .Element , append : bool = False
@@ -826,7 +835,7 @@ def generate_heading_target(
826
835
def render_heading (self , token : SyntaxTreeNode ) -> None :
827
836
"""Render a heading, e.g. `# Heading`."""
828
837
829
- level = int (token .tag [1 ])
838
+ level = int (token .tag [1 ]) + self . _heading_offset
830
839
831
840
# sections are only allowed as a parent of a document or another section
832
841
# the only exception to this, is if a directive has called a nested parse,
@@ -1667,6 +1676,7 @@ def run_directive(
1667
1676
# to allow for altering relative image reference links
1668
1677
directive_class .option_spec ["relative-images" ] = directives .flag
1669
1678
directive_class .option_spec ["relative-docs" ] = directives .path
1679
+ directive_class .option_spec ["heading-offset" ] = directives .nonnegative_int
1670
1680
1671
1681
try :
1672
1682
parsed = parse_directive_text (directive_class , first_line , content )
0 commit comments