@@ -118,6 +118,7 @@ def __getattr__(self, name: str):
118118 "current_node" ,
119119 "reporter" ,
120120 "language_module_rst" ,
121+ "_heading_offset" ,
121122 "_level_to_section" ,
122123 ):
123124 raise AttributeError (
@@ -142,6 +143,7 @@ def setup_render(
142143 self .language_module_rst : ModuleType = get_language_rst (
143144 self .document .settings .language_code
144145 )
146+ self ._heading_offset : int = 0
145147 # a mapping of heading levels to its currently associated node
146148 self ._level_to_section : dict [int , nodes .document | nodes .section ] = {
147149 0 : self .document
@@ -324,13 +326,15 @@ def nested_render_text(
324326 lineno : int ,
325327 inline : bool = False ,
326328 temp_root_node : None | nodes .Element = None ,
329+ heading_offset : int = 0 ,
327330 ) -> None :
328331 """Render unparsed text (appending to the current node).
329332
330333 :param text: the text to render
331334 :param lineno: the starting line number of the text, within the full source
332335 :param inline: whether the text is inline or block
333336 :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
334338 """
335339 tokens = (
336340 self .md .parseInline (text , self .md_env )
@@ -347,22 +351,27 @@ def nested_render_text(
347351 if token .map :
348352 token .map = [token .map [0 ] + lineno , token .map [1 ] + lineno ]
349353
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 )
360365 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 :
363369 self .md_env ["temp_root_node" ] = current_root_node
364370 self ._level_to_section = current_level_to_section
365371
372+ with _restore ():
373+ self ._render_tokens (tokens )
374+
366375 @contextmanager
367376 def current_node_context (
368377 self , node : nodes .Element , append : bool = False
@@ -826,7 +835,7 @@ def generate_heading_target(
826835 def render_heading (self , token : SyntaxTreeNode ) -> None :
827836 """Render a heading, e.g. `# Heading`."""
828837
829- level = int (token .tag [1 ])
838+ level = int (token .tag [1 ]) + self . _heading_offset
830839
831840 # sections are only allowed as a parent of a document or another section
832841 # the only exception to this, is if a directive has called a nested parse,
@@ -1667,6 +1676,7 @@ def run_directive(
16671676 # to allow for altering relative image reference links
16681677 directive_class .option_spec ["relative-images" ] = directives .flag
16691678 directive_class .option_spec ["relative-docs" ] = directives .path
1679+ directive_class .option_spec ["heading-offset" ] = directives .nonnegative_int
16701680
16711681 try :
16721682 parsed = parse_directive_text (directive_class , first_line , content )
0 commit comments