Skip to content

Commit 7fe6411

Browse files
authored
👌 Improve: Apply implicit targets to nested headings (#718)
This commit builds on ac111ea, to allow for implicit heading slugs to be applied also to nested headings, i.e. so they can be referenced in Markdown links
1 parent 7ecf1bb commit 7fe6411

File tree

3 files changed

+57
-44
lines changed

3 files changed

+57
-44
lines changed

myst_parser/mdit_to_docutils/base.py

Lines changed: 55 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -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

tests/test_renderers/fixtures/docutil_syntax_elements.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ Nested heading
117117
.
118118
<document source="notset">
119119
<block_quote>
120-
<rubric level="1">
120+
<rubric ids="heading" level="1" names="heading">
121121
heading
122122
.
123123

tests/test_renderers/fixtures/sphinx_syntax_elements.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ Nested heading
117117
.
118118
<document source="<src>/index.md">
119119
<block_quote>
120-
<rubric level="1">
120+
<rubric ids="heading" level="1" names="heading">
121121
heading
122122
.
123123

0 commit comments

Comments
 (0)