@@ -338,83 +338,102 @@ impl<'a, I: Iterator<Item = Event<'a>>> Iterator for CodeBlocks<'_, 'a, I> {
338338}
339339
340340/// Make headings links with anchor IDs and build up TOC.
341- struct LinkReplacer < ' a , ' b , I : Iterator < Item = Event < ' a > > > {
341+ struct LinkReplacer < ' a , I : Iterator < Item = Event < ' a > > > {
342342 inner : I ,
343343 links : & ' a [ RenderedLink ] ,
344- shortcut_link : Option < & ' b RenderedLink > ,
344+ shortcut_link : Option < & ' a RenderedLink > ,
345345}
346346
347- impl < ' a , I : Iterator < Item = Event < ' a > > > LinkReplacer < ' a , ' _ , I > {
347+ impl < ' a , I : Iterator < Item = Event < ' a > > > LinkReplacer < ' a , I > {
348348 fn new ( iter : I , links : & ' a [ RenderedLink ] ) -> Self {
349349 LinkReplacer { inner : iter, links, shortcut_link : None }
350350 }
351351}
352352
353- impl < ' a : ' b , ' b , I : Iterator < Item = Event < ' a > > > Iterator for LinkReplacer < ' a , ' b , I > {
353+ impl < ' a , I : Iterator < Item = Event < ' a > > > Iterator for LinkReplacer < ' a , I > {
354354 type Item = Event < ' a > ;
355355
356356 fn next ( & mut self ) -> Option < Self :: Item > {
357+ use pulldown_cmark:: LinkType ;
358+
357359 let mut event = self . inner . next ( ) ;
358360
359- // Remove disambiguators from shortcut links (`[fn@f]`)
361+ // Replace intra-doc links and remove disambiguators from shortcut links (`[fn@f]`).
360362 match & mut event {
363+ // This is a shortcut link that was resolved by the broken_link_callback: `[fn@f]`
364+ // Remove any disambiguator.
361365 Some ( Event :: Start ( Tag :: Link (
362- pulldown_cmark:: LinkType :: ShortcutUnknown ,
366+ // [fn@f] or [fn@f][]
367+ LinkType :: ShortcutUnknown | LinkType :: CollapsedUnknown ,
363368 dest,
364369 title,
365370 ) ) ) => {
366371 debug ! ( "saw start of shortcut link to {} with title {}" , dest, title) ;
367- let link = if let Some ( link) =
368- self . links . iter ( ) . find ( |& link| * link. original_text == * * dest)
369- {
370- // Not sure why this is necessary - maybe the broken_link_callback doesn't always work?
371- * dest = CowStr :: Borrowed ( link. href . as_ref ( ) ) ;
372- Some ( link)
373- } else {
374- self . links . iter ( ) . find ( |& link| * link. href == * * dest)
375- } ;
372+ // If this is a shortcut link, it was resolved by the broken_link_callback.
373+ // So the URL will already be updated properly.
374+ let link = self . links . iter ( ) . find ( |& link| * link. href == * * dest) ;
375+ // Since this is an external iterator, we can't replace the inner text just yet.
376+ // Store that we saw a link so we know to replace it later.
376377 if let Some ( link) = link {
377378 trace ! ( "it matched" ) ;
378379 assert ! ( self . shortcut_link. is_none( ) , "shortcut links cannot be nested" ) ;
379380 self . shortcut_link = Some ( link) ;
380381 }
381382 }
382- Some ( Event :: End ( Tag :: Link ( pulldown_cmark:: LinkType :: ShortcutUnknown , dest, _) ) ) => {
383+ // Now that we're done with the shortcut link, don't replace any more text.
384+ Some ( Event :: End ( Tag :: Link (
385+ LinkType :: ShortcutUnknown | LinkType :: CollapsedUnknown ,
386+ dest,
387+ _,
388+ ) ) ) => {
383389 debug ! ( "saw end of shortcut link to {}" , dest) ;
384- if let Some ( _link ) = self . links . iter ( ) . find ( |& link| * link. href == * * dest) {
390+ if self . links . iter ( ) . find ( |& link| * link. href == * * dest) . is_some ( ) {
385391 assert ! ( self . shortcut_link. is_some( ) , "saw closing link without opening tag" ) ;
386392 self . shortcut_link = None ;
387393 }
388394 }
389- // Handle backticks in inline code blocks
395+ // Handle backticks in inline code blocks, but only if we're in the middle of a shortcut link.
396+ // [`fn@f`]
390397 Some ( Event :: Code ( text) ) => {
391398 trace ! ( "saw code {}" , text) ;
392399 if let Some ( link) = self . shortcut_link {
393400 trace ! ( "original text was {}" , link. original_text) ;
401+ // NOTE: this only replaces if the code block is the *entire* text.
402+ // If only part of the link has code highlighting, the disambiguator will not be removed.
403+ // e.g. [fn@`f`]
404+ // This is a limitation from `collect_intra_doc_links`: it passes a full link,
405+ // and does not distinguish at all between code blocks.
406+ // So we could never be sure we weren't replacing too much:
407+ // [fn@my_`f`unc] is treated the same as [my_func()] in that pass.
408+ //
409+ // NOTE: &[1..len() - 1] is to strip the backticks
394410 if * * text == link. original_text [ 1 ..link. original_text . len ( ) - 1 ] {
395411 debug ! ( "replacing {} with {}" , text, link. new_text) ;
396- * text = link. new_text . clone ( ) . into ( ) ;
412+ * text = CowStr :: Borrowed ( & link. new_text ) ;
397413 }
398414 }
399415 }
400- // Replace plain text in links
416+ // Replace plain text in links, but only in the middle of a shortcut link.
417+ // [fn@f]
401418 Some ( Event :: Text ( text) ) => {
402419 trace ! ( "saw text {}" , text) ;
403420 if let Some ( link) = self . shortcut_link {
404421 trace ! ( "original text was {}" , link. original_text) ;
422+ // NOTE: same limitations as `Event::Code`
405423 if * * text == * link. original_text {
406424 debug ! ( "replacing {} with {}" , text, link. new_text) ;
407- * text = link. new_text . clone ( ) . into ( ) ;
425+ * text = CowStr :: Borrowed ( & link. new_text ) ;
408426 }
409427 }
410428 }
429+ // If this is a link, but not a shortcut link,
430+ // replace the URL, since the broken_link_callback was not called.
411431 Some ( Event :: Start ( Tag :: Link ( _, dest, _) ) ) => {
412432 if let Some ( link) = self . links . iter ( ) . find ( |& link| * link. original_text == * * dest) {
413- // Not sure why this is necessary - maybe the broken_link_callback doesn't always work?
414433 * dest = CowStr :: Borrowed ( link. href . as_ref ( ) ) ;
415434 }
416435 }
417- // Anything else couldn't have been a valid Rust path
436+ // Anything else couldn't have been a valid Rust path, so no need to replace the text.
418437 _ => { }
419438 }
420439
0 commit comments