From 19f9f658d64c4cb3eae674db677aabbe9b41cb0f Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Mon, 3 Apr 2023 17:12:19 -0700 Subject: [PATCH] rustdoc: migrate document functions to return a Display --- src/librustdoc/html/render/mod.rs | 282 ++++++++++++----------- src/librustdoc/html/render/print_item.rs | 49 ++-- src/librustdoc/html/sources.rs | 6 +- 3 files changed, 178 insertions(+), 159 deletions(-) diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index 920fbdcf0d6..49986433dfb 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -70,7 +70,7 @@ use crate::formats::{AssocItemRender, Impl, RenderMode}; use crate::html::escape::Escape; use crate::html::format::{ - href, join_with_double_colon, print_abi_with_space, print_constness_with_space, + display_fn, href, join_with_double_colon, print_abi_with_space, print_constness_with_space, print_default_space, print_generic_bounds, print_where_clause, visibility_print_with_space, Buffer, Ending, HrefError, PrintWithSpace, }; @@ -409,128 +409,134 @@ fn scrape_examples_help(shared: &SharedContext<'_>) -> String { ) } -fn document( - w: &mut Buffer, - cx: &mut Context<'_>, - item: &clean::Item, - parent: Option<&clean::Item>, +fn document<'a, 'cx: 'a>( + cx: &'a mut Context<'cx>, + item: &'a clean::Item, + parent: Option<&'a clean::Item>, heading_offset: HeadingOffset, -) { +) -> impl fmt::Display + 'a + Captures<'cx> { if let Some(ref name) = item.name { info!("Documenting {}", name); } - document_item_info(cx, item, parent).render_into(w).unwrap(); - if parent.is_none() { - document_full_collapsible(w, item, cx, heading_offset); - } else { - document_full(w, item, cx, heading_offset); - } + + display_fn(move |f| { + document_item_info(cx, item, parent).render_into(f).unwrap(); + if parent.is_none() { + write!(f, "{}", document_full_collapsible(item, cx, heading_offset))?; + } else { + write!(f, "{}", document_full(item, cx, heading_offset))?; + } + Ok(()) + }) } /// Render md_text as markdown. -fn render_markdown( - w: &mut Buffer, - cx: &mut Context<'_>, - md_text: &str, +fn render_markdown<'a, 'cx: 'a>( + cx: &'a mut Context<'cx>, + md_text: &'a str, links: Vec, heading_offset: HeadingOffset, -) { - write!( - w, - "
{}
", - Markdown { - content: md_text, - links: &links, - ids: &mut cx.id_map, - error_codes: cx.shared.codes, - edition: cx.shared.edition(), - playground: &cx.shared.playground, - heading_offset, - } - .into_string() - ) +) -> impl fmt::Display + 'a + Captures<'cx> { + display_fn(move |f| { + write!( + f, + "
{}
", + Markdown { + content: md_text, + links: &links, + ids: &mut cx.id_map, + error_codes: cx.shared.codes, + edition: cx.shared.edition(), + playground: &cx.shared.playground, + heading_offset, + } + .into_string() + ) + }) } /// Writes a documentation block containing only the first paragraph of the documentation. If the /// docs are longer, a "Read more" link is appended to the end. -fn document_short( - w: &mut Buffer, - item: &clean::Item, - cx: &mut Context<'_>, - link: AssocItemLink<'_>, - parent: &clean::Item, +fn document_short<'a, 'cx: 'a>( + item: &'a clean::Item, + cx: &'a mut Context<'cx>, + link: AssocItemLink<'a>, + parent: &'a clean::Item, show_def_docs: bool, -) { - document_item_info(cx, item, Some(parent)).render_into(w).unwrap(); - if !show_def_docs { - return; - } - if let Some(s) = item.doc_value() { - let (mut summary_html, has_more_content) = - MarkdownSummaryLine(&s, &item.links(cx)).into_string_with_has_more_content(); +) -> impl fmt::Display + 'a + Captures<'cx> { + display_fn(move |f| { + document_item_info(cx, item, Some(parent)).render_into(f).unwrap(); + if !show_def_docs { + return Ok(()); + } + if let Some(s) = item.doc_value() { + let (mut summary_html, has_more_content) = + MarkdownSummaryLine(&s, &item.links(cx)).into_string_with_has_more_content(); - if has_more_content { - let link = format!(r#" Read more"#, assoc_href_attr(item, link, cx)); + if has_more_content { + let link = format!(r#" Read more"#, assoc_href_attr(item, link, cx)); - if let Some(idx) = summary_html.rfind("

") { - summary_html.insert_str(idx, &link); + if let Some(idx) = summary_html.rfind("

") { + summary_html.insert_str(idx, &link); + } else { + summary_html.push_str(&link); + } + } + + write!(f, "
{}
", summary_html)?; + } + Ok(()) + }) +} + +fn document_full_collapsible<'a, 'cx: 'a>( + item: &'a clean::Item, + cx: &'a mut Context<'cx>, + heading_offset: HeadingOffset, +) -> impl fmt::Display + 'a + Captures<'cx> { + document_full_inner(item, cx, true, heading_offset) +} + +fn document_full<'a, 'cx: 'a>( + item: &'a clean::Item, + cx: &'a mut Context<'cx>, + heading_offset: HeadingOffset, +) -> impl fmt::Display + 'a + Captures<'cx> { + document_full_inner(item, cx, false, heading_offset) +} + +fn document_full_inner<'a, 'cx: 'a>( + item: &'a clean::Item, + cx: &'a mut Context<'cx>, + is_collapsible: bool, + heading_offset: HeadingOffset, +) -> impl fmt::Display + 'a + Captures<'cx> { + display_fn(move |f| { + if let Some(s) = item.collapsed_doc_value() { + debug!("Doc block: =====\n{}\n=====", s); + if is_collapsible { + write!( + f, + "
\ + \ + Expand description\ + {}
", + render_markdown(cx, &s, item.links(cx), heading_offset) + )?; } else { - summary_html.push_str(&link); + write!(f, "{}", render_markdown(cx, &s, item.links(cx), heading_offset))?; } } - write!(w, "
{}
", summary_html,); - } -} + let kind = match &*item.kind { + clean::ItemKind::StrippedItem(box kind) | kind => kind, + }; -fn document_full_collapsible( - w: &mut Buffer, - item: &clean::Item, - cx: &mut Context<'_>, - heading_offset: HeadingOffset, -) { - document_full_inner(w, item, cx, true, heading_offset); -} - -fn document_full( - w: &mut Buffer, - item: &clean::Item, - cx: &mut Context<'_>, - heading_offset: HeadingOffset, -) { - document_full_inner(w, item, cx, false, heading_offset); -} - -fn document_full_inner( - w: &mut Buffer, - item: &clean::Item, - cx: &mut Context<'_>, - is_collapsible: bool, - heading_offset: HeadingOffset, -) { - if let Some(s) = item.collapsed_doc_value() { - debug!("Doc block: =====\n{}\n=====", s); - if is_collapsible { - w.write_str( - "
\ - \ - Expand description\ - ", - ); - render_markdown(w, cx, &s, item.links(cx), heading_offset); - w.write_str("
"); - } else { - render_markdown(w, cx, &s, item.links(cx), heading_offset); + if let clean::ItemKind::FunctionItem(..) | clean::ItemKind::MethodItem(..) = kind { + render_call_locations(f, cx, item); } - } - - let kind = match &*item.kind { - clean::ItemKind::StrippedItem(box kind) | kind => kind, - }; - - if let clean::ItemKind::FunctionItem(..) | clean::ItemKind::MethodItem(..) = kind { - render_call_locations(w, cx, item); - } + Ok(()) + }) } #[derive(Template)] @@ -1485,18 +1491,25 @@ fn doc_impl_item( document_item_info(cx, it, Some(parent)) .render_into(&mut info_buffer) .unwrap(); - document_full(&mut doc_buffer, item, cx, HeadingOffset::H5); + write!( + &mut doc_buffer, + "{}", + document_full(item, cx, HeadingOffset::H5) + ); short_documented = false; } else { // In case the item isn't documented, // provide short documentation from the trait. - document_short( + write!( &mut doc_buffer, - it, - cx, - link, - parent, - rendering_params.show_def_docs, + "{}", + document_short( + it, + cx, + link, + parent, + rendering_params.show_def_docs, + ) ); } } @@ -1505,18 +1518,15 @@ fn doc_impl_item( .render_into(&mut info_buffer) .unwrap(); if rendering_params.show_def_docs { - document_full(&mut doc_buffer, item, cx, HeadingOffset::H5); + write!(&mut doc_buffer, "{}", document_full(item, cx, HeadingOffset::H5)); short_documented = false; } } } else { - document_short( + write!( &mut doc_buffer, - item, - cx, - link, - parent, - rendering_params.show_def_docs, + "{}", + document_short(item, cx, link, parent, rendering_params.show_def_docs,) ); } } @@ -2213,7 +2223,7 @@ fn collect_paths_for_type(first_ty: clean::Type, cache: &Cache) -> Vec { const NUM_VISIBLE_LINES: usize = 10; /// Generates the HTML for example call locations generated via the --scrape-examples flag. -fn render_call_locations(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Item) { +fn render_call_locations(mut w: W, cx: &mut Context<'_>, item: &clean::Item) { let tcx = cx.tcx(); let def_id = item.item_id.expect_def_id(); let key = tcx.def_path_hash(def_id); @@ -2222,7 +2232,7 @@ fn render_call_locations(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Ite // Generate a unique ID so users can link to this section for a given method let id = cx.id_map.derive("scraped-examples"); write!( - w, + &mut w, "
\ \
\ @@ -2231,7 +2241,8 @@ fn render_call_locations(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Ite
", root_path = cx.root_path(), id = id - ); + ) + .unwrap(); // Create a URL to a particular location in a reverse-dependency's source file let link_to_loc = |call_data: &CallData, loc: &CallLocation| -> (String, String) { @@ -2249,7 +2260,7 @@ fn render_call_locations(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Ite }; // Generate the HTML for a single example, being the title and code block - let write_example = |w: &mut Buffer, (path, call_data): (&PathBuf, &CallData)| -> bool { + let write_example = |mut w: &mut W, (path, call_data): (&PathBuf, &CallData)| -> bool { let contents = match fs::read_to_string(&path) { Ok(contents) => contents, Err(err) => { @@ -2297,7 +2308,7 @@ fn render_call_locations(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Ite let locations_encoded = serde_json::to_string(&line_ranges).unwrap(); write!( - w, + &mut w, "
\
\ {name} ({title})\ @@ -2310,10 +2321,12 @@ fn render_call_locations(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Ite // The locations are encoded as a data attribute, so they can be read // later by the JS for interactions. locations = Escape(&locations_encoded) - ); + ) + .unwrap(); if line_ranges.len() > 1 { - write!(w, r#" "#); + write!(w, r#" "#) + .unwrap(); } // Look for the example file in the source map if it exists, otherwise return a dummy span @@ -2340,7 +2353,7 @@ fn render_call_locations(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Ite decoration_info.insert("highlight", byte_ranges); sources::print_src( - w, + &mut w, contents_subset, file_span, cx, @@ -2348,7 +2361,7 @@ fn render_call_locations(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Ite highlight::DecorationInfo(decoration_info), sources::SourceContext::Embedded { offset: line_min, needs_expansion }, ); - write!(w, "
"); + write!(w, "
").unwrap(); true }; @@ -2382,7 +2395,7 @@ fn sort_criterion<'a>( // An example may fail to write if its source can't be read for some reason, so this method // continues iterating until a write succeeds - let write_and_skip_failure = |w: &mut Buffer, it: &mut Peekable<_>| { + let write_and_skip_failure = |w: &mut W, it: &mut Peekable<_>| { while let Some(example) = it.next() { if write_example(&mut *w, example) { break; @@ -2391,7 +2404,7 @@ fn sort_criterion<'a>( }; // Write just one example that's visible by default in the method's description. - write_and_skip_failure(w, &mut it); + write_and_skip_failure(&mut w, &mut it); // Then add the remaining examples in a hidden section. if it.peek().is_some() { @@ -2404,17 +2417,19 @@ fn sort_criterion<'a>(
Hide additional examples
\
\
" - ); + ) + .unwrap(); // Only generate inline code for MAX_FULL_EXAMPLES number of examples. Otherwise we could // make the page arbitrarily huge! for _ in 0..MAX_FULL_EXAMPLES { - write_and_skip_failure(w, &mut it); + write_and_skip_failure(&mut w, &mut it); } // For the remaining examples, generate a
    containing links to the source files. if it.peek().is_some() { - write!(w, r#"").unwrap(); } - write!(w, "
"); + write!(w, "").unwrap(); } - write!(w, ""); + write!(w, "").unwrap(); } diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs index b223c49a899..ea53f8d107d 100644 --- a/src/librustdoc/html/render/print_item.rs +++ b/src/librustdoc/html/render/print_item.rs @@ -218,7 +218,7 @@ fn toggle_close(w: &mut Buffer) { } fn item_module(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Item, items: &[clean::Item]) { - document(w, cx, item, None, HeadingOffset::H2); + write!(w, "{}", document(cx, item, None, HeadingOffset::H2)); let mut indices = (0..items.len()).filter(|i| !items[*i].is_stripped()).collect::>(); @@ -562,7 +562,7 @@ fn item_function(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, f: &cle notable_traits = notable_traits.unwrap_or_default(), ); }); - document(w, cx, it, None, HeadingOffset::H2); + write!(w, "{}", document(cx, it, None, HeadingOffset::H2)); } fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean::Trait) { @@ -717,7 +717,7 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean: }); // Trait documentation - document(w, cx, it, None, HeadingOffset::H2); + write!(w, "{}", document(cx, it, None, HeadingOffset::H2)); fn write_small_section_header(w: &mut Buffer, id: &str, title: &str, extra_content: &str) { write!( @@ -735,7 +735,7 @@ fn trait_item(w: &mut Buffer, cx: &mut Context<'_>, m: &clean::Item, t: &clean:: let item_type = m.type_(); let id = cx.derive_id(format!("{}.{}", item_type, name)); let mut content = Buffer::empty_from(w); - document(&mut content, cx, m, Some(t), HeadingOffset::H5); + write!(&mut content, "{}", document(cx, m, Some(t), HeadingOffset::H5)); let toggled = !content.is_empty(); if toggled { let method_toggle_class = if item_type.is_method() { " method-toggle" } else { "" }; @@ -1068,7 +1068,7 @@ fn item_trait_alias(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: & ); }); - document(w, cx, it, None, HeadingOffset::H2); + write!(w, "{}", document(cx, it, None, HeadingOffset::H2)); // Render any items associated directly to this alias, as otherwise they // won't be visible anywhere in the docs. It would be nice to also show @@ -1090,7 +1090,7 @@ fn item_opaque_ty(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &cl ); }); - document(w, cx, it, None, HeadingOffset::H2); + write!(w, "{}", document(cx, it, None, HeadingOffset::H2)); // Render any items associated directly to this alias, as otherwise they // won't be visible anywhere in the docs. It would be nice to also show @@ -1117,7 +1117,7 @@ fn write_content(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean:: write_content(w, cx, it, t); - document(w, cx, it, None, HeadingOffset::H2); + write!(w, "{}", document(cx, it, None, HeadingOffset::H2)); let def_id = it.item_id.expect_def_id(); // Render any items associated directly to this alias, as otherwise they @@ -1134,7 +1134,7 @@ fn item_union(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, s: &clean: render_union(w, it, Some(&s.generics), &s.fields, cx); }); - document(w, cx, it, None, HeadingOffset::H2); + write!(w, "{}", document(cx, it, None, HeadingOffset::H2)); let mut fields = s .fields @@ -1166,7 +1166,7 @@ fn item_union(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, s: &clean: if let Some(stability_class) = field.stability_class(cx.tcx()) { write!(w, ""); } - document(w, cx, field, Some(it), HeadingOffset::H3); + write!(w, "{}", document(cx, field, Some(it), HeadingOffset::H3)); } } let def_id = it.item_id.expect_def_id(); @@ -1248,7 +1248,7 @@ fn item_enum(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, e: &clean:: } }); - document(w, cx, it, None, HeadingOffset::H2); + write!(w, "{}", document(cx, it, None, HeadingOffset::H2)); if count_variants != 0 { write!( @@ -1324,10 +1324,13 @@ fn item_enum(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, e: &clean:: {f}: {t}\ ", f = field.name.unwrap(), - t = ty.print(cx) + t = ty.print(cx), + ); + write!( + w, + "{}", + document(cx, field, Some(variant), HeadingOffset::H5) ); - document(w, cx, field, Some(variant), HeadingOffset::H5); - write!(w, ""); } _ => unreachable!(), } @@ -1335,7 +1338,7 @@ fn item_enum(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, e: &clean:: w.write_str(""); } - document(w, cx, variant, Some(it), HeadingOffset::H4); + write!(w, "{}", document(cx, variant, Some(it), HeadingOffset::H4)); } write!(w, ""); } @@ -1346,7 +1349,7 @@ fn item_enum(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, e: &clean:: fn item_macro(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean::Macro) { highlight::render_item_decl_with_highlighting(&t.source, w); - document(w, cx, it, None, HeadingOffset::H2) + write!(w, "{}", document(cx, it, None, HeadingOffset::H2)) } fn item_proc_macro(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, m: &clean::ProcMacro) { @@ -1372,12 +1375,12 @@ fn item_proc_macro(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, m: &c } } }); - document(w, cx, it, None, HeadingOffset::H2) + write!(w, "{}", document(cx, it, None, HeadingOffset::H2)) } fn item_primitive(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item) { let def_id = it.item_id.expect_def_id(); - document(w, cx, it, None, HeadingOffset::H2); + write!(w, "{}", document(cx, it, None, HeadingOffset::H2)); if it.name.map(|n| n.as_str() != "reference").unwrap_or(false) { render_assoc_items(w, cx, it, def_id, AssocItemRender::All); } else { @@ -1435,7 +1438,7 @@ fn item_constant(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, c: &cle } }); - document(w, cx, it, None, HeadingOffset::H2) + write!(w, "{}", document(cx, it, None, HeadingOffset::H2)) } fn item_struct(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, s: &clean::Struct) { @@ -1444,7 +1447,7 @@ fn item_struct(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, s: &clean render_struct(w, it, Some(&s.generics), s.ctor_kind, &s.fields, "", true, cx); }); - document(w, cx, it, None, HeadingOffset::H2); + write!(w, "{}", document(cx, it, None, HeadingOffset::H2)); let mut fields = s .fields @@ -1478,7 +1481,7 @@ fn item_struct(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, s: &clean item_type = ItemType::StructField, ty = ty.print(cx) ); - document(w, cx, field, Some(it), HeadingOffset::H3); + write!(w, "{}", document(cx, field, Some(it), HeadingOffset::H3)); } } } @@ -1499,7 +1502,7 @@ fn item_static(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, s: &clean typ = s.type_.print(cx) ); }); - document(w, cx, it, None, HeadingOffset::H2) + write!(w, "{}", document(cx, it, None, HeadingOffset::H2)) } fn item_foreign_type(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item) { @@ -1514,13 +1517,13 @@ fn item_foreign_type(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item) { ); }); - document(w, cx, it, None, HeadingOffset::H2); + write!(w, "{}", document(cx, it, None, HeadingOffset::H2)); render_assoc_items(w, cx, it, it.item_id.expect_def_id(), AssocItemRender::All) } fn item_keyword(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item) { - document(w, cx, it, None, HeadingOffset::H2) + write!(w, "{}", document(cx, it, None, HeadingOffset::H2)) } /// Compare two strings treating multi-digit numbers as single units (i.e. natural sort order). diff --git a/src/librustdoc/html/sources.rs b/src/librustdoc/html/sources.rs index 1d298f52f75..c8397967c87 100644 --- a/src/librustdoc/html/sources.rs +++ b/src/librustdoc/html/sources.rs @@ -2,7 +2,6 @@ use crate::docfs::PathError; use crate::error::Error; use crate::html::format; -use crate::html::format::Buffer; use crate::html::highlight; use crate::html::layout; use crate::html::render::Context; @@ -17,6 +16,7 @@ use std::cell::RefCell; use std::ffi::OsStr; +use std::fmt; use std::fs; use std::ops::RangeInclusive; use std::path::{Component, Path, PathBuf}; @@ -294,7 +294,7 @@ pub(crate) enum SourceContext { /// Wrapper struct to render the source code of a file. This will do things like /// adding line numbers to the left-hand side. pub(crate) fn print_src( - buf: &mut Buffer, + mut writer: impl fmt::Write, s: &str, file_span: rustc_span::Span, context: &Context<'_>, @@ -329,5 +329,5 @@ struct Source { ); Ok(()) }); - Source { embedded, needs_expansion, lines, code_html: code }.render_into(buf).unwrap(); + Source { embedded, needs_expansion, lines, code_html: code }.render_into(&mut writer).unwrap(); }