Skip to content

Commit 303653e

Browse files
committed
rustdoc: use javascript to layout notable traits popups
Fixes #102576
1 parent 8e0cac1 commit 303653e

16 files changed

+260
-90
lines changed

src/librustdoc/html/format.rs

-4
Original file line numberDiff line numberDiff line change
@@ -107,10 +107,6 @@ impl Buffer {
107107
self.buffer
108108
}
109109

110-
pub(crate) fn insert_str(&mut self, idx: usize, s: &str) {
111-
self.buffer.insert_str(idx, s);
112-
}
113-
114110
pub(crate) fn push_str(&mut self, s: &str) {
115111
self.buffer.push_str(s);
116112
}

src/librustdoc/html/render/context.rs

+6-1
Original file line numberDiff line numberDiff line change
@@ -69,11 +69,13 @@ pub(crate) struct Context<'tcx> {
6969
/// the source files are present in the html rendering, then this will be
7070
/// `true`.
7171
pub(crate) include_sources: bool,
72+
/// Collection of all types with notable traits referenced in the current module.
73+
pub(crate) types_with_notable_traits: FxHashSet<clean::Type>,
7274
}
7375

7476
// `Context` is cloned a lot, so we don't want the size to grow unexpectedly.
7577
#[cfg(all(not(windows), target_arch = "x86_64", target_pointer_width = "64"))]
76-
rustc_data_structures::static_assert_size!(Context<'_>, 128);
78+
rustc_data_structures::static_assert_size!(Context<'_>, 160);
7779

7880
/// Shared mutable state used in [`Context`] and elsewhere.
7981
pub(crate) struct SharedContext<'tcx> {
@@ -532,6 +534,7 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
532534
deref_id_map: FxHashMap::default(),
533535
shared: Rc::new(scx),
534536
include_sources,
537+
types_with_notable_traits: FxHashSet::default(),
535538
};
536539

537540
if emit_crate {
@@ -560,6 +563,7 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
560563
id_map: IdMap::new(),
561564
shared: Rc::clone(&self.shared),
562565
include_sources: self.include_sources,
566+
types_with_notable_traits: FxHashSet::default(),
563567
}
564568
}
565569

@@ -803,6 +807,7 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
803807
}
804808
}
805809
}
810+
806811
Ok(())
807812
}
808813

src/librustdoc/html/render/mod.rs

+97-48
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ use rustc_span::{
5959
symbol::{sym, Symbol},
6060
BytePos, FileName, RealFileName,
6161
};
62-
use serde::ser::SerializeSeq;
62+
use serde::ser::{SerializeMap, SerializeSeq};
6363
use serde::{Serialize, Serializer};
6464

6565
use crate::clean::{self, ItemId, RenderedLink, SelfTy};
@@ -803,7 +803,7 @@ fn assoc_method(
803803
d: &clean::FnDecl,
804804
link: AssocItemLink<'_>,
805805
parent: ItemType,
806-
cx: &Context<'_>,
806+
cx: &mut Context<'_>,
807807
render_mode: RenderMode,
808808
) {
809809
let tcx = cx.tcx();
@@ -836,6 +836,8 @@ fn assoc_method(
836836
+ name.as_str().len()
837837
+ generics_len;
838838

839+
let notable_traits = d.output.as_return().and_then(|output| notable_traits_button(output, cx));
840+
839841
let (indent, indent_str, end_newline) = if parent == ItemType::Trait {
840842
header_len += 4;
841843
let indent_str = " ";
@@ -861,13 +863,9 @@ fn assoc_method(
861863
name = name,
862864
generics = g.print(cx),
863865
decl = d.full_print(header_len, indent, cx),
864-
notable_traits = d
865-
.output
866-
.as_return()
867-
.and_then(|output| notable_traits_decl(output, cx))
868-
.unwrap_or_default(),
866+
notable_traits = notable_traits.unwrap_or_default(),
869867
where_clause = print_where_clause(g, cx, indent, end_newline),
870-
)
868+
);
871869
}
872870

873871
/// Writes a span containing the versions at which an item became stable and/or const-stable. For
@@ -967,7 +965,7 @@ fn render_assoc_item(
967965
item: &clean::Item,
968966
link: AssocItemLink<'_>,
969967
parent: ItemType,
970-
cx: &Context<'_>,
968+
cx: &mut Context<'_>,
971969
render_mode: RenderMode,
972970
) {
973971
match &*item.kind {
@@ -1277,8 +1275,8 @@ fn should_render_item(item: &clean::Item, deref_mut_: bool, tcx: TyCtxt<'_>) ->
12771275
}
12781276
}
12791277

1280-
fn notable_traits_decl(ty: &clean::Type, cx: &Context<'_>) -> Option<String> {
1281-
let mut out = Buffer::html();
1278+
pub(crate) fn notable_traits_button(ty: &clean::Type, cx: &mut Context<'_>) -> Option<String> {
1279+
let mut has_notable_trait = false;
12821280

12831281
let did = ty.def_id(cx.cache())?;
12841282

@@ -1291,6 +1289,7 @@ fn notable_traits_decl(ty: &clean::Type, cx: &Context<'_>) -> Option<String> {
12911289
{
12921290
return None;
12931291
}
1292+
12941293
if let Some(impls) = cx.cache().impls.get(&did) {
12951294
for i in impls {
12961295
let impl_ = i.inner_impl();
@@ -1304,56 +1303,106 @@ fn notable_traits_decl(ty: &clean::Type, cx: &Context<'_>) -> Option<String> {
13041303

13051304
if cx.cache().traits.get(&trait_did).map_or(false, |t| t.is_notable_trait(cx.tcx()))
13061305
{
1307-
if out.is_empty() {
1308-
write!(
1309-
&mut out,
1310-
"<span class=\"notable\">Notable traits for {}</span>\
1311-
<code class=\"content\">",
1312-
impl_.for_.print(cx)
1313-
);
1314-
}
1306+
has_notable_trait = true;
1307+
}
1308+
}
1309+
}
1310+
}
1311+
1312+
if has_notable_trait {
1313+
cx.types_with_notable_traits.insert(ty.clone());
1314+
Some(format!(
1315+
"<span class=\"notable-traits\" data-ty=\"{ty:#}\">\
1316+
<span class=\"notable-traits-tooltip\">ⓘ</span>\
1317+
</span>",
1318+
ty = ty.print(cx),
1319+
))
1320+
} else {
1321+
None
1322+
}
1323+
}
1324+
1325+
fn notable_traits_decl(ty: &clean::Type, cx: &Context<'_>) -> (String, String) {
1326+
let mut out = Buffer::html();
1327+
1328+
let did = ty.def_id(cx.cache()).expect("notable_traits_button already checked this");
13151329

1316-
//use the "where" class here to make it small
1330+
let impls = cx.cache().impls.get(&did).expect("notable_traits_button already checked this");
1331+
1332+
for i in impls {
1333+
let impl_ = i.inner_impl();
1334+
if !impl_.for_.without_borrowed_ref().is_same(ty.without_borrowed_ref(), cx.cache()) {
1335+
// Two different types might have the same did,
1336+
// without actually being the same.
1337+
continue;
1338+
}
1339+
if let Some(trait_) = &impl_.trait_ {
1340+
let trait_did = trait_.def_id();
1341+
1342+
if cx.cache().traits.get(&trait_did).map_or(false, |t| t.is_notable_trait(cx.tcx())) {
1343+
if out.is_empty() {
13171344
write!(
13181345
&mut out,
1319-
"<span class=\"where fmt-newline\">{}</span>",
1320-
impl_.print(false, cx)
1346+
"<h3 class=\"notable\">Notable traits for <code>{}</code></h3>\
1347+
<pre class=\"content\"><code>",
1348+
impl_.for_.print(cx)
13211349
);
1322-
for it in &impl_.items {
1323-
if let clean::AssocTypeItem(ref tydef, ref _bounds) = *it.kind {
1324-
out.push_str("<span class=\"where fmt-newline\"> ");
1325-
let empty_set = FxHashSet::default();
1326-
let src_link = AssocItemLink::GotoSource(trait_did.into(), &empty_set);
1327-
assoc_type(
1328-
&mut out,
1329-
it,
1330-
&tydef.generics,
1331-
&[], // intentionally leaving out bounds
1332-
Some(&tydef.type_),
1333-
src_link,
1334-
0,
1335-
cx,
1336-
);
1337-
out.push_str(";</span>");
1338-
}
1350+
}
1351+
1352+
//use the "where" class here to make it small
1353+
write!(
1354+
&mut out,
1355+
"<span class=\"where fmt-newline\">{}</span>",
1356+
impl_.print(false, cx)
1357+
);
1358+
for it in &impl_.items {
1359+
if let clean::AssocTypeItem(ref tydef, ref _bounds) = *it.kind {
1360+
out.push_str("<span class=\"where fmt-newline\"> ");
1361+
let empty_set = FxHashSet::default();
1362+
let src_link = AssocItemLink::GotoSource(trait_did.into(), &empty_set);
1363+
assoc_type(
1364+
&mut out,
1365+
it,
1366+
&tydef.generics,
1367+
&[], // intentionally leaving out bounds
1368+
Some(&tydef.type_),
1369+
src_link,
1370+
0,
1371+
cx,
1372+
);
1373+
out.push_str(";</span>");
13391374
}
13401375
}
13411376
}
13421377
}
13431378
}
1344-
13451379
if out.is_empty() {
1346-
return None;
1380+
write!(&mut out, "</code></pre>",);
13471381
}
13481382

1349-
out.insert_str(
1350-
0,
1351-
"<span class=\"notable-traits\"><span class=\"notable-traits-tooltip\">ⓘ\
1352-
<span class=\"notable-traits-tooltiptext\"><span class=\"docblock\">",
1353-
);
1354-
out.push_str("</code></span></span></span></span>");
1383+
(format!("{:#}", ty.print(cx)), out.into_inner())
1384+
}
13551385

1356-
Some(out.into_inner())
1386+
pub(crate) fn notable_traits_json<'a>(
1387+
tys: impl Iterator<Item = &'a clean::Type>,
1388+
cx: &Context<'_>,
1389+
) -> String {
1390+
let mp: Vec<(String, String)> = tys.map(|ty| notable_traits_decl(ty, cx)).collect();
1391+
struct NotableTraitsMap(Vec<(String, String)>);
1392+
impl Serialize for NotableTraitsMap {
1393+
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1394+
where
1395+
S: Serializer,
1396+
{
1397+
let mut map = serializer.serialize_map(Some(self.0.len()))?;
1398+
for item in &self.0 {
1399+
map.serialize_entry(&item.0, &item.1)?;
1400+
}
1401+
map.end()
1402+
}
1403+
}
1404+
serde_json::to_string(&NotableTraitsMap(mp))
1405+
.expect("serialize (string, string) -> json object cannot fail")
13571406
}
13581407

13591408
#[derive(Clone, Copy, Debug)]

src/librustdoc/html/render/print_item.rs

+19-10
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,10 @@ use std::rc::Rc;
1717

1818
use super::{
1919
collect_paths_for_type, document, ensure_trailing_slash, get_filtered_impls_for_reference,
20-
item_ty_to_section, notable_traits_decl, render_all_impls, render_assoc_item,
21-
render_assoc_items, render_attributes_in_code, render_attributes_in_pre, render_impl,
22-
render_rightside, render_stability_since_raw, AssocItemLink, Context, ImplRenderingParameters,
20+
item_ty_to_section, notable_traits_button, notable_traits_json, render_all_impls,
21+
render_assoc_item, render_assoc_items, render_attributes_in_code, render_attributes_in_pre,
22+
render_impl, render_rightside, render_stability_since_raw, AssocItemLink, Context,
23+
ImplRenderingParameters,
2324
};
2425
use crate::clean;
2526
use crate::config::ModuleSorting;
@@ -183,6 +184,16 @@ pub(super) fn print_item(
183184
unreachable!();
184185
}
185186
}
187+
188+
// Render notable-traits.js used for all methods in this module.
189+
if !cx.types_with_notable_traits.is_empty() {
190+
write!(
191+
buf,
192+
r#"<script type="text/json" id="notable-traits-data">{}</script>"#,
193+
notable_traits_json(cx.types_with_notable_traits.iter(), cx)
194+
);
195+
cx.types_with_notable_traits.clear();
196+
}
186197
}
187198

188199
/// For large structs, enums, unions, etc, determine whether to hide their fields
@@ -516,6 +527,9 @@ fn item_function(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, f: &cle
516527
+ name.as_str().len()
517528
+ generics_len;
518529

530+
let notable_traits =
531+
f.decl.output.as_return().and_then(|output| notable_traits_button(output, cx));
532+
519533
wrap_into_item_decl(w, |w| {
520534
wrap_item(w, "fn", |w| {
521535
render_attributes_in_pre(w, it, "");
@@ -533,16 +547,11 @@ fn item_function(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, f: &cle
533547
generics = f.generics.print(cx),
534548
where_clause = print_where_clause(&f.generics, cx, 0, Ending::Newline),
535549
decl = f.decl.full_print(header_len, 0, cx),
536-
notable_traits = f
537-
.decl
538-
.output
539-
.as_return()
540-
.and_then(|output| notable_traits_decl(output, cx))
541-
.unwrap_or_default(),
550+
notable_traits = notable_traits.unwrap_or_default(),
542551
);
543552
});
544553
});
545-
document(w, cx, it, None, HeadingOffset::H2)
554+
document(w, cx, it, None, HeadingOffset::H2);
546555
}
547556

548557
fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean::Trait) {

src/librustdoc/html/static/css/noscript.css

+6
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,9 @@ nav.sub {
2222
.source .sidebar {
2323
display: none;
2424
}
25+
26+
.notable-traits {
27+
/* layout requires javascript
28+
https://github.com/rust-lang/rust/issues/102576 */
29+
display: none;
30+
}

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

+14-9
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,8 @@ h4.code-header {
183183
font-weight: 600;
184184
margin: 0;
185185
padding: 0;
186+
/* position notable traits in mobile mode within the header */
187+
position: relative;
186188
}
187189

188190
#crate-search,
@@ -1268,13 +1270,12 @@ h3.variant {
12681270
cursor: pointer;
12691271
}
12701272

1271-
.notable-traits:hover .notable-traits-tooltiptext,
1272-
.notable-traits .notable-traits-tooltiptext.force-tooltip {
1273+
.notable-traits .notable-traits-tooltiptext {
12731274
display: inline-block;
1275+
visibility: hidden;
12741276
}
12751277

1276-
.notable-traits .notable-traits-tooltiptext {
1277-
display: none;
1278+
.notable-traits-tooltiptext {
12781279
padding: 5px 3px 3px 3px;
12791280
border-radius: 6px;
12801281
margin-left: 5px;
@@ -1292,22 +1293,26 @@ h3.variant {
12921293
content: "\00a0\00a0\00a0";
12931294
}
12941295

1295-
.notable-traits .docblock {
1296+
.notable-traits-tooltiptext .docblock {
12961297
margin: 0;
12971298
}
12981299

1299-
.notable-traits .notable {
1300-
margin: 0;
1301-
margin-bottom: 13px;
1300+
.notable-traits-tooltiptext .notable {
13021301
font-size: 1.1875rem;
13031302
font-weight: 600;
13041303
display: block;
13051304
}
13061305

1307-
.notable-traits .docblock code.content {
1306+
.notable-traits-tooltiptext pre, .notable-traits-tooltiptext code {
1307+
background: transparent;
1308+
}
1309+
1310+
.notable-traits-tooltiptext .docblock pre.content {
13081311
margin: 0;
13091312
padding: 0;
13101313
font-size: 1.25rem;
1314+
white-space: pre-wrap;
1315+
overflow: hidden;
13111316
}
13121317

13131318
.search-failed {

0 commit comments

Comments
 (0)