Skip to content

Commit 70fec3e

Browse files
committedFeb 23, 2025
refactor: Use the on_env hook to fix cross-references
Previously, we used the `on_post_page` hook to fix cross-references. This was problematic because it cornered us to the last per-page event, making it difficult to coordinate ourselves with other plugins such as mkdocstrings. Fixing cross-references must be done once all pages have been rendered to HTML. The earliest we can do that is in `on_env`, which acts as a synchronization barrier here. We coordinate the logic with mkdocstrings thanks to event priorities: autorefs' `on_env` must run after mkdocstrings has finished loading inventories, so that all cross-references can be fixed. Since mkdocstrings uses the default priority, 0, our -50 priority makes us run last. Discussion-mkdocs-3917: mkdocs/mkdocs#3917
1 parent 791782e commit 70fec3e

File tree

2 files changed

+43
-34
lines changed

2 files changed

+43
-34
lines changed
 

‎src/mkdocs_autorefs/plugin.py

+42-33
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,9 @@
22
33
After each page is processed by the Markdown converter, this plugin stores absolute URLs of every HTML anchors
44
it finds to later be able to fix unresolved references.
5-
It stores them during the [`on_page_content` event hook](https://www.mkdocs.org/user-guide/plugins/#on_page_content).
65
7-
Just before writing the final HTML to the disc, during the
8-
[`on_post_page` event hook](https://www.mkdocs.org/user-guide/plugins/#on_post_page),
9-
this plugin searches for references of the form `[identifier][]` or `[title][identifier]` that were not resolved,
10-
and fixes them using the previously stored identifier-URL mapping.
6+
Once every page has been rendered and all identifiers and their URLs collected,
7+
the plugin fixes unresolved references in the HTML content of the pages.
118
"""
129

1310
from __future__ import annotations
@@ -22,15 +19,17 @@
2219

2320
from mkdocs.config.base import Config
2421
from mkdocs.config.config_options import Type
25-
from mkdocs.plugins import BasePlugin
22+
from mkdocs.plugins import BasePlugin, event_priority
2623
from mkdocs.structure.pages import Page
2724

2825
from mkdocs_autorefs.references import AutorefsExtension, fix_refs, relative_url
2926

3027
if TYPE_CHECKING:
3128
from collections.abc import Sequence
3229

30+
from jinja2.environment import Environment
3331
from mkdocs.config.defaults import MkDocsConfig
32+
from mkdocs.structure.files import Files
3433
from mkdocs.structure.pages import Page
3534
from mkdocs.structure.toc import AnchorLink
3635

@@ -67,9 +66,9 @@ class AutorefsPlugin(BasePlugin[AutorefsConfig]):
6766
6867
This plugin defines the following event hooks:
6968
70-
- `on_config`
71-
- `on_page_content`
72-
- `on_post_page`
69+
- `on_config`, to configure itself
70+
- `on_page_markdown`, to set the current page in order for Markdown extension to use it
71+
- `on_env`, to apply cross-references once all pages have been rendered
7372
7473
Check the [Developing Plugins](https://www.mkdocs.org/user-guide/plugins/#developing-plugins) page of `mkdocs`
7574
for more information about its plugin system.
@@ -337,37 +336,47 @@ def map_urls(self, base_url: str, anchor: AnchorLink) -> None:
337336
for child in anchor.children:
338337
self.map_urls(base_url, child)
339338

340-
def on_post_page(self, output: str, page: Page, **kwargs: Any) -> str: # noqa: ARG002
341-
"""Fix cross-references.
339+
@event_priority(-50) # Late, after mkdocstrings has finished loading inventories.
340+
def on_env(self, env: Environment, /, *, config: MkDocsConfig, files: Files) -> Environment: # noqa: ARG002
341+
"""Apply cross-references.
342342
343-
Hook for the [`on_post_page` event](https://www.mkdocs.org/user-guide/plugins/#on_post_page).
343+
Hook for the [`on_env` event](https://www.mkdocs.org/user-guide/plugins/#on_env).
344344
In this hook, we try to fix unresolved references of the form `[title][identifier]` or `[identifier][]`.
345345
Doing that allows the user of `autorefs` to cross-reference objects in their documentation strings.
346346
It uses the native Markdown syntax so it's easy to remember and use.
347347
348-
We log a warning for each reference that we couldn't map to an URL, but try to be smart and ignore identifiers
349-
that do not look legitimate (sometimes documentation can contain strings matching
350-
our [`AUTO_REF_RE`][mkdocs_autorefs.references.AUTO_REF_RE] regular expression that did not intend to reference anything).
351-
We currently ignore references when their identifier contains a space or a slash.
348+
We log a warning for each reference that we couldn't map to an URL.
352349
353350
Arguments:
354-
output: HTML converted from Markdown.
355-
page: The related MkDocs page instance.
356-
kwargs: Additional arguments passed by MkDocs.
351+
env: The MkDocs environment.
352+
config: The MkDocs config object.
353+
files: The list of files in the MkDocs project.
357354
358355
Returns:
359-
Modified HTML.
356+
The unmodified environment.
360357
"""
361-
log.debug("Fixing references in page %s", page.file.src_path)
362-
363-
# YORE: Bump 2: Replace `, fallback=self.get_fallback_anchor` with `` within line.
364-
url_mapper = functools.partial(self.get_item_url, from_url=page.url, fallback=self.get_fallback_anchor)
365-
# YORE: Bump 2: Replace `, _legacy_refs=self.legacy_refs` with `` within line.
366-
fixed_output, unmapped = fix_refs(output, url_mapper, _legacy_refs=self.legacy_refs)
367-
368-
if unmapped and log.isEnabledFor(logging.WARNING):
369-
for ref, context in unmapped:
370-
message = f"from {context.filepath}:{context.lineno}: ({context.origin}) " if context else ""
371-
log.warning(f"{page.file.src_path}: {message}Could not find cross-reference target '{ref}'")
372-
373-
return fixed_output
358+
for file in files:
359+
if file.page and file.page.content:
360+
log.debug("Applying cross-refs in page %s", file.page.file.src_path)
361+
362+
# YORE: Bump 2: Replace `, fallback=self.get_fallback_anchor` with `` within line.
363+
url_mapper = functools.partial(
364+
self.get_item_url,
365+
from_url=file.page.url,
366+
fallback=self.get_fallback_anchor,
367+
)
368+
# YORE: Bump 2: Replace `, _legacy_refs=self.legacy_refs` with `` within line.
369+
file.page.content, unmapped = fix_refs(
370+
file.page.content,
371+
url_mapper,
372+
_legacy_refs=self.legacy_refs,
373+
)
374+
375+
if unmapped and log.isEnabledFor(logging.WARNING):
376+
for ref, context in unmapped:
377+
message = f"from {context.filepath}:{context.lineno}: ({context.origin}) " if context else ""
378+
log.warning(
379+
f"{file.page.file.src_path}: {message}Could not find cross-reference target '{ref}'",
380+
)
381+
382+
return env

‎src/mkdocs_autorefs/references.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ def __getattr__(name: str) -> Any:
6262
"""The autoref HTML tag regular expression.
6363
6464
A regular expression to match mkdocs-autorefs' special reference markers
65-
in the [`on_post_page` hook][mkdocs_autorefs.plugin.AutorefsPlugin.on_post_page].
65+
in the [`on_env` hook][mkdocs_autorefs.plugin.AutorefsPlugin.on_env].
6666
"""
6767

6868

0 commit comments

Comments
 (0)
Please sign in to comment.