Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
pawamoy committed Feb 16, 2024
1 parent 0bf8de9 commit 017d66f
Show file tree
Hide file tree
Showing 5 changed files with 84 additions and 25 deletions.
9 changes: 9 additions & 0 deletions docs/changelog.md
Original file line number Diff line number Diff line change
@@ -1 +1,10 @@
--8<-- "CHANGELOG.md"

[](#hello){#hello2}

## Hello

Hello.

Link to [Hello 1][hello1].
Link to [Hello 2][hello2].
9 changes: 9 additions & 0 deletions docs/index.md
Original file line number Diff line number Diff line change
@@ -1 +1,10 @@
--8<-- "README.md"

[](#hello){#hello1}

## Hello

Hello.

Link to [Hello 1][hello1].
Link to [Hello 2][hello2].
45 changes: 26 additions & 19 deletions src/mkdocs_autorefs/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,23 @@
from typing import TYPE_CHECKING, Any, Callable, Sequence
from urllib.parse import urlsplit

from mkdocs.plugins import BasePlugin
from markdown.extensions import Extension
from markdown.core import Markdown
from mkdocs.config.base import Config
from mkdocs.config.config_options import Type
from mkdocs.config.defaults import MkDocsConfig
from mkdocs.plugins import BasePlugin
from markdown.treeprocessors import Treeprocessor

from mkdocs_autorefs.references import AutorefsExtension, fix_refs, relative_url

from mkdocs.structure.files import Files
from mkdocs.structure.pages import Page
from mkdocs_autorefs.references import AutorefsExtension, fix_refs, relative_url, AnchorScannerTreeProcessor
from functools import partial
if TYPE_CHECKING:
from mkdocs.config.defaults import MkDocsConfig
from mkdocs.structure.pages import Page
from mkdocs.structure.toc import AnchorLink
from xml.etree.ElementTree import Element

try:
from mkdocs.plugins import get_plugin_logger
Expand All @@ -40,7 +47,10 @@


class AutorefsConfig(Config):
"""Configuration options for the Autorefs plugin."""

scan_anchors = Type(bool, default=False)
"""Whether to scan HTML pages for anchors defining references."""


class AutorefsPlugin(BasePlugin[AutorefsConfig]):
Expand All @@ -56,16 +66,20 @@ class AutorefsPlugin(BasePlugin[AutorefsConfig]):
for more information about its plugin system.
"""

scan_anchors: bool = False
scan_toc: bool = True
scan_anchors: bool = False
current_page: str | None = None

_re_anchors = re.compile(r'<a(?:\s+href="([^"]*)")?\s+id="([^"]+)"\s*>')

def __init__(self) -> None:
"""Initialize the object."""
super().__init__()
self._url_map: dict[str, str] = {}
self._abs_url_map: dict[str, str] = {}
self._extension: AutorefsExtension | None = None
self.get_fallback_anchor: Callable[[str], str | None] | None = None
self.current_page: str | None = None

def register_anchor(self, page: str, identifier: str, anchor: str | None = None) -> None:
"""Register that an anchor corresponding to an identifier was encountered when rendering the page.
Expand Down Expand Up @@ -141,20 +155,13 @@ def on_config(self, config: MkDocsConfig) -> MkDocsConfig | None:
The modified config.
"""
log.debug("Adding AutorefsExtension to the list")
config["markdown_extensions"].append(AutorefsExtension())
anchor_scanner_factory = partial(AnchorScannerTreeProcessor, self) if self.scan_anchors or self.config.scan_anchors else None
# anchor_scanner_factory = None
self._extension = AutorefsExtension(anchor_scanner_factory=anchor_scanner_factory)
config["markdown_extensions"].append(self._extension)
return config

def on_page_markdown(self, markdown: str, page: Page, **kwargs: Any) -> str: # noqa: ARG002
"""Remember which page is the current one.
Arguments:
markdown: Input Markdown.
page: The related MkDocs page instance.
kwargs: Additional arguments passed by MkDocs.
Returns:
The same Markdown. We only use this hook to map anchors to URLs.
"""
def on_page_markdown(self, markdown: str, *, page: Page, **kwargs: Any) -> str | None:
self.current_page = page.url
return markdown

Expand All @@ -179,9 +186,9 @@ def on_page_content(self, html: str, page: Page, **kwargs: Any) -> str: # noqa:
for item in page.toc.items:
self.map_urls(page.url, item)

if self.config.scan_anchors or self.scan_anchors:
for href, id in re.findall(r'<a(?:\s+href="([^"]*)")?\s+id="([^"]+)">', html):
self.register_anchor(page.url, identifier=id, anchor=href.lstrip("#"))
# if self.scan_anchors or self.config.scan_anchors:
# for href, hid in re.findall(self._re_anchors, html):
# self.register_anchor(page.url, identifier=hid, anchor=href.lstrip("#"))

return html

Expand Down
32 changes: 32 additions & 0 deletions src/mkdocs_autorefs/references.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,15 @@
from urllib.parse import urlsplit
from xml.etree.ElementTree import Element

from markdown.core import Markdown
from markdown.extensions import Extension
from markdown.treeprocessors import Treeprocessor
from markdown.inlinepatterns import REFERENCE_RE, ReferenceInlineProcessor
from markdown.util import INLINE_PLACEHOLDER_RE

if TYPE_CHECKING:
from markdown import Markdown
from mkdocs_autorefs.plugin import AutorefsPlugin

AUTO_REF_RE = re.compile(
r"<span data-(?P<kind>autorefs-identifier|autorefs-optional|autorefs-optional-hover)="
Expand Down Expand Up @@ -197,9 +200,31 @@ def fix_refs(html: str, url_mapper: Callable[[str], str]) -> tuple[str, list[str
return html, unmapped


class AnchorScannerTreeProcessor(Treeprocessor):
def __init__(self, plugin: AutorefsPlugin, md: Markdown | None = None) -> None:
super().__init__(md)
self.plugin = plugin

def run(self, root: Element) -> None:
if self.plugin.current_page is not None:
self._scan_anchors(root)

def _scan_anchors(self, parent: Element) -> None:
for el in parent:
if el.tag == "a" and (hid := el.get("id")):
self.plugin.register_anchor(self.plugin.current_page, hid, el.get("href", "").lstrip("#"))
else:
self._scan_anchors(el)


class AutorefsExtension(Extension):
"""Extension that inserts auto-references in Markdown."""

def __init__(self, anchor_scanner_factory: Callable[[Markdown], AnchorScannerTreeProcessor] | None, **kwargs: Any) -> None:
super().__init__(**kwargs)
self.anchor_scanner_factory = anchor_scanner_factory
self.anchor_scanner: AnchorScannerTreeProcessor | None = None

def extendMarkdown(self, md: Markdown) -> None: # noqa: N802 (casing: parent method's name)
"""Register the extension.
Expand All @@ -213,3 +238,10 @@ def extendMarkdown(self, md: Markdown) -> None: # noqa: N802 (casing: parent me
"mkdocs-autorefs",
priority=168, # Right after markdown.inlinepatterns.ReferenceInlineProcessor
)
if self.anchor_scanner_factory:
self.anchor_scanner = self.anchor_scanner_factory(md)
md.treeprocessors.register(
self.anchor_scanner,
"mkdocs-autorefs-anchors-scanner",
priority=0,
)
14 changes: 8 additions & 6 deletions tests/test_plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@
import pytest

from mkdocs_autorefs.plugin import AutorefsPlugin
from mkdocs.structure.pages import Page
from mkdocs.structure.files import File


def test_url_registration() -> None:
Expand Down Expand Up @@ -68,17 +66,21 @@ def test_register_html_anchors() -> None:
"""Check that HT?ML anchors are registered when enabled."""
plugin = AutorefsPlugin()
plugin.scan_toc = False
plugin.config["scan_anchors"] = plugin.scan_anchors = True
plugin.scan_anchors = True

class Page:
url = "/page/url"

plugin.on_page_content(
"""
<a id="foo.bar">
<a id="foo.baz" >
<a href="#foo.baz">
<a id="foo.qux" href="#fooqux">
<a href="quxfoo" id="qux.foo">
""",
page=Page(),
page=Page(), # type: ignore[arg-type]
)
assert "foo.bar" in plugin._url_map
assert "foo.baz" in plugin._url_map
assert "foo.baz" not in plugin._url_map
assert "foo.qux" in plugin._url_map
assert "qux.foo" in plugin._url_map

0 comments on commit 017d66f

Please sign in to comment.