Skip to content

Commit

Permalink
Add Top TOC support to rustdoc
Browse files Browse the repository at this point in the history
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).
  • Loading branch information
notriddle committed Jul 18, 2024
1 parent 5753b30 commit f862bd8
Show file tree
Hide file tree
Showing 15 changed files with 262 additions and 123 deletions.
6 changes: 0 additions & 6 deletions src/librustdoc/clean/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -511,9 +511,6 @@ impl Item {
pub(crate) fn is_mod(&self) -> bool {
self.type_() == ItemType::Module
}
pub(crate) fn is_trait(&self) -> bool {
self.type_() == ItemType::Trait
}
pub(crate) fn is_struct(&self) -> bool {
self.type_() == ItemType::Struct
}
Expand Down Expand Up @@ -541,9 +538,6 @@ impl Item {
pub(crate) fn is_ty_method(&self) -> bool {
self.type_() == ItemType::TyMethod
}
pub(crate) fn is_type_alias(&self) -> bool {
self.type_() == ItemType::TypeAlias
}
pub(crate) fn is_primitive(&self) -> bool {
self.type_() == ItemType::Primitive
}
Expand Down
47 changes: 36 additions & 11 deletions src/librustdoc/html/markdown.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ use crate::html::format::Buffer;
use crate::html::highlight;
use crate::html::length_limit::HtmlWithLimit;
use crate::html::render::small_url_encode;
use crate::html::toc::TocBuilder;
use crate::html::toc::{Toc, TocBuilder};

use pulldown_cmark::{
html, BrokenLink, BrokenLinkCallback, CodeBlockKind, CowStr, Event, LinkType, OffsetIter,
Expand Down Expand Up @@ -102,6 +102,7 @@ pub struct Markdown<'a> {
/// A struct like `Markdown` that renders the markdown with a table of contents.
pub(crate) struct MarkdownWithToc<'a> {
pub(crate) content: &'a str,
pub(crate) links: &'a [RenderedLink],
pub(crate) ids: &'a mut IdMap,
pub(crate) error_codes: ErrorCodes,
pub(crate) edition: Edition,
Expand Down Expand Up @@ -531,9 +532,9 @@ impl<'a, 'b, 'ids, I: Iterator<Item = SpannedEvent<'a>>> Iterator
let id = self.id_map.derive(id);

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

Expand Down Expand Up @@ -1396,10 +1397,23 @@ impl Markdown<'_> {
}

impl MarkdownWithToc<'_> {
pub(crate) fn into_string(self) -> String {
let MarkdownWithToc { content: md, ids, error_codes: codes, edition, playground } = self;
pub(crate) fn into_parts(self) -> (Toc, String) {
let MarkdownWithToc { content: md, links, ids, error_codes: codes, edition, playground } =
self;

let p = Parser::new_ext(md, main_body_opts()).into_offset_iter();
// This is actually common enough to special-case
if md.is_empty() {
return (Toc { entries: Vec::new() }, String::new());
}
let mut replacer = |broken_link: BrokenLink<'_>| {
links
.iter()
.find(|link| &*link.original_text == &*broken_link.reference)
.map(|link| (link.href.as_str().into(), link.tooltip.as_str().into()))
};

let p = Parser::new_with_broken_link_callback(md, main_body_opts(), Some(&mut replacer));
let p = p.into_offset_iter();

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

Expand All @@ -1413,7 +1427,11 @@ impl MarkdownWithToc<'_> {
html::push_html(&mut s, p);
}

format!("<nav id=\"TOC\">{toc}</nav>{s}", toc = toc.into_toc().print())
(toc.into_toc(), s)
}
pub(crate) fn into_string(self) -> String {
let (toc, s) = self.into_parts();
format!("<nav id=\"TOC\">{toc}</nav>{s}", toc = toc.print())
}
}

Expand Down Expand Up @@ -1592,7 +1610,16 @@ pub(crate) fn plain_text_summary(md: &str, link_names: &[RenderedLink]) -> Strin

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

for event in p {
plain_text_from_events(p, &mut s);

s
}

pub(crate) fn plain_text_from_events<'a>(
events: impl Iterator<Item = pulldown_cmark::Event<'a>>,
s: &mut String,
) {
for event in events {
match &event {
Event::Text(text) => s.push_str(text),
Event::Code(code) => {
Expand All @@ -1607,8 +1634,6 @@ pub(crate) fn plain_text_summary(md: &str, link_names: &[RenderedLink]) -> Strin
_ => (),
}
}

s
}

#[derive(Debug)]
Expand Down
3 changes: 2 additions & 1 deletion src/librustdoc/html/render/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -626,7 +626,8 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
let all = shared.all.replace(AllTypes::new());
let mut sidebar = Buffer::html();

let blocks = sidebar_module_like(all.item_sections());
// all.html is not customizable, so a blank id map is fine
let blocks = sidebar_module_like(all.item_sections(), &mut IdMap::new());
let bar = Sidebar {
title_prefix: "",
title: "",
Expand Down
Loading

0 comments on commit f862bd8

Please sign in to comment.