From 450916305fe8482dfe2f192c0d288b0490d3a517 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Wed, 11 Jun 2025 21:29:06 +0200 Subject: [PATCH 1/4] Put negative implementors first and apply same ordering logic to foreign implementors --- src/librustdoc/formats/mod.rs | 4 ++ src/librustdoc/html/render/print_item.rs | 43 ++++++++++++++++-- src/librustdoc/html/render/write_shared.rs | 45 +++++++++++++++++-- .../html/render/write_shared/tests.rs | 6 +-- src/librustdoc/html/static/css/rustdoc.css | 3 ++ src/librustdoc/html/static/js/main.js | 39 ++++++++++++---- src/librustdoc/html/static/js/rustdoc.d.ts | 2 +- 7 files changed, 122 insertions(+), 20 deletions(-) diff --git a/src/librustdoc/formats/mod.rs b/src/librustdoc/formats/mod.rs index 2e8b5d9f59489..80c110ec07f56 100644 --- a/src/librustdoc/formats/mod.rs +++ b/src/librustdoc/formats/mod.rs @@ -76,4 +76,8 @@ impl Impl { }; true } + + pub(crate) fn is_negative_trait_impl(&self) -> bool { + self.inner_impl().is_negative_trait_impl() + } } diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs index 0c511738d7c8a..47e5876263526 100644 --- a/src/librustdoc/html/render/print_item.rs +++ b/src/librustdoc/html/render/print_item.rs @@ -646,6 +646,27 @@ fn item_function(cx: &Context<'_>, it: &clean::Item, f: &clean::Function) -> imp }) } +/// Struct used to handle insertion of "negative impl" marker in the generated DOM. +/// +/// This marker appears once in all trait impl lists to divide negative impls from positive impls. +struct NegativeMarker { + inserted_negative_marker: bool, +} + +impl NegativeMarker { + fn new() -> Self { + Self { inserted_negative_marker: false } + } + + fn insert_if_needed(&mut self, w: &mut fmt::Formatter<'_>, implementor: &Impl) -> fmt::Result { + if !self.inserted_negative_marker && !implementor.is_negative_trait_impl() { + write!(w, "
")?; + self.inserted_negative_marker = true; + } + Ok(()) + } +} + fn item_trait(cx: &Context<'_>, it: &clean::Item, t: &clean::Trait) -> impl fmt::Display { fmt::from_fn(|w| { let tcx = cx.tcx(); @@ -1072,7 +1093,9 @@ fn item_trait(cx: &Context<'_>, it: &clean::Item, t: &clean::Trait) -> impl fmt: "
", ) )?; + let mut negative_marker = NegativeMarker::new(); for implementor in concrete { + negative_marker.insert_if_needed(w, implementor)?; write!(w, "{}", render_implementor(cx, implementor, it, &implementor_dups, &[]))?; } w.write_str("
")?; @@ -1088,7 +1111,9 @@ fn item_trait(cx: &Context<'_>, it: &clean::Item, t: &clean::Trait) -> impl fmt: "
", ) )?; + let mut negative_marker = NegativeMarker::new(); for implementor in synthetic { + negative_marker.insert_if_needed(w, implementor)?; write!( w, "{}", @@ -2302,11 +2327,18 @@ where } #[derive(PartialEq, Eq)] -struct ImplString(String); +struct ImplString { + rendered: String, + is_negative: bool, +} impl ImplString { fn new(i: &Impl, cx: &Context<'_>) -> ImplString { - ImplString(format!("{}", print_impl(i.inner_impl(), false, cx))) + let impl_ = i.inner_impl(); + ImplString { + is_negative: impl_.is_negative_trait_impl(), + rendered: format!("{}", print_impl(impl_, false, cx)), + } } } @@ -2318,7 +2350,12 @@ impl PartialOrd for ImplString { impl Ord for ImplString { fn cmp(&self, other: &Self) -> Ordering { - compare_names(&self.0, &other.0) + // We sort negative impls first. + match (self.is_negative, other.is_negative) { + (false, true) => Ordering::Greater, + (true, false) => Ordering::Less, + _ => compare_names(&self.rendered, &other.rendered), + } } } diff --git a/src/librustdoc/html/render/write_shared.rs b/src/librustdoc/html/render/write_shared.rs index 6bf116c3b75ad..ca33531174b1b 100644 --- a/src/librustdoc/html/render/write_shared.rs +++ b/src/librustdoc/html/render/write_shared.rs @@ -14,6 +14,7 @@ //! or contains "invocation-specific". use std::cell::RefCell; +use std::cmp::Ordering; use std::ffi::{OsStr, OsString}; use std::fs::File; use std::io::{self, Write as _}; @@ -47,6 +48,7 @@ use crate::formats::item_type::ItemType; use crate::html::format::{print_impl, print_path}; use crate::html::layout; use crate::html::render::ordered_json::{EscapedJson, OrderedJson}; +use crate::html::render::print_item::compare_names; use crate::html::render::search_index::{SerializedSearchIndex, build_index}; use crate::html::render::sorted_template::{self, FileFormat, SortedTemplate}; use crate::html::render::{AssocItemLink, ImplRenderingParameters, StylePath}; @@ -667,7 +669,7 @@ impl TraitAliasPart { fn blank() -> SortedTemplate<::FileFormat> { SortedTemplate::from_before_after( r"(function() { - var implementors = Object.fromEntries([", + const implementors = Object.fromEntries([", r"]); if (window.register_implementors) { window.register_implementors(implementors); @@ -720,10 +722,12 @@ impl TraitAliasPart { { None } else { + let impl_ = imp.inner_impl(); Some(Implementor { - text: print_impl(imp.inner_impl(), false, cx).to_string(), + text: print_impl(impl_, false, cx).to_string(), synthetic: imp.inner_impl().kind.is_auto(), types: collect_paths_for_type(&imp.inner_impl().for_, cache), + is_negative: impl_.is_negative_trait_impl(), }) } }) @@ -742,8 +746,15 @@ impl TraitAliasPart { } path.push(format!("{remote_item_type}.{}.js", remote_path[remote_path.len() - 1])); - let part = OrderedJson::array_sorted( - implementors.map(|implementor| OrderedJson::serialize(implementor).unwrap()), + let mut implementors = implementors.collect::>(); + implementors.sort_unstable(); + + let part = OrderedJson::array_unsorted( + implementors + .iter() + .map(OrderedJson::serialize) + .collect::, _>>() + .unwrap(), ); path_parts.push(path, OrderedJson::array_unsorted([crate_name_json, &part])); } @@ -751,10 +762,12 @@ impl TraitAliasPart { } } +#[derive(Eq)] struct Implementor { text: String, synthetic: bool, types: Vec, + is_negative: bool, } impl Serialize for Implementor { @@ -764,6 +777,7 @@ impl Serialize for Implementor { { let mut seq = serializer.serialize_seq(None)?; seq.serialize_element(&self.text)?; + seq.serialize_element(if self.is_negative { &1 } else { &0 })?; if self.synthetic { seq.serialize_element(&1)?; seq.serialize_element(&self.types)?; @@ -772,6 +786,29 @@ impl Serialize for Implementor { } } +impl PartialEq for Implementor { + fn eq(&self, other: &Self) -> bool { + self.cmp(other) == Ordering::Equal + } +} + +impl PartialOrd for Implementor { + fn partial_cmp(&self, other: &Self) -> Option { + Some(Ord::cmp(self, other)) + } +} + +impl Ord for Implementor { + fn cmp(&self, other: &Self) -> Ordering { + // We sort negative impls first. + match (self.is_negative, other.is_negative) { + (false, true) => Ordering::Greater, + (true, false) => Ordering::Less, + _ => compare_names(&self.text, &other.text), + } + } +} + /// Collect the list of aliased types and their aliases. /// /// diff --git a/src/librustdoc/html/render/write_shared/tests.rs b/src/librustdoc/html/render/write_shared/tests.rs index 1989a1f87aae6..48d592c22f6f0 100644 --- a/src/librustdoc/html/render/write_shared/tests.rs +++ b/src/librustdoc/html/render/write_shared/tests.rs @@ -68,7 +68,7 @@ fn trait_alias_template() { assert_eq!( but_last_line(&template.to_string()), r#"(function() { - var implementors = Object.fromEntries([]); + const implementors = Object.fromEntries([]); if (window.register_implementors) { window.register_implementors(implementors); } else { @@ -80,7 +80,7 @@ fn trait_alias_template() { assert_eq!( but_last_line(&template.to_string()), r#"(function() { - var implementors = Object.fromEntries([["a"]]); + const implementors = Object.fromEntries([["a"]]); if (window.register_implementors) { window.register_implementors(implementors); } else { @@ -92,7 +92,7 @@ fn trait_alias_template() { assert_eq!( but_last_line(&template.to_string()), r#"(function() { - var implementors = Object.fromEntries([["a"],["b"]]); + const implementors = Object.fromEntries([["a"],["b"]]); if (window.register_implementors) { window.register_implementors(implementors); } else { diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css index 7f47856948493..36f44a8072af9 100644 --- a/src/librustdoc/html/static/css/rustdoc.css +++ b/src/librustdoc/html/static/css/rustdoc.css @@ -2976,6 +2976,9 @@ in src-script.js and main.js { margin-bottom: 0.75em; } +.negative-marker { + display: none; +} .variants > .docblock, .implementors-toggle > .docblock, diff --git a/src/librustdoc/html/static/js/main.js b/src/librustdoc/html/static/js/main.js index dff40485e9a90..fb6b704921818 100644 --- a/src/librustdoc/html/static/js/main.js +++ b/src/librustdoc/html/static/js/main.js @@ -800,21 +800,34 @@ function preLoadCss(cssUrl) { // window.register_implementors = imp => { - const implementors = document.getElementById("implementors-list"); - const synthetic_implementors = document.getElementById("synthetic-implementors-list"); + /** Takes an ID as input and returns a list of two elements. The first element is the DOM + * element with the given ID and the second is the "negative marker", meaning the location + * between the negative and non-negative impls. + * + * @param {string} id: ID of the DOM element. + * + * @return {[HTMLElement|null, HTMLElement|null]} + */ + function implementorsElems(id) { + const elem = document.getElementById(id); + return [elem, elem ? elem.querySelector(".negative-marker") : null]; + } + const implementors = implementorsElems("implementors-list"); + const synthetic_implementors = implementorsElems("synthetic-implementors-list"); const inlined_types = new Set(); const TEXT_IDX = 0; - const SYNTHETIC_IDX = 1; - const TYPES_IDX = 2; + const IS_NEG_IDX = 1; + const SYNTHETIC_IDX = 2; + const TYPES_IDX = 3; - if (synthetic_implementors) { + if (synthetic_implementors[0]) { // This `inlined_types` variable is used to avoid having the same implementation // showing up twice. For example "String" in the "Sync" doc page. // // By the way, this is only used by and useful for traits implemented automatically // (like "Send" and "Sync"). - onEachLazy(synthetic_implementors.getElementsByClassName("impl"), el => { + onEachLazy(synthetic_implementors[0].getElementsByClassName("impl"), el => { const aliases = el.getAttribute("data-aliases"); if (!aliases) { return; @@ -827,7 +840,7 @@ function preLoadCss(cssUrl) { } // @ts-expect-error - let currentNbImpls = implementors.getElementsByClassName("impl").length; + let currentNbImpls = implementors[0].getElementsByClassName("impl").length; // @ts-expect-error const traitName = document.querySelector(".main-heading h1 > .trait").textContent; const baseIdName = "impl-" + traitName + "-"; @@ -884,8 +897,16 @@ function preLoadCss(cssUrl) { addClass(display, "impl"); display.appendChild(anchor); display.appendChild(code); - // @ts-expect-error - list.appendChild(display); + + // If this is a negative implementor, we put it into the right location (just + // before the negative impl marker). + if (struct[IS_NEG_IDX]) { + // @ts-expect-error + list[1].before(display); + } else { + // @ts-expect-error + list[0].appendChild(display); + } currentNbImpls += 1; } } diff --git a/src/librustdoc/html/static/js/rustdoc.d.ts b/src/librustdoc/html/static/js/rustdoc.d.ts index 18d3b6a455d3e..60df4fc10b8c5 100644 --- a/src/librustdoc/html/static/js/rustdoc.d.ts +++ b/src/librustdoc/html/static/js/rustdoc.d.ts @@ -520,7 +520,7 @@ declare namespace rustdoc { * Provided by generated `trait.impl` files. */ type Implementors = { - [key: string]: Array<[string, number, Array]> + [key: string]: Array<[string, 0|1, number, Array]> } type TypeImpls = { From 985178dacf81d2baa21d0261c2cc85b374d0dd8c Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Wed, 11 Jun 2025 22:19:47 +0200 Subject: [PATCH 2/4] Add GUI test to ensure that negative impls are correctly sorted --- tests/rustdoc-gui/implementors.goml | 47 +++++++++++++++---- .../rustdoc-gui/src/lib2/implementors/lib.rs | 5 ++ tests/rustdoc-gui/src/lib2/lib.rs | 3 ++ 3 files changed, 45 insertions(+), 10 deletions(-) diff --git a/tests/rustdoc-gui/implementors.goml b/tests/rustdoc-gui/implementors.goml index b39b95c1a9bff..99fb9b9aff54f 100644 --- a/tests/rustdoc-gui/implementors.goml +++ b/tests/rustdoc-gui/implementors.goml @@ -2,18 +2,45 @@ // have the same display than the "local" ones. go-to: "file://" + |DOC_PATH| + "/implementors/trait.Whatever.html" assert: "#implementors-list" -// There are supposed to be two implementors listed. -assert-count: ("#implementors-list .impl", 2) +// There are supposed to be four implementors listed. +assert-count: ("#implementors-list .impl", 4) +// There are supposed to be two non-negative implementors. +assert-count: ("#implementors-list .negative-marker ~ *", 2) // Now we check that both implementors have an anchor, an ID and a similar DOM. -assert: ("#implementors-list .impl:nth-child(1) > a.anchor") -assert-attribute: ("#implementors-list .impl:nth-child(1)", {"id": "impl-Whatever-for-Struct"}) -assert-attribute: ("#implementors-list .impl:nth-child(1) > a.anchor", {"href": "#impl-Whatever-for-Struct"}) -assert: "#implementors-list .impl:nth-child(1) > .code-header" +define-function: ( + "check-dom", + [id], + block { + assert-attribute: (|id| + " > a.anchor", {"href": |id|}) + assert: |id| + " > .code-header" + }, +) + +call-function: ("check-dom", {"id": "#impl-Whatever-for-Struct2"}) +call-function: ("check-dom", {"id": "#impl-Whatever-2"}) +call-function: ("check-dom", {"id": "#impl-Whatever-for-Struct"}) +call-function: ("check-dom", {"id": "#impl-Whatever-3"}) -assert: ("#implementors-list .impl:nth-child(2) > a.anchor") -assert-attribute: ("#implementors-list .impl:nth-child(2)", {"id": "impl-Whatever-1"}) -assert-attribute: ("#implementors-list .impl:nth-child(2) > a.anchor", {"href": "#impl-Whatever-1"}) -assert: "#implementors-list .impl:nth-child(2) > .code-header" +// Ensure that negative impl are sorted first. +assert-property: ( + "#implementors-list > *:nth-child(1) > h3", + {"textContent": "impl !Whatever for Struct2"}, +) +assert-property: ( + "#implementors-list > *:nth-child(2) > h3", + {"textContent": "impl !Whatever for StructToImplOnReexport"}, +) +// Third one is the negative marker. +assert-attribute: ("#implementors-list > *:nth-child(3)", {"class": "negative-marker"}) +// This one is a `` so the selector is a bit different. +assert-property: ( + "#implementors-list > *:nth-child(4) section > h3", + {"textContent": "impl Whatever for Struct"}, +) +assert-property: ( + "#implementors-list > *:nth-child(5) > h3", + {"textContent": "impl Whatever for Foo"}, +) go-to: "file://" + |DOC_PATH| + "/test_docs/struct.HasEmptyTraits.html" compare-elements-position-near-false: ( diff --git a/tests/rustdoc-gui/src/lib2/implementors/lib.rs b/tests/rustdoc-gui/src/lib2/implementors/lib.rs index 2842ac50dc1e8..f081820039275 100644 --- a/tests/rustdoc-gui/src/lib2/implementors/lib.rs +++ b/tests/rustdoc-gui/src/lib2/implementors/lib.rs @@ -1,3 +1,5 @@ +#![feature(negative_impls)] + pub trait Whatever { type Foo; @@ -5,11 +7,14 @@ pub trait Whatever { } pub struct Struct; +pub struct Struct2; impl Whatever for Struct { type Foo = u8; } +impl !Whatever for Struct2 {} + impl http::HttpTrait for Struct {} mod traits { diff --git a/tests/rustdoc-gui/src/lib2/lib.rs b/tests/rustdoc-gui/src/lib2/lib.rs index 400488cbe8570..b87fdeea89da0 100644 --- a/tests/rustdoc-gui/src/lib2/lib.rs +++ b/tests/rustdoc-gui/src/lib2/lib.rs @@ -1,6 +1,7 @@ // ignore-tidy-linelength #![feature(doc_cfg)] +#![feature(negative_impls)] pub mod another_folder; pub mod another_mod; @@ -60,6 +61,8 @@ impl implementors::Whatever for Foo { type Foo = u32; } +impl !implementors::Whatever for StructToImplOnReexport {} + #[doc(inline)] pub use implementors::TraitToReexport; From 13091ddc932be37c0ad3db7581e5f7776cf429d0 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Tue, 24 Jun 2025 17:05:43 +0200 Subject: [PATCH 3/4] Make implementors list visible only when filled --- src/librustdoc/html/static/css/noscript.css | 4 ++++ src/librustdoc/html/static/css/rustdoc.css | 6 ++++++ src/librustdoc/html/static/js/main.js | 14 ++++++++++---- tests/rustdoc-gui/implementors.goml | 7 ++++++- 4 files changed, 26 insertions(+), 5 deletions(-) diff --git a/src/librustdoc/html/static/css/noscript.css b/src/librustdoc/html/static/css/noscript.css index 5c02e2eb26a3e..6b80d3e7bbee6 100644 --- a/src/librustdoc/html/static/css/noscript.css +++ b/src/librustdoc/html/static/css/noscript.css @@ -29,6 +29,10 @@ nav.sub { display: none; } +#synthetic-implementors-list, #implementors-list { + display: block; +} + /* Begin: styles for themes Keep the default light and dark themes synchronized with the ones in rustdoc.css */ diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css index 36f44a8072af9..a5e43e1d7d19b 100644 --- a/src/librustdoc/html/static/css/rustdoc.css +++ b/src/librustdoc/html/static/css/rustdoc.css @@ -1158,6 +1158,12 @@ div.where { margin-left: calc(var(--docblock-indent) + var(--impl-items-indent)); } +#synthetic-implementors-list, #implementors-list { + /* To prevent layout shift when loading the page with extra implementors being loaded + from JS, we hide the list until it's complete. */ + display: none; +} + .item-info code { font-size: 0.875rem; } diff --git a/src/librustdoc/html/static/js/main.js b/src/librustdoc/html/static/js/main.js index fb6b704921818..72d7e61318142 100644 --- a/src/librustdoc/html/static/js/main.js +++ b/src/librustdoc/html/static/js/main.js @@ -813,7 +813,7 @@ function preLoadCss(cssUrl) { return [elem, elem ? elem.querySelector(".negative-marker") : null]; } const implementors = implementorsElems("implementors-list"); - const synthetic_implementors = implementorsElems("synthetic-implementors-list"); + const syntheticImplementors = implementorsElems("synthetic-implementors-list"); const inlined_types = new Set(); const TEXT_IDX = 0; @@ -821,13 +821,13 @@ function preLoadCss(cssUrl) { const SYNTHETIC_IDX = 2; const TYPES_IDX = 3; - if (synthetic_implementors[0]) { + if (syntheticImplementors[0]) { // This `inlined_types` variable is used to avoid having the same implementation // showing up twice. For example "String" in the "Sync" doc page. // // By the way, this is only used by and useful for traits implemented automatically // (like "Send" and "Sync"). - onEachLazy(synthetic_implementors[0].getElementsByClassName("impl"), el => { + onEachLazy(syntheticImplementors[0].getElementsByClassName("impl"), el => { const aliases = el.getAttribute("data-aliases"); if (!aliases) { return; @@ -862,7 +862,7 @@ function preLoadCss(cssUrl) { struct_loop: for (const struct of structs) { - const list = struct[SYNTHETIC_IDX] ? synthetic_implementors : implementors; + const list = struct[SYNTHETIC_IDX] ? syntheticImplementors : implementors; // The types list is only used for synthetic impls. // If this changes, `main.js` and `write_shared.rs` both need changed. @@ -910,6 +910,12 @@ function preLoadCss(cssUrl) { currentNbImpls += 1; } } + if (implementors[0]) { + implementors[0].style.display = "block"; + } + if (syntheticImplementors[0]) { + syntheticImplementors[0].style.display = "block"; + } }; if (window.pending_implementors) { window.register_implementors(window.pending_implementors); diff --git a/tests/rustdoc-gui/implementors.goml b/tests/rustdoc-gui/implementors.goml index 99fb9b9aff54f..d4542c6f78172 100644 --- a/tests/rustdoc-gui/implementors.goml +++ b/tests/rustdoc-gui/implementors.goml @@ -1,7 +1,7 @@ // The goal of this test is to check that the external trait implementors, generated with JS, // have the same display than the "local" ones. go-to: "file://" + |DOC_PATH| + "/implementors/trait.Whatever.html" -assert: "#implementors-list" +wait-for-css: ("#implementors-list", {"display": "block"}) // There are supposed to be four implementors listed. assert-count: ("#implementors-list .impl", 4) // There are supposed to be two non-negative implementors. @@ -66,3 +66,8 @@ assert-count: ("#implementors-list .impl", 1) go-to: "file://" + |DOC_PATH| + "/http/trait.HttpTrait.html" assert-count: ("#implementors-list .impl", 1) assert-attribute: ("#implementors-list .impl a.trait", {"href": "../http/trait.HttpTrait.html"}) + +// Now we check that if JS is disabled, the implementors list will be visible. +javascript: false +reload: +assert-css: ("#implementors-list", {"display": "block"}) From fb9d3a5db4c3358f8ca22302f0e078675bc1a596 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Wed, 10 Dec 2025 17:34:08 +0100 Subject: [PATCH 4/4] Simplify the sorting of impls and use CSS classes instead of plain JS to show/hide implementors --- src/librustdoc/html/render/print_item.rs | 10 +++---- src/librustdoc/html/render/write_shared.rs | 33 +++++---------------- src/librustdoc/html/static/css/noscript.css | 2 +- src/librustdoc/html/static/css/rustdoc.css | 2 +- src/librustdoc/html/static/js/main.js | 4 +-- 5 files changed, 17 insertions(+), 34 deletions(-) diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs index 47e5876263526..f88a5b8974fb9 100644 --- a/src/librustdoc/html/render/print_item.rs +++ b/src/librustdoc/html/render/print_item.rs @@ -650,18 +650,18 @@ fn item_function(cx: &Context<'_>, it: &clean::Item, f: &clean::Function) -> imp /// /// This marker appears once in all trait impl lists to divide negative impls from positive impls. struct NegativeMarker { - inserted_negative_marker: bool, + inserted: bool, } impl NegativeMarker { fn new() -> Self { - Self { inserted_negative_marker: false } + Self { inserted: false } } fn insert_if_needed(&mut self, w: &mut fmt::Formatter<'_>, implementor: &Impl) -> fmt::Result { - if !self.inserted_negative_marker && !implementor.is_negative_trait_impl() { - write!(w, "
")?; - self.inserted_negative_marker = true; + if !self.inserted && !implementor.is_negative_trait_impl() { + w.write_str("
")?; + self.inserted = true; } Ok(()) } diff --git a/src/librustdoc/html/render/write_shared.rs b/src/librustdoc/html/render/write_shared.rs index ca33531174b1b..e0a37c95257c8 100644 --- a/src/librustdoc/html/render/write_shared.rs +++ b/src/librustdoc/html/render/write_shared.rs @@ -747,7 +747,14 @@ impl TraitAliasPart { path.push(format!("{remote_item_type}.{}.js", remote_path[remote_path.len() - 1])); let mut implementors = implementors.collect::>(); - implementors.sort_unstable(); + implementors.sort_unstable_by(|a, b| { + // We sort negative impls first. + match (a.is_negative, b.is_negative) { + (false, true) => Ordering::Greater, + (true, false) => Ordering::Less, + _ => compare_names(&a.text, &b.text), + } + }); let part = OrderedJson::array_unsorted( implementors @@ -762,7 +769,6 @@ impl TraitAliasPart { } } -#[derive(Eq)] struct Implementor { text: String, synthetic: bool, @@ -786,29 +792,6 @@ impl Serialize for Implementor { } } -impl PartialEq for Implementor { - fn eq(&self, other: &Self) -> bool { - self.cmp(other) == Ordering::Equal - } -} - -impl PartialOrd for Implementor { - fn partial_cmp(&self, other: &Self) -> Option { - Some(Ord::cmp(self, other)) - } -} - -impl Ord for Implementor { - fn cmp(&self, other: &Self) -> Ordering { - // We sort negative impls first. - match (self.is_negative, other.is_negative) { - (false, true) => Ordering::Greater, - (true, false) => Ordering::Less, - _ => compare_names(&self.text, &other.text), - } - } -} - /// Collect the list of aliased types and their aliases. /// /// diff --git a/src/librustdoc/html/static/css/noscript.css b/src/librustdoc/html/static/css/noscript.css index 6b80d3e7bbee6..6f44854b6914e 100644 --- a/src/librustdoc/html/static/css/noscript.css +++ b/src/librustdoc/html/static/css/noscript.css @@ -29,7 +29,7 @@ nav.sub { display: none; } -#synthetic-implementors-list, #implementors-list { +#synthetic-implementors-list:not(.loaded), #implementors-list:not(.loaded) { display: block; } diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css index a5e43e1d7d19b..69a79f2736e77 100644 --- a/src/librustdoc/html/static/css/rustdoc.css +++ b/src/librustdoc/html/static/css/rustdoc.css @@ -1158,7 +1158,7 @@ div.where { margin-left: calc(var(--docblock-indent) + var(--impl-items-indent)); } -#synthetic-implementors-list, #implementors-list { +#synthetic-implementors-list:not(.loaded), #implementors-list:not(.loaded) { /* To prevent layout shift when loading the page with extra implementors being loaded from JS, we hide the list until it's complete. */ display: none; diff --git a/src/librustdoc/html/static/js/main.js b/src/librustdoc/html/static/js/main.js index 72d7e61318142..476ecd42d6f91 100644 --- a/src/librustdoc/html/static/js/main.js +++ b/src/librustdoc/html/static/js/main.js @@ -911,10 +911,10 @@ function preLoadCss(cssUrl) { } } if (implementors[0]) { - implementors[0].style.display = "block"; + implementors[0].classList.add("loaded"); } if (syntheticImplementors[0]) { - syntheticImplementors[0].style.display = "block"; + syntheticImplementors[0].classList.add("loaded"); } }; if (window.pending_implementors) {