Skip to content

Commit 4509163

Browse files
Put negative implementors first and apply same ordering logic to foreign implementors
1 parent bad5026 commit 4509163

File tree

7 files changed

+122
-20
lines changed

7 files changed

+122
-20
lines changed

src/librustdoc/formats/mod.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,4 +76,8 @@ impl Impl {
7676
};
7777
true
7878
}
79+
80+
pub(crate) fn is_negative_trait_impl(&self) -> bool {
81+
self.inner_impl().is_negative_trait_impl()
82+
}
7983
}

src/librustdoc/html/render/print_item.rs

Lines changed: 40 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -646,6 +646,27 @@ fn item_function(cx: &Context<'_>, it: &clean::Item, f: &clean::Function) -> imp
646646
})
647647
}
648648

649+
/// Struct used to handle insertion of "negative impl" marker in the generated DOM.
650+
///
651+
/// This marker appears once in all trait impl lists to divide negative impls from positive impls.
652+
struct NegativeMarker {
653+
inserted_negative_marker: bool,
654+
}
655+
656+
impl NegativeMarker {
657+
fn new() -> Self {
658+
Self { inserted_negative_marker: false }
659+
}
660+
661+
fn insert_if_needed(&mut self, w: &mut fmt::Formatter<'_>, implementor: &Impl) -> fmt::Result {
662+
if !self.inserted_negative_marker && !implementor.is_negative_trait_impl() {
663+
write!(w, "<div class=\"negative-marker\"></div>")?;
664+
self.inserted_negative_marker = true;
665+
}
666+
Ok(())
667+
}
668+
}
669+
649670
fn item_trait(cx: &Context<'_>, it: &clean::Item, t: &clean::Trait) -> impl fmt::Display {
650671
fmt::from_fn(|w| {
651672
let tcx = cx.tcx();
@@ -1072,7 +1093,9 @@ fn item_trait(cx: &Context<'_>, it: &clean::Item, t: &clean::Trait) -> impl fmt:
10721093
"<div id=\"implementors-list\">",
10731094
)
10741095
)?;
1096+
let mut negative_marker = NegativeMarker::new();
10751097
for implementor in concrete {
1098+
negative_marker.insert_if_needed(w, implementor)?;
10761099
write!(w, "{}", render_implementor(cx, implementor, it, &implementor_dups, &[]))?;
10771100
}
10781101
w.write_str("</div>")?;
@@ -1088,7 +1111,9 @@ fn item_trait(cx: &Context<'_>, it: &clean::Item, t: &clean::Trait) -> impl fmt:
10881111
"<div id=\"synthetic-implementors-list\">",
10891112
)
10901113
)?;
1114+
let mut negative_marker = NegativeMarker::new();
10911115
for implementor in synthetic {
1116+
negative_marker.insert_if_needed(w, implementor)?;
10921117
write!(
10931118
w,
10941119
"{}",
@@ -2302,11 +2327,18 @@ where
23022327
}
23032328

23042329
#[derive(PartialEq, Eq)]
2305-
struct ImplString(String);
2330+
struct ImplString {
2331+
rendered: String,
2332+
is_negative: bool,
2333+
}
23062334

23072335
impl ImplString {
23082336
fn new(i: &Impl, cx: &Context<'_>) -> ImplString {
2309-
ImplString(format!("{}", print_impl(i.inner_impl(), false, cx)))
2337+
let impl_ = i.inner_impl();
2338+
ImplString {
2339+
is_negative: impl_.is_negative_trait_impl(),
2340+
rendered: format!("{}", print_impl(impl_, false, cx)),
2341+
}
23102342
}
23112343
}
23122344

@@ -2318,7 +2350,12 @@ impl PartialOrd for ImplString {
23182350

23192351
impl Ord for ImplString {
23202352
fn cmp(&self, other: &Self) -> Ordering {
2321-
compare_names(&self.0, &other.0)
2353+
// We sort negative impls first.
2354+
match (self.is_negative, other.is_negative) {
2355+
(false, true) => Ordering::Greater,
2356+
(true, false) => Ordering::Less,
2357+
_ => compare_names(&self.rendered, &other.rendered),
2358+
}
23222359
}
23232360
}
23242361

src/librustdoc/html/render/write_shared.rs

Lines changed: 41 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
//! or contains "invocation-specific".
1515
1616
use std::cell::RefCell;
17+
use std::cmp::Ordering;
1718
use std::ffi::{OsStr, OsString};
1819
use std::fs::File;
1920
use std::io::{self, Write as _};
@@ -47,6 +48,7 @@ use crate::formats::item_type::ItemType;
4748
use crate::html::format::{print_impl, print_path};
4849
use crate::html::layout;
4950
use crate::html::render::ordered_json::{EscapedJson, OrderedJson};
51+
use crate::html::render::print_item::compare_names;
5052
use crate::html::render::search_index::{SerializedSearchIndex, build_index};
5153
use crate::html::render::sorted_template::{self, FileFormat, SortedTemplate};
5254
use crate::html::render::{AssocItemLink, ImplRenderingParameters, StylePath};
@@ -667,7 +669,7 @@ impl TraitAliasPart {
667669
fn blank() -> SortedTemplate<<Self as CciPart>::FileFormat> {
668670
SortedTemplate::from_before_after(
669671
r"(function() {
670-
var implementors = Object.fromEntries([",
672+
const implementors = Object.fromEntries([",
671673
r"]);
672674
if (window.register_implementors) {
673675
window.register_implementors(implementors);
@@ -720,10 +722,12 @@ impl TraitAliasPart {
720722
{
721723
None
722724
} else {
725+
let impl_ = imp.inner_impl();
723726
Some(Implementor {
724-
text: print_impl(imp.inner_impl(), false, cx).to_string(),
727+
text: print_impl(impl_, false, cx).to_string(),
725728
synthetic: imp.inner_impl().kind.is_auto(),
726729
types: collect_paths_for_type(&imp.inner_impl().for_, cache),
730+
is_negative: impl_.is_negative_trait_impl(),
727731
})
728732
}
729733
})
@@ -742,19 +746,28 @@ impl TraitAliasPart {
742746
}
743747
path.push(format!("{remote_item_type}.{}.js", remote_path[remote_path.len() - 1]));
744748

745-
let part = OrderedJson::array_sorted(
746-
implementors.map(|implementor| OrderedJson::serialize(implementor).unwrap()),
749+
let mut implementors = implementors.collect::<Vec<_>>();
750+
implementors.sort_unstable();
751+
752+
let part = OrderedJson::array_unsorted(
753+
implementors
754+
.iter()
755+
.map(OrderedJson::serialize)
756+
.collect::<Result<Vec<_>, _>>()
757+
.unwrap(),
747758
);
748759
path_parts.push(path, OrderedJson::array_unsorted([crate_name_json, &part]));
749760
}
750761
Ok(path_parts)
751762
}
752763
}
753764

765+
#[derive(Eq)]
754766
struct Implementor {
755767
text: String,
756768
synthetic: bool,
757769
types: Vec<String>,
770+
is_negative: bool,
758771
}
759772

760773
impl Serialize for Implementor {
@@ -764,6 +777,7 @@ impl Serialize for Implementor {
764777
{
765778
let mut seq = serializer.serialize_seq(None)?;
766779
seq.serialize_element(&self.text)?;
780+
seq.serialize_element(if self.is_negative { &1 } else { &0 })?;
767781
if self.synthetic {
768782
seq.serialize_element(&1)?;
769783
seq.serialize_element(&self.types)?;
@@ -772,6 +786,29 @@ impl Serialize for Implementor {
772786
}
773787
}
774788

789+
impl PartialEq for Implementor {
790+
fn eq(&self, other: &Self) -> bool {
791+
self.cmp(other) == Ordering::Equal
792+
}
793+
}
794+
795+
impl PartialOrd for Implementor {
796+
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
797+
Some(Ord::cmp(self, other))
798+
}
799+
}
800+
801+
impl Ord for Implementor {
802+
fn cmp(&self, other: &Self) -> Ordering {
803+
// We sort negative impls first.
804+
match (self.is_negative, other.is_negative) {
805+
(false, true) => Ordering::Greater,
806+
(true, false) => Ordering::Less,
807+
_ => compare_names(&self.text, &other.text),
808+
}
809+
}
810+
}
811+
775812
/// Collect the list of aliased types and their aliases.
776813
/// <https://github.com/search?q=repo%3Arust-lang%2Frust+[RUSTDOCIMPL]+type.impl&type=code>
777814
///

src/librustdoc/html/render/write_shared/tests.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ fn trait_alias_template() {
6868
assert_eq!(
6969
but_last_line(&template.to_string()),
7070
r#"(function() {
71-
var implementors = Object.fromEntries([]);
71+
const implementors = Object.fromEntries([]);
7272
if (window.register_implementors) {
7373
window.register_implementors(implementors);
7474
} else {
@@ -80,7 +80,7 @@ fn trait_alias_template() {
8080
assert_eq!(
8181
but_last_line(&template.to_string()),
8282
r#"(function() {
83-
var implementors = Object.fromEntries([["a"]]);
83+
const implementors = Object.fromEntries([["a"]]);
8484
if (window.register_implementors) {
8585
window.register_implementors(implementors);
8686
} else {
@@ -92,7 +92,7 @@ fn trait_alias_template() {
9292
assert_eq!(
9393
but_last_line(&template.to_string()),
9494
r#"(function() {
95-
var implementors = Object.fromEntries([["a"],["b"]]);
95+
const implementors = Object.fromEntries([["a"],["b"]]);
9696
if (window.register_implementors) {
9797
window.register_implementors(implementors);
9898
} else {

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2976,6 +2976,9 @@ in src-script.js and main.js
29762976
{
29772977
margin-bottom: 0.75em;
29782978
}
2979+
.negative-marker {
2980+
display: none;
2981+
}
29792982

29802983
.variants > .docblock,
29812984
.implementors-toggle > .docblock,

src/librustdoc/html/static/js/main.js

Lines changed: 30 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -800,21 +800,34 @@ function preLoadCss(cssUrl) {
800800

801801
// <https://github.com/search?q=repo%3Arust-lang%2Frust+[RUSTDOCIMPL]+trait.impl&type=code>
802802
window.register_implementors = imp => {
803-
const implementors = document.getElementById("implementors-list");
804-
const synthetic_implementors = document.getElementById("synthetic-implementors-list");
803+
/** Takes an ID as input and returns a list of two elements. The first element is the DOM
804+
* element with the given ID and the second is the "negative marker", meaning the location
805+
* between the negative and non-negative impls.
806+
*
807+
* @param {string} id: ID of the DOM element.
808+
*
809+
* @return {[HTMLElement|null, HTMLElement|null]}
810+
*/
811+
function implementorsElems(id) {
812+
const elem = document.getElementById(id);
813+
return [elem, elem ? elem.querySelector(".negative-marker") : null];
814+
}
815+
const implementors = implementorsElems("implementors-list");
816+
const synthetic_implementors = implementorsElems("synthetic-implementors-list");
805817
const inlined_types = new Set();
806818

807819
const TEXT_IDX = 0;
808-
const SYNTHETIC_IDX = 1;
809-
const TYPES_IDX = 2;
820+
const IS_NEG_IDX = 1;
821+
const SYNTHETIC_IDX = 2;
822+
const TYPES_IDX = 3;
810823

811-
if (synthetic_implementors) {
824+
if (synthetic_implementors[0]) {
812825
// This `inlined_types` variable is used to avoid having the same implementation
813826
// showing up twice. For example "String" in the "Sync" doc page.
814827
//
815828
// By the way, this is only used by and useful for traits implemented automatically
816829
// (like "Send" and "Sync").
817-
onEachLazy(synthetic_implementors.getElementsByClassName("impl"), el => {
830+
onEachLazy(synthetic_implementors[0].getElementsByClassName("impl"), el => {
818831
const aliases = el.getAttribute("data-aliases");
819832
if (!aliases) {
820833
return;
@@ -827,7 +840,7 @@ function preLoadCss(cssUrl) {
827840
}
828841

829842
// @ts-expect-error
830-
let currentNbImpls = implementors.getElementsByClassName("impl").length;
843+
let currentNbImpls = implementors[0].getElementsByClassName("impl").length;
831844
// @ts-expect-error
832845
const traitName = document.querySelector(".main-heading h1 > .trait").textContent;
833846
const baseIdName = "impl-" + traitName + "-";
@@ -884,8 +897,16 @@ function preLoadCss(cssUrl) {
884897
addClass(display, "impl");
885898
display.appendChild(anchor);
886899
display.appendChild(code);
887-
// @ts-expect-error
888-
list.appendChild(display);
900+
901+
// If this is a negative implementor, we put it into the right location (just
902+
// before the negative impl marker).
903+
if (struct[IS_NEG_IDX]) {
904+
// @ts-expect-error
905+
list[1].before(display);
906+
} else {
907+
// @ts-expect-error
908+
list[0].appendChild(display);
909+
}
889910
currentNbImpls += 1;
890911
}
891912
}

src/librustdoc/html/static/js/rustdoc.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -520,7 +520,7 @@ declare namespace rustdoc {
520520
* Provided by generated `trait.impl` files.
521521
*/
522522
type Implementors = {
523-
[key: string]: Array<[string, number, Array<string>]>
523+
[key: string]: Array<[string, 0|1, number, Array<string>]>
524524
}
525525

526526
type TypeImpls = {

0 commit comments

Comments
 (0)