@@ -146,8 +146,8 @@ def setup_render(
146146 self ._level_to_section : dict [int , nodes .document | nodes .section ] = {
147147 0 : self .document
148148 }
149- # mapping of section slug to section node
150- self ._slug_to_section : dict [str , nodes . section ] = {}
149+ # mapping of section slug to (line, id, implicit_text)
150+ self ._heading_slugs : dict [str , tuple [ int | None , str , str ] ] = {}
151151
152152 @property
153153 def sphinx_env (self ) -> BuildEnvironment | None :
@@ -249,13 +249,11 @@ def _render_finalise(self) -> None:
249249 """Finalise the render of the document."""
250250
251251 # save for later reference resolution
252- slugs = {
253- slug : (snode .line , snode ["ids" ][0 ], clean_astext (snode [0 ]))
254- for slug , snode in self ._slug_to_section .items ()
255- }
256- self .document .myst_slugs = slugs
257- if slugs and self .sphinx_env :
258- self .sphinx_env .metadata [self .sphinx_env .docname ]["myst_slugs" ] = slugs
252+ self .document .myst_slugs = self ._heading_slugs
253+ if self ._heading_slugs and self .sphinx_env :
254+ self .sphinx_env .metadata [self .sphinx_env .docname ][
255+ "myst_slugs"
256+ ] = self ._heading_slugs
259257
260258 # log warnings for duplicate reference definitions
261259 # "duplicate_refs": [{"href": "ijk", "label": "B", "map": [4, 5], "title": ""}],
@@ -779,6 +777,52 @@ def blocks_mathjax_processing(self) -> bool:
779777 and self .md_config .update_mathjax
780778 )
781779
780+ def generate_heading_target (
781+ self ,
782+ token : SyntaxTreeNode ,
783+ level : int ,
784+ node : nodes .Element ,
785+ title_node : nodes .Element ,
786+ ) -> None :
787+ """Generate a heading target, and add it to the document."""
788+
789+ implicit_text = clean_astext (title_node )
790+
791+ # create a target reference for the section, based on the heading text.
792+ # Note, this is an implicit target, meaning that it is not prioritised,
793+ # during ref resolution, and is not stored in the document.
794+ # TODO this is purely to mimic docutils, but maybe we don't need it?
795+ # (since we have the slugify logic below)
796+ name = nodes .fully_normalize_name (implicit_text )
797+ node ["names" ].append (name )
798+ self .document .note_implicit_target (node , node )
799+
800+ if level > self .md_config .heading_anchors :
801+ return
802+
803+ # Create an implicit reference slug.
804+ # The problem with this reference slug,
805+ # is that it might not be in the "normalised" format required by docutils,
806+ # https://docutils.sourceforge.io/docs/ref/rst/restructuredtext.html#normalized-reference-names
807+ # so we store it separately, and have separate logic than docutils
808+ # TODO maybe revisit this assumption, or improve the logic
809+ try :
810+ slug = compute_unique_slug (
811+ token ,
812+ self ._heading_slugs ,
813+ self .md_config .heading_slug_func ,
814+ )
815+ except Exception as error :
816+ self .create_warning (
817+ str (error ),
818+ MystWarnings .HEADING_SLUG ,
819+ line = token_line (token , default = 0 ),
820+ append_to = self .current_node ,
821+ )
822+ else :
823+ node ["slug" ] = slug
824+ self ._heading_slugs [slug ] = (node .line , node ["ids" ][0 ], implicit_text )
825+
782826 def render_heading (self , token : SyntaxTreeNode ) -> None :
783827 """Render a heading, e.g. `# Heading`."""
784828
@@ -802,6 +846,7 @@ def render_heading(self, token: SyntaxTreeNode) -> None:
802846 self .copy_attributes (token , rubric , ("class" , "id" ))
803847 with self .current_node_context (rubric , append = True ):
804848 self .render_children (token )
849+ self .generate_heading_target (token , level , rubric , rubric )
805850 return
806851
807852 # create the section node
@@ -825,39 +870,7 @@ def render_heading(self, token: SyntaxTreeNode) -> None:
825870 with self .current_node_context (title_node ):
826871 self .render_children (token )
827872
828- # create a target reference for the section, based on the heading text.
829- # Note, this is an implicit target, meaning that it is not prioritised,
830- # during ref resolution, and is not stored in the document.
831- # TODO this is purely to mimic docutils, but maybe we don't need it?
832- # (since we have the slugify logic below)
833- name = nodes .fully_normalize_name (title_node .astext ())
834- new_section ["names" ].append (name )
835- self .document .note_implicit_target (new_section , new_section )
836-
837- if level <= self .md_config .heading_anchors :
838-
839- # Create an implicit reference slug.
840- # The problem with this reference slug,
841- # is that it might not be in the "normalised" format required by docutils,
842- # https://docutils.sourceforge.io/docs/ref/rst/restructuredtext.html#normalized-reference-names
843- # so we store it separately, and have separate logic than docutils
844- # TODO maybe revisit this assumption, or improve the logic
845- try :
846- slug = compute_unique_slug (
847- token ,
848- self ._slug_to_section ,
849- self .md_config .heading_slug_func ,
850- )
851- except Exception as error :
852- self .create_warning (
853- str (error ),
854- MystWarnings .HEADING_SLUG ,
855- line = token_line (token , default = 0 ),
856- append_to = self .current_node ,
857- )
858- else :
859- new_section ["slug" ] = slug
860- self ._slug_to_section [slug ] = new_section
873+ self .generate_heading_target (token , level , new_section , title_node )
861874
862875 # set the section as the current node for subsequent rendering
863876 self .current_node = new_section
0 commit comments