1
Fork 0

rustdoc: use javascript to layout notable traits popups

Fixes #102576
This commit is contained in:
Michael Howell 2022-11-07 15:53:30 -07:00
parent 8e0cac18cd
commit 303653ef65
16 changed files with 264 additions and 94 deletions

View file

@ -107,10 +107,6 @@ impl Buffer {
self.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) { pub(crate) fn push_str(&mut self, s: &str) {
self.buffer.push_str(s); self.buffer.push_str(s);
} }

View file

@ -69,11 +69,13 @@ pub(crate) struct Context<'tcx> {
/// the source files are present in the html rendering, then this will be /// the source files are present in the html rendering, then this will be
/// `true`. /// `true`.
pub(crate) include_sources: bool, 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. // `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"))] #[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. /// Shared mutable state used in [`Context`] and elsewhere.
pub(crate) struct SharedContext<'tcx> { pub(crate) struct SharedContext<'tcx> {
@ -532,6 +534,7 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
deref_id_map: FxHashMap::default(), deref_id_map: FxHashMap::default(),
shared: Rc::new(scx), shared: Rc::new(scx),
include_sources, include_sources,
types_with_notable_traits: FxHashSet::default(),
}; };
if emit_crate { if emit_crate {
@ -560,6 +563,7 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
id_map: IdMap::new(), id_map: IdMap::new(),
shared: Rc::clone(&self.shared), shared: Rc::clone(&self.shared),
include_sources: self.include_sources, include_sources: self.include_sources,
types_with_notable_traits: FxHashSet::default(),
} }
} }
@ -803,6 +807,7 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
} }
} }
} }
Ok(()) Ok(())
} }

View file

@ -59,7 +59,7 @@ use rustc_span::{
symbol::{sym, Symbol}, symbol::{sym, Symbol},
BytePos, FileName, RealFileName, BytePos, FileName, RealFileName,
}; };
use serde::ser::SerializeSeq; use serde::ser::{SerializeMap, SerializeSeq};
use serde::{Serialize, Serializer}; use serde::{Serialize, Serializer};
use crate::clean::{self, ItemId, RenderedLink, SelfTy}; use crate::clean::{self, ItemId, RenderedLink, SelfTy};
@ -803,7 +803,7 @@ fn assoc_method(
d: &clean::FnDecl, d: &clean::FnDecl,
link: AssocItemLink<'_>, link: AssocItemLink<'_>,
parent: ItemType, parent: ItemType,
cx: &Context<'_>, cx: &mut Context<'_>,
render_mode: RenderMode, render_mode: RenderMode,
) { ) {
let tcx = cx.tcx(); let tcx = cx.tcx();
@ -836,6 +836,8 @@ fn assoc_method(
+ name.as_str().len() + name.as_str().len()
+ generics_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 { let (indent, indent_str, end_newline) = if parent == ItemType::Trait {
header_len += 4; header_len += 4;
let indent_str = " "; let indent_str = " ";
@ -861,13 +863,9 @@ fn assoc_method(
name = name, name = name,
generics = g.print(cx), generics = g.print(cx),
decl = d.full_print(header_len, indent, cx), decl = d.full_print(header_len, indent, cx),
notable_traits = d notable_traits = notable_traits.unwrap_or_default(),
.output
.as_return()
.and_then(|output| notable_traits_decl(output, cx))
.unwrap_or_default(),
where_clause = print_where_clause(g, cx, indent, end_newline), 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 /// 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(
item: &clean::Item, item: &clean::Item,
link: AssocItemLink<'_>, link: AssocItemLink<'_>,
parent: ItemType, parent: ItemType,
cx: &Context<'_>, cx: &mut Context<'_>,
render_mode: RenderMode, render_mode: RenderMode,
) { ) {
match &*item.kind { match &*item.kind {
@ -1277,8 +1275,8 @@ fn should_render_item(item: &clean::Item, deref_mut_: bool, tcx: TyCtxt<'_>) ->
} }
} }
fn notable_traits_decl(ty: &clean::Type, cx: &Context<'_>) -> Option<String> { pub(crate) fn notable_traits_button(ty: &clean::Type, cx: &mut Context<'_>) -> Option<String> {
let mut out = Buffer::html(); let mut has_notable_trait = false;
let did = ty.def_id(cx.cache())?; let did = ty.def_id(cx.cache())?;
@ -1291,6 +1289,7 @@ fn notable_traits_decl(ty: &clean::Type, cx: &Context<'_>) -> Option<String> {
{ {
return None; return None;
} }
if let Some(impls) = cx.cache().impls.get(&did) { if let Some(impls) = cx.cache().impls.get(&did) {
for i in impls { for i in impls {
let impl_ = i.inner_impl(); let impl_ = i.inner_impl();
@ -1304,11 +1303,48 @@ fn notable_traits_decl(ty: &clean::Type, cx: &Context<'_>) -> Option<String> {
if cx.cache().traits.get(&trait_did).map_or(false, |t| t.is_notable_trait(cx.tcx())) if cx.cache().traits.get(&trait_did).map_or(false, |t| t.is_notable_trait(cx.tcx()))
{ {
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() { if out.is_empty() {
write!( write!(
&mut out, &mut out,
"<span class=\"notable\">Notable traits for {}</span>\ "<h3 class=\"notable\">Notable traits for <code>{}</code></h3>\
<code class=\"content\">", <pre class=\"content\"><code>",
impl_.for_.print(cx) impl_.for_.print(cx)
); );
} }
@ -1340,20 +1376,33 @@ fn notable_traits_decl(ty: &clean::Type, cx: &Context<'_>) -> Option<String> {
} }
} }
} }
}
if out.is_empty() { if out.is_empty() {
return None; write!(&mut out, "</code></pre>",);
} }
out.insert_str( (format!("{:#}", ty.print(cx)), out.into_inner())
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>");
Some(out.into_inner()) pub(crate) fn notable_traits_json<'a>(
tys: impl Iterator<Item = &'a clean::Type>,
cx: &Context<'_>,
) -> String {
let mp: Vec<(String, String)> = tys.map(|ty| notable_traits_decl(ty, cx)).collect();
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)] #[derive(Clone, Copy, Debug)]

View file

@ -17,9 +17,10 @@ use std::rc::Rc;
use super::{ use super::{
collect_paths_for_type, document, ensure_trailing_slash, get_filtered_impls_for_reference, 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, item_ty_to_section, notable_traits_button, notable_traits_json, render_all_impls,
render_assoc_items, render_attributes_in_code, render_attributes_in_pre, render_impl, render_assoc_item, render_assoc_items, render_attributes_in_code, render_attributes_in_pre,
render_rightside, render_stability_since_raw, AssocItemLink, Context, ImplRenderingParameters, render_impl, render_rightside, render_stability_since_raw, AssocItemLink, Context,
ImplRenderingParameters,
}; };
use crate::clean; use crate::clean;
use crate::config::ModuleSorting; use crate::config::ModuleSorting;
@ -183,6 +184,16 @@ pub(super) fn print_item(
unreachable!(); 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 /// 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
+ name.as_str().len() + name.as_str().len()
+ generics_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_into_item_decl(w, |w| {
wrap_item(w, "fn", |w| { wrap_item(w, "fn", |w| {
render_attributes_in_pre(w, it, ""); render_attributes_in_pre(w, it, "");
@ -533,16 +547,11 @@ fn item_function(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, f: &cle
generics = f.generics.print(cx), generics = f.generics.print(cx),
where_clause = print_where_clause(&f.generics, cx, 0, Ending::Newline), where_clause = print_where_clause(&f.generics, cx, 0, Ending::Newline),
decl = f.decl.full_print(header_len, 0, cx), decl = f.decl.full_print(header_len, 0, cx),
notable_traits = f notable_traits = notable_traits.unwrap_or_default(),
.decl
.output
.as_return()
.and_then(|output| notable_traits_decl(output, cx))
.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) { fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean::Trait) {

View file

@ -22,3 +22,9 @@ nav.sub {
.source .sidebar { .source .sidebar {
display: none; display: none;
} }
.notable-traits {
/* layout requires javascript
https://github.com/rust-lang/rust/issues/102576 */
display: none;
}

View file

@ -183,6 +183,8 @@ h4.code-header {
font-weight: 600; font-weight: 600;
margin: 0; margin: 0;
padding: 0; padding: 0;
/* position notable traits in mobile mode within the header */
position: relative;
} }
#crate-search, #crate-search,
@ -1268,13 +1270,12 @@ h3.variant {
cursor: pointer; cursor: pointer;
} }
.notable-traits:hover .notable-traits-tooltiptext, .notable-traits .notable-traits-tooltiptext {
.notable-traits .notable-traits-tooltiptext.force-tooltip {
display: inline-block; display: inline-block;
visibility: hidden;
} }
.notable-traits .notable-traits-tooltiptext { .notable-traits-tooltiptext {
display: none;
padding: 5px 3px 3px 3px; padding: 5px 3px 3px 3px;
border-radius: 6px; border-radius: 6px;
margin-left: 5px; margin-left: 5px;
@ -1292,22 +1293,26 @@ h3.variant {
content: "\00a0\00a0\00a0"; content: "\00a0\00a0\00a0";
} }
.notable-traits .docblock { .notable-traits-tooltiptext .docblock {
margin: 0; margin: 0;
} }
.notable-traits .notable { .notable-traits-tooltiptext .notable {
margin: 0;
margin-bottom: 13px;
font-size: 1.1875rem; font-size: 1.1875rem;
font-weight: 600; font-weight: 600;
display: block; display: block;
} }
.notable-traits .docblock code.content { .notable-traits-tooltiptext pre, .notable-traits-tooltiptext code {
background: transparent;
}
.notable-traits-tooltiptext .docblock pre.content {
margin: 0; margin: 0;
padding: 0; padding: 0;
font-size: 1.25rem; font-size: 1.25rem;
white-space: pre-wrap;
overflow: hidden;
} }
.search-failed { .search-failed {

View file

@ -790,6 +790,19 @@ function loadCss(cssUrl) {
// we need to switch away from mobile mode and make the main content area scrollable. // we need to switch away from mobile mode and make the main content area scrollable.
hideSidebar(); hideSidebar();
} }
if (window.CURRENT_NOTABLE_ELEMENT) {
// As a workaround to the behavior of `contains: layout` used in doc togglers, the
// notable traits popup is positioned using javascript.
//
// This means when the window is resized, we need to redo the layout.
const base = window.CURRENT_NOTABLE_ELEMENT.NOTABLE_BASE;
const force_visible = base.NOTABLE_FORCE_VISIBLE;
hideNotable();
if (force_visible) {
showNotable(base);
base.NOTABLE_FORCE_VISIBLE = true;
}
}
}); });
function handleClick(id, f) { function handleClick(id, f) {
@ -822,10 +835,78 @@ function loadCss(cssUrl) {
}); });
}); });
function showNotable(e) {
if (!window.NOTABLE_TRAITS) {
const data = document.getElementById("notable-traits-data");
if (data) {
window.NOTABLE_TRAITS = JSON.parse(data.innerText);
} else {
throw new Error("showNotable() called on page without any notable traits!");
}
}
if (window.CURRENT_NOTABLE_ELEMENT && window.CURRENT_NOTABLE_ELEMENT.NOTABLE_BASE === e) {
// Make this function idempotent.
return;
}
hideNotable();
const ty = e.getAttribute("data-ty");
const tooltip = e.getElementsByClassName("notable-traits-tooltip")[0];
const wrapper = document.createElement("div");
wrapper.innerHTML = "<div class=\"docblock\">" + window.NOTABLE_TRAITS[ty] + "</div>";
wrapper.className = "notable-traits-tooltiptext";
tooltip.appendChild(wrapper);
const pos = wrapper.getBoundingClientRect();
tooltip.removeChild(wrapper);
wrapper.style.top = (pos.top + window.scrollY) + "px";
wrapper.style.left = (pos.left + window.scrollX) + "px";
wrapper.style.width = pos.width + "px";
document.documentElement.appendChild(wrapper);
window.CURRENT_NOTABLE_ELEMENT = wrapper;
window.CURRENT_NOTABLE_ELEMENT.NOTABLE_BASE = e;
wrapper.onpointerleave = function(ev) {
// If this is a synthetic touch event, ignore it. A click event will be along shortly.
if (ev.pointerType !== "mouse") {
return;
}
if (!e.NOTABLE_FORCE_VISIBLE && !elemIsInParent(event.relatedTarget, e)) {
hideNotable();
}
};
}
function hideNotable() {
if (window.CURRENT_NOTABLE_ELEMENT) {
window.CURRENT_NOTABLE_ELEMENT.NOTABLE_BASE.NOTABLE_FORCE_VISIBLE = false;
document.documentElement.removeChild(window.CURRENT_NOTABLE_ELEMENT);
window.CURRENT_NOTABLE_ELEMENT = null;
}
}
onEachLazy(document.getElementsByClassName("notable-traits"), e => { onEachLazy(document.getElementsByClassName("notable-traits"), e => {
e.onclick = function() { e.onclick = function() {
this.getElementsByClassName("notable-traits-tooltiptext")[0] this.NOTABLE_FORCE_VISIBLE = this.NOTABLE_FORCE_VISIBLE ? false : true;
.classList.toggle("force-tooltip"); if (window.CURRENT_NOTABLE_ELEMENT && !this.NOTABLE_FORCE_VISIBLE) {
hideNotable();
} else {
showNotable(this);
}
};
e.onpointerenter = function(ev) {
// If this is a synthetic touch event, ignore it. A click event will be along shortly.
if (ev.pointerType !== "mouse") {
return;
}
showNotable(this);
};
e.onpointerleave = function(ev) {
// If this is a synthetic touch event, ignore it. A click event will be along shortly.
if (ev.pointerType !== "mouse") {
return;
}
if (!this.NOTABLE_FORCE_VISIBLE &&
!elemIsInParent(event.relatedTarget, window.CURRENT_NOTABLE_ELEMENT)) {
hideNotable();
}
}; };
}); });

View file

@ -25,22 +25,28 @@ assert-position: (
{"x": 951}, {"x": 951},
) )
// The tooltip should be beside the `i` // The tooltip should be beside the `i`
// Also, clicking the tooltip should bring its text into the DOM
assert-count: ("//*[@class='notable-traits-tooltiptext']", 0)
click: "//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits']" click: "//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits']"
assert-count: ("//*[@class='notable-traits-tooltiptext']", 1)
compare-elements-position-near: ( compare-elements-position-near: (
"//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits']", "//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits']",
"//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits-tooltiptext force-tooltip']", "//*[@class='notable-traits-tooltiptext']",
{"y": 2} {"y": 2}
) )
compare-elements-position-false: ( compare-elements-position-false: (
"//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits']", "//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits']",
"//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits-tooltiptext force-tooltip']", "//*[@class='notable-traits-tooltiptext']",
("x") ("x")
) )
// The docblock should be flush with the border. // The docblock should be flush with the border.
assert-css: ( assert-css: (
"//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits-tooltiptext force-tooltip']/*[@class='docblock']", "//*[@class='notable-traits-tooltiptext']/*[@class='docblock']",
{"margin-left": "0px"} {"margin-left": "0px"}
) )
click: "//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits']"
move-cursor-to: "//h1"
assert-count: ("//*[@class='notable-traits-tooltiptext']", 0)
// Now only the `i` should be on the next line. // Now only the `i` should be on the next line.
size: (1055, 600) size: (1055, 600)
@ -98,26 +104,31 @@ assert-position: (
{"x": 289}, {"x": 289},
) )
// The tooltip should be below `i` // The tooltip should be below `i`
click: "//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits']"
assert-count: ("//*[@class='notable-traits-tooltiptext']", 1)
compare-elements-position-near-false: ( compare-elements-position-near-false: (
"//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits']", "//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits']",
"//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits-tooltiptext force-tooltip']", "//*[@class='notable-traits-tooltiptext']",
{"y": 2} {"y": 2}
) )
compare-elements-position-false: ( compare-elements-position-false: (
"//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits']", "//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits']",
"//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits-tooltiptext force-tooltip']", "//*[@class='notable-traits-tooltiptext']",
("x") ("x")
) )
compare-elements-position-near: ( compare-elements-position-near: (
"//*[@id='method.create_an_iterator_from_read']/parent::*", "//*[@id='method.create_an_iterator_from_read']",
"//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits-tooltiptext force-tooltip']", "//*[@class='notable-traits-tooltiptext']",
{"x": 5} {"x": 10}
) )
// The docblock should be flush with the border. // The docblock should be flush with the border.
assert-css: ( assert-css: (
"//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits-tooltiptext force-tooltip']/*[@class='docblock']", "//*[@class='notable-traits-tooltiptext']/*[@class='docblock']",
{"margin-left": "0px"} {"margin-left": "0px"}
) )
click: "//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits']"
move-cursor-to: "//h1"
assert-count: ("//*[@class='notable-traits-tooltiptext']", 0)
// Checking on very small mobile. The `i` should be on its own line. // Checking on very small mobile. The `i` should be on its own line.
size: (365, 600) size: (365, 600)

View file

@ -0,0 +1 @@
<script type="text/json" id="notable-traits-data">{"&amp;'static [SomeStruct]":"&lt;h3 class=\"notable\"&gt;Notable traits for &lt;code&gt;&amp;amp;[&lt;a class=\"struct\" href=\"struct.SomeStruct.html\" title=\"struct doc_notable_trait_slice::SomeStruct\"&gt;SomeStruct&lt;/a&gt;]&lt;/code&gt;&lt;/h3&gt;&lt;pre class=\"content\"&gt;&lt;code&gt;&lt;span class=\"where fmt-newline\"&gt;impl &lt;a class=\"trait\" href=\"trait.SomeTrait.html\" title=\"trait doc_notable_trait_slice::SomeTrait\"&gt;SomeTrait&lt;/a&gt; for &amp;amp;[&lt;a class=\"struct\" href=\"struct.SomeStruct.html\" title=\"struct doc_notable_trait_slice::SomeStruct\"&gt;SomeStruct&lt;/a&gt;]&lt;/span&gt;"}</script>

View file

@ -8,13 +8,13 @@ pub struct OtherStruct;
impl SomeTrait for &[SomeStruct] {} impl SomeTrait for &[SomeStruct] {}
// @has doc_notable_trait_slice/fn.bare_fn_matches.html // @has doc_notable_trait_slice/fn.bare_fn_matches.html
// @has - '//code[@class="content"]' 'impl SomeTrait for &[SomeStruct]' // @snapshot bare_fn_matches - '//script[@id="notable-traits-data"]'
pub fn bare_fn_matches() -> &'static [SomeStruct] { pub fn bare_fn_matches() -> &'static [SomeStruct] {
&[] &[]
} }
// @has doc_notable_trait_slice/fn.bare_fn_no_matches.html // @has doc_notable_trait_slice/fn.bare_fn_no_matches.html
// @!has - '//code[@class="content"]' 'impl SomeTrait for &[SomeStruct]' // @count - '//script[@id="notable-traits-data"]' 0
pub fn bare_fn_no_matches() -> &'static [OtherStruct] { pub fn bare_fn_no_matches() -> &'static [OtherStruct] {
&[] &[]
} }

View file

@ -0,0 +1 @@
<script type="text/json" id="notable-traits-data">{"SomeStruct":"&lt;h3 class=\"notable\"&gt;Notable traits for &lt;code&gt;&lt;a class=\"struct\" href=\"struct.SomeStruct.html\" title=\"struct doc_notable_trait::SomeStruct\"&gt;SomeStruct&lt;/a&gt;&lt;/code&gt;&lt;/h3&gt;&lt;pre class=\"content\"&gt;&lt;code&gt;&lt;span class=\"where fmt-newline\"&gt;impl &lt;a class=\"trait\" href=\"trait.SomeTrait.html\" title=\"trait doc_notable_trait::SomeTrait\"&gt;SomeTrait&lt;/a&gt; for &lt;a class=\"struct\" href=\"struct.SomeStruct.html\" title=\"struct doc_notable_trait::SomeStruct\"&gt;SomeStruct&lt;/a&gt;&lt;/span&gt;"}</script>

View file

@ -9,7 +9,8 @@ impl<T: SomeTrait> SomeTrait for Wrapper<T> {}
#[doc(notable_trait)] #[doc(notable_trait)]
pub trait SomeTrait { pub trait SomeTrait {
// @has doc_notable_trait/trait.SomeTrait.html // @has doc_notable_trait/trait.SomeTrait.html
// @has - '//code[@class="content"]' 'impl<T: SomeTrait> SomeTrait for Wrapper<T>' // @has - '//span[@class="notable-traits"]/@data-ty' 'Wrapper<Self>'
// @snapshot wrap-me - '//script[@id="notable-traits-data"]'
fn wrap_me(self) -> Wrapper<Self> where Self: Sized { fn wrap_me(self) -> Wrapper<Self> where Self: Sized {
Wrapper { Wrapper {
inner: self, inner: self,
@ -22,15 +23,16 @@ impl SomeTrait for SomeStruct {}
impl SomeStruct { impl SomeStruct {
// @has doc_notable_trait/struct.SomeStruct.html // @has doc_notable_trait/struct.SomeStruct.html
// @has - '//code[@class="content"]' 'impl SomeTrait for SomeStruct' // @has - '//span[@class="notable-traits"]/@data-ty' 'SomeStruct'
// @has - '//code[@class="content"]' 'impl<T: SomeTrait> SomeTrait for Wrapper<T>' // @snapshot some-struct-new - '//script[@id="notable-traits-data"]'
pub fn new() -> SomeStruct { pub fn new() -> SomeStruct {
SomeStruct SomeStruct
} }
} }
// @has doc_notable_trait/fn.bare_fn.html // @has doc_notable_trait/fn.bare_fn.html
// @has - '//code[@class="content"]' 'impl SomeTrait for SomeStruct' // @has - '//span[@class="notable-traits"]/@data-ty' 'SomeStruct'
// @snapshot bare-fn - '//script[@id="notable-traits-data"]'
pub fn bare_fn() -> SomeStruct { pub fn bare_fn() -> SomeStruct {
SomeStruct SomeStruct
} }

View file

@ -0,0 +1 @@
<script type="text/json" id="notable-traits-data">{"Wrapper&lt;Self&gt;":"&lt;h3 class=\"notable\"&gt;Notable traits for &lt;code&gt;&lt;a class=\"struct\" href=\"struct.Wrapper.html\" title=\"struct doc_notable_trait::Wrapper\"&gt;Wrapper&lt;/a&gt;&amp;lt;T&amp;gt;&lt;/code&gt;&lt;/h3&gt;&lt;pre class=\"content\"&gt;&lt;code&gt;&lt;span class=\"where fmt-newline\"&gt;impl&amp;lt;T:&amp;nbsp;&lt;a class=\"trait\" href=\"trait.SomeTrait.html\" title=\"trait doc_notable_trait::SomeTrait\"&gt;SomeTrait&lt;/a&gt;&amp;gt; &lt;a class=\"trait\" href=\"trait.SomeTrait.html\" title=\"trait doc_notable_trait::SomeTrait\"&gt;SomeTrait&lt;/a&gt; for &lt;a class=\"struct\" href=\"struct.Wrapper.html\" title=\"struct doc_notable_trait::Wrapper\"&gt;Wrapper&lt;/a&gt;&amp;lt;T&amp;gt;&lt;/span&gt;","SomeStruct":"&lt;h3 class=\"notable\"&gt;Notable traits for &lt;code&gt;&lt;a class=\"struct\" href=\"struct.SomeStruct.html\" title=\"struct doc_notable_trait::SomeStruct\"&gt;SomeStruct&lt;/a&gt;&lt;/code&gt;&lt;/h3&gt;&lt;pre class=\"content\"&gt;&lt;code&gt;&lt;span class=\"where fmt-newline\"&gt;impl &lt;a class=\"trait\" href=\"trait.SomeTrait.html\" title=\"trait doc_notable_trait::SomeTrait\"&gt;SomeTrait&lt;/a&gt; for &lt;a class=\"struct\" href=\"struct.SomeStruct.html\" title=\"struct doc_notable_trait::SomeStruct\"&gt;SomeStruct&lt;/a&gt;&lt;/span&gt;"}</script>

View file

@ -0,0 +1 @@
<script type="text/json" id="notable-traits-data">{"Wrapper&lt;Self&gt;":"&lt;h3 class=\"notable\"&gt;Notable traits for &lt;code&gt;&lt;a class=\"struct\" href=\"struct.Wrapper.html\" title=\"struct doc_notable_trait::Wrapper\"&gt;Wrapper&lt;/a&gt;&amp;lt;T&amp;gt;&lt;/code&gt;&lt;/h3&gt;&lt;pre class=\"content\"&gt;&lt;code&gt;&lt;span class=\"where fmt-newline\"&gt;impl&amp;lt;T:&amp;nbsp;&lt;a class=\"trait\" href=\"trait.SomeTrait.html\" title=\"trait doc_notable_trait::SomeTrait\"&gt;SomeTrait&lt;/a&gt;&amp;gt; &lt;a class=\"trait\" href=\"trait.SomeTrait.html\" title=\"trait doc_notable_trait::SomeTrait\"&gt;SomeTrait&lt;/a&gt; for &lt;a class=\"struct\" href=\"struct.Wrapper.html\" title=\"struct doc_notable_trait::Wrapper\"&gt;Wrapper&lt;/a&gt;&amp;lt;T&amp;gt;&lt;/span&gt;"}</script>

View file

@ -0,0 +1 @@
<script type="text/json" id="notable-traits-data">{"Odd":"&lt;h3 class=\"notable\"&gt;Notable traits for &lt;code&gt;&lt;a class=\"struct\" href=\"struct.Odd.html\" title=\"struct foo::Odd\"&gt;Odd&lt;/a&gt;&lt;/code&gt;&lt;/h3&gt;&lt;pre class=\"content\"&gt;&lt;code&gt;&lt;span class=\"where fmt-newline\"&gt;impl &lt;a class=\"trait\" href=\"{{channel}}/core/iter/traits/iterator/trait.Iterator.html\" title=\"trait core::iter::traits::iterator::Iterator\"&gt;Iterator&lt;/a&gt; for &lt;a class=\"struct\" href=\"struct.Odd.html\" title=\"struct foo::Odd\"&gt;Odd&lt;/a&gt;&lt;/span&gt;&lt;span class=\"where fmt-newline\"&gt; type &lt;a href=\"{{channel}}/core/iter/traits/iterator/trait.Iterator.html#associatedtype.Item\" class=\"associatedtype\"&gt;Item&lt;/a&gt; = &lt;a class=\"primitive\" href=\"{{channel}}/std/primitive.usize.html\"&gt;usize&lt;/a&gt;;&lt;/span&gt;"}</script>

View file

@ -3,7 +3,8 @@
use std::iter::Iterator; use std::iter::Iterator;
// @has foo/struct.Odd.html // @has foo/struct.Odd.html
// @has - '//*[@id="method.new"]//span[@class="notable-traits"]//code/span' 'impl Iterator for Odd' // @has - '//*[@id="method.new"]//span[@class="notable-traits"]/@data-ty' 'Odd'
// @snapshot odd - '//script[@id="notable-traits-data"]'
pub struct Odd { pub struct Odd {
current: usize, current: usize,
} }