Skip to content

Commit d0ee71e

Browse files
Put negative implementors first and apply same ordering logic to foreign implementors
1 parent 40daf23 commit d0ee71e

File tree

7 files changed

+107
-20
lines changed

7 files changed

+107
-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: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -643,6 +643,25 @@ fn item_function(cx: &Context<'_>, it: &clean::Item, f: &clean::Function) -> imp
643643
})
644644
}
645645

646+
/// Struct used to handle insertion of "negative impl" marker in the generated DOM.
647+
struct NegativeMarker {
648+
inserted_negative_marker: bool,
649+
}
650+
651+
impl NegativeMarker {
652+
fn new() -> Self {
653+
Self { inserted_negative_marker: false }
654+
}
655+
656+
fn insert_if_needed(&mut self, w: &mut fmt::Formatter<'_>, implementor: &Impl) -> fmt::Result {
657+
if !self.inserted_negative_marker && !implementor.is_negative_trait_impl() {
658+
write!(w, "<div class=\"negative-marker\"></div>")?;
659+
self.inserted_negative_marker = true;
660+
}
661+
Ok(())
662+
}
663+
}
664+
646665
fn item_trait(cx: &Context<'_>, it: &clean::Item, t: &clean::Trait) -> impl fmt::Display {
647666
fmt::from_fn(|w| {
648667
let tcx = cx.tcx();
@@ -1069,7 +1088,9 @@ fn item_trait(cx: &Context<'_>, it: &clean::Item, t: &clean::Trait) -> impl fmt:
10691088
"<div id=\"implementors-list\">",
10701089
)
10711090
)?;
1091+
let mut negative_marker = NegativeMarker::new();
10721092
for implementor in concrete {
1093+
negative_marker.insert_if_needed(w, implementor)?;
10731094
write!(w, "{}", render_implementor(cx, implementor, it, &implementor_dups, &[]))?;
10741095
}
10751096
w.write_str("</div>")?;
@@ -1085,7 +1106,9 @@ fn item_trait(cx: &Context<'_>, it: &clean::Item, t: &clean::Trait) -> impl fmt:
10851106
"<div id=\"synthetic-implementors-list\">",
10861107
)
10871108
)?;
1109+
let mut negative_marker = NegativeMarker::new();
10881110
for implementor in synthetic {
1111+
negative_marker.insert_if_needed(w, implementor)?;
10891112
write!(
10901113
w,
10911114
"{}",
@@ -2302,11 +2325,18 @@ where
23022325
}
23032326

23042327
#[derive(PartialEq, Eq)]
2305-
struct ImplString(String);
2328+
struct ImplString {
2329+
rendered: String,
2330+
is_negative: bool,
2331+
}
23062332

23072333
impl ImplString {
23082334
fn new(i: &Impl, cx: &Context<'_>) -> ImplString {
2309-
ImplString(format!("{}", i.inner_impl().print(false, cx)))
2335+
let impl_ = i.inner_impl();
2336+
ImplString {
2337+
is_negative: impl_.is_negative_trait_impl(),
2338+
rendered: format!("{}", impl_.print(false, cx)),
2339+
}
23102340
}
23112341
}
23122342

@@ -2318,7 +2348,11 @@ impl PartialOrd for ImplString {
23182348

23192349
impl Ord for ImplString {
23202350
fn cmp(&self, other: &Self) -> Ordering {
2321-
compare_names(&self.0, &other.0)
2351+
match (self.is_negative, other.is_negative) {
2352+
(false, true) => Ordering::Greater,
2353+
(true, false) => Ordering::Less,
2354+
_ => compare_names(&self.rendered, &other.rendered),
2355+
}
23222356
}
23232357
}
23242358

src/librustdoc/html/render/write_shared.rs

Lines changed: 29 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::OsString;
1819
use std::fs::File;
1920
use std::io::{self, Write as _};
@@ -46,6 +47,7 @@ use crate::formats::Impl;
4647
use crate::formats::item_type::ItemType;
4748
use crate::html::layout;
4849
use crate::html::render::ordered_json::{EscapedJson, OrderedJson};
50+
use crate::html::render::print_item::compare_names;
4951
use crate::html::render::search_index::{SerializedSearchIndex, build_index};
5052
use crate::html::render::sorted_template::{self, FileFormat, SortedTemplate};
5153
use crate::html::render::{AssocItemLink, ImplRenderingParameters, StylePath};
@@ -703,7 +705,7 @@ impl TraitAliasPart {
703705
fn blank() -> SortedTemplate<<Self as CciPart>::FileFormat> {
704706
SortedTemplate::from_before_after(
705707
r"(function() {
706-
var implementors = Object.fromEntries([",
708+
const implementors = Object.fromEntries([",
707709
r"]);
708710
if (window.register_implementors) {
709711
window.register_implementors(implementors);
@@ -741,7 +743,7 @@ impl TraitAliasPart {
741743
},
742744
};
743745

744-
let implementors = imps
746+
let mut implementors = imps
745747
.iter()
746748
.filter_map(|imp| {
747749
// If the trait and implementation are in the same crate, then
@@ -756,10 +758,12 @@ impl TraitAliasPart {
756758
{
757759
None
758760
} else {
761+
let impl_ = imp.inner_impl();
759762
Some(Implementor {
760-
text: imp.inner_impl().print(false, cx).to_string(),
763+
text: impl_.print(false, cx).to_string(),
761764
synthetic: imp.inner_impl().kind.is_auto(),
762765
types: collect_paths_for_type(&imp.inner_impl().for_, cache),
766+
is_negative: impl_.is_negative_trait_impl(),
763767
})
764768
}
765769
})
@@ -778,7 +782,9 @@ impl TraitAliasPart {
778782
}
779783
path.push(format!("{remote_item_type}.{}.js", remote_path[remote_path.len() - 1]));
780784

781-
let part = OrderedJson::array_sorted(
785+
implementors.sort_unstable();
786+
787+
let part = OrderedJson::array_unsorted(
782788
implementors
783789
.iter()
784790
.map(OrderedJson::serialize)
@@ -791,10 +797,12 @@ impl TraitAliasPart {
791797
}
792798
}
793799

800+
#[derive(PartialEq, Eq)]
794801
struct Implementor {
795802
text: String,
796803
synthetic: bool,
797804
types: Vec<String>,
805+
is_negative: bool,
798806
}
799807

800808
impl Serialize for Implementor {
@@ -804,6 +812,7 @@ impl Serialize for Implementor {
804812
{
805813
let mut seq = serializer.serialize_seq(None)?;
806814
seq.serialize_element(&self.text)?;
815+
seq.serialize_element(if self.is_negative { &1 } else { &0 })?;
807816
if self.synthetic {
808817
seq.serialize_element(&1)?;
809818
seq.serialize_element(&self.types)?;
@@ -812,6 +821,22 @@ impl Serialize for Implementor {
812821
}
813822
}
814823

824+
impl PartialOrd for Implementor {
825+
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
826+
Some(Ord::cmp(self, other))
827+
}
828+
}
829+
830+
impl Ord for Implementor {
831+
fn cmp(&self, other: &Self) -> Ordering {
832+
match (self.is_negative, other.is_negative) {
833+
(false, true) => Ordering::Greater,
834+
(true, false) => Ordering::Less,
835+
_ => compare_names(&self.text, &other.text),
836+
}
837+
}
838+
}
839+
815840
/// Collect the list of aliased types and their aliases.
816841
/// <https://github.com/search?q=repo%3Arust-lang%2Frust+[RUSTDOCIMPL]+type.impl&type=code>
817842
///

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ fn trait_alias_template() {
113113
assert_eq!(
114114
but_last_line(&template.to_string()),
115115
r#"(function() {
116-
var implementors = Object.fromEntries([]);
116+
const implementors = Object.fromEntries([]);
117117
if (window.register_implementors) {
118118
window.register_implementors(implementors);
119119
} else {
@@ -125,7 +125,7 @@ fn trait_alias_template() {
125125
assert_eq!(
126126
but_last_line(&template.to_string()),
127127
r#"(function() {
128-
var implementors = Object.fromEntries([["a"]]);
128+
const implementors = Object.fromEntries([["a"]]);
129129
if (window.register_implementors) {
130130
window.register_implementors(implementors);
131131
} else {
@@ -137,7 +137,7 @@ fn trait_alias_template() {
137137
assert_eq!(
138138
but_last_line(&template.to_string()),
139139
r#"(function() {
140-
var implementors = Object.fromEntries([["a"],["b"]]);
140+
const implementors = Object.fromEntries([["a"],["b"]]);
141141
if (window.register_implementors) {
142142
window.register_implementors(implementors);
143143
} else {

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2776,6 +2776,9 @@ in src-script.js and main.js
27762776
{
27772777
margin-bottom: 0.75em;
27782778
}
2779+
.negative-marker {
2780+
display: none;
2781+
}
27792782

27802783
.variants > .docblock,
27812784
.implementors-toggle > .docblock,

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

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

675675
// <https://github.com/search?q=repo%3Arust-lang%2Frust+[RUSTDOCIMPL]+trait.impl&type=code>
676676
window.register_implementors = imp => {
677-
const implementors = document.getElementById("implementors-list");
678-
const synthetic_implementors = document.getElementById("synthetic-implementors-list");
677+
/** Takes an ID as input and returns a list of two elements. The first element is the DOM
678+
* element with the given ID and the second is the "negative marker", meaning the location
679+
* between the negative and non-negative impls.
680+
*
681+
* @param {string} id: ID of the DOM element.
682+
*
683+
* @return {[HTMLElement|null, HTMLElement|null]}
684+
*/
685+
function implementorsElems(id) {
686+
const elem = document.getElementById(id);
687+
return [elem, elem ? elem.querySelector(".negative-marker") : null];
688+
}
689+
const implementors = implementorsElems("implementors-list");
690+
const synthetic_implementors = implementorsElems("synthetic-implementors-list");
679691
const inlined_types = new Set();
680692

681693
const TEXT_IDX = 0;
682-
const SYNTHETIC_IDX = 1;
683-
const TYPES_IDX = 2;
694+
const IS_NEG_IDX = 1;
695+
const SYNTHETIC_IDX = 2;
696+
const TYPES_IDX = 3;
684697

685-
if (synthetic_implementors) {
698+
if (synthetic_implementors[0]) {
686699
// This `inlined_types` variable is used to avoid having the same implementation
687700
// showing up twice. For example "String" in the "Sync" doc page.
688701
//
689702
// By the way, this is only used by and useful for traits implemented automatically
690703
// (like "Send" and "Sync").
691-
onEachLazy(synthetic_implementors.getElementsByClassName("impl"), el => {
704+
onEachLazy(synthetic_implementors[0].getElementsByClassName("impl"), el => {
692705
const aliases = el.getAttribute("data-aliases");
693706
if (!aliases) {
694707
return;
@@ -701,7 +714,7 @@ function preLoadCss(cssUrl) {
701714
}
702715

703716
// @ts-expect-error
704-
let currentNbImpls = implementors.getElementsByClassName("impl").length;
717+
let currentNbImpls = implementors[0].getElementsByClassName("impl").length;
705718
// @ts-expect-error
706719
const traitName = document.querySelector(".main-heading h1 > .trait").textContent;
707720
const baseIdName = "impl-" + traitName + "-";
@@ -758,8 +771,16 @@ function preLoadCss(cssUrl) {
758771
addClass(display, "impl");
759772
display.appendChild(anchor);
760773
display.appendChild(code);
761-
// @ts-expect-error
762-
list.appendChild(display);
774+
775+
// If this is a negative implementor, we put it into the right location (just
776+
// before the negative impl marker).
777+
if (struct[IS_NEG_IDX]) {
778+
// @ts-expect-error
779+
list[1].before(display);
780+
} else {
781+
// @ts-expect-error
782+
list[0].appendChild(display);
783+
}
763784
currentNbImpls += 1;
764785
}
765786
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -467,7 +467,7 @@ declare namespace rustdoc {
467467
* Provied by generated `trait.impl` files.
468468
*/
469469
type Implementors = {
470-
[key: string]: Array<[string, number, Array<string>]>
470+
[key: string]: Array<[string, number, number, Array<string>]>
471471
}
472472

473473
type TypeImpls = {

0 commit comments

Comments
 (0)