Skip to content

Commit dd0e2eb

Browse files
Put negative implementors first and apply same ordering logic to foreign implementors
1 parent 38bc246 commit dd0e2eb

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
@@ -647,6 +647,27 @@ fn item_function(cx: &Context<'_>, it: &clean::Item, f: &clean::Function) -> imp
647647
})
648648
}
649649

650+
/// Struct used to handle insertion of "negative impl" marker in the generated DOM.
651+
///
652+
/// This marker appears once in all trait impl lists to divide negative impls from positive impls.
653+
struct NegativeMarker {
654+
inserted_negative_marker: bool,
655+
}
656+
657+
impl NegativeMarker {
658+
fn new() -> Self {
659+
Self { inserted_negative_marker: false }
660+
}
661+
662+
fn insert_if_needed(&mut self, w: &mut fmt::Formatter<'_>, implementor: &Impl) -> fmt::Result {
663+
if !self.inserted_negative_marker && !implementor.is_negative_trait_impl() {
664+
write!(w, "<div class=\"negative-marker\"></div>")?;
665+
self.inserted_negative_marker = true;
666+
}
667+
Ok(())
668+
}
669+
}
670+
650671
fn item_trait(cx: &Context<'_>, it: &clean::Item, t: &clean::Trait) -> impl fmt::Display {
651672
fmt::from_fn(|w| {
652673
let tcx = cx.tcx();
@@ -1073,7 +1094,9 @@ fn item_trait(cx: &Context<'_>, it: &clean::Item, t: &clean::Trait) -> impl fmt:
10731094
"<div id=\"implementors-list\">",
10741095
)
10751096
)?;
1097+
let mut negative_marker = NegativeMarker::new();
10761098
for implementor in concrete {
1099+
negative_marker.insert_if_needed(w, implementor)?;
10771100
write!(w, "{}", render_implementor(cx, implementor, it, &implementor_dups, &[]))?;
10781101
}
10791102
w.write_str("</div>")?;
@@ -1089,7 +1112,9 @@ fn item_trait(cx: &Context<'_>, it: &clean::Item, t: &clean::Trait) -> impl fmt:
10891112
"<div id=\"synthetic-implementors-list\">",
10901113
)
10911114
)?;
1115+
let mut negative_marker = NegativeMarker::new();
10921116
for implementor in synthetic {
1117+
negative_marker.insert_if_needed(w, implementor)?;
10931118
write!(
10941119
w,
10951120
"{}",
@@ -2305,11 +2330,18 @@ where
23052330
}
23062331

23072332
#[derive(PartialEq, Eq)]
2308-
struct ImplString(String);
2333+
struct ImplString {
2334+
rendered: String,
2335+
is_negative: bool,
2336+
}
23092337

23102338
impl ImplString {
23112339
fn new(i: &Impl, cx: &Context<'_>) -> ImplString {
2312-
ImplString(format!("{}", i.inner_impl().print(false, cx)))
2340+
let impl_ = i.inner_impl();
2341+
ImplString {
2342+
is_negative: impl_.is_negative_trait_impl(),
2343+
rendered: format!("{}", impl_.print(false, cx)),
2344+
}
23132345
}
23142346
}
23152347

@@ -2321,7 +2353,12 @@ impl PartialOrd for ImplString {
23212353

23222354
impl Ord for ImplString {
23232355
fn cmp(&self, other: &Self) -> Ordering {
2324-
compare_names(&self.0, &other.0)
2356+
// We sort negative impls first.
2357+
match (self.is_negative, other.is_negative) {
2358+
(false, true) => Ordering::Greater,
2359+
(true, false) => Ordering::Less,
2360+
_ => compare_names(&self.rendered, &other.rendered),
2361+
}
23252362
}
23262363
}
23272364

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};
@@ -650,7 +652,7 @@ impl TraitAliasPart {
650652
fn blank() -> SortedTemplate<<Self as CciPart>::FileFormat> {
651653
SortedTemplate::from_before_after(
652654
r"(function() {
653-
var implementors = Object.fromEntries([",
655+
const implementors = Object.fromEntries([",
654656
r"]);
655657
if (window.register_implementors) {
656658
window.register_implementors(implementors);
@@ -703,10 +705,12 @@ impl TraitAliasPart {
703705
{
704706
None
705707
} else {
708+
let impl_ = imp.inner_impl();
706709
Some(Implementor {
707-
text: imp.inner_impl().print(false, cx).to_string(),
710+
text: impl_.print(false, cx).to_string(),
708711
synthetic: imp.inner_impl().kind.is_auto(),
709712
types: collect_paths_for_type(&imp.inner_impl().for_, cache),
713+
is_negative: impl_.is_negative_trait_impl(),
710714
})
711715
}
712716
})
@@ -725,19 +729,28 @@ impl TraitAliasPart {
725729
}
726730
path.push(format!("{remote_item_type}.{}.js", remote_path[remote_path.len() - 1]));
727731

728-
let part = OrderedJson::array_sorted(
729-
implementors.map(|implementor| OrderedJson::serialize(implementor).unwrap()),
732+
let mut implementors = implementors.collect::<Vec<_>>();
733+
implementors.sort_unstable();
734+
735+
let part = OrderedJson::array_unsorted(
736+
implementors
737+
.iter()
738+
.map(OrderedJson::serialize)
739+
.collect::<Result<Vec<_>, _>>()
740+
.unwrap(),
730741
);
731742
path_parts.push(path, OrderedJson::array_unsorted([crate_name_json, &part]));
732743
}
733744
Ok(path_parts)
734745
}
735746
}
736747

748+
#[derive(Eq)]
737749
struct Implementor {
738750
text: String,
739751
synthetic: bool,
740752
types: Vec<String>,
753+
is_negative: bool,
741754
}
742755

743756
impl Serialize for Implementor {
@@ -747,6 +760,7 @@ impl Serialize for Implementor {
747760
{
748761
let mut seq = serializer.serialize_seq(None)?;
749762
seq.serialize_element(&self.text)?;
763+
seq.serialize_element(if self.is_negative { &1 } else { &0 })?;
750764
if self.synthetic {
751765
seq.serialize_element(&1)?;
752766
seq.serialize_element(&self.types)?;
@@ -755,6 +769,29 @@ impl Serialize for Implementor {
755769
}
756770
}
757771

772+
impl PartialEq for Implementor {
773+
fn eq(&self, other: &Self) -> bool {
774+
self.cmp(other) == Ordering::Equal
775+
}
776+
}
777+
778+
impl PartialOrd for Implementor {
779+
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
780+
Some(Ord::cmp(self, other))
781+
}
782+
}
783+
784+
impl Ord for Implementor {
785+
fn cmp(&self, other: &Self) -> Ordering {
786+
// We sort negative impls first.
787+
match (self.is_negative, other.is_negative) {
788+
(false, true) => Ordering::Greater,
789+
(true, false) => Ordering::Less,
790+
_ => compare_names(&self.text, &other.text),
791+
}
792+
}
793+
}
794+
758795
/// Collect the list of aliased types and their aliases.
759796
/// <https://github.com/search?q=repo%3Arust-lang%2Frust+[RUSTDOCIMPL]+type.impl&type=code>
760797
///

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
@@ -799,21 +799,34 @@ function preLoadCss(cssUrl) {
799799

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

806818
const TEXT_IDX = 0;
807-
const SYNTHETIC_IDX = 1;
808-
const TYPES_IDX = 2;
819+
const IS_NEG_IDX = 1;
820+
const SYNTHETIC_IDX = 2;
821+
const TYPES_IDX = 3;
809822

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

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

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)