5
5
import logging
6
6
import re
7
7
import warnings
8
+ from abc import ABC , abstractmethod
9
+ from dataclasses import dataclass
8
10
from functools import lru_cache
9
11
from html import escape , unescape
10
12
from html .parser import HTMLParser
20
22
from markdown .util import HTML_PLACEHOLDER_RE , INLINE_PLACEHOLDER_RE
21
23
22
24
if TYPE_CHECKING :
25
+ from pathlib import Path
26
+
23
27
from markdown import Markdown
24
28
25
29
from mkdocs_autorefs .plugin import AutorefsPlugin
@@ -59,10 +63,56 @@ def __getattr__(name: str) -> Any:
59
63
"""
60
64
61
65
66
+ class AutorefsHookInterface (ABC ):
67
+ """An interface for hooking into how AutoRef handles inline references."""
68
+
69
+ @dataclass
70
+ class Context :
71
+ """The context around an auto-reference."""
72
+
73
+ domain : str
74
+ role : str
75
+ origin : str
76
+ filepath : str | Path
77
+ lineno : int
78
+
79
+ def as_dict (self ) -> dict [str , str ]:
80
+ """Convert the context to a dictionary of HTML attributes."""
81
+ return {
82
+ "domain" : self .domain ,
83
+ "role" : self .role ,
84
+ "origin" : self .origin ,
85
+ "filepath" : str (self .filepath ),
86
+ "lineno" : str (self .lineno ),
87
+ }
88
+
89
+ @abstractmethod
90
+ def expand_identifier (self , identifier : str ) -> str :
91
+ """Expand an identifier in a given context.
92
+
93
+ Parameters:
94
+ identifier: The identifier to expand.
95
+
96
+ Returns:
97
+ The expanded identifier.
98
+ """
99
+ raise NotImplementedError
100
+
101
+ @abstractmethod
102
+ def get_context (self ) -> AutorefsHookInterface .Context :
103
+ """Get the current context.
104
+
105
+ Returns:
106
+ The current context.
107
+ """
108
+ raise NotImplementedError
109
+
110
+
62
111
class AutorefsInlineProcessor (ReferenceInlineProcessor ):
63
112
"""A Markdown extension to handle inline references."""
64
113
65
114
name : str = "mkdocs-autorefs"
115
+ hook : AutorefsHookInterface | None = None
66
116
67
117
def __init__ (self , * args : Any , ** kwargs : Any ) -> None : # noqa: D107
68
118
super ().__init__ (REFERENCE_RE , * args , ** kwargs )
@@ -145,6 +195,9 @@ def _make_tag(self, identifier: str, text: str) -> Element:
145
195
A new element.
146
196
"""
147
197
el = Element ("autoref" )
198
+ if self .hook :
199
+ identifier = self .hook .expand_identifier (identifier )
200
+ el .attrib .update (self .hook .get_context ().as_dict ())
148
201
el .set ("identifier" , identifier )
149
202
el .text = text
150
203
return el
@@ -177,7 +230,10 @@ def relative_url(url_a: str, url_b: str) -> str:
177
230
178
231
179
232
# YORE: Bump 2: Remove block.
180
- def _legacy_fix_ref (url_mapper : Callable [[str ], str ], unmapped : list [str ]) -> Callable :
233
+ def _legacy_fix_ref (
234
+ url_mapper : Callable [[str ], str ],
235
+ unmapped : list [tuple [str , AutorefsHookInterface .Context | None ]],
236
+ ) -> Callable :
181
237
"""Return a `repl` function for [`re.sub`](https://docs.python.org/3/library/re.html#re.sub).
182
238
183
239
In our context, we match Markdown references and replace them with HTML links.
@@ -210,7 +266,7 @@ def inner(match: Match) -> str:
210
266
return title
211
267
if kind == "autorefs-optional-hover" :
212
268
return f'<span title="{ identifier } ">{ title } </span>'
213
- unmapped .append (identifier )
269
+ unmapped .append (( identifier , None ) )
214
270
if title == identifier :
215
271
return f"[{ identifier } ][]"
216
272
return f"[{ title } ][{ identifier } ]"
@@ -233,7 +289,30 @@ def inner(match: Match) -> str:
233
289
234
290
235
291
class _AutorefsAttrs (dict ):
236
- _handled_attrs : ClassVar [set [str ]] = {"identifier" , "optional" , "hover" , "class" }
292
+ _handled_attrs : ClassVar [set [str ]] = {
293
+ "identifier" ,
294
+ "optional" ,
295
+ "hover" ,
296
+ "class" ,
297
+ "domain" ,
298
+ "role" ,
299
+ "origin" ,
300
+ "filepath" ,
301
+ "lineno" ,
302
+ }
303
+
304
+ @property
305
+ def context (self ) -> AutorefsHookInterface .Context | None :
306
+ try :
307
+ return AutorefsHookInterface .Context (
308
+ domain = self ["domain" ],
309
+ role = self ["role" ],
310
+ origin = self ["origin" ],
311
+ filepath = self ["filepath" ],
312
+ lineno = int (self ["lineno" ]),
313
+ )
314
+ except KeyError :
315
+ return None
237
316
238
317
@property
239
318
def remaining (self ) -> str :
@@ -257,7 +336,10 @@ def handle_starttag(self, tag: str, attrs: list[tuple[str, str | None]]) -> None
257
336
_html_attrs_parser = _HTMLAttrsParser ()
258
337
259
338
260
- def fix_ref (url_mapper : Callable [[str ], str ], unmapped : list [str ]) -> Callable :
339
+ def fix_ref (
340
+ url_mapper : Callable [[str ], str ],
341
+ unmapped : list [tuple [str , AutorefsHookInterface .Context | None ]],
342
+ ) -> Callable :
261
343
"""Return a `repl` function for [`re.sub`](https://docs.python.org/3/library/re.html#re.sub).
262
344
263
345
In our context, we match Markdown references and replace them with HTML links.
@@ -290,7 +372,7 @@ def inner(match: Match) -> str:
290
372
if hover :
291
373
return f'<span title="{ identifier } ">{ title } </span>'
292
374
return title
293
- unmapped .append (identifier )
375
+ unmapped .append (( identifier , attrs . context ) )
294
376
if title == identifier :
295
377
return f"[{ identifier } ][]"
296
378
return f"[{ title } ][{ identifier } ]"
@@ -310,7 +392,12 @@ def inner(match: Match) -> str:
310
392
311
393
312
394
# YORE: Bump 2: Replace `, *, _legacy_refs: bool = True` with `` within line.
313
- def fix_refs (html : str , url_mapper : Callable [[str ], str ], * , _legacy_refs : bool = True ) -> tuple [str , list [str ]]:
395
+ def fix_refs (
396
+ html : str ,
397
+ url_mapper : Callable [[str ], str ],
398
+ * ,
399
+ _legacy_refs : bool = True ,
400
+ ) -> tuple [str , list [tuple [str , AutorefsHookInterface .Context | None ]]]:
314
401
"""Fix all references in the given HTML text.
315
402
316
403
Arguments:
@@ -319,9 +406,9 @@ def fix_refs(html: str, url_mapper: Callable[[str], str], *, _legacy_refs: bool
319
406
such as [mkdocs_autorefs.plugin.AutorefsPlugin.get_item_url][].
320
407
321
408
Returns:
322
- The fixed HTML.
409
+ The fixed HTML, and a list of unmapped identifiers (string and optional context) .
323
410
"""
324
- unmapped : list [str ] = []
411
+ unmapped : list [tuple [ str , AutorefsHookInterface . Context | None ] ] = []
325
412
html = AUTOREF_RE .sub (fix_ref (url_mapper , unmapped ), html )
326
413
327
414
# YORE: Bump 2: Remove block.
0 commit comments