Skip to content

Commit 0d4df6c

Browse files
committed
Add Top TOC support to rustdoc
This commit adds the headers for the top level documentation to rustdoc's existing table of contents, along with associated items. It only show two levels of headers. Going further would require the sidebar to be wider, and that seems unnecessary (the crates that have manually-built TOCs usually don't need deeply nested headers).
1 parent 037f515 commit 0d4df6c

15 files changed

+249
-122
lines changed

src/librustdoc/clean/types.rs

-6
Original file line numberDiff line numberDiff line change
@@ -503,9 +503,6 @@ impl Item {
503503
pub(crate) fn is_mod(&self) -> bool {
504504
self.type_() == ItemType::Module
505505
}
506-
pub(crate) fn is_trait(&self) -> bool {
507-
self.type_() == ItemType::Trait
508-
}
509506
pub(crate) fn is_struct(&self) -> bool {
510507
self.type_() == ItemType::Struct
511508
}
@@ -533,9 +530,6 @@ impl Item {
533530
pub(crate) fn is_ty_method(&self) -> bool {
534531
self.type_() == ItemType::TyMethod
535532
}
536-
pub(crate) fn is_type_alias(&self) -> bool {
537-
self.type_() == ItemType::TypeAlias
538-
}
539533
pub(crate) fn is_primitive(&self) -> bool {
540534
self.type_() == ItemType::Primitive
541535
}

src/librustdoc/html/markdown.rs

+32-10
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ use crate::html::format::Buffer;
5050
use crate::html::highlight;
5151
use crate::html::length_limit::HtmlWithLimit;
5252
use crate::html::render::small_url_encode;
53-
use crate::html::toc::TocBuilder;
53+
use crate::html::toc::{Toc, TocBuilder};
5454

5555
use pulldown_cmark::{
5656
html, BrokenLink, CodeBlockKind, CowStr, Event, LinkType, OffsetIter, Options, Parser, Tag,
@@ -102,6 +102,7 @@ pub struct Markdown<'a> {
102102
/// A struct like `Markdown` that renders the markdown with a table of contents.
103103
pub(crate) struct MarkdownWithToc<'a> {
104104
pub(crate) content: &'a str,
105+
pub(crate) links: &'a [RenderedLink],
105106
pub(crate) ids: &'a mut IdMap,
106107
pub(crate) error_codes: ErrorCodes,
107108
pub(crate) edition: Edition,
@@ -540,9 +541,9 @@ impl<'a, 'b, 'ids, I: Iterator<Item = SpannedEvent<'a>>> Iterator
540541
let id = self.id_map.derive(id);
541542

542543
if let Some(ref mut builder) = self.toc {
543-
let mut html_header = String::new();
544-
html::push_html(&mut html_header, self.buf.iter().map(|(ev, _)| ev.clone()));
545-
let sec = builder.push(level as u32, html_header, id.clone());
544+
let mut text_header = String::new();
545+
plain_text_from_events(self.buf.iter().map(|(ev, _)| ev.clone()), &mut text_header);
546+
let sec = builder.push(level as u32, text_header, id.clone());
546547
self.buf.push_front((Event::Html(format!("{sec} ").into()), 0..0));
547548
}
548549

@@ -1435,17 +1436,30 @@ impl Markdown<'_> {
14351436
}
14361437

14371438
impl MarkdownWithToc<'_> {
1438-
pub(crate) fn into_string(self) -> String {
1439+
pub(crate) fn into_parts(self) -> (Toc, String) {
14391440
let MarkdownWithToc {
14401441
content: md,
1442+
links,
14411443
ids,
14421444
error_codes: codes,
14431445
edition,
14441446
playground,
14451447
custom_code_classes_in_docs,
14461448
} = self;
14471449

1448-
let p = Parser::new_ext(md, main_body_opts()).into_offset_iter();
1450+
// This is actually common enough to special-case
1451+
if md.is_empty() {
1452+
return (Toc { entries: Vec::new() }, String::new());
1453+
}
1454+
let mut replacer = |broken_link: BrokenLink<'_>| {
1455+
links
1456+
.iter()
1457+
.find(|link| &*link.original_text == &*broken_link.reference)
1458+
.map(|link| (link.href.as_str().into(), link.tooltip.as_str().into()))
1459+
};
1460+
1461+
let p = Parser::new_with_broken_link_callback(md, main_body_opts(), Some(&mut replacer));
1462+
let p = p.into_offset_iter();
14491463

14501464
let mut s = String::with_capacity(md.len() * 3 / 2);
14511465

@@ -1459,7 +1473,11 @@ impl MarkdownWithToc<'_> {
14591473
html::push_html(&mut s, p);
14601474
}
14611475

1462-
format!("<nav id=\"TOC\">{toc}</nav>{s}", toc = toc.into_toc().print())
1476+
(toc.into_toc(), s)
1477+
}
1478+
pub(crate) fn into_string(self) -> String {
1479+
let (toc, s) = self.into_parts();
1480+
format!("<nav id=\"TOC\">{toc}</nav>{s}", toc = toc.print())
14631481
}
14641482
}
14651483

@@ -1638,7 +1656,13 @@ pub(crate) fn plain_text_summary(md: &str, link_names: &[RenderedLink]) -> Strin
16381656

16391657
let p = Parser::new_with_broken_link_callback(md, summary_opts(), Some(&mut replacer));
16401658

1641-
for event in p {
1659+
plain_text_from_events(p, &mut s);
1660+
1661+
s
1662+
}
1663+
1664+
pub(crate) fn plain_text_from_events<'a>(events: impl Iterator<Item=pulldown_cmark::Event<'a>>, s: &mut String) {
1665+
for event in events {
16421666
match &event {
16431667
Event::Text(text) => s.push_str(text),
16441668
Event::Code(code) => {
@@ -1653,8 +1677,6 @@ pub(crate) fn plain_text_summary(md: &str, link_names: &[RenderedLink]) -> Strin
16531677
_ => (),
16541678
}
16551679
}
1656-
1657-
s
16581680
}
16591681

16601682
#[derive(Debug)]

src/librustdoc/html/render/context.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -624,7 +624,8 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
624624
let all = shared.all.replace(AllTypes::new());
625625
let mut sidebar = Buffer::html();
626626

627-
let blocks = sidebar_module_like(all.item_sections());
627+
// all.html is not customizable, so a blank id map is fine
628+
let blocks = sidebar_module_like(all.item_sections(), &mut IdMap::new());
628629
let bar = Sidebar {
629630
title_prefix: "",
630631
title: "",

0 commit comments

Comments
 (0)