@@ -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