Skip to content

Commit ea3e26a

Browse files
Always display first line of impl blocks even when collapsed
1 parent 8aca4ba commit ea3e26a

File tree

3 files changed

+112
-28
lines changed

3 files changed

+112
-28
lines changed

src/librustdoc/html/markdown.rs

+62-13
Original file line numberDiff line numberDiff line change
@@ -1305,8 +1305,20 @@ impl LangString {
13051305
}
13061306
}
13071307

1308-
impl Markdown<'_> {
1308+
impl<'a> Markdown<'a> {
13091309
pub fn into_string(self) -> String {
1310+
// This is actually common enough to special-case
1311+
if self.content.is_empty() {
1312+
return String::new();
1313+
}
1314+
1315+
let mut s = String::with_capacity(self.content.len() * 3 / 2);
1316+
html::push_html(&mut s, self.into_iter());
1317+
1318+
s
1319+
}
1320+
1321+
fn into_iter(self) -> CodeBlocks<'a, 'a, impl Iterator<Item = Event<'a>>> {
13101322
let Markdown {
13111323
content: md,
13121324
links,
@@ -1317,30 +1329,21 @@ impl Markdown<'_> {
13171329
heading_offset,
13181330
} = self;
13191331

1320-
// This is actually common enough to special-case
1321-
if md.is_empty() {
1322-
return String::new();
1323-
}
1324-
let mut replacer = |broken_link: BrokenLink<'_>| {
1332+
let replacer = move |broken_link: BrokenLink<'_>| {
13251333
links
13261334
.iter()
13271335
.find(|link| &*link.original_text == &*broken_link.reference)
13281336
.map(|link| (link.href.as_str().into(), link.tooltip.as_str().into()))
13291337
};
13301338

1331-
let p = Parser::new_with_broken_link_callback(md, main_body_opts(), Some(&mut replacer));
1339+
let p = Parser::new_with_broken_link_callback(md, main_body_opts(), Some(replacer));
13321340
let p = p.into_offset_iter();
13331341

1334-
let mut s = String::with_capacity(md.len() * 3 / 2);
1335-
13361342
let p = HeadingLinks::new(p, None, ids, heading_offset);
13371343
let p = footnotes::Footnotes::new(p);
13381344
let p = LinkReplacer::new(p.map(|(ev, _)| ev), links);
13391345
let p = TableWrapper::new(p);
1340-
let p = CodeBlocks::new(p, codes, edition, playground);
1341-
html::push_html(&mut s, p);
1342-
1343-
s
1346+
CodeBlocks::new(p, codes, edition, playground)
13441347
}
13451348
}
13461349

@@ -1413,6 +1416,52 @@ impl MarkdownItemInfo<'_> {
14131416
}
14141417
}
14151418

1419+
pub(crate) fn markdown_split_summary_and_content(
1420+
md: Markdown<'_>,
1421+
) -> (Option<String>, Option<String>) {
1422+
if md.content.is_empty() {
1423+
return (None, None);
1424+
}
1425+
let mut p = md.into_iter();
1426+
1427+
let mut event_level = 0;
1428+
let mut summary_events = Vec::new();
1429+
let mut get_next_tag = false;
1430+
1431+
let mut end_of_summary = false;
1432+
while let Some(event) = p.next() {
1433+
match event {
1434+
Event::Start(_) => event_level += 1,
1435+
Event::End(kind) => {
1436+
event_level -= 1;
1437+
if event_level == 0 {
1438+
// We're back at the "top" so it means we're done with the summary.
1439+
end_of_summary = true;
1440+
// We surround tables with `<div>` HTML tags so this is a special case.
1441+
get_next_tag = kind == TagEnd::Table;
1442+
}
1443+
}
1444+
_ => {}
1445+
}
1446+
summary_events.push(event);
1447+
if end_of_summary {
1448+
if get_next_tag && let Some(event) = p.next() {
1449+
summary_events.push(event);
1450+
}
1451+
break;
1452+
}
1453+
}
1454+
let mut summary = String::new();
1455+
html::push_html(&mut summary, summary_events.into_iter());
1456+
if summary.is_empty() {
1457+
return (None, None);
1458+
}
1459+
let mut content = String::new();
1460+
html::push_html(&mut content, p);
1461+
1462+
if content.is_empty() { (Some(summary), None) } else { (Some(summary), Some(content)) }
1463+
}
1464+
14161465
impl MarkdownSummaryLine<'_> {
14171466
pub(crate) fn into_string_with_has_more_content(self) -> (String, bool) {
14181467
let MarkdownSummaryLine(md, links) = self;

src/librustdoc/html/render/mod.rs

+27-15
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ use crate::html::format::{
7575
};
7676
use crate::html::markdown::{
7777
HeadingOffset, IdMap, Markdown, MarkdownItemInfo, MarkdownSummaryLine,
78+
markdown_split_summary_and_content,
7879
};
7980
use crate::html::static_files::SCRAPE_EXAMPLES_HELP_MD;
8081
use crate::html::{highlight, sources};
@@ -1937,6 +1938,22 @@ fn render_impl(
19371938
if rendering_params.toggle_open_by_default { " open" } else { "" }
19381939
);
19391940
}
1941+
1942+
let (before_dox, after_dox) = i
1943+
.impl_item
1944+
.opt_doc_value()
1945+
.map(|dox| {
1946+
markdown_split_summary_and_content(Markdown {
1947+
content: &*dox,
1948+
links: &i.impl_item.links(cx),
1949+
ids: &mut cx.id_map,
1950+
error_codes: cx.shared.codes,
1951+
edition: cx.shared.edition(),
1952+
playground: &cx.shared.playground,
1953+
heading_offset: HeadingOffset::H4,
1954+
})
1955+
})
1956+
.unwrap_or((None, None));
19401957
render_impl_summary(
19411958
w,
19421959
cx,
@@ -1945,33 +1962,23 @@ fn render_impl(
19451962
rendering_params.show_def_docs,
19461963
use_absolute,
19471964
aliases,
1965+
&before_dox,
19481966
);
19491967
if toggled {
19501968
w.write_str("</summary>");
19511969
}
19521970

1953-
if let Some(ref dox) = i.impl_item.opt_doc_value() {
1971+
if before_dox.is_some() {
19541972
if trait_.is_none() && impl_.items.is_empty() {
19551973
w.write_str(
19561974
"<div class=\"item-info\">\
19571975
<div class=\"stab empty-impl\">This impl block contains no items.</div>\
19581976
</div>",
19591977
);
19601978
}
1961-
write!(
1962-
w,
1963-
"<div class=\"docblock\">{}</div>",
1964-
Markdown {
1965-
content: &*dox,
1966-
links: &i.impl_item.links(cx),
1967-
ids: &mut cx.id_map,
1968-
error_codes: cx.shared.codes,
1969-
edition: cx.shared.edition(),
1970-
playground: &cx.shared.playground,
1971-
heading_offset: HeadingOffset::H4,
1972-
}
1973-
.into_string()
1974-
);
1979+
if let Some(after_dox) = after_dox {
1980+
write!(w, "<div class=\"docblock\">{after_dox}</div>");
1981+
}
19751982
}
19761983
if !default_impl_items.is_empty() || !impl_items.is_empty() {
19771984
w.write_str("<div class=\"impl-items\">");
@@ -2032,6 +2039,7 @@ pub(crate) fn render_impl_summary(
20322039
// This argument is used to reference same type with different paths to avoid duplication
20332040
// in documentation pages for trait with automatic implementations like "Send" and "Sync".
20342041
aliases: &[String],
2042+
doc: &Option<String>,
20352043
) {
20362044
let inner_impl = i.inner_impl();
20372045
let id = cx.derive_id(get_id_for_impl(cx.tcx(), i.impl_item.item_id));
@@ -2083,6 +2091,10 @@ pub(crate) fn render_impl_summary(
20832091
);
20842092
}
20852093

2094+
if let Some(doc) = doc {
2095+
write!(w, "<div class=\"docblock\">{doc}</div>");
2096+
}
2097+
20862098
w.write_str("</section>");
20872099
}
20882100

src/librustdoc/html/static/css/rustdoc.css

+23
Original file line numberDiff line numberDiff line change
@@ -2183,6 +2183,29 @@ details.toggle[open] > summary::after {
21832183
content: "Collapse";
21842184
}
21852185

2186+
details.toggle:not([open]) > summary .docblock {
2187+
max-height: calc(1.5em + 0.75em);
2188+
overflow-y: hidden;
2189+
}
2190+
details.toggle:not([open]) > summary .docblock::after {
2191+
content: '';
2192+
position: absolute;
2193+
bottom: 0;
2194+
left: 0;
2195+
right: 0;
2196+
pointer-events: none;
2197+
background: linear-gradient(
2198+
to top,
2199+
var(--scrape-example-code-wrapper-background-start),
2200+
var(--scrape-example-code-wrapper-background-end)
2201+
);
2202+
height: 0.7em;
2203+
z-index: 1;
2204+
}
2205+
details.toggle > summary .docblock {
2206+
margin-top: 0.75em;
2207+
}
2208+
21862209
/* This is needed in docblocks to have the "▶" element to be on the same line. */
21872210
.docblock summary > * {
21882211
display: inline-block;

0 commit comments

Comments
 (0)