Skip to content

Commit 6c071f7

Browse files
Put negative implementors first and apply same ordering logic to foreign implementors
1 parent 22be76b commit 6c071f7

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
@@ -643,6 +643,27 @@ 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+
///
648+
/// This marker appears once in all trait impl lists to divide negative impls from positive impls.
649+
struct NegativeMarker {
650+
inserted_negative_marker: bool,
651+
}
652+
653+
impl NegativeMarker {
654+
fn new() -> Self {
655+
Self { inserted_negative_marker: false }
656+
}
657+
658+
fn insert_if_needed(&mut self, w: &mut fmt::Formatter<'_>, implementor: &Impl) -> fmt::Result {
659+
if !self.inserted_negative_marker && !implementor.is_negative_trait_impl() {
660+
write!(w, "<div class=\"negative-marker\"></div>")?;
661+
self.inserted_negative_marker = true;
662+
}
663+
Ok(())
664+
}
665+
}
666+
646667
fn item_trait(cx: &Context<'_>, it: &clean::Item, t: &clean::Trait) -> impl fmt::Display {
647668
fmt::from_fn(|w| {
648669
let tcx = cx.tcx();
@@ -1069,7 +1090,9 @@ fn item_trait(cx: &Context<'_>, it: &clean::Item, t: &clean::Trait) -> impl fmt:
10691090
"<div id=\"implementors-list\">",
10701091
)
10711092
)?;
1093+
let mut negative_marker = NegativeMarker::new();
10721094
for implementor in concrete {
1095+
negative_marker.insert_if_needed(w, implementor)?;
10731096
write!(w, "{}", render_implementor(cx, implementor, it, &implementor_dups, &[]))?;
10741097
}
10751098
w.write_str("</div>")?;
@@ -1085,7 +1108,9 @@ fn item_trait(cx: &Context<'_>, it: &clean::Item, t: &clean::Trait) -> impl fmt:
10851108
"<div id=\"synthetic-implementors-list\">",
10861109
)
10871110
)?;
1111+
let mut negative_marker = NegativeMarker::new();
10881112
for implementor in synthetic {
1113+
negative_marker.insert_if_needed(w, implementor)?;
10891114
write!(
10901115
w,
10911116
"{}",
@@ -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!("{}", i.inner_impl().print(false, cx)))
2337+
let impl_ = i.inner_impl();
2338+
ImplString {
2339+
is_negative: impl_.is_negative_trait_impl(),
2340+
rendered: format!("{}", impl_.print(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::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};
@@ -695,7 +697,7 @@ impl TraitAliasPart {
695697
fn blank() -> SortedTemplate<<Self as CciPart>::FileFormat> {
696698
SortedTemplate::from_before_after(
697699
r"(function() {
698-
var implementors = Object.fromEntries([",
700+
const implementors = Object.fromEntries([",
699701
r"]);
700702
if (window.register_implementors) {
701703
window.register_implementors(implementors);
@@ -748,10 +750,12 @@ impl TraitAliasPart {
748750
{
749751
None
750752
} else {
753+
let impl_ = imp.inner_impl();
751754
Some(Implementor {
752-
text: imp.inner_impl().print(false, cx).to_string(),
755+
text: impl_.print(false, cx).to_string(),
753756
synthetic: imp.inner_impl().kind.is_auto(),
754757
types: collect_paths_for_type(&imp.inner_impl().for_, cache),
758+
is_negative: impl_.is_negative_trait_impl(),
755759
})
756760
}
757761
})
@@ -770,19 +774,28 @@ impl TraitAliasPart {
770774
}
771775
path.push(format!("{remote_item_type}.{}.js", remote_path[remote_path.len() - 1]));
772776

773-
let part = OrderedJson::array_sorted(
774-
implementors.map(|implementor| OrderedJson::serialize(implementor).unwrap()),
777+
let mut implementors = implementors.collect::<Vec<_>>();
778+
implementors.sort_unstable();
779+
780+
let part = OrderedJson::array_unsorted(
781+
implementors
782+
.iter()
783+
.map(OrderedJson::serialize)
784+
.collect::<Result<Vec<_>, _>>()
785+
.unwrap(),
775786
);
776787
path_parts.push(path, OrderedJson::array_unsorted([crate_name_json, &part]));
777788
}
778789
Ok(path_parts)
779790
}
780791
}
781792

793+
#[derive(Eq)]
782794
struct Implementor {
783795
text: String,
784796
synthetic: bool,
785797
types: Vec<String>,
798+
is_negative: bool,
786799
}
787800

788801
impl Serialize for Implementor {
@@ -792,6 +805,7 @@ impl Serialize for Implementor {
792805
{
793806
let mut seq = serializer.serialize_seq(None)?;
794807
seq.serialize_element(&self.text)?;
808+
seq.serialize_element(if self.is_negative { &1 } else { &0 })?;
795809
if self.synthetic {
796810
seq.serialize_element(&1)?;
797811
seq.serialize_element(&self.types)?;
@@ -800,6 +814,29 @@ impl Serialize for Implementor {
800814
}
801815
}
802816

817+
impl PartialEq for Implementor {
818+
fn eq(&self, other: &Self) -> bool {
819+
self.cmp(other) == Ordering::Equal
820+
}
821+
}
822+
823+
impl PartialOrd for Implementor {
824+
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
825+
Some(Ord::cmp(self, other))
826+
}
827+
}
828+
829+
impl Ord for Implementor {
830+
fn cmp(&self, other: &Self) -> Ordering {
831+
// We sort negative impls first.
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+
803840
/// Collect the list of aliased types and their aliases.
804841
/// <https://github.com/search?q=repo%3Arust-lang%2Frust+[RUSTDOCIMPL]+type.impl&type=code>
805842
///

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ fn trait_alias_template() {
101101
assert_eq!(
102102
but_last_line(&template.to_string()),
103103
r#"(function() {
104-
var implementors = Object.fromEntries([]);
104+
const implementors = Object.fromEntries([]);
105105
if (window.register_implementors) {
106106
window.register_implementors(implementors);
107107
} else {
@@ -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([["a"]]);
116+
const implementors = Object.fromEntries([["a"]]);
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"],["b"]]);
128+
const implementors = Object.fromEntries([["a"],["b"]]);
129129
if (window.register_implementors) {
130130
window.register_implementors(implementors);
131131
} 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
@@ -465,7 +465,7 @@ declare namespace rustdoc {
465465
* Provied by generated `trait.impl` files.
466466
*/
467467
type Implementors = {
468-
[key: string]: Array<[string, number, Array<string>]>
468+
[key: string]: Array<[string, 0|1, number, Array<string>]>
469469
}
470470

471471
type TypeImpls = {

0 commit comments

Comments
 (0)