From fad5f51183fad999b5d346735e91cf08990e5132 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Fri, 25 Oct 2024 23:06:52 +0200 Subject: [PATCH 1/8] Always display first line of impl blocks even when collapsed --- src/librustdoc/html/markdown.rs | 95 +++++++++++++++++----- src/librustdoc/html/markdown/footnotes.rs | 25 ++++-- src/librustdoc/html/render/mod.rs | 45 ++++++---- src/librustdoc/html/static/css/rustdoc.css | 23 ++++++ 4 files changed, 141 insertions(+), 47 deletions(-) diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs index 5e3d06a4ae79a..9982130c6d30e 100644 --- a/src/librustdoc/html/markdown.rs +++ b/src/librustdoc/html/markdown.rs @@ -32,6 +32,8 @@ use std::iter::Peekable; use std::ops::{ControlFlow, Range}; use std::path::PathBuf; use std::str::{self, CharIndices}; +use std::sync::atomic::AtomicUsize; +use std::sync::{Arc, Weak}; use pulldown_cmark::{ BrokenLink, CodeBlockKind, CowStr, Event, LinkType, Options, Parser, Tag, TagEnd, html, @@ -1301,8 +1303,20 @@ impl LangString { } } -impl Markdown<'_> { +impl<'a> Markdown<'a> { pub fn into_string(self) -> String { + // This is actually common enough to special-case + if self.content.is_empty() { + return String::new(); + } + + let mut s = String::with_capacity(self.content.len() * 3 / 2); + html::push_html(&mut s, self.into_iter()); + + s + } + + fn into_iter(self) -> CodeBlocks<'a, 'a, impl Iterator>> { let Markdown { content: md, links, @@ -1313,32 +1327,23 @@ impl Markdown<'_> { heading_offset, } = self; - // This is actually common enough to special-case - if md.is_empty() { - return String::new(); - } - let mut replacer = |broken_link: BrokenLink<'_>| { + let replacer = move |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 = Parser::new_with_broken_link_callback(md, main_body_opts(), Some(replacer)); let p = p.into_offset_iter(); - let mut s = String::with_capacity(md.len() * 3 / 2); - ids.handle_footnotes(|ids, existing_footnotes| { let p = HeadingLinks::new(p, None, ids, heading_offset); let p = footnotes::Footnotes::new(p, existing_footnotes); let p = LinkReplacer::new(p.map(|(ev, _)| ev), links); let p = TableWrapper::new(p); - let p = CodeBlocks::new(p, codes, edition, playground); - html::push_html(&mut s, p); - }); - - s + CodeBlocks::new(p, codes, edition, playground) + }) } } @@ -1413,6 +1418,52 @@ impl MarkdownItemInfo<'_> { } } +pub(crate) fn markdown_split_summary_and_content( + md: Markdown<'_>, +) -> (Option, Option) { + if md.content.is_empty() { + return (None, None); + } + let mut p = md.into_iter(); + + let mut event_level = 0; + let mut summary_events = Vec::new(); + let mut get_next_tag = false; + + let mut end_of_summary = false; + while let Some(event) = p.next() { + match event { + Event::Start(_) => event_level += 1, + Event::End(kind) => { + event_level -= 1; + if event_level == 0 { + // We're back at the "top" so it means we're done with the summary. + end_of_summary = true; + // We surround tables with `
` HTML tags so this is a special case. + get_next_tag = kind == TagEnd::Table; + } + } + _ => {} + } + summary_events.push(event); + if end_of_summary { + if get_next_tag && let Some(event) = p.next() { + summary_events.push(event); + } + break; + } + } + let mut summary = String::new(); + html::push_html(&mut summary, summary_events.into_iter()); + if summary.is_empty() { + return (None, None); + } + let mut content = String::new(); + html::push_html(&mut content, p); + + if content.is_empty() { (Some(summary), None) } else { (Some(summary), Some(content)) } +} + impl MarkdownSummaryLine<'_> { pub(crate) fn into_string_with_has_more_content(self) -> (String, bool) { let MarkdownSummaryLine(md, links) = self; @@ -1882,7 +1933,7 @@ pub(crate) fn rust_code_blocks(md: &str, extra_info: &ExtraInfo<'_>) -> Vec, - existing_footnotes: usize, + existing_footnotes: Arc, } fn is_default_id(id: &str) -> bool { @@ -1942,7 +1993,7 @@ fn is_default_id(id: &str) -> bool { impl IdMap { pub fn new() -> Self { - IdMap { map: FxHashMap::default(), existing_footnotes: 0 } + IdMap { map: FxHashMap::default(), existing_footnotes: Arc::new(AtomicUsize::new(0)) } } pub(crate) fn derive + ToString>(&mut self, candidate: S) -> String { @@ -1970,15 +2021,17 @@ impl IdMap { /// Method to handle `existing_footnotes` increment automatically (to prevent forgetting /// about it). - pub(crate) fn handle_footnotes(&mut self, closure: F) { - let mut existing_footnotes = self.existing_footnotes; + pub(crate) fn handle_footnotes<'a, T, F: FnOnce(&'a mut Self, Weak) -> T>( + &'a mut self, + closure: F, + ) -> T { + let existing_footnotes = Arc::downgrade(&self.existing_footnotes); - closure(self, &mut existing_footnotes); - self.existing_footnotes = existing_footnotes; + closure(self, existing_footnotes) } pub(crate) fn clear(&mut self) { self.map.clear(); - self.existing_footnotes = 0; + self.existing_footnotes = Arc::new(AtomicUsize::new(0)); } } diff --git a/src/librustdoc/html/markdown/footnotes.rs b/src/librustdoc/html/markdown/footnotes.rs index ebf55b3837393..519778e4d3b5f 100644 --- a/src/librustdoc/html/markdown/footnotes.rs +++ b/src/librustdoc/html/markdown/footnotes.rs @@ -1,5 +1,8 @@ //! Markdown footnote handling. + use std::fmt::Write as _; +use std::sync::atomic::{AtomicUsize, Ordering}; +use std::sync::{Arc, Weak}; use pulldown_cmark::{CowStr, Event, Tag, TagEnd, html}; use rustc_data_structures::fx::FxIndexMap; @@ -8,10 +11,11 @@ use super::SpannedEvent; /// Moves all footnote definitions to the end and add back links to the /// references. -pub(super) struct Footnotes<'a, 'b, I> { +pub(super) struct Footnotes<'a, I> { inner: I, footnotes: FxIndexMap>, - existing_footnotes: &'b mut usize, + existing_footnotes: Arc, + start_id: usize, } /// The definition of a single footnote. @@ -21,13 +25,16 @@ struct FootnoteDef<'a> { id: usize, } -impl<'a, 'b, I: Iterator>> Footnotes<'a, 'b, I> { - pub(super) fn new(iter: I, existing_footnotes: &'b mut usize) -> Self { - Footnotes { inner: iter, footnotes: FxIndexMap::default(), existing_footnotes } +impl<'a, I: Iterator>> Footnotes<'a, I> { + pub(super) fn new(iter: I, existing_footnotes: Weak) -> Self { + let existing_footnotes = + existing_footnotes.upgrade().expect("`existing_footnotes` was dropped"); + let start_id = existing_footnotes.load(Ordering::Relaxed); + Footnotes { inner: iter, footnotes: FxIndexMap::default(), existing_footnotes, start_id } } fn get_entry(&mut self, key: &str) -> (&mut Vec>, usize) { - let new_id = self.footnotes.len() + 1 + *self.existing_footnotes; + let new_id = self.footnotes.len() + 1 + self.start_id; let key = key.to_owned(); let FootnoteDef { content, id } = self.footnotes.entry(key).or_insert(FootnoteDef { content: Vec::new(), id: new_id }); @@ -44,7 +51,7 @@ impl<'a, 'b, I: Iterator>> Footnotes<'a, 'b, I> { id, // Although the ID count is for the whole page, the footnote reference // are local to the item so we make this ID "local" when displayed. - id - *self.existing_footnotes + id - self.start_id ); Event::Html(reference.into()) } @@ -64,7 +71,7 @@ impl<'a, 'b, I: Iterator>> Footnotes<'a, 'b, I> { } } -impl<'a, I: Iterator>> Iterator for Footnotes<'a, '_, I> { +impl<'a, I: Iterator>> Iterator for Footnotes<'a, I> { type Item = SpannedEvent<'a>; fn next(&mut self) -> Option { @@ -87,7 +94,7 @@ impl<'a, I: Iterator>> Iterator for Footnotes<'a, '_, I> // After all the markdown is emmited, emit an
then all the footnotes // in a list. let defs: Vec<_> = self.footnotes.drain(..).map(|(_, x)| x).collect(); - *self.existing_footnotes += defs.len(); + self.existing_footnotes.fetch_add(defs.len(), Ordering::Relaxed); let defs_html = render_footnotes_defs(defs); return Some((Event::Html(defs_html.into()), 0..0)); } else { diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index f436725757232..b841f01813a7c 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -74,6 +74,7 @@ use crate::html::format::{ }; use crate::html::markdown::{ HeadingOffset, IdMap, Markdown, MarkdownItemInfo, MarkdownSummaryLine, + markdown_split_summary_and_content, }; use crate::html::static_files::SCRAPE_EXAMPLES_HELP_MD; use crate::html::{highlight, sources}; @@ -1904,7 +1905,6 @@ fn render_impl( } } - let trait_is_none = trait_.is_none(); // If we've implemented a trait, then also emit documentation for all // default items which weren't overridden in the implementation block. // We don't emit documentation for default items if they appear in the @@ -1936,6 +1936,22 @@ fn render_impl( if rendering_params.toggle_open_by_default { " open" } else { "" } ); } + + let (before_dox, after_dox) = i + .impl_item + .opt_doc_value() + .map(|dox| { + markdown_split_summary_and_content(Markdown { + content: &*dox, + links: &i.impl_item.links(cx), + ids: &mut cx.id_map.borrow_mut(), + error_codes: cx.shared.codes, + edition: cx.shared.edition(), + playground: &cx.shared.playground, + heading_offset: HeadingOffset::H4, + }) + }) + .unwrap_or((None, None)); render_impl_summary( w, cx, @@ -1944,33 +1960,23 @@ fn render_impl( rendering_params.show_def_docs, use_absolute, aliases, + &before_dox, ); if toggled { w.write_str(""); } - if let Some(ref dox) = i.impl_item.opt_doc_value() { - if trait_is_none && impl_.items.is_empty() { + if before_dox.is_some() { + if trait_.is_none() && impl_.items.is_empty() { w.write_str( "
\
This impl block contains no items.
\
", ); } - write!( - w, - "
{}
", - Markdown { - content: dox, - links: &i.impl_item.links(cx), - ids: &mut cx.id_map.borrow_mut(), - error_codes: cx.shared.codes, - edition: cx.shared.edition(), - playground: &cx.shared.playground, - heading_offset: HeadingOffset::H4, - } - .into_string() - ); + if let Some(after_dox) = after_dox { + write!(w, "
{after_dox}
"); + } } if !default_impl_items.is_empty() || !impl_items.is_empty() { w.write_str("
"); @@ -2031,6 +2037,7 @@ pub(crate) fn render_impl_summary( // This argument is used to reference same type with different paths to avoid duplication // in documentation pages for trait with automatic implementations like "Send" and "Sync". aliases: &[String], + doc: &Option, ) { let inner_impl = i.inner_impl(); let id = cx.derive_id(get_id_for_impl(cx.tcx(), i.impl_item.item_id)); @@ -2082,6 +2089,10 @@ pub(crate) fn render_impl_summary( ); } + if let Some(doc) = doc { + write!(w, "
{doc}
"); + } + w.write_str(""); } diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css index 90aa5904dafe8..52b3710ef16d5 100644 --- a/src/librustdoc/html/static/css/rustdoc.css +++ b/src/librustdoc/html/static/css/rustdoc.css @@ -2210,6 +2210,29 @@ details.toggle[open] > summary::after { content: "Collapse"; } +details.toggle:not([open]) > summary .docblock { + max-height: calc(1.5em + 0.75em); + overflow-y: hidden; +} +details.toggle:not([open]) > summary .docblock::after { + content: ''; + position: absolute; + bottom: 0; + left: 0; + right: 0; + pointer-events: none; + background: linear-gradient( + to top, + var(--scrape-example-code-wrapper-background-start), + var(--scrape-example-code-wrapper-background-end) + ); + height: 0.7em; + z-index: 1; +} +details.toggle > summary .docblock { + margin-top: 0.75em; +} + /* This is needed in docblocks to have the "▶" element to be on the same line. */ .docblock summary > * { display: inline-block; From 32e6826a95a17ad853d0d6fe1281399e88347f57 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Fri, 25 Oct 2024 23:07:10 +0200 Subject: [PATCH 2/8] Update browser-ui-test version to 0.18.2 --- .../docker/host-x86_64/x86_64-gnu-tools/browser-ui-test.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-tools/browser-ui-test.version b/src/ci/docker/host-x86_64/x86_64-gnu-tools/browser-ui-test.version index 6b2d58c8ef350..7211b157c6945 100644 --- a/src/ci/docker/host-x86_64/x86_64-gnu-tools/browser-ui-test.version +++ b/src/ci/docker/host-x86_64/x86_64-gnu-tools/browser-ui-test.version @@ -1 +1 @@ -0.18.1 \ No newline at end of file +0.18.2 \ No newline at end of file From 5d26accecdeb623a7843c4204439fb86ee07fab5 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Fri, 25 Oct 2024 23:08:33 +0200 Subject: [PATCH 3/8] Add GUI test for impl block doc display --- tests/rustdoc-gui/impl-block-doc.goml | 33 ++++++++++++++++++++++++++ tests/rustdoc-gui/src/test_docs/lib.rs | 17 +++++++++++++ 2 files changed, 50 insertions(+) create mode 100644 tests/rustdoc-gui/impl-block-doc.goml diff --git a/tests/rustdoc-gui/impl-block-doc.goml b/tests/rustdoc-gui/impl-block-doc.goml new file mode 100644 index 0000000000000..6c7d7f4cbc185 --- /dev/null +++ b/tests/rustdoc-gui/impl-block-doc.goml @@ -0,0 +1,33 @@ +// Checks that the first sentence of an impl block doc is always visible even when the impl +// block is collapsed. +go-to: "file://" + |DOC_PATH| + "/test_docs/struct.ImplDoc.html" + +set-window-size: (900, 600) + +define-function: ( + "compare-size-and-pos", + [nth_impl], + block { + // First we collapse the impl block. + store-value: (impl_path, "#implementations-list details:nth-of-type(" + |nth_impl| + ")") + set-property: (|impl_path|, {"open": false}) + wait-for: |impl_path| + ":not([open])" + + store-value: (impl_path, |impl_path| + " summary") + store-size: (|impl_path|, {"height": impl_height}) + store-position: (|impl_path|, {"y": impl_y}) + + store-size: (|impl_path| + " .docblock", {"height": doc_height}) + store-position: (|impl_path| + " .docblock", {"y": doc_y}) + + assert: |impl_y| + |impl_height| >= |doc_y| + } +) + +call-function: ("compare-size-and-pos", {"nth_impl": 1}) +// Since the first impl block has a long line, we ensure that it doesn't display all of it. +assert: (|impl_y| + |impl_height|) <= (|doc_y| + |doc_height|) + +call-function: ("compare-size-and-pos", {"nth_impl": 2}) +// The second impl block has a short line. +assert: (|impl_y| + |impl_height|) >= (|doc_y| + |doc_height|) diff --git a/tests/rustdoc-gui/src/test_docs/lib.rs b/tests/rustdoc-gui/src/test_docs/lib.rs index 352995c49030b..bdb958bf0e1b4 100644 --- a/tests/rustdoc-gui/src/test_docs/lib.rs +++ b/tests/rustdoc-gui/src/test_docs/lib.rs @@ -652,3 +652,20 @@ pub mod long_list { //! * [`FromBytes`](#a) indicates that a type may safely be converted from an arbitrary byte //! sequence } + +pub struct ImplDoc; + +/// bla sondfosdnf sdfasd fadsd fdsa f ads fad sf sad f sad fasdfsafsa df dsafasdasd fsa dfadsfasd +/// fads fadfadd +/// +/// bla +impl ImplDoc { + pub fn bar() {} +} + +/// bla +/// +/// bla +impl ImplDoc { + pub fn bar2() {} +} From abcd094c5174cf25f3de1879c57b4c69c7fe4a94 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Sat, 26 Oct 2024 00:20:31 +0200 Subject: [PATCH 4/8] Update GUI tests --- tests/rustdoc-gui/impl-doc.goml | 2 +- tests/rustdoc-gui/item-info-overflow.goml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/rustdoc-gui/impl-doc.goml b/tests/rustdoc-gui/impl-doc.goml index 4ec46de404fa4..1845255178aa7 100644 --- a/tests/rustdoc-gui/impl-doc.goml +++ b/tests/rustdoc-gui/impl-doc.goml @@ -3,7 +3,7 @@ go-to: "file://" + |DOC_PATH| + "/test_docs/struct.TypeWithImplDoc.html" // The text is about 24px tall, so if there's a margin, then their position will be >24px apart compare-elements-position-near-false: ( - "#implementations-list > .implementors-toggle > .docblock > p", + "#implementations-list > .implementors-toggle .docblock > p", "#implementations-list > .implementors-toggle > .impl-items", {"y": 24} ) diff --git a/tests/rustdoc-gui/item-info-overflow.goml b/tests/rustdoc-gui/item-info-overflow.goml index 23c53c0376235..c325beb6d0669 100644 --- a/tests/rustdoc-gui/item-info-overflow.goml +++ b/tests/rustdoc-gui/item-info-overflow.goml @@ -16,7 +16,7 @@ assert-text: ( go-to: "file://" + |DOC_PATH| + "/lib2/struct.LongItemInfo2.html" compare-elements-property: ( "#impl-SimpleTrait-for-LongItemInfo2 .item-info", - "#impl-SimpleTrait-for-LongItemInfo2 + .docblock", + "#impl-SimpleTrait-for-LongItemInfo2 .docblock", ["scrollWidth"], ) assert-property: ( From 6e0dabd9e209a0fe902313b367df365d6476b875 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Mon, 28 Oct 2024 17:39:17 +0100 Subject: [PATCH 5/8] Turn `markdown_split_summary_and_content` into a method of `Markdown` --- src/librustdoc/html/markdown.rs | 95 ++++++++++++++++--------------- src/librustdoc/html/render/mod.rs | 6 +- 2 files changed, 52 insertions(+), 49 deletions(-) diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs index 9982130c6d30e..90c49270566d7 100644 --- a/src/librustdoc/html/markdown.rs +++ b/src/librustdoc/html/markdown.rs @@ -1345,6 +1345,55 @@ impl<'a> Markdown<'a> { CodeBlocks::new(p, codes, edition, playground) }) } + + /// Convert markdown to (summary, remaining) HTML. + /// + /// - The summary is the first top-level Markdown element (usually a paragraph, but potentially + /// any block). + /// - The remaining docs contain everything after the summary. + pub(crate) fn split_summary_and_content(self) -> (Option, Option) { + if self.content.is_empty() { + return (None, None); + } + let mut p = self.into_iter(); + + let mut event_level = 0; + let mut summary_events = Vec::new(); + let mut get_next_tag = false; + + let mut end_of_summary = false; + while let Some(event) = p.next() { + match event { + Event::Start(_) => event_level += 1, + Event::End(kind) => { + event_level -= 1; + if event_level == 0 { + // We're back at the "top" so it means we're done with the summary. + end_of_summary = true; + // We surround tables with `
` HTML tags so this is a special case. + get_next_tag = kind == TagEnd::Table; + } + } + _ => {} + } + summary_events.push(event); + if end_of_summary { + if get_next_tag && let Some(event) = p.next() { + summary_events.push(event); + } + break; + } + } + let mut summary = String::new(); + html::push_html(&mut summary, summary_events.into_iter()); + if summary.is_empty() { + return (None, None); + } + let mut content = String::new(); + html::push_html(&mut content, p); + + if content.is_empty() { (Some(summary), None) } else { (Some(summary), Some(content)) } + } } impl MarkdownWithToc<'_> { @@ -1418,52 +1467,6 @@ impl MarkdownItemInfo<'_> { } } -pub(crate) fn markdown_split_summary_and_content( - md: Markdown<'_>, -) -> (Option, Option) { - if md.content.is_empty() { - return (None, None); - } - let mut p = md.into_iter(); - - let mut event_level = 0; - let mut summary_events = Vec::new(); - let mut get_next_tag = false; - - let mut end_of_summary = false; - while let Some(event) = p.next() { - match event { - Event::Start(_) => event_level += 1, - Event::End(kind) => { - event_level -= 1; - if event_level == 0 { - // We're back at the "top" so it means we're done with the summary. - end_of_summary = true; - // We surround tables with `
` HTML tags so this is a special case. - get_next_tag = kind == TagEnd::Table; - } - } - _ => {} - } - summary_events.push(event); - if end_of_summary { - if get_next_tag && let Some(event) = p.next() { - summary_events.push(event); - } - break; - } - } - let mut summary = String::new(); - html::push_html(&mut summary, summary_events.into_iter()); - if summary.is_empty() { - return (None, None); - } - let mut content = String::new(); - html::push_html(&mut content, p); - - if content.is_empty() { (Some(summary), None) } else { (Some(summary), Some(content)) } -} - impl MarkdownSummaryLine<'_> { pub(crate) fn into_string_with_has_more_content(self) -> (String, bool) { let MarkdownSummaryLine(md, links) = self; diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index b841f01813a7c..e013829e5e0c8 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -74,7 +74,6 @@ use crate::html::format::{ }; use crate::html::markdown::{ HeadingOffset, IdMap, Markdown, MarkdownItemInfo, MarkdownSummaryLine, - markdown_split_summary_and_content, }; use crate::html::static_files::SCRAPE_EXAMPLES_HELP_MD; use crate::html::{highlight, sources}; @@ -1941,7 +1940,7 @@ fn render_impl( .impl_item .opt_doc_value() .map(|dox| { - markdown_split_summary_and_content(Markdown { + Markdown { content: &*dox, links: &i.impl_item.links(cx), ids: &mut cx.id_map.borrow_mut(), @@ -1949,7 +1948,8 @@ fn render_impl( edition: cx.shared.edition(), playground: &cx.shared.playground, heading_offset: HeadingOffset::H4, - }) + } + .split_summary_and_content() }) .unwrap_or((None, None)); render_impl_summary( From 448d9adc3804fcd7ecc7e89529b8565754028b91 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Tue, 29 Oct 2024 15:44:25 +0100 Subject: [PATCH 6/8] Use text ellipsis instead of bottom blurring --- src/librustdoc/html/static/css/rustdoc.css | 27 +++++++++++++--------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css index 52b3710ef16d5..cc69a2c4dada0 100644 --- a/src/librustdoc/html/static/css/rustdoc.css +++ b/src/librustdoc/html/static/css/rustdoc.css @@ -2214,20 +2214,25 @@ details.toggle:not([open]) > summary .docblock { max-height: calc(1.5em + 0.75em); overflow-y: hidden; } -details.toggle:not([open]) > summary .docblock::after { - content: ''; +details.toggle:not([open]) > summary .docblock > :first-child { + max-width: calc(100% - 1em); + overflow: hidden; + width: fit-content; + white-space: nowrap; + position: relative; + padding-right: 1em; +} +details.toggle:not([open]) > summary .docblock > :first-child::after { + content: "…"; position: absolute; - bottom: 0; - left: 0; right: 0; - pointer-events: none; - background: linear-gradient( - to top, - var(--scrape-example-code-wrapper-background-start), - var(--scrape-example-code-wrapper-background-end) - ); - height: 0.7em; + top: 0; + bottom: 0; z-index: 1; + background-color: var(--main-background-color); + /* In case this ends up in a heading or a `` item. */ + font-weight: normal; + font: 1rem/1.5 "Source Serif 4", NanumBarunGothic, serif; } details.toggle > summary .docblock { margin-top: 0.75em; From 90feb9a64514b949b9a986e07c8eb14e4a5ca923 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Tue, 19 Nov 2024 22:11:28 +0100 Subject: [PATCH 7/8] Improve positioning of "..." in collapsed impl block --- src/librustdoc/html/static/css/rustdoc.css | 11 +++++++--- tests/rustdoc-gui/impl-block-doc.goml | 9 ++++++++ .../source-code-page-code-scroll.goml | 4 ++-- tests/rustdoc-gui/src/test_docs/lib.rs | 22 +++++++++++++++++++ 4 files changed, 41 insertions(+), 5 deletions(-) diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css index cc69a2c4dada0..27496381b2ce0 100644 --- a/src/librustdoc/html/static/css/rustdoc.css +++ b/src/librustdoc/html/static/css/rustdoc.css @@ -2215,7 +2215,7 @@ details.toggle:not([open]) > summary .docblock { overflow-y: hidden; } details.toggle:not([open]) > summary .docblock > :first-child { - max-width: calc(100% - 1em); + max-width: 100%; overflow: hidden; width: fit-content; white-space: nowrap; @@ -2230,10 +2230,15 @@ details.toggle:not([open]) > summary .docblock > :first-child::after { bottom: 0; z-index: 1; background-color: var(--main-background-color); - /* In case this ends up in a heading or a `` item. */ - font-weight: normal; font: 1rem/1.5 "Source Serif 4", NanumBarunGothic, serif; + /* To make it look a bit better and not have it stuck to the preceding element. */ + padding-left: 0.2em; } +details.toggle:not([open]) > summary .docblock > div:first-child::after { + /* This is to make the "..." always appear at the bottom. */ + padding-top: calc(1.5em + 0.75em - 1.2rem); +} + details.toggle > summary .docblock { margin-top: 0.75em; } diff --git a/tests/rustdoc-gui/impl-block-doc.goml b/tests/rustdoc-gui/impl-block-doc.goml index 6c7d7f4cbc185..906ce1a37c6fe 100644 --- a/tests/rustdoc-gui/impl-block-doc.goml +++ b/tests/rustdoc-gui/impl-block-doc.goml @@ -31,3 +31,12 @@ assert: (|impl_y| + |impl_height|) <= (|doc_y| + |doc_height|) call-function: ("compare-size-and-pos", {"nth_impl": 2}) // The second impl block has a short line. assert: (|impl_y| + |impl_height|) >= (|doc_y| + |doc_height|) + +// FIXME: Needs `if` condition to make this test check that `padding-top` on the "..." element +// is as expected for tables. +call-function: ("compare-size-and-pos", {"nth_impl": 3}) +assert: (|impl_y| + |impl_height|) >= (|doc_y| + |doc_height|) +call-function: ("compare-size-and-pos", {"nth_impl": 4}) +assert: (|impl_y| + |impl_height|) >= (|doc_y| + |doc_height|) +call-function: ("compare-size-and-pos", {"nth_impl": 5}) +assert: (|impl_y| + |impl_height|) >= (|doc_y| + |doc_height|) diff --git a/tests/rustdoc-gui/source-code-page-code-scroll.goml b/tests/rustdoc-gui/source-code-page-code-scroll.goml index 31ab281d6ce09..60012db6c8c8b 100644 --- a/tests/rustdoc-gui/source-code-page-code-scroll.goml +++ b/tests/rustdoc-gui/source-code-page-code-scroll.goml @@ -2,7 +2,7 @@ go-to: "file://" + |DOC_PATH| + "/src/test_docs/lib.rs.html" set-window-size: (800, 1000) // "scrollWidth" should be superior than "clientWidth". -assert-property: ("body", {"scrollWidth": 1114, "clientWidth": 800}) +assert-property: ("body", {"scrollWidth": 1776, "clientWidth": 800}) // Both properties should be equal (ie, no scroll on the code block). -assert-property: (".example-wrap .rust", {"scrollWidth": 1000, "clientWidth": 1000}) +assert-property: (".example-wrap .rust", {"scrollWidth": 1662, "clientWidth": 1662}) diff --git a/tests/rustdoc-gui/src/test_docs/lib.rs b/tests/rustdoc-gui/src/test_docs/lib.rs index bdb958bf0e1b4..91aa2c3fae54a 100644 --- a/tests/rustdoc-gui/src/test_docs/lib.rs +++ b/tests/rustdoc-gui/src/test_docs/lib.rs @@ -669,3 +669,25 @@ impl ImplDoc { impl ImplDoc { pub fn bar2() {} } + +// ignore-tidy-linelength +/// | this::is::a::kinda::very::long::header::number::one | this::is::a::kinda::very::long::header::number::two | this::is::a::kinda::very::long::header::number::three | +/// |-|-|-| +/// | bla | bli | blob | +impl ImplDoc { + pub fn bar3() {} +} + +/// # h1 +/// +/// bla +impl ImplDoc { + pub fn bar4() {} +} + +/// * list +/// * list +/// * list +impl ImplDoc { + pub fn bar5() {} +} From 854ebe7522ef3b91e708a45077bbcd8da061e876 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Mon, 2 Dec 2024 17:11:36 +0100 Subject: [PATCH 8/8] Update GUI test after rebase --- tests/rustdoc-gui/docblock-table-overflow.goml | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/tests/rustdoc-gui/docblock-table-overflow.goml b/tests/rustdoc-gui/docblock-table-overflow.goml index abfa820ef2708..18e5b4d7f3590 100644 --- a/tests/rustdoc-gui/docblock-table-overflow.goml +++ b/tests/rustdoc-gui/docblock-table-overflow.goml @@ -10,12 +10,8 @@ assert-property: (".top-doc .docblock table", {"scrollWidth": "1572"}) // Checking it works on other doc blocks as well... -// Logically, the ".docblock" and the "

" should have the same scroll width. -compare-elements-property: ( - "#implementations-list > details .docblock", - "#implementations-list > details .docblock > p", - ["scrollWidth"], -) -assert-property: ("#implementations-list > details .docblock", {"scrollWidth": "835"}) +// Logically, the ".docblock" and the "

" should have the same scroll width (if we exclude the margin). +assert-property: ("#implementations-list > details .docblock", {"scrollWidth": 816}) +assert-property: ("#implementations-list > details .docblock > p", {"scrollWidth": 835}) // However, since there is overflow in the , its scroll width is bigger. assert-property: ("#implementations-list > details .docblock table", {"scrollWidth": "1572"})