Skip to content

Commit 08ea5fe

Browse files
committed
refactor: Store actual page instance instead of URL in plugin's current_page attribute
We will need the page instance for the backlinks feature, to build breadcrumbs with titles and URLs of the parents of each backlink.
1 parent 502832e commit 08ea5fe

File tree

5 files changed

+67
-30
lines changed

5 files changed

+67
-30
lines changed

src/mkdocs_autorefs/plugin.py

Lines changed: 33 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ class AutorefsPlugin(BasePlugin[AutorefsConfig]):
9999
"""
100100

101101
scan_toc: bool = True
102-
current_page: str | None = None
102+
current_page: Page | None = None
103103
# YORE: Bump 2: Remove line.
104104
legacy_refs: bool = True
105105

@@ -138,6 +138,8 @@ def __init__(self) -> None:
138138
self._abs_url_map: dict[str, str] = {}
139139
# YORE: Bump 2: Remove line.
140140
self._get_fallback_anchor: Callable[[str], tuple[str, ...]] | None = None
141+
# YORE: Bump 2: Remove line.
142+
self._url_to_page: dict[str, Page] = {}
141143

142144
self._link_titles: bool | Literal["external"] = True
143145
self._strip_title_tags: bool = False
@@ -162,7 +164,7 @@ def get_fallback_anchor(self, value: Callable[[str], tuple[str, ...]] | None) ->
162164

163165
def register_anchor(
164166
self,
165-
page: str,
167+
page: Page,
166168
identifier: str,
167169
anchor: str | None = None,
168170
*,
@@ -172,18 +174,26 @@ def register_anchor(
172174
"""Register that an anchor corresponding to an identifier was encountered when rendering the page.
173175
174176
Arguments:
175-
page: The relative URL of the current page. Examples: `'foo/bar/'`, `'foo/index.html'`
177+
page: The page where the anchor was found.
176178
identifier: The identifier to register.
177179
anchor: The anchor on the page, without `#`. If not provided, defaults to the identifier.
178180
title: The title of the anchor (optional).
179181
primary: Whether this anchor is the primary one for the identifier.
180182
"""
181-
page_anchor = f"{page}#{anchor or identifier}"
183+
# YORE: Bump 2: Remove block.
184+
if isinstance(page, str):
185+
try:
186+
page = self._url_to_page[page]
187+
except KeyError:
188+
page = self.current_page
189+
190+
url = f"{page.url}#{anchor or identifier}"
182191
url_map = self._primary_url_map if primary else self._secondary_url_map
183192
if identifier in url_map:
184-
if page_anchor not in url_map[identifier]:
185-
url_map[identifier].append(page_anchor)
193+
if url not in url_map[identifier]:
194+
url_map[identifier].append(url)
186195
else:
196+
url_map[identifier] = [url]
187197
if title and url not in self._title_map:
188198
self._title_map[url] = title
189199

@@ -345,7 +355,9 @@ def on_page_markdown(self, markdown: str, page: Page, **kwargs: Any) -> str: #
345355
The same Markdown. We only use this hook to keep a reference to the current page URL,
346356
used during Markdown conversion by the anchor scanner tree processor.
347357
"""
348-
self.current_page = page.url
358+
# YORE: Bump 2: Remove line.
359+
self._url_to_page[page.url] = page
360+
self.current_page = page
349361
return markdown
350362

351363
def on_page_content(self, html: str, page: Page, **kwargs: Any) -> str: # noqa: ARG002
@@ -364,24 +376,33 @@ def on_page_content(self, html: str, page: Page, **kwargs: Any) -> str: # noqa:
364376
Returns:
365377
The same HTML. We only use this hook to map anchors to URLs.
366378
"""
379+
self.current_page = page
380+
# Collect `std`-domain URLs.
367381
if self.scan_toc:
368382
log.debug("Mapping identifiers to URLs for page %s", page.file.src_path)
369383
for item in page.toc.items:
370-
self.map_urls(page.url, item)
384+
self.map_urls(page, item)
371385
return html
372386

373-
def map_urls(self, base_url: str, anchor: AnchorLink) -> None:
387+
def map_urls(self, page: Page, anchor: AnchorLink) -> None:
374388
"""Recurse on every anchor to map its ID to its absolute URL.
375389
376390
This method populates `self._primary_url_map` by side-effect.
377391
378392
Arguments:
379-
base_url: The base URL to use as a prefix for each anchor's relative URL.
393+
page: The page containing the anchors.
380394
anchor: The anchor to process and to recurse on.
381395
"""
382-
self.register_anchor(base_url, anchor.id, title=anchor.title, primary=True)
396+
# YORE: Bump 2: Remove block.
397+
if isinstance(page, str):
398+
try:
399+
page = self._url_to_page[page]
400+
except KeyError:
401+
page = self.current_page
402+
403+
self.register_anchor(page, anchor.id, title=anchor.title, primary=True)
383404
for child in anchor.children:
384-
self.map_urls(base_url, child)
405+
self.map_urls(page, child)
385406

386407
@event_priority(-50) # Late, after mkdocstrings has finished loading inventories.
387408
def on_env(self, env: Environment, /, *, config: MkDocsConfig, files: Files) -> Environment: # noqa: ARG002

src/mkdocs_autorefs/references.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -542,7 +542,7 @@ def __init__(self, plugin: AutorefsPlugin, md: Markdown | None = None) -> None:
542542

543543
def run(self, root: Element) -> None: # noqa: D102
544544
if self.plugin.current_page is not None:
545-
pending_anchors = _PendingAnchors(self.plugin, self.plugin.current_page)
545+
pending_anchors = _PendingAnchors(self.plugin)
546546
self._scan_anchors(root, pending_anchors)
547547
pending_anchors.flush()
548548

@@ -580,18 +580,18 @@ def _scan_anchors(self, parent: Element, pending_anchors: _PendingAnchors, last_
580580
class _PendingAnchors:
581581
"""A collection of HTML anchors that may or may not become aliased to an upcoming heading."""
582582

583-
def __init__(self, plugin: AutorefsPlugin, current_page: str):
583+
def __init__(self, plugin: AutorefsPlugin):
584584
self.plugin = plugin
585-
self.current_page = current_page
586585
self.anchors: list[str] = []
587586

588587
def append(self, anchor: str) -> None:
589588
self.anchors.append(anchor)
590589

591590
def flush(self, alias_to: str | None = None, title: str | None = None) -> None:
592-
for anchor in self.anchors:
593-
self.plugin.register_anchor(self.current_page, anchor, alias_to, title=title, primary=True)
594-
self.anchors.clear()
591+
if page := self.plugin.current_page:
592+
for anchor in self.anchors:
593+
self.plugin.register_anchor(page, anchor, alias_to, title=title, primary=True)
594+
self.anchors.clear()
595595

596596

597597
@lru_cache

tests/helpers.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
"""Helper functions for the tests."""
2+
3+
from mkdocs.config.defaults import MkDocsConfig
4+
from mkdocs.structure.files import File
5+
from mkdocs.structure.pages import Page
6+
7+
8+
def create_page(url: str) -> Page:
9+
"""Create a page with the given URL."""
10+
return Page(
11+
title=url,
12+
file=File(url, "docs", "site", use_directory_urls=False),
13+
config=MkDocsConfig(),
14+
)

tests/test_plugin.py

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,13 @@
1111

1212
from mkdocs_autorefs.plugin import AutorefsConfig, AutorefsPlugin
1313
from mkdocs_autorefs.references import fix_refs
14+
from tests.helpers import create_page
1415

1516

1617
def test_url_registration() -> None:
1718
"""Check that URLs can be registered, then obtained."""
1819
plugin = AutorefsPlugin()
19-
plugin.register_anchor(identifier="foo", page="foo1.html", primary=True)
20+
plugin.register_anchor(identifier="foo", page=create_page("foo1.html"), primary=True)
2021
plugin.register_url(identifier="bar", url="https://example.org/bar.html")
2122

2223
assert plugin.get_item_url("foo") == ("foo1.html#foo", None)
@@ -28,7 +29,7 @@ def test_url_registration() -> None:
2829
def test_url_registration_with_from_url() -> None:
2930
"""Check that URLs can be registered, then obtained, relative to a page."""
3031
plugin = AutorefsPlugin()
31-
plugin.register_anchor(identifier="foo", page="foo1.html", primary=True)
32+
plugin.register_anchor(identifier="foo", page=create_page("foo1.html"), primary=True)
3233
plugin.register_url(identifier="bar", url="https://example.org/bar.html")
3334

3435
assert plugin.get_item_url("foo", from_url="a/b.html") == ("../foo1.html#foo", None)
@@ -41,7 +42,7 @@ def test_url_registration_with_from_url() -> None:
4142
def test_url_registration_with_fallback() -> None:
4243
"""Check that URLs can be registered, then obtained through a fallback."""
4344
plugin = AutorefsPlugin()
44-
plugin.register_anchor(identifier="foo", page="foo1.html", primary=True)
45+
plugin.register_anchor(identifier="foo", page=create_page("foo1.html"), primary=True)
4546
plugin.register_url(identifier="bar", url="https://example.org/bar.html")
4647

4748
# URL map will be updated with baz -> foo1.html#foo
@@ -60,7 +61,7 @@ def test_url_registration_with_fallback() -> None:
6061
def test_dont_make_relative_urls_relative_again() -> None:
6162
"""Check that URLs are not made relative more than once."""
6263
plugin = AutorefsPlugin()
63-
plugin.register_anchor(identifier="foo.bar.baz", page="foo/bar/baz.html", primary=True)
64+
plugin.register_anchor(identifier="foo.bar.baz", page=create_page("foo/bar/baz.html"), primary=True)
6465

6566
for _ in range(2):
6667
assert plugin.get_item_url("foo.bar.baz", from_url="baz/bar/foo.html") == (
@@ -96,7 +97,7 @@ def test_find_closest_url(base: str, urls: list[str], expected: str) -> None:
9697
def test_register_secondary_url() -> None:
9798
"""Test registering secondary URLs."""
9899
plugin = AutorefsPlugin()
99-
plugin.register_anchor(identifier="foo", page="foo.html", primary=False)
100+
plugin.register_anchor(identifier="foo", page=create_page("foo.html"), primary=False)
100101
assert plugin._secondary_url_map == {"foo": ["foo.html#foo"]}
101102

102103

@@ -105,8 +106,8 @@ def test_warn_multiple_urls(caplog: pytest.LogCaptureFixture, primary: bool) ->
105106
"""Warn when multiple URLs are found for the same identifier."""
106107
plugin = AutorefsPlugin()
107108
plugin.config = AutorefsConfig()
108-
plugin.register_anchor(identifier="foo", page="foo.html", primary=primary)
109-
plugin.register_anchor(identifier="foo", page="bar.html", primary=primary)
109+
plugin.register_anchor(identifier="foo", page=create_page("foo.html"), primary=primary)
110+
plugin.register_anchor(identifier="foo", page=create_page("bar.html"), primary=primary)
110111
url_mapper = functools.partial(plugin.get_item_url, from_url="/hello")
111112
# YORE: Bump 2: Replace `, _legacy_refs=False` with `` within line.
112113
fix_refs('<autoref identifier="foo">Foo</autoref>', url_mapper, _legacy_refs=False)
@@ -120,8 +121,8 @@ def test_use_closest_url(caplog: pytest.LogCaptureFixture, primary: bool) -> Non
120121
plugin = AutorefsPlugin()
121122
plugin.config = AutorefsConfig()
122123
plugin.config.resolve_closest = True
123-
plugin.register_anchor(identifier="foo", page="foo.html", primary=primary)
124-
plugin.register_anchor(identifier="foo", page="bar.html", primary=primary)
124+
plugin.register_anchor(identifier="foo", page=create_page("foo.html"), primary=primary)
125+
plugin.register_anchor(identifier="foo", page=create_page("bar.html"), primary=primary)
125126
url_mapper = functools.partial(plugin.get_item_url, from_url="/hello")
126127
# YORE: Bump 2: Replace `, _legacy_refs=False` with `` within line.
127128
fix_refs('<autoref identifier="foo">Foo</autoref>', url_mapper, _legacy_refs=False)

tests/test_references.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
from mkdocs_autorefs.plugin import AutorefsPlugin
1212
from mkdocs_autorefs.references import AutorefsExtension, AutorefsHookInterface, fix_refs, relative_url
13+
from tests.helpers import create_page
1314

1415
if TYPE_CHECKING:
1516
from collections.abc import Mapping
@@ -302,7 +303,7 @@ def test_register_markdown_anchors() -> None:
302303
"""Check that Markdown anchors are registered when enabled."""
303304
plugin = AutorefsPlugin()
304305
md = markdown.Markdown(extensions=["attr_list", "toc", AutorefsExtension(plugin)])
305-
plugin.current_page = "page"
306+
plugin.current_page = create_page("page")
306307
md.convert(
307308
dedent(
308309
"""
@@ -363,7 +364,7 @@ def test_register_markdown_anchors_with_admonition() -> None:
363364
"""Check that Markdown anchors are registered inside a nested admonition element."""
364365
plugin = AutorefsPlugin()
365366
md = markdown.Markdown(extensions=["attr_list", "toc", "admonition", AutorefsExtension(plugin)])
366-
plugin.current_page = "page"
367+
plugin.current_page = create_page("page")
367368
md.convert(
368369
dedent(
369370
"""
@@ -430,7 +431,7 @@ def test_mark_identifiers_as_exact(markdown_ref: str, exact_expected: bool) -> N
430431
"""Mark code and explicit identifiers as exact (no `slug` attribute in autoref elements)."""
431432
plugin = AutorefsPlugin()
432433
md = markdown.Markdown(extensions=["attr_list", "toc", AutorefsExtension(plugin)])
433-
plugin.current_page = "page"
434+
plugin.current_page = create_page("page")
434435
output = md.convert(markdown_ref)
435436
if exact_expected:
436437
assert "slug=" not in output

0 commit comments

Comments
 (0)