From fad5f51183fad999b5d346735e91cf08990e5132 Mon Sep 17 00:00:00 2001
From: Guillaume Gomez <guillaume1.gomez@gmail.com>
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 5e3d06a4ae79..9982130c6d30 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<Item = Event<'a>>> {
         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<String>, Option<String>) {
+    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 `<div>` 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<Rust
 #[derive(Clone, Default, Debug)]
 pub struct IdMap {
     map: FxHashMap<String, usize>,
-    existing_footnotes: usize,
+    existing_footnotes: Arc<AtomicUsize>,
 }
 
 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<S: AsRef<str> + 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<F: FnOnce(&mut Self, &mut usize)>(&mut self, closure: F) {
-        let mut existing_footnotes = self.existing_footnotes;
+    pub(crate) fn handle_footnotes<'a, T, F: FnOnce(&'a mut Self, Weak<AtomicUsize>) -> 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 ebf55b383739..519778e4d3b5 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<String, FootnoteDef<'a>>,
-    existing_footnotes: &'b mut usize,
+    existing_footnotes: Arc<AtomicUsize>,
+    start_id: usize,
 }
 
 /// The definition of a single footnote.
@@ -21,13 +25,16 @@ struct FootnoteDef<'a> {
     id: usize,
 }
 
-impl<'a, 'b, I: Iterator<Item = SpannedEvent<'a>>> 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<Item = SpannedEvent<'a>>> Footnotes<'a, I> {
+    pub(super) fn new(iter: I, existing_footnotes: Weak<AtomicUsize>) -> 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<Event<'a>>, 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<Item = SpannedEvent<'a>>> 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<Item = SpannedEvent<'a>>> Footnotes<'a, 'b, I> {
     }
 }
 
-impl<'a, I: Iterator<Item = SpannedEvent<'a>>> Iterator for Footnotes<'a, '_, I> {
+impl<'a, I: Iterator<Item = SpannedEvent<'a>>> Iterator for Footnotes<'a, I> {
     type Item = SpannedEvent<'a>;
 
     fn next(&mut self) -> Option<Self::Item> {
@@ -87,7 +94,7 @@ impl<'a, I: Iterator<Item = SpannedEvent<'a>>> Iterator for Footnotes<'a, '_, I>
                         // After all the markdown is emmited, emit an <hr> 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 f43672575723..b841f01813a7 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("</summary>");
         }
 
-        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(
                     "<div class=\"item-info\">\
                          <div class=\"stab empty-impl\">This impl block contains no items.</div>\
                      </div>",
                 );
             }
-            write!(
-                w,
-                "<div class=\"docblock\">{}</div>",
-                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, "<div class=\"docblock\">{after_dox}</div>");
+            }
         }
         if !default_impl_items.is_empty() || !impl_items.is_empty() {
             w.write_str("<div class=\"impl-items\">");
@@ -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<String>,
 ) {
     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, "<div class=\"docblock\">{doc}</div>");
+    }
+
     w.write_str("</section>");
 }
 
diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css
index 90aa5904dafe..52b3710ef16d 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 <guillaume1.gomez@gmail.com>
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 6b2d58c8ef35..7211b157c694 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 <guillaume1.gomez@gmail.com>
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 000000000000..6c7d7f4cbc18
--- /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 352995c49030..bdb958bf0e1b 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 <guillaume1.gomez@gmail.com>
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 4ec46de404fa..1845255178aa 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 23c53c037623..c325beb6d066 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 <guillaume1.gomez@gmail.com>
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 9982130c6d30..90c49270566d 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<String>, Option<String>) {
+        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 `<div>` 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<String>, Option<String>) {
-    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 `<div>` 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 b841f01813a7..e013829e5e0c 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 <guillaume1.gomez@gmail.com>
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 52b3710ef16d..cc69a2c4dada 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 `<code>` 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 <guillaume1.gomez@gmail.com>
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 cc69a2c4dada..27496381b2ce 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 `<code>` 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 6c7d7f4cbc18..906ce1a37c6f 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 31ab281d6ce0..60012db6c8c8 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 bdb958bf0e1b..91aa2c3fae54 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 <guillaume1.gomez@gmail.com>
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 abfa820ef270..18e5b4d7f359 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 "<p>" 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 "<p>" 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 <table>, its scroll width is bigger.
 assert-property: ("#implementations-list > details .docblock table", {"scrollWidth": "1572"})