@@ -72,6 +72,9 @@ def __init__(self, **kwargs):
72
72
self .found_refs : dict [str , int ] = {}
73
73
self .used_refs : set [str ] = set ()
74
74
75
+ # Backward compatibility with old '%d' placeholder
76
+ self .setConfig ('BACKLINK_TITLE' , self .getConfig ("BACKLINK_TITLE" ).replace ("%d" , "{}" ))
77
+
75
78
self .reset ()
76
79
77
80
def extendMarkdown (self , md ):
@@ -90,6 +93,11 @@ def extendMarkdown(self, md):
90
93
# `codehilite`) so they can run on the the contents of the div.
91
94
md .treeprocessors .register (FootnoteTreeprocessor (self ), 'footnote' , 50 )
92
95
96
+ # Insert a tree-processor to reorder the footnotes if necessary. This must be after
97
+ # `inline` tree-processor so it can access the footnote reference order
98
+ # (self.footnote_order) that gets populated by the FootnoteInlineProcessor.
99
+ md .treeprocessors .register (FootnoteReorderingProcessor (self ), 'footnote-reorder' , 19 )
100
+
93
101
# Insert a tree-processor that will run after inline is done.
94
102
# In this tree-processor we want to check our duplicate footnote tracker
95
103
# And add additional `backrefs` to the footnote pointing back to the
@@ -181,17 +189,12 @@ def makeFootnotesDiv(self, root: etree.Element) -> etree.Element | None:
181
189
if not list (self .footnotes .keys ()):
182
190
return None
183
191
184
- self .reorderFootnoteDict ()
185
-
186
192
div = etree .Element ("div" )
187
193
div .set ('class' , 'footnote' )
188
194
etree .SubElement (div , "hr" )
189
195
ol = etree .SubElement (div , "ol" )
190
196
surrogate_parent = etree .Element ("div" )
191
197
192
- # Backward compatibility with old '%d' placeholder
193
- backlink_title = self .getConfig ("BACKLINK_TITLE" ).replace ("%d" , "{}" )
194
-
195
198
for index , id in enumerate (self .footnotes .keys (), start = 1 ):
196
199
li = etree .SubElement (ol , "li" )
197
200
li .set ("id" , self .makeFootnoteId (id ))
@@ -207,7 +210,7 @@ def makeFootnotesDiv(self, root: etree.Element) -> etree.Element | None:
207
210
backlink .set ("class" , "footnote-backref" )
208
211
backlink .set (
209
212
"title" ,
210
- backlink_title .format (index )
213
+ self . getConfig ( 'BACKLINK_TITLE' ) .format (index )
211
214
)
212
215
backlink .text = FN_BACKLINK_TEXT
213
216
@@ -221,21 +224,6 @@ def makeFootnotesDiv(self, root: etree.Element) -> etree.Element | None:
221
224
p .append (backlink )
222
225
return div
223
226
224
- def reorderFootnoteDict (self ) -> None :
225
- """ Reorder the footnotes dict based on the order of references found. """
226
- ordered_footnotes = OrderedDict ()
227
-
228
- for ref in self .footnote_order :
229
- if ref in self .footnotes :
230
- ordered_footnotes [ref ] = self .footnotes [ref ]
231
-
232
- # Add back any footnotes that were defined but not referenced.
233
- for id , text in self .footnotes .items ():
234
- if id not in ordered_footnotes :
235
- ordered_footnotes [id ] = text
236
-
237
- self .footnotes = ordered_footnotes
238
-
239
227
240
228
class FootnoteBlockProcessor (BlockProcessor ):
241
229
""" Find footnote definitions and references, storing both for later use. """
@@ -253,11 +241,6 @@ def run(self, parent: etree.Element, blocks: list[str]) -> bool:
253
241
""" Find, set, and remove footnote definitions. Find footnote references."""
254
242
block = blocks .pop (0 )
255
243
256
- # Find any footnote references in the block to determine order.
257
- for match in RE_REFERENCE .finditer (block ):
258
- ref_id = match .group (1 )
259
- self .footnotes .addFootnoteRef (ref_id )
260
-
261
244
m = self .RE .search (block )
262
245
if m :
263
246
id = m .group (1 )
@@ -342,13 +325,15 @@ def __init__(self, pattern: str, footnotes: FootnoteExtension):
342
325
def handleMatch (self , m : re .Match [str ], data : str ) -> tuple [etree .Element | None , int | None , int | None ]:
343
326
id = m .group (1 )
344
327
if id in self .footnotes .footnotes .keys ():
328
+ self .footnotes .addFootnoteRef (id )
329
+
345
330
sup = etree .Element ("sup" )
346
331
a = etree .SubElement (sup , "a" )
347
332
sup .set ('id' , self .footnotes .makeFootnoteRefId (id , found = True ))
348
333
a .set ('href' , '#' + self .footnotes .makeFootnoteId (id ))
349
334
a .set ('class' , 'footnote-ref' )
350
335
a .text = self .footnotes .getConfig ("SUPERSCRIPT_TEXT" ).format (
351
- list ( self .footnotes .footnotes . keys ()) .index (id ) + 1
336
+ self .footnotes .footnote_order .index (id ) + 1
352
337
)
353
338
return sup , m .start (0 ), m .end (0 )
354
339
else :
@@ -431,6 +416,44 @@ def run(self, root: etree.Element) -> None:
431
416
root .append (footnotesDiv )
432
417
433
418
419
+ class FootnoteReorderingProcessor (Treeprocessor ):
420
+ """ Reorder list items in the footnotes div. """
421
+
422
+ def __init__ (self , footnotes : FootnoteExtension ):
423
+ self .footnotes = footnotes
424
+
425
+ def run (self , root : etree .Element ) -> None :
426
+ if not self .footnotes .footnotes :
427
+ return
428
+ if self .footnotes .footnote_order != list (self .footnotes .footnotes .keys ()):
429
+ for div in root .iter ('div' ):
430
+ if div .attrib .get ('class' , '' ) == 'footnote' :
431
+ self .reorder_footnotes (div )
432
+ break
433
+
434
+ def reorder_footnotes (self , parent : etree .Element ) -> None :
435
+ old_list = parent .find ('ol' )
436
+ parent .remove (old_list )
437
+ items = old_list .findall ('li' )
438
+
439
+ def order_by_id (li ) -> int :
440
+ id = li .attrib .get ('id' , '' ).split (self .footnotes .get_separator (), 1 )[- 1 ]
441
+ return (
442
+ self .footnotes .footnote_order .index (id )
443
+ if id in self .footnotes .footnote_order
444
+ else len (self .footnotes .footnotes )
445
+ )
446
+
447
+ items = sorted (items , key = order_by_id )
448
+
449
+ new_list = etree .SubElement (parent , 'ol' )
450
+
451
+ for index , item in enumerate (items , start = 1 ):
452
+ backlink = item .find ('.//a[@class="footnote-backref"]' )
453
+ backlink .set ("title" , self .footnotes .getConfig ("BACKLINK_TITLE" ).format (index ))
454
+ new_list .append (item )
455
+
456
+
434
457
class FootnotePostprocessor (Postprocessor ):
435
458
""" Replace placeholders with html entities. """
436
459
def __init__ (self , footnotes : FootnoteExtension ):
0 commit comments