@@ -25,6 +25,7 @@ use rustc_session::lint;
25
25
use rustc_span:: edition:: Edition ;
26
26
use rustc_span:: Span ;
27
27
use std:: borrow:: Cow ;
28
+ use std:: cell:: RefCell ;
28
29
use std:: collections:: VecDeque ;
29
30
use std:: default:: Default ;
30
31
use std:: fmt:: Write ;
@@ -414,11 +415,13 @@ impl<'a, I: Iterator<Item = Event<'a>>> Iterator for LinkReplacer<'a, I> {
414
415
}
415
416
}
416
417
418
+ type SpannedEvent < ' a > = ( Event < ' a > , Range < usize > ) ;
419
+
417
420
/// Make headings links with anchor IDs and build up TOC.
418
421
struct HeadingLinks < ' a , ' b , ' ids , I > {
419
422
inner : I ,
420
423
toc : Option < & ' b mut TocBuilder > ,
421
- buf : VecDeque < Event < ' a > > ,
424
+ buf : VecDeque < SpannedEvent < ' a > > ,
422
425
id_map : & ' ids mut IdMap ,
423
426
}
424
427
@@ -428,48 +431,48 @@ impl<'a, 'b, 'ids, I> HeadingLinks<'a, 'b, 'ids, I> {
428
431
}
429
432
}
430
433
431
- impl < ' a , ' b , ' ids , I : Iterator < Item = Event < ' a > > > Iterator for HeadingLinks < ' a , ' b , ' ids , I > {
432
- type Item = Event < ' a > ;
434
+ impl < ' a , ' b , ' ids , I : Iterator < Item = SpannedEvent < ' a > > > Iterator
435
+ for HeadingLinks < ' a , ' b , ' ids , I >
436
+ {
437
+ type Item = SpannedEvent < ' a > ;
433
438
434
439
fn next ( & mut self ) -> Option < Self :: Item > {
435
440
if let Some ( e) = self . buf . pop_front ( ) {
436
441
return Some ( e) ;
437
442
}
438
443
439
444
let event = self . inner . next ( ) ;
440
- if let Some ( Event :: Start ( Tag :: Heading ( level) ) ) = event {
445
+ if let Some ( ( Event :: Start ( Tag :: Heading ( level) ) , _ ) ) = event {
441
446
let mut id = String :: new ( ) ;
442
447
for event in & mut self . inner {
443
- match & event {
448
+ match & event. 0 {
444
449
Event :: End ( Tag :: Heading ( ..) ) => break ,
450
+ Event :: Start ( Tag :: Link ( _, _, _) ) | Event :: End ( Tag :: Link ( ..) ) => { }
445
451
Event :: Text ( text) | Event :: Code ( text) => {
446
452
id. extend ( text. chars ( ) . filter_map ( slugify) ) ;
453
+ self . buf . push_back ( event) ;
447
454
}
448
- _ => { }
449
- }
450
- match event {
451
- Event :: Start ( Tag :: Link ( _, _, _) ) | Event :: End ( Tag :: Link ( ..) ) => { }
452
- event => self . buf . push_back ( event) ,
455
+ _ => self . buf . push_back ( event) ,
453
456
}
454
457
}
455
458
let id = self . id_map . derive ( id) ;
456
459
457
460
if let Some ( ref mut builder) = self . toc {
458
461
let mut html_header = String :: new ( ) ;
459
- html:: push_html ( & mut html_header, self . buf . iter ( ) . cloned ( ) ) ;
462
+ html:: push_html ( & mut html_header, self . buf . iter ( ) . map ( | ( ev , _ ) | ev . clone ( ) ) ) ;
460
463
let sec = builder. push ( level as u32 , html_header, id. clone ( ) ) ;
461
- self . buf . push_front ( Event :: Html ( format ! ( "{} " , sec) . into ( ) ) ) ;
464
+ self . buf . push_front ( ( Event :: Html ( format ! ( "{} " , sec) . into ( ) ) , 0 .. 0 ) ) ;
462
465
}
463
466
464
- self . buf . push_back ( Event :: Html ( format ! ( "</a></h{}>" , level) . into ( ) ) ) ;
467
+ self . buf . push_back ( ( Event :: Html ( format ! ( "</a></h{}>" , level) . into ( ) ) , 0 .. 0 ) ) ;
465
468
466
469
let start_tags = format ! (
467
470
"<h{level} id=\" {id}\" class=\" section-header\" >\
468
471
<a href=\" #{id}\" >",
469
472
id = id,
470
473
level = level
471
474
) ;
472
- return Some ( Event :: Html ( start_tags. into ( ) ) ) ;
475
+ return Some ( ( Event :: Html ( start_tags. into ( ) ) , 0 .. 0 ) ) ;
473
476
}
474
477
event
475
478
}
@@ -555,23 +558,23 @@ impl<'a, I> Footnotes<'a, I> {
555
558
}
556
559
}
557
560
558
- impl < ' a , I : Iterator < Item = Event < ' a > > > Iterator for Footnotes < ' a , I > {
559
- type Item = Event < ' a > ;
561
+ impl < ' a , I : Iterator < Item = SpannedEvent < ' a > > > Iterator for Footnotes < ' a , I > {
562
+ type Item = SpannedEvent < ' a > ;
560
563
561
564
fn next ( & mut self ) -> Option < Self :: Item > {
562
565
loop {
563
566
match self . inner . next ( ) {
564
- Some ( Event :: FootnoteReference ( ref reference) ) => {
567
+ Some ( ( Event :: FootnoteReference ( ref reference) , range ) ) => {
565
568
let entry = self . get_entry ( & reference) ;
566
569
let reference = format ! (
567
570
"<sup id=\" fnref{0}\" ><a href=\" #fn{0}\" >{0}</a></sup>" ,
568
571
( * entry) . 1
569
572
) ;
570
- return Some ( Event :: Html ( reference. into ( ) ) ) ;
573
+ return Some ( ( Event :: Html ( reference. into ( ) ) , range ) ) ;
571
574
}
572
- Some ( Event :: Start ( Tag :: FootnoteDefinition ( def) ) ) => {
575
+ Some ( ( Event :: Start ( Tag :: FootnoteDefinition ( def) ) , _ ) ) => {
573
576
let mut content = Vec :: new ( ) ;
574
- for event in & mut self . inner {
577
+ for ( event, _ ) in & mut self . inner {
575
578
if let Event :: End ( Tag :: FootnoteDefinition ( ..) ) = event {
576
579
break ;
577
580
}
@@ -602,7 +605,7 @@ impl<'a, I: Iterator<Item = Event<'a>>> Iterator for Footnotes<'a, I> {
602
605
ret. push_str ( "</li>" ) ;
603
606
}
604
607
ret. push_str ( "</ol></div>" ) ;
605
- return Some ( Event :: Html ( ret. into ( ) ) ) ;
608
+ return Some ( ( Event :: Html ( ret. into ( ) ) , 0 .. 0 ) ) ;
606
609
} else {
607
610
return None ;
608
611
}
@@ -912,13 +915,14 @@ impl Markdown<'_> {
912
915
} ;
913
916
914
917
let p = Parser :: new_with_broken_link_callback ( md, opts ( ) , Some ( & mut replacer) ) ;
918
+ let p = p. into_offset_iter ( ) ;
915
919
916
920
let mut s = String :: with_capacity ( md. len ( ) * 3 / 2 ) ;
917
921
918
922
let p = HeadingLinks :: new ( p, None , & mut ids) ;
919
- let p = LinkReplacer :: new ( p, links) ;
920
- let p = CodeBlocks :: new ( p, codes, edition, playground) ;
921
923
let p = Footnotes :: new ( p) ;
924
+ let p = LinkReplacer :: new ( p. map ( |( ev, _) | ev) , links) ;
925
+ let p = CodeBlocks :: new ( p, codes, edition, playground) ;
922
926
html:: push_html ( & mut s, p) ;
923
927
924
928
s
@@ -929,16 +933,16 @@ impl MarkdownWithToc<'_> {
929
933
crate fn into_string ( self ) -> String {
930
934
let MarkdownWithToc ( md, mut ids, codes, edition, playground) = self ;
931
935
932
- let p = Parser :: new_ext ( md, opts ( ) ) ;
936
+ let p = Parser :: new_ext ( md, opts ( ) ) . into_offset_iter ( ) ;
933
937
934
938
let mut s = String :: with_capacity ( md. len ( ) * 3 / 2 ) ;
935
939
936
940
let mut toc = TocBuilder :: new ( ) ;
937
941
938
942
{
939
943
let p = HeadingLinks :: new ( p, Some ( & mut toc) , & mut ids) ;
940
- let p = CodeBlocks :: new ( p, codes, edition, playground) ;
941
944
let p = Footnotes :: new ( p) ;
945
+ let p = CodeBlocks :: new ( p. map ( |( ev, _) | ev) , codes, edition, playground) ;
942
946
html:: push_html ( & mut s, p) ;
943
947
}
944
948
@@ -954,19 +958,19 @@ impl MarkdownHtml<'_> {
954
958
if md. is_empty ( ) {
955
959
return String :: new ( ) ;
956
960
}
957
- let p = Parser :: new_ext ( md, opts ( ) ) ;
961
+ let p = Parser :: new_ext ( md, opts ( ) ) . into_offset_iter ( ) ;
958
962
959
963
// Treat inline HTML as plain text.
960
- let p = p. map ( |event| match event {
961
- Event :: Html ( text) => Event :: Text ( text) ,
964
+ let p = p. map ( |event| match event. 0 {
965
+ Event :: Html ( text) => ( Event :: Text ( text) , event . 1 ) ,
962
966
_ => event,
963
967
} ) ;
964
968
965
969
let mut s = String :: with_capacity ( md. len ( ) * 3 / 2 ) ;
966
970
967
971
let p = HeadingLinks :: new ( p, None , & mut ids) ;
968
- let p = CodeBlocks :: new ( p, codes, edition, playground) ;
969
972
let p = Footnotes :: new ( p) ;
973
+ let p = CodeBlocks :: new ( p. map ( |( ev, _) | ev) , codes, edition, playground) ;
970
974
html:: push_html ( & mut s, p) ;
971
975
972
976
s
@@ -1119,56 +1123,65 @@ crate fn plain_text_summary(md: &str) -> String {
1119
1123
s
1120
1124
}
1121
1125
1122
- crate fn markdown_links ( md : & str ) -> Vec < ( String , Option < Range < usize > > ) > {
1126
+ crate fn markdown_links ( md : & str ) -> Vec < ( String , Range < usize > ) > {
1123
1127
if md. is_empty ( ) {
1124
1128
return vec ! [ ] ;
1125
1129
}
1126
1130
1127
- let mut links = vec ! [ ] ;
1128
- let mut shortcut_links = vec ! [ ] ;
1129
-
1130
- {
1131
- let locate = |s : & str | unsafe {
1132
- let s_start = s. as_ptr ( ) ;
1133
- let s_end = s_start. add ( s. len ( ) ) ;
1134
- let md_start = md. as_ptr ( ) ;
1135
- let md_end = md_start. add ( md. len ( ) ) ;
1136
- if md_start <= s_start && s_end <= md_end {
1137
- let start = s_start. offset_from ( md_start) as usize ;
1138
- let end = s_end. offset_from ( md_start) as usize ;
1139
- Some ( start..end)
1140
- } else {
1141
- None
1142
- }
1143
- } ;
1131
+ let links = RefCell :: new ( vec ! [ ] ) ;
1132
+
1133
+ // FIXME: remove this function once pulldown_cmark can provide spans for link definitions.
1134
+ let locate = |s : & str , fallback : Range < usize > | unsafe {
1135
+ let s_start = s. as_ptr ( ) ;
1136
+ let s_end = s_start. add ( s. len ( ) ) ;
1137
+ let md_start = md. as_ptr ( ) ;
1138
+ let md_end = md_start. add ( md. len ( ) ) ;
1139
+ if md_start <= s_start && s_end <= md_end {
1140
+ let start = s_start. offset_from ( md_start) as usize ;
1141
+ let end = s_end. offset_from ( md_start) as usize ;
1142
+ start..end
1143
+ } else {
1144
+ fallback
1145
+ }
1146
+ } ;
1147
+
1148
+ let span_for_link = |link : & CowStr < ' _ > , span : Range < usize > | {
1149
+ // For diagnostics, we want to underline the link's definition but `span` will point at
1150
+ // where the link is used. This is a problem for reference-style links, where the definition
1151
+ // is separate from the usage.
1152
+ match link {
1153
+ // `Borrowed` variant means the string (the link's destination) may come directly from
1154
+ // the markdown text and we can locate the original link destination.
1155
+ // NOTE: LinkReplacer also provides `Borrowed` but possibly from other sources,
1156
+ // so `locate()` can fall back to use `span`.
1157
+ CowStr :: Borrowed ( s) => locate ( s, span) ,
1158
+
1159
+ // For anything else, we can only use the provided range.
1160
+ CowStr :: Boxed ( _) | CowStr :: Inlined ( _) => span,
1161
+ }
1162
+ } ;
1144
1163
1145
- let mut push = |link : BrokenLink < ' _ > | {
1146
- // FIXME: use `link.span` instead of `locate`
1147
- // (doing it now includes the `[]` as well as the text)
1148
- shortcut_links. push ( ( link. reference . to_owned ( ) , locate ( link. reference ) ) ) ;
1149
- None
1150
- } ;
1151
- let p = Parser :: new_with_broken_link_callback ( md, opts ( ) , Some ( & mut push) ) ;
1152
-
1153
- // There's no need to thread an IdMap through to here because
1154
- // the IDs generated aren't going to be emitted anywhere.
1155
- let mut ids = IdMap :: new ( ) ;
1156
- let iter = Footnotes :: new ( HeadingLinks :: new ( p, None , & mut ids) ) ;
1157
-
1158
- for ev in iter {
1159
- if let Event :: Start ( Tag :: Link ( _, dest, _) ) = ev {
1160
- debug ! ( "found link: {}" , dest) ;
1161
- links. push ( match dest {
1162
- CowStr :: Borrowed ( s) => ( s. to_owned ( ) , locate ( s) ) ,
1163
- s @ ( CowStr :: Boxed ( ..) | CowStr :: Inlined ( ..) ) => ( s. into_string ( ) , None ) ,
1164
- } ) ;
1165
- }
1164
+ let mut push = |link : BrokenLink < ' _ > | {
1165
+ let span = span_for_link ( & CowStr :: Borrowed ( link. reference ) , link. span ) ;
1166
+ links. borrow_mut ( ) . push ( ( link. reference . to_owned ( ) , span) ) ;
1167
+ None
1168
+ } ;
1169
+ let p = Parser :: new_with_broken_link_callback ( md, opts ( ) , Some ( & mut push) ) . into_offset_iter ( ) ;
1170
+
1171
+ // There's no need to thread an IdMap through to here because
1172
+ // the IDs generated aren't going to be emitted anywhere.
1173
+ let mut ids = IdMap :: new ( ) ;
1174
+ let iter = Footnotes :: new ( HeadingLinks :: new ( p, None , & mut ids) ) ;
1175
+
1176
+ for ev in iter {
1177
+ if let Event :: Start ( Tag :: Link ( _, dest, _) ) = ev. 0 {
1178
+ debug ! ( "found link: {}" , dest) ;
1179
+ let span = span_for_link ( & dest, ev. 1 ) ;
1180
+ links. borrow_mut ( ) . push ( ( dest. into_string ( ) , span) ) ;
1166
1181
}
1167
1182
}
1168
1183
1169
- links. append ( & mut shortcut_links) ;
1170
-
1171
- links
1184
+ links. into_inner ( )
1172
1185
}
1173
1186
1174
1187
#[ derive( Debug ) ]
0 commit comments