Revert "Remove "important traits" feature"

This reverts commit 1244ced958.
This commit is contained in:
Manish Goregaokar 2020-07-06 12:53:44 -07:00
parent 6ee1b62c81
commit 98450757e5
15 changed files with 393 additions and 9 deletions

View File

@ -150,6 +150,27 @@ Book][unstable-doc-cfg] and [its tracking issue][issue-doc-cfg].
[unstable-doc-cfg]: ../unstable-book/language-features/doc-cfg.html
[issue-doc-cfg]: https://github.com/rust-lang/rust/issues/43781
### Adding your trait to the "Important Traits" dialog
Rustdoc keeps a list of a few traits that are believed to be "fundamental" to a given type when
implemented on it. These traits are intended to be the primary interface for their types, and are
often the only thing available to be documented on their types. For this reason, Rustdoc will track
when a given type implements one of these traits and call special attention to it when a function
returns one of these types. This is the "Important Traits" dialog, visible as a circle-i button next
to the function, which, when clicked, shows the dialog.
In the standard library, the traits that qualify for inclusion are `Iterator`, `io::Read`, and
`io::Write`. However, rather than being implemented as a hard-coded list, these traits have a
special marker attribute on them: `#[doc(spotlight)]`. This means that you could apply this
attribute to your own trait to include it in the "Important Traits" dialog in documentation.
The `#[doc(spotlight)]` attribute currently requires the `#![feature(doc_spotlight)]` feature gate.
For more information, see [its chapter in the Unstable Book][unstable-spotlight] and [its tracking
issue][issue-spotlight].
[unstable-spotlight]: ../unstable-book/language-features/doc-spotlight.html
[issue-spotlight]: https://github.com/rust-lang/rust/issues/45040
### Exclude certain dependencies from documentation
The standard library uses several dependencies which, in turn, use several types and traits from the

View File

@ -0,0 +1,30 @@
# `doc_spotlight`
The tracking issue for this feature is: [#45040]
The `doc_spotlight` feature allows the use of the `spotlight` parameter to the `#[doc]` attribute,
to "spotlight" a specific trait on the return values of functions. Adding a `#[doc(spotlight)]`
attribute to a trait definition will make rustdoc print extra information for functions which return
a type that implements that trait. This attribute is applied to the `Iterator`, `io::Read`, and
`io::Write` traits in the standard library.
You can do this on your own traits, like this:
```
#![feature(doc_spotlight)]
#[doc(spotlight)]
pub trait MyTrait {}
pub struct MyStruct;
impl MyTrait for MyStruct {}
/// The docs for this function will have an extra line about `MyStruct` implementing `MyTrait`,
/// without having to write that yourself!
pub fn my_fn() -> MyStruct { MyStruct }
```
This feature was originally implemented in PR [#45039].
[#45040]: https://github.com/rust-lang/rust/issues/45040
[#45039]: https://github.com/rust-lang/rust/pull/45039

View File

@ -368,6 +368,9 @@ declare_features! (
/// Allows `#[doc(masked)]`.
(active, doc_masked, "1.21.0", Some(44027), None),
/// Allows `#[doc(spotlight)]`.
(active, doc_spotlight, "1.22.0", Some(45040), None),
/// Allows `#[doc(include = "some-file")]`.
(active, external_doc, "1.22.0", Some(44732), None),

View File

@ -12,7 +12,7 @@ use rustc_metadata::creader::LoadedMacro;
use rustc_middle::ty;
use rustc_mir::const_eval::is_min_const_fn;
use rustc_span::hygiene::MacroKind;
use rustc_span::symbol::Symbol;
use rustc_span::symbol::{sym, Symbol};
use rustc_span::Span;
use crate::clean::{self, GetDefId, ToSource, TypeKind};
@ -194,6 +194,7 @@ pub fn build_external_trait(cx: &DocContext<'_>, did: DefId) -> clean::Trait {
let generics = (cx.tcx.generics_of(did), predicates).clean(cx);
let generics = filter_non_trait_generics(did, generics);
let (generics, supertrait_bounds) = separate_supertrait_bounds(generics);
let is_spotlight = load_attrs(cx, did).clean(cx).has_doc_flag(sym::spotlight);
let is_auto = cx.tcx.trait_is_auto(did);
clean::Trait {
auto: auto_trait,
@ -201,6 +202,7 @@ pub fn build_external_trait(cx: &DocContext<'_>, did: DefId) -> clean::Trait {
generics,
items: trait_items,
bounds: supertrait_bounds,
is_spotlight,
is_auto,
}
}

View File

@ -1007,6 +1007,7 @@ impl Clean<FnRetTy> for hir::FnRetTy<'_> {
impl Clean<Item> for doctree::Trait<'_> {
fn clean(&self, cx: &DocContext<'_>) -> Item {
let attrs = self.attrs.clean(cx);
let is_spotlight = attrs.has_doc_flag(sym::spotlight);
Item {
name: Some(self.name.clean(cx)),
attrs,
@ -1021,6 +1022,7 @@ impl Clean<Item> for doctree::Trait<'_> {
items: self.items.iter().map(|ti| ti.clean(cx)).collect(),
generics: self.generics.clean(cx),
bounds: self.bounds.clean(cx),
is_spotlight,
is_auto: self.is_auto.clean(cx),
}),
}

View File

@ -995,6 +995,7 @@ pub struct Trait {
pub items: Vec<Item>,
pub generics: Generics,
pub bounds: Vec<GenericBound>,
pub is_spotlight: bool,
pub is_auto: bool,
}

View File

@ -63,10 +63,22 @@ impl Buffer {
Buffer { for_html: false, buffer: String::new() }
}
crate fn is_empty(&self) -> bool {
self.buffer.is_empty()
}
crate fn into_inner(self) -> String {
self.buffer
}
crate fn insert_str(&mut self, idx: usize, s: &str) {
self.buffer.insert_str(idx, s);
}
crate fn push_str(&mut self, s: &str) {
self.buffer.push_str(s);
}
// Intended for consumption by write! and writeln! (std::fmt) but without
// the fmt::Result return type imposed by fmt::Write (and avoiding the trait
// import).

View File

@ -2410,7 +2410,7 @@ fn item_function(w: &mut Buffer, cx: &Context, it: &clean::Item, f: &clean::Func
f.generics.print()
)
.len();
write!(w, "<pre class='rust fn'>");
write!(w, "{}<pre class='rust fn'>", render_spotlight_traits(it));
render_attributes(w, it, false);
write!(
w,
@ -2612,7 +2612,12 @@ fn item_trait(w: &mut Buffer, cx: &Context, it: &clean::Item, t: &clean::Trait)
let name = m.name.as_ref().unwrap();
let item_type = m.type_();
let id = cx.derive_id(format!("{}.{}", item_type, name));
write!(w, "<h3 id='{id}' class='method'><code>", id = id);
write!(
w,
"<h3 id='{id}' class='method'>{extra}<code>",
extra = render_spotlight_traits(m),
id = id
);
render_assoc_item(w, m, AssocItemLink::Anchor(Some(&id)), ItemType::Impl);
write!(w, "</code>");
render_stability_since(w, m, t);
@ -3559,6 +3564,76 @@ fn should_render_item(item: &clean::Item, deref_mut_: bool) -> bool {
}
}
fn render_spotlight_traits(item: &clean::Item) -> String {
match item.inner {
clean::FunctionItem(clean::Function { ref decl, .. })
| clean::TyMethodItem(clean::TyMethod { ref decl, .. })
| clean::MethodItem(clean::Method { ref decl, .. })
| clean::ForeignFunctionItem(clean::Function { ref decl, .. }) => spotlight_decl(decl),
_ => String::new(),
}
}
fn spotlight_decl(decl: &clean::FnDecl) -> String {
let mut out = Buffer::html();
let mut trait_ = String::new();
if let Some(did) = decl.output.def_id() {
let c = cache();
if let Some(impls) = c.impls.get(&did) {
for i in impls {
let impl_ = i.inner_impl();
if impl_.trait_.def_id().map_or(false, |d| c.traits[&d].is_spotlight) {
if out.is_empty() {
out.push_str(&format!(
"<h3 class=\"important\">Important traits for {}</h3>\
<code class=\"content\">",
impl_.for_.print()
));
trait_.push_str(&impl_.for_.print().to_string());
}
//use the "where" class here to make it small
out.push_str(&format!(
"<span class=\"where fmt-newline\">{}</span>",
impl_.print()
));
let t_did = impl_.trait_.def_id().unwrap();
for it in &impl_.items {
if let clean::TypedefItem(ref tydef, _) = it.inner {
out.push_str("<span class=\"where fmt-newline\"> ");
assoc_type(
&mut out,
it,
&[],
Some(&tydef.type_),
AssocItemLink::GotoSource(t_did, &FxHashSet::default()),
"",
);
out.push_str(";</span>");
}
}
}
}
}
}
if !out.is_empty() {
out.insert_str(
0,
&format!(
"<div class=\"important-traits\"><div class='tooltip'>ⓘ\
<span class='tooltiptext'>Important traits for {}</span></div>\
<div class=\"content hidden\">",
trait_
),
);
out.push_str("</code></div></div>");
}
out.into_inner()
}
fn render_impl(
w: &mut Buffer,
cx: &Context,
@ -3665,12 +3740,14 @@ fn render_impl(
(true, " hidden")
};
match item.inner {
clean::MethodItem(clean::Method { .. })
| clean::TyMethodItem(clean::TyMethod { .. }) => {
clean::MethodItem(clean::Method { ref decl, .. })
| clean::TyMethodItem(clean::TyMethod { ref decl, .. }) => {
// Only render when the method is not static or we allow static methods
if render_method_item {
let id = cx.derive_id(format!("{}.{}", item_type, name));
write!(w, "<h4 id='{}' class=\"{}{}\"><code>", id, item_type, extra_class);
write!(w, "<h4 id='{}' class=\"{}{}\">", id, item_type, extra_class);
write!(w, "{}", spotlight_decl(decl));
write!(w, "<code>");
render_assoc_item(w, item, link.anchor(&id), ItemType::Impl);
write!(w, "</code>");
render_stability_since_raw(w, item.stable_since(), outer_version);

View File

@ -365,6 +365,7 @@ function defocusSearchBar() {
function handleEscape(ev) {
var help = getHelpElement();
var search = getSearchElement();
hideModal();
if (hasClass(help, "hidden") === false) {
displayHelp(false, ev, help);
} else if (hasClass(search, "hidden") === false) {
@ -397,6 +398,7 @@ function defocusSearchBar() {
case "s":
case "S":
displayHelp(false, ev);
hideModal();
ev.preventDefault();
focusSearchBar();
break;
@ -409,6 +411,7 @@ function defocusSearchBar() {
case "?":
if (ev.shiftKey) {
hideModal();
displayHelp(true, ev);
}
break;
@ -2636,6 +2639,31 @@ function defocusSearchBar() {
});
}());
function showModal(content) {
var modal = document.createElement("div");
modal.id = "important";
addClass(modal, "modal");
modal.innerHTML = "<div class=\"modal-content\"><div class=\"close\" id=\"modal-close\">✕" +
"</div><div class=\"whiter\"></div><span class=\"docblock\">" + content +
"</span></div>";
document.getElementsByTagName("body")[0].appendChild(modal);
document.getElementById("modal-close").onclick = hideModal;
modal.onclick = hideModal;
}
function hideModal() {
var modal = document.getElementById("important");
if (modal) {
modal.parentNode.removeChild(modal);
}
}
onEachLazy(document.getElementsByClassName("important-traits"), function(e) {
e.onclick = function() {
showModal(e.lastElementChild.innerHTML);
};
});
// In the search display, allows to switch between tabs.
function printTab(nb) {
if (nb === 0 || nb === 1 || nb === 2) {

View File

@ -146,9 +146,12 @@ code, pre, a.test-arrow {
border-radius: 3px;
padding: 0 0.1em;
}
.docblock pre code, .docblock-short pre code {
.docblock pre code, .docblock-short pre code, .docblock code.spotlight {
padding: 0;
}
.docblock code.spotlight :last-child {
padding-bottom: 0.6em;
}
pre {
padding: 14px;
}
@ -523,7 +526,7 @@ h4 > code, h3 > code, .invisible > code {
font-size: 0.8em;
}
.content .methods > div {
.content .methods > div:not(.important-traits) {
margin-left: 40px;
margin-bottom: 15px;
}
@ -1098,7 +1101,7 @@ h3 > .collapse-toggle, h4 > .collapse-toggle {
font-size: 20px;
}
.tooltip .tooltiptext {
.important-traits .tooltip .tooltiptext {
border: 1px solid;
font-weight: normal;
}
@ -1144,6 +1147,17 @@ pre.rust {
font-size: 16px;
}
.important-traits {
cursor: pointer;
z-index: 2;
}
h4 > .important-traits {
position: absolute;
left: -44px;
top: 2px;
}
#all-types {
text-align: center;
border: 1px solid;
@ -1370,6 +1384,12 @@ pre.rust {
z-index: 1;
}
h4 > .important-traits {
position: absolute;
left: -22px;
top: 24px;
}
#titles > div > div.count {
float: left;
width: 100%;
@ -1472,12 +1492,82 @@ pre.rust {
}
}
.modal {
position: fixed;
width: 100vw;
height: 100vh;
z-index: 10000;
top: 0;
left: 0;
}
.modal-content {
display: block;
max-width: 60%;
min-width: 200px;
padding: 8px;
top: 40%;
position: absolute;
left: 50%;
transform: translate(-50%, -40%);
border: 1px solid;
border-radius: 4px;
border-top-right-radius: 0;
}
.modal-content > .docblock {
margin: 0;
}
h3.important {
margin: 0;
margin-bottom: 13px;
font-size: 19px;
}
.modal-content > .docblock > code.content {
margin: 0;
padding: 0;
font-size: 20px;
}
.modal-content > .close {
position: absolute;
font-weight: 900;
right: -25px;
top: -1px;
font-size: 18px;
width: 25px;
padding-right: 2px;
border-top-right-radius: 5px;
border-bottom-right-radius: 5px;
text-align: center;
border: 1px solid;
border-right: 0;
cursor: pointer;
}
.modal-content > .whiter {
height: 25px;
position: absolute;
width: 3px;
right: -2px;
top: 0px;
}
#main > div.important-traits {
position: absolute;
left: -24px;
margin-top: 16px;
}
.content > .methods > .method > div.important-traits {
position: absolute;
font-weight: 400;
left: -42px;
margin-top: 2px;
}
kbd {
display: inline-block;
padding: 3px 5px;

View File

@ -337,6 +337,12 @@ pre.ignore:hover, .information:hover + pre.ignore {
border-color: transparent black transparent transparent;
}
.important-traits .tooltip .tooltiptext {
background-color: white;
color: black;
border-color: black;
}
#titles > div:not(.selected) {
background-color: #252525;
border-top-color: #252525;
@ -350,6 +356,33 @@ pre.ignore:hover, .information:hover + pre.ignore {
color: #888;
}
.modal {
background-color: rgba(0,0,0,0.3);
}
.modal-content {
background-color: #272727;
border-color: #999;
}
.modal-content > .close {
background-color: #272727;
border-color: #999;
}
.modal-content > .close:hover {
background-color: #ff1f1f;
color: white;
}
.modal-content > .whiter {
background-color: #272727;
}
.modal-content > .close:hover + .whiter {
background-color: #ff1f1f;
}
@media (max-width: 700px) {
.sidebar-menu {
background-color: #505050;

View File

@ -331,6 +331,12 @@ pre.ignore:hover, .information:hover + pre.ignore {
border-color: transparent black transparent transparent;
}
.important-traits .tooltip .tooltiptext {
background-color: white;
color: black;
border-color: black;
}
#titles > div:not(.selected) {
background-color: #e6e6e6;
border-top-color: #e6e6e6;
@ -344,6 +350,33 @@ pre.ignore:hover, .information:hover + pre.ignore {
color: #888;
}
.modal {
background-color: rgba(0,0,0,0.3);
}
.modal-content {
background-color: #eee;
border-color: #999;
}
.modal-content > .close {
background-color: #eee;
border-color: #999;
}
.modal-content > .close:hover {
background-color: #ff1f1f;
color: white;
}
.modal-content > .whiter {
background-color: #eee;
}
.modal-content > .close:hover + .whiter {
background-color: #ff1f1f;
}
@media (max-width: 700px) {
.sidebar-menu {
background-color: #F1F1F1;

View File

@ -0,0 +1,36 @@
#![feature(doc_spotlight)]
pub struct Wrapper<T> {
inner: T,
}
impl<T: SomeTrait> SomeTrait for Wrapper<T> {}
#[doc(spotlight)]
pub trait SomeTrait {
// @has doc_spotlight/trait.SomeTrait.html
// @has - '//code[@class="content"]' 'impl<T: SomeTrait> SomeTrait for Wrapper<T>'
fn wrap_me(self) -> Wrapper<Self> where Self: Sized {
Wrapper {
inner: self,
}
}
}
pub struct SomeStruct;
impl SomeTrait for SomeStruct {}
impl SomeStruct {
// @has doc_spotlight/struct.SomeStruct.html
// @has - '//code[@class="content"]' 'impl SomeTrait for SomeStruct'
// @has - '//code[@class="content"]' 'impl<T: SomeTrait> SomeTrait for Wrapper<T>'
pub fn new() -> SomeStruct {
SomeStruct
}
}
// @has doc_spotlight/fn.bare_fn.html
// @has - '//code[@class="content"]' 'impl SomeTrait for SomeStruct'
pub fn bare_fn() -> SomeStruct {
SomeStruct
}

View File

@ -0,0 +1,4 @@
#[doc(spotlight)] //~ ERROR: `#[doc(spotlight)]` is experimental
trait SomeTrait {}
fn main() {}

View File

@ -0,0 +1,12 @@
error[E0658]: `#[doc(spotlight)]` is experimental
--> $DIR/feature-gate-doc_spotlight.rs:1:1
|
LL | #[doc(spotlight)]
| ^^^^^^^^^^^^^^^^^
|
= note: see issue #45040 <https://github.com/rust-lang/rust/issues/45040> for more information
= help: add `#![feature(doc_spotlight)]` to the crate attributes to enable
error: aborting due to previous error
For more information about this error, try `rustc --explain E0658`.