@@ -146,8 +146,8 @@ def setup_render(
146
146
self ._level_to_section : dict [int , nodes .document | nodes .section ] = {
147
147
0 : self .document
148
148
}
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 ] ] = {}
151
151
152
152
@property
153
153
def sphinx_env (self ) -> BuildEnvironment | None :
@@ -249,13 +249,11 @@ def _render_finalise(self) -> None:
249
249
"""Finalise the render of the document."""
250
250
251
251
# 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
259
257
260
258
# log warnings for duplicate reference definitions
261
259
# "duplicate_refs": [{"href": "ijk", "label": "B", "map": [4, 5], "title": ""}],
@@ -779,6 +777,52 @@ def blocks_mathjax_processing(self) -> bool:
779
777
and self .md_config .update_mathjax
780
778
)
781
779
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
+
782
826
def render_heading (self , token : SyntaxTreeNode ) -> None :
783
827
"""Render a heading, e.g. `# Heading`."""
784
828
@@ -802,6 +846,7 @@ def render_heading(self, token: SyntaxTreeNode) -> None:
802
846
self .copy_attributes (token , rubric , ("class" , "id" ))
803
847
with self .current_node_context (rubric , append = True ):
804
848
self .render_children (token )
849
+ self .generate_heading_target (token , level , rubric , rubric )
805
850
return
806
851
807
852
# create the section node
@@ -825,39 +870,7 @@ def render_heading(self, token: SyntaxTreeNode) -> None:
825
870
with self .current_node_context (title_node ):
826
871
self .render_children (token )
827
872
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 )
861
874
862
875
# set the section as the current node for subsequent rendering
863
876
self .current_node = new_section
0 commit comments