Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: Backup anchors without hrefs, for compatibility with autorefs' Markdown anchors #651

Merged
merged 9 commits into from Feb 22, 2024
26 changes: 18 additions & 8 deletions src/mkdocstrings/handlers/rendering.py
Expand Up @@ -146,19 +146,29 @@ def __init__(self, md: Markdown, id_prefix: str):
self.id_prefix = id_prefix

def run(self, root: Element) -> None: # noqa: D102 (ignore missing docstring)
if not self.id_prefix:
return
for el in root.iter():
id_attr = el.get("id")
if id_attr:
el.set("id", self.id_prefix + id_attr)
if self.id_prefix:
self._prefix_ids(root)

def _prefix_ids(self, root: Element) -> None:
index = -1
for el in reversed(root):
pawamoy marked this conversation as resolved.
Show resolved Hide resolved
index += 1

self._prefix_ids(el)
oprypin marked this conversation as resolved.
Show resolved Hide resolved
href_attr = el.get("href")

if id_attr := el.get("id"):
if el.tag == "a" and not href_attr:
new_el = copy.deepcopy(el)
oprypin marked this conversation as resolved.
Show resolved Hide resolved
new_el.set("id", self.id_prefix + id_attr)
root.insert(index + 1, new_el)
else:
el.set("id", self.id_prefix + id_attr)

if href_attr and href_attr.startswith("#"):
el.set("href", "#" + self.id_prefix + href_attr[1:])

name_attr = el.get("name")
if name_attr:
if name_attr := el.get("name"):
el.set("name", self.id_prefix + name_attr)

if el.tag == "label":
Expand Down
14 changes: 14 additions & 0 deletions tests/fixtures/markdown_anchors.py
@@ -0,0 +1,14 @@
"""Module docstring.

[](){#anchor}

Paragraph.

[](){#heading-anchor}
## Heading

[](#has-href1)
[](#has-href2){#with-id}

Pararaph.
"""
21 changes: 21 additions & 0 deletions tests/test_extension.py
Expand Up @@ -172,3 +172,24 @@ def test_removing_duplicated_headings(ext_markdown: Markdown) -> None:
assert output.count(">Heading two<") == 1
assert output.count(">Heading three<") == 1
assert output.count('class="mkdocstrings') == 0


@pytest.mark.parametrize("ext_markdown", [{"markdown_extensions": [{"attr_list": {}}]}], indirect=["ext_markdown"])
def test_backup_of_anchors(ext_markdown: Markdown) -> None:
"""Anchors with empty `href` are backed up."""
output = ext_markdown.convert("::: tests.fixtures.markdown_anchors")

# Anchors with id and no href are been backed up and updated.
assert 'id="anchor"' in output
assert 'id="tests.fixtures.markdown_anchors--anchor"' in output
assert 'id="heading-anchor"' in output
assert 'id="tests.fixtures.markdown_anchors--heading-anchor"' in output

# Anchors with href and with or without id have been updated but not backed up.
assert 'id="tests.fixtures.markdown_anchors--with-id"' in output
assert 'id="with-id"' not in output

assert 'href="#tests.fixtures.markdown_anchors--has-href1"' in output
assert 'href="#tests.fixtures.markdown_anchors--has-href2"' in output
assert 'href="#has-href1"' not in output
assert 'href="#has-href2"' not in output