Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

rustdoc: use javascript to layout notable traits popups #104129

Merged
merged 5 commits into from
Nov 11, 2022
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 0 additions & 4 deletions src/librustdoc/html/format.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,10 +107,6 @@ impl Buffer {
self.buffer
}

pub(crate) fn insert_str(&mut self, idx: usize, s: &str) {
self.buffer.insert_str(idx, s);
}

pub(crate) fn push_str(&mut self, s: &str) {
self.buffer.push_str(s);
}
Expand Down
7 changes: 6 additions & 1 deletion src/librustdoc/html/render/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,11 +69,13 @@ pub(crate) struct Context<'tcx> {
/// the source files are present in the html rendering, then this will be
/// `true`.
pub(crate) include_sources: bool,
/// Collection of all types with notable traits referenced in the current module.
pub(crate) types_with_notable_traits: FxHashSet<clean::Type>,
}

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

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

if emit_crate {
Expand Down Expand Up @@ -560,6 +563,7 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
id_map: IdMap::new(),
shared: Rc::clone(&self.shared),
include_sources: self.include_sources,
types_with_notable_traits: FxHashSet::default(),
}
}

Expand Down Expand Up @@ -803,6 +807,7 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
}
}
}

Ok(())
}

Expand Down
195 changes: 122 additions & 73 deletions src/librustdoc/html/render/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ use rustc_span::{
symbol::{sym, Symbol},
BytePos, FileName, RealFileName,
};
use serde::ser::SerializeSeq;
use serde::ser::{SerializeMap, SerializeSeq};
use serde::{Serialize, Serializer};

use crate::clean::{self, ItemId, RenderedLink, SelfTy};
Expand Down Expand Up @@ -803,7 +803,7 @@ fn assoc_method(
d: &clean::FnDecl,
link: AssocItemLink<'_>,
parent: ItemType,
cx: &Context<'_>,
cx: &mut Context<'_>,
render_mode: RenderMode,
) {
let tcx = cx.tcx();
Expand Down Expand Up @@ -836,6 +836,8 @@ fn assoc_method(
+ name.as_str().len()
+ generics_len;

let notable_traits = d.output.as_return().and_then(|output| notable_traits_button(output, cx));

let (indent, indent_str, end_newline) = if parent == ItemType::Trait {
header_len += 4;
let indent_str = " ";
Expand All @@ -861,9 +863,9 @@ fn assoc_method(
name = name,
generics = g.print(cx),
decl = d.full_print(header_len, indent, cx),
notable_traits = notable_traits_decl(d, cx),
notable_traits = notable_traits.unwrap_or_default(),
where_clause = print_where_clause(g, cx, indent, end_newline),
)
);
}

/// Writes a span containing the versions at which an item became stable and/or const-stable. For
Expand Down Expand Up @@ -963,7 +965,7 @@ fn render_assoc_item(
item: &clean::Item,
link: AssocItemLink<'_>,
parent: ItemType,
cx: &Context<'_>,
cx: &mut Context<'_>,
render_mode: RenderMode,
) {
match &*item.kind {
Expand Down Expand Up @@ -1273,88 +1275,135 @@ fn should_render_item(item: &clean::Item, deref_mut_: bool, tcx: TyCtxt<'_>) ->
}
}

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

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

if let Some((did, ty)) = decl.output.as_return().and_then(|t| Some((t.def_id(cx.cache())?, t)))
// Box has pass-through impls for Read, Write, Iterator, and Future when the
// boxed type implements one of those. We don't want to treat every Box return
// as being notably an Iterator (etc), though, so we exempt it. Pin has the same
// issue, with a pass-through impl for Future.
if Some(did) == cx.tcx().lang_items().owned_box()
|| Some(did) == cx.tcx().lang_items().pin_type()
{
// Box has pass-through impls for Read, Write, Iterator, and Future when the
// boxed type implements one of those. We don't want to treat every Box return
// as being notably an Iterator (etc), though, so we exempt it. Pin has the same
// issue, with a pass-through impl for Future.
if Some(did) == cx.tcx().lang_items().owned_box()
|| Some(did) == cx.tcx().lang_items().pin_type()
{
return "".to_string();
}
if let Some(impls) = cx.cache().impls.get(&did) {
for i in impls {
let impl_ = i.inner_impl();
if !impl_.for_.without_borrowed_ref().is_same(ty.without_borrowed_ref(), cx.cache())
return None;
}

if let Some(impls) = cx.cache().impls.get(&did) {
for i in impls {
let impl_ = i.inner_impl();
if !impl_.for_.without_borrowed_ref().is_same(ty.without_borrowed_ref(), cx.cache()) {
// Two different types might have the same did,
// without actually being the same.
continue;
}
if let Some(trait_) = &impl_.trait_ {
let trait_did = trait_.def_id();

if cx.cache().traits.get(&trait_did).map_or(false, |t| t.is_notable_trait(cx.tcx()))
{
// Two different types might have the same did,
// without actually being the same.
continue;
has_notable_trait = true;
}
}
}
}

if has_notable_trait {
cx.types_with_notable_traits.insert(ty.clone());
Some(format!(
"<span class=\"notable-traits\" data-ty=\"{ty:#}\">\
<span class=\"notable-traits-tooltip\">ⓘ</span>\
</span>",
ty = ty.print(cx),
))
} else {
None
}
}

fn notable_traits_decl(ty: &clean::Type, cx: &Context<'_>) -> (String, String) {
let mut out = Buffer::html();

let did = ty.def_id(cx.cache()).expect("notable_traits_button already checked this");

let impls = cx.cache().impls.get(&did).expect("notable_traits_button already checked this");

for i in impls {
let impl_ = i.inner_impl();
if !impl_.for_.without_borrowed_ref().is_same(ty.without_borrowed_ref(), cx.cache()) {
// Two different types might have the same did,
// without actually being the same.
continue;
}
if let Some(trait_) = &impl_.trait_ {
let trait_did = trait_.def_id();

if cx.cache().traits.get(&trait_did).map_or(false, |t| t.is_notable_trait(cx.tcx())) {
if out.is_empty() {
write!(
&mut out,
"<h3 class=\"notable\">Notable traits for <code>{}</code></h3>\
<pre class=\"content\"><code>",
impl_.for_.print(cx)
);
}
if let Some(trait_) = &impl_.trait_ {
let trait_did = trait_.def_id();

if cx
.cache()
.traits
.get(&trait_did)
.map_or(false, |t| t.is_notable_trait(cx.tcx()))
{
if out.is_empty() {
write!(
&mut out,
"<span class=\"notable\">Notable traits for {}</span>\
<code class=\"content\">",
impl_.for_.print(cx)
);
}

//use the "where" class here to make it small
write!(
//use the "where" class here to make it small
write!(
&mut out,
"<span class=\"where fmt-newline\">{}</span>",
impl_.print(false, cx)
);
for it in &impl_.items {
if let clean::AssocTypeItem(ref tydef, ref _bounds) = *it.kind {
out.push_str("<span class=\"where fmt-newline\"> ");
let empty_set = FxHashSet::default();
let src_link = AssocItemLink::GotoSource(trait_did.into(), &empty_set);
assoc_type(
&mut out,
"<span class=\"where fmt-newline\">{}</span>",
impl_.print(false, cx)
it,
&tydef.generics,
&[], // intentionally leaving out bounds
Some(&tydef.type_),
src_link,
0,
cx,
);
for it in &impl_.items {
if let clean::AssocTypeItem(ref tydef, ref _bounds) = *it.kind {
out.push_str("<span class=\"where fmt-newline\"> ");
let empty_set = FxHashSet::default();
let src_link =
AssocItemLink::GotoSource(trait_did.into(), &empty_set);
assoc_type(
&mut out,
it,
&tydef.generics,
&[], // intentionally leaving out bounds
Some(&tydef.type_),
src_link,
0,
cx,
);
out.push_str(";</span>");
}
}
out.push_str(";</span>");
}
}
}
}
}

if !out.is_empty() {
out.insert_str(
0,
"<span class=\"notable-traits\"><span class=\"notable-traits-tooltip\">ⓘ\
<span class=\"notable-traits-tooltiptext\"><span class=\"docblock\">",
);
out.push_str("</code></span></span></span></span>");
if out.is_empty() {
write!(&mut out, "</code></pre>",);
}

out.into_inner()
(format!("{:#}", ty.print(cx)), out.into_inner())
}

pub(crate) fn notable_traits_json<'a>(
tys: impl Iterator<Item = &'a clean::Type>,
cx: &Context<'_>,
) -> String {
let mut mp: Vec<(String, String)> = tys.map(|ty| notable_traits_decl(ty, cx)).collect();
mp.sort_by(|(name1, _html1), (name2, _html2)| name1.cmp(name2));
struct NotableTraitsMap(Vec<(String, String)>);
impl Serialize for NotableTraitsMap {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut map = serializer.serialize_map(Some(self.0.len()))?;
for item in &self.0 {
map.serialize_entry(&item.0, &item.1)?;
}
map.end()
}
}
serde_json::to_string(&NotableTraitsMap(mp))
.expect("serialize (string, string) -> json object cannot fail")
}

#[derive(Clone, Copy, Debug)]
Expand Down
24 changes: 19 additions & 5 deletions src/librustdoc/html/render/print_item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,10 @@ use std::rc::Rc;

use super::{
collect_paths_for_type, document, ensure_trailing_slash, get_filtered_impls_for_reference,
item_ty_to_section, notable_traits_decl, render_all_impls, render_assoc_item,
render_assoc_items, render_attributes_in_code, render_attributes_in_pre, render_impl,
render_rightside, render_stability_since_raw, AssocItemLink, Context, ImplRenderingParameters,
item_ty_to_section, notable_traits_button, notable_traits_json, render_all_impls,
render_assoc_item, render_assoc_items, render_attributes_in_code, render_attributes_in_pre,
render_impl, render_rightside, render_stability_since_raw, AssocItemLink, Context,
ImplRenderingParameters,
};
use crate::clean;
use crate::config::ModuleSorting;
Expand Down Expand Up @@ -183,6 +184,16 @@ pub(super) fn print_item(
unreachable!();
}
}

// Render notable-traits.js used for all methods in this module.
if !cx.types_with_notable_traits.is_empty() {
write!(
buf,
r#"<script type="text/json" id="notable-traits-data">{}</script>"#,
notable_traits_json(cx.types_with_notable_traits.iter(), cx)
);
cx.types_with_notable_traits.clear();
}
}

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

let notable_traits =
f.decl.output.as_return().and_then(|output| notable_traits_button(output, cx));

wrap_into_item_decl(w, |w| {
wrap_item(w, "fn", |w| {
render_attributes_in_pre(w, it, "");
Expand All @@ -533,11 +547,11 @@ fn item_function(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, f: &cle
generics = f.generics.print(cx),
where_clause = print_where_clause(&f.generics, cx, 0, Ending::Newline),
decl = f.decl.full_print(header_len, 0, cx),
notable_traits = notable_traits_decl(&f.decl, cx),
notable_traits = notable_traits.unwrap_or_default(),
);
});
});
document(w, cx, it, None, HeadingOffset::H2)
document(w, cx, it, None, HeadingOffset::H2);
}

fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean::Trait) {
Expand Down
6 changes: 6 additions & 0 deletions src/librustdoc/html/static/css/noscript.css
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,9 @@ nav.sub {
.source .sidebar {
display: none;
}

.notable-traits {
/* layout requires javascript
https://github.com/rust-lang/rust/issues/102576 */
display: none;
}
Loading