@@ -447,61 +447,61 @@ impl<'a, I: Iterator<Item = Event<'a>>> Iterator for LinkReplacer<'a, I> {
447
447
}
448
448
449
449
/// Make headings links with anchor IDs and build up TOC.
450
- struct HeadingLinks < ' a , ' b , ' ids , I : Iterator < Item = Event < ' a > > > {
450
+ struct HeadingLinks < ' a , ' b , ' ids , I > {
451
451
inner : I ,
452
452
toc : Option < & ' b mut TocBuilder > ,
453
- buf : VecDeque < Event < ' a > > ,
453
+ buf : VecDeque < ( Event < ' a > , Range < usize > ) > ,
454
454
id_map : & ' ids mut IdMap ,
455
455
}
456
456
457
- impl < ' a , ' b , ' ids , I : Iterator < Item = Event < ' a > > > HeadingLinks < ' a , ' b , ' ids , I > {
457
+ impl < ' a , ' b , ' ids , I > HeadingLinks < ' a , ' b , ' ids , I > {
458
458
fn new ( iter : I , toc : Option < & ' b mut TocBuilder > , ids : & ' ids mut IdMap ) -> Self {
459
459
HeadingLinks { inner : iter, toc, buf : VecDeque :: new ( ) , id_map : ids }
460
460
}
461
461
}
462
462
463
- impl < ' a , ' b , ' ids , I : Iterator < Item = Event < ' a > > > Iterator for HeadingLinks < ' a , ' b , ' ids , I > {
464
- type Item = Event < ' a > ;
463
+ impl < ' a , ' b , ' ids , I : Iterator < Item = ( Event < ' a > , Range < usize > ) > > Iterator
464
+ for HeadingLinks < ' a , ' b , ' ids , I >
465
+ {
466
+ type Item = ( Event < ' a > , Range < usize > ) ;
465
467
466
468
fn next ( & mut self ) -> Option < Self :: Item > {
467
469
if let Some ( e) = self . buf . pop_front ( ) {
468
470
return Some ( e) ;
469
471
}
470
472
471
473
let event = self . inner . next ( ) ;
472
- if let Some ( Event :: Start ( Tag :: Heading ( level) ) ) = event {
474
+ if let Some ( ( Event :: Start ( Tag :: Heading ( level) ) , _ ) ) = event {
473
475
let mut id = String :: new ( ) ;
474
476
for event in & mut self . inner {
475
- match & event {
477
+ match & event. 0 {
476
478
Event :: End ( Tag :: Heading ( ..) ) => break ,
479
+ Event :: Start ( Tag :: Link ( _, _, _) ) | Event :: End ( Tag :: Link ( ..) ) => { }
477
480
Event :: Text ( text) | Event :: Code ( text) => {
478
481
id. extend ( text. chars ( ) . filter_map ( slugify) ) ;
482
+ self . buf . push_back ( event) ;
479
483
}
480
- _ => { }
481
- }
482
- match event {
483
- Event :: Start ( Tag :: Link ( _, _, _) ) | Event :: End ( Tag :: Link ( ..) ) => { }
484
- event => self . buf . push_back ( event) ,
484
+ _ => self . buf . push_back ( event) ,
485
485
}
486
486
}
487
487
let id = self . id_map . derive ( id) ;
488
488
489
489
if let Some ( ref mut builder) = self . toc {
490
490
let mut html_header = String :: new ( ) ;
491
- html:: push_html ( & mut html_header, self . buf . iter ( ) . cloned ( ) ) ;
491
+ html:: push_html ( & mut html_header, self . buf . iter ( ) . map ( | ( ev , _ ) | ev . clone ( ) ) ) ;
492
492
let sec = builder. push ( level as u32 , html_header, id. clone ( ) ) ;
493
- self . buf . push_front ( Event :: Html ( format ! ( "{} " , sec) . into ( ) ) ) ;
493
+ self . buf . push_front ( ( Event :: Html ( format ! ( "{} " , sec) . into ( ) ) , 0 .. 0 ) ) ;
494
494
}
495
495
496
- self . buf . push_back ( Event :: Html ( format ! ( "</a></h{}>" , level) . into ( ) ) ) ;
496
+ self . buf . push_back ( ( Event :: Html ( format ! ( "</a></h{}>" , level) . into ( ) ) , 0 .. 0 ) ) ;
497
497
498
498
let start_tags = format ! (
499
499
"<h{level} id=\" {id}\" class=\" section-header\" >\
500
500
<a href=\" #{id}\" >",
501
501
id = id,
502
502
level = level
503
503
) ;
504
- return Some ( Event :: Html ( start_tags. into ( ) ) ) ;
504
+ return Some ( ( Event :: Html ( start_tags. into ( ) ) , 0 .. 0 ) ) ;
505
505
}
506
506
event
507
507
}
@@ -575,39 +575,40 @@ impl<'a, I: Iterator<Item = Event<'a>>> Iterator for SummaryLine<'a, I> {
575
575
576
576
/// Moves all footnote definitions to the end and add back links to the
577
577
/// references.
578
- struct Footnotes < ' a , I : Iterator < Item = Event < ' a > > > {
578
+ struct Footnotes < ' a , I > {
579
579
inner : I ,
580
580
footnotes : FxHashMap < String , ( Vec < Event < ' a > > , u16 ) > ,
581
581
}
582
582
583
- impl < ' a , I : Iterator < Item = Event < ' a > > > Footnotes < ' a , I > {
583
+ impl < ' a , I > Footnotes < ' a , I > {
584
584
fn new ( iter : I ) -> Self {
585
585
Footnotes { inner : iter, footnotes : FxHashMap :: default ( ) }
586
586
}
587
+
587
588
fn get_entry ( & mut self , key : & str ) -> & mut ( Vec < Event < ' a > > , u16 ) {
588
589
let new_id = self . footnotes . keys ( ) . count ( ) + 1 ;
589
590
let key = key. to_owned ( ) ;
590
591
self . footnotes . entry ( key) . or_insert ( ( Vec :: new ( ) , new_id as u16 ) )
591
592
}
592
593
}
593
594
594
- impl < ' a , I : Iterator < Item = Event < ' a > > > Iterator for Footnotes < ' a , I > {
595
- type Item = Event < ' a > ;
595
+ impl < ' a , I : Iterator < Item = ( Event < ' a > , Range < usize > ) > > Iterator for Footnotes < ' a , I > {
596
+ type Item = ( Event < ' a > , Range < usize > ) ;
596
597
597
598
fn next ( & mut self ) -> Option < Self :: Item > {
598
599
loop {
599
600
match self . inner . next ( ) {
600
- Some ( Event :: FootnoteReference ( ref reference) ) => {
601
+ Some ( ( Event :: FootnoteReference ( ref reference) , range ) ) => {
601
602
let entry = self . get_entry ( & reference) ;
602
603
let reference = format ! (
603
604
"<sup id=\" fnref{0}\" ><a href=\" #fn{0}\" >{0}</a></sup>" ,
604
605
( * entry) . 1
605
606
) ;
606
- return Some ( Event :: Html ( reference. into ( ) ) ) ;
607
+ return Some ( ( Event :: Html ( reference. into ( ) ) , range ) ) ;
607
608
}
608
- Some ( Event :: Start ( Tag :: FootnoteDefinition ( def) ) ) => {
609
+ Some ( ( Event :: Start ( Tag :: FootnoteDefinition ( def) ) , _ ) ) => {
609
610
let mut content = Vec :: new ( ) ;
610
- for event in & mut self . inner {
611
+ for ( event, _ ) in & mut self . inner {
611
612
if let Event :: End ( Tag :: FootnoteDefinition ( ..) ) = event {
612
613
break ;
613
614
}
@@ -638,7 +639,7 @@ impl<'a, I: Iterator<Item = Event<'a>>> Iterator for Footnotes<'a, I> {
638
639
ret. push_str ( "</li>" ) ;
639
640
}
640
641
ret. push_str ( "</ol></div>" ) ;
641
- return Some ( Event :: Html ( ret. into ( ) ) ) ;
642
+ return Some ( ( Event :: Html ( ret. into ( ) ) , 0 .. 0 ) ) ;
642
643
} else {
643
644
return None ;
644
645
}
@@ -946,13 +947,14 @@ impl Markdown<'_> {
946
947
} ;
947
948
948
949
let p = Parser :: new_with_broken_link_callback ( md, opts ( ) , Some ( & mut replacer) ) ;
950
+ let p = p. into_offset_iter ( ) ;
949
951
950
952
let mut s = String :: with_capacity ( md. len ( ) * 3 / 2 ) ;
951
953
952
954
let p = HeadingLinks :: new ( p, None , & mut ids) ;
953
- let p = LinkReplacer :: new ( p, links) ;
954
- let p = CodeBlocks :: new ( p, codes, edition, playground) ;
955
955
let p = Footnotes :: new ( p) ;
956
+ let p = LinkReplacer :: new ( p. map ( |( ev, _) | ev) , links) ;
957
+ let p = CodeBlocks :: new ( p, codes, edition, playground) ;
956
958
html:: push_html ( & mut s, p) ;
957
959
958
960
s
@@ -963,16 +965,16 @@ impl MarkdownWithToc<'_> {
963
965
crate fn into_string ( self ) -> String {
964
966
let MarkdownWithToc ( md, mut ids, codes, edition, playground) = self ;
965
967
966
- let p = Parser :: new_ext ( md, opts ( ) ) ;
968
+ let p = Parser :: new_ext ( md, opts ( ) ) . into_offset_iter ( ) ;
967
969
968
970
let mut s = String :: with_capacity ( md. len ( ) * 3 / 2 ) ;
969
971
970
972
let mut toc = TocBuilder :: new ( ) ;
971
973
972
974
{
973
975
let p = HeadingLinks :: new ( p, Some ( & mut toc) , & mut ids) ;
974
- let p = CodeBlocks :: new ( p, codes, edition, playground) ;
975
976
let p = Footnotes :: new ( p) ;
977
+ let p = CodeBlocks :: new ( p. map ( |( ev, _) | ev) , codes, edition, playground) ;
976
978
html:: push_html ( & mut s, p) ;
977
979
}
978
980
@@ -988,19 +990,19 @@ impl MarkdownHtml<'_> {
988
990
if md. is_empty ( ) {
989
991
return String :: new ( ) ;
990
992
}
991
- let p = Parser :: new_ext ( md, opts ( ) ) ;
993
+ let p = Parser :: new_ext ( md, opts ( ) ) . into_offset_iter ( ) ;
992
994
993
995
// Treat inline HTML as plain text.
994
- let p = p. map ( |event| match event {
995
- Event :: Html ( text) => Event :: Text ( text) ,
996
+ let p = p. map ( |event| match event. 0 {
997
+ Event :: Html ( text) => ( Event :: Text ( text) , event . 1 ) ,
996
998
_ => event,
997
999
} ) ;
998
1000
999
1001
let mut s = String :: with_capacity ( md. len ( ) * 3 / 2 ) ;
1000
1002
1001
1003
let p = HeadingLinks :: new ( p, None , & mut ids) ;
1002
- let p = CodeBlocks :: new ( p, codes, edition, playground) ;
1003
1004
let p = Footnotes :: new ( p) ;
1005
+ let p = CodeBlocks :: new ( p. map ( |( ev, _) | ev) , codes, edition, playground) ;
1004
1006
html:: push_html ( & mut s, p) ;
1005
1007
1006
1008
s
@@ -1153,50 +1155,45 @@ crate fn plain_text_summary(md: &str) -> String {
1153
1155
s
1154
1156
}
1155
1157
1156
- crate fn markdown_links ( md : & str ) -> Vec < ( String , Option < Range < usize > > ) > {
1158
+ crate fn markdown_links ( md : & str ) -> Vec < ( String , Range < usize > ) > {
1157
1159
if md. is_empty ( ) {
1158
1160
return vec ! [ ] ;
1159
1161
}
1160
1162
1161
1163
let mut links = vec ! [ ] ;
1164
+ // Used to avoid mutable borrow issues in the `push` closure
1165
+ // Probably it would be more efficient to use a `RefCell` but it doesn't seem worth the churn.
1162
1166
let mut shortcut_links = vec ! [ ] ;
1163
1167
1164
- {
1165
- let locate = |s : & str | unsafe {
1166
- let s_start = s. as_ptr ( ) ;
1167
- let s_end = s_start. add ( s. len ( ) ) ;
1168
- let md_start = md. as_ptr ( ) ;
1169
- let md_end = md_start. add ( md. len ( ) ) ;
1170
- if md_start <= s_start && s_end <= md_end {
1171
- let start = s_start. offset_from ( md_start) as usize ;
1172
- let end = s_end. offset_from ( md_start) as usize ;
1173
- Some ( start..end)
1174
- } else {
1175
- None
1176
- }
1177
- } ;
1178
-
1179
- let mut push = |link : BrokenLink < ' _ > | {
1180
- // FIXME: use `link.span` instead of `locate`
1181
- // (doing it now includes the `[]` as well as the text)
1182
- shortcut_links. push ( ( link. reference . to_owned ( ) , locate ( link. reference ) ) ) ;
1183
- None
1184
- } ;
1185
- let p = Parser :: new_with_broken_link_callback ( md, opts ( ) , Some ( & mut push) ) ;
1186
-
1187
- // There's no need to thread an IdMap through to here because
1188
- // the IDs generated aren't going to be emitted anywhere.
1189
- let mut ids = IdMap :: new ( ) ;
1190
- let iter = Footnotes :: new ( HeadingLinks :: new ( p, None , & mut ids) ) ;
1191
-
1192
- for ev in iter {
1193
- if let Event :: Start ( Tag :: Link ( _, dest, _) ) = ev {
1194
- debug ! ( "found link: {}" , dest) ;
1195
- links. push ( match dest {
1196
- CowStr :: Borrowed ( s) => ( s. to_owned ( ) , locate ( s) ) ,
1197
- s @ ( CowStr :: Boxed ( ..) | CowStr :: Inlined ( ..) ) => ( s. into_string ( ) , None ) ,
1198
- } ) ;
1168
+ let span_for_link = |link : & str , span : Range < usize > | {
1169
+ // Pulldown includes the `[]` as well as the URL. Only highlight the relevant span.
1170
+ // NOTE: uses `rfind` in case the title and url are the same: `[Ok][Ok]`
1171
+ match md[ span. clone ( ) ] . rfind ( link) {
1172
+ Some ( start) => {
1173
+ let start = span. start + start;
1174
+ start..start + link. len ( )
1199
1175
}
1176
+ // This can happen for things other than intra-doc links, like `#1` expanded to `https://github.com/rust-lang/rust/issues/1`.
1177
+ None => span,
1178
+ }
1179
+ } ;
1180
+ let mut push = |link : BrokenLink < ' _ > | {
1181
+ let span = span_for_link ( link. reference , link. span ) ;
1182
+ shortcut_links. push ( ( link. reference . to_owned ( ) , span) ) ;
1183
+ None
1184
+ } ;
1185
+ let p = Parser :: new_with_broken_link_callback ( md, opts ( ) , Some ( & mut push) ) ;
1186
+
1187
+ // There's no need to thread an IdMap through to here because
1188
+ // the IDs generated aren't going to be emitted anywhere.
1189
+ let mut ids = IdMap :: new ( ) ;
1190
+ let iter = Footnotes :: new ( HeadingLinks :: new ( p. into_offset_iter ( ) , None , & mut ids) ) ;
1191
+
1192
+ for ev in iter {
1193
+ if let Event :: Start ( Tag :: Link ( _, dest, _) ) = ev. 0 {
1194
+ debug ! ( "found link: {}" , dest) ;
1195
+ let span = span_for_link ( & dest, ev. 1 ) ;
1196
+ links. push ( ( dest. into_string ( ) , span) ) ;
1200
1197
}
1201
1198
}
1202
1199
0 commit comments