@@ -141,7 +141,7 @@ def __init__(self) -> None:
141141        self ._primary_url_map : dict [str , list [str ]] =  {}
142142        self ._secondary_url_map : dict [str , list [str ]] =  {}
143143        self ._title_map : dict [str , str ] =  {}
144-         self ._backlink_page_map : dict [str , Page ] =  {}
144+         self ._breadcrumbs_map : dict [str , BacklinkCrumb ] =  {}
145145        self ._abs_url_map : dict [str , str ] =  {}
146146        self ._backlinks : dict [str , dict [str , set [str ]]] =  defaultdict (lambda : defaultdict (set ))
147147        # YORE: Bump 2: Remove line. 
@@ -299,6 +299,7 @@ def on_env(self, env: Environment, /, *, config: MkDocsConfig, files: Files) ->
299299    # ----------------------------------------------------------------------- # 
300300    # Utilities                                                               # 
301301    # ----------------------------------------------------------------------- # 
302+     # TODO: Maybe stop exposing this method in the future. 
302303    def  map_urls (self , page : Page , anchor : AnchorLink ) ->  None :
303304        """Recurse on every anchor to map its ID to its absolute URL. 
304305
@@ -308,6 +309,9 @@ def map_urls(self, page: Page, anchor: AnchorLink) -> None:
308309            page: The page containing the anchors. 
309310            anchor: The anchor to process and to recurse on. 
310311        """ 
312+         return  self ._map_urls (page , anchor )
313+ 
314+     def  _map_urls (self , page : Page , anchor : AnchorLink , parent : BacklinkCrumb  |  None  =  None ) ->  None :
311315        # YORE: Bump 2: Remove block. 
312316        if  isinstance (page , str ):
313317            try :
@@ -316,8 +320,36 @@ def map_urls(self, page: Page, anchor: AnchorLink) -> None:
316320                page  =  self .current_page 
317321
318322        self .register_anchor (page , anchor .id , title = anchor .title , primary = True )
323+         breadcrumb  =  self ._get_breadcrumb (page , anchor , parent )
319324        for  child  in  anchor .children :
320-             self .map_urls (page , child )
325+             self ._map_urls (page , child , breadcrumb )
326+ 
327+     def  _get_breadcrumb (
328+         self ,
329+         page : Page  |  Section ,
330+         anchor : AnchorLink  |  None  =  None ,
331+         parent : BacklinkCrumb  |  None  =  None ,
332+     ) ->  BacklinkCrumb :
333+         parent_breadcrumb  =  None  if  page .parent  is  None  else  self ._get_breadcrumb (page .parent )
334+         if  parent  is  None :
335+             if  isinstance (page , Page ):
336+                 if  (parent_url  :=  page .url ) not  in self ._breadcrumbs_map :
337+                     self ._breadcrumbs_map [parent_url ] =  BacklinkCrumb (
338+                         title = page .title ,
339+                         url = parent_url ,
340+                         parent = parent_breadcrumb ,
341+                     )
342+                 parent  =  self ._breadcrumbs_map [parent_url ]
343+             else :
344+                 parent  =  BacklinkCrumb (title = page .title , url = "" , parent = parent_breadcrumb )
345+         if  anchor  is  None :
346+             return  parent 
347+         if  (url  :=  f"{ page .url } { anchor .id }  ) not  in self ._breadcrumbs_map :  # type: ignore[union-attr] 
348+             # Skip the parent page if the anchor is a top-level heading, to reduce repetition. 
349+             if  anchor .level  ==  1 :
350+                 parent  =  parent .parent 
351+             self ._breadcrumbs_map [url ] =  BacklinkCrumb (title = anchor .title , url = url , parent = parent )
352+         return  self ._breadcrumbs_map [url ]
321353
322354    def  _record_backlink (self , identifier : str , backlink_type : str , backlink_anchor : str , page_url : str ) ->  None :
323355        """Record a backlink. 
@@ -351,23 +383,22 @@ def get_backlinks(self, *identifiers: str, from_url: str) -> dict[str, set[Backl
351383            backlinks  =  self ._backlinks .get (identifier , {})
352384            for  backlink_type , backlink_urls  in  backlinks .items ():
353385                for  backlink_url  in  backlink_urls :
354-                     relative_backlinks [backlink_type ].add (self ._crumbs (from_url , backlink_url ))
386+                     relative_backlinks [backlink_type ].add (self ._get_backlink (from_url , backlink_url ))
355387        return  relative_backlinks 
356388
357-     def  _crumbs (self , from_url : str , backlink_url : str ) ->  Backlink :
358-         backlink_page : Page  =  self ._backlink_page_map [backlink_url ]
359-         backlink_title  =  self ._title_map .get (backlink_url , "" )
360-         crumbs : list [BacklinkCrumb ] =  [
361-             BacklinkCrumb (backlink_title , relative_url (from_url , backlink_url )),
362-             BacklinkCrumb (backlink_page .title , relative_url (from_url , backlink_page .url  +  "#" )),
363-         ]
364-         page : Page  |  Section  =  backlink_page 
365-         while  page .parent :
366-             page  =  page .parent 
367-             if  url  :=  getattr (page , "url" , "" ):
368-                 url  =  relative_url (from_url , url  +  "#" )
369-             crumbs .append (BacklinkCrumb (page .title , url ))
370-         return  Backlink (tuple (reversed (crumbs )))
389+     def  _get_backlink (self , from_url : str , backlink_url : str ) ->  Backlink :
390+         breadcrumbs  =  []
391+         breadcrumb : BacklinkCrumb  |  None  =  self ._breadcrumbs_map [backlink_url ]
392+         while  breadcrumb :
393+             breadcrumbs .append (
394+                 BacklinkCrumb (
395+                     title = breadcrumb .title ,
396+                     url = breadcrumb .url  and  relative_url (from_url , breadcrumb .url ),
397+                     parent = breadcrumb .parent ,
398+                 ),
399+             )
400+             breadcrumb  =  breadcrumb .parent 
401+         return  Backlink (tuple (reversed (breadcrumbs )))
371402
372403    def  register_anchor (
373404        self ,
@@ -403,8 +434,6 @@ def register_anchor(
403434            url_map [identifier ] =  [url ]
404435        if  title  and  url  not  in self ._title_map :
405436            self ._title_map [url ] =  title 
406-         if  self .record_backlinks  and  url  not  in self ._backlink_page_map :
407-             self ._backlink_page_map [url ] =  page 
408437
409438    def  register_url (self , identifier : str , url : str ) ->  None :
410439        """Register that the identifier should be turned into a link to this URL. 
0 commit comments