Skip to content

Commit a7c66d9

Browse files
authored
Rollup merge of rust-lang#74370 - Manishearth:re-spotlight, r=GuillaumeGomez
Reintroduce spotlight / "important traits" feature (Reopened version of rust-lang#74111 because Github is broken, see discussion there) Fixes rust-lang#73785 This PR reintroduces the "spotlight" ("important traits") feature. A couple changes have been made: As there were concerns about its visibility, it has been moved to be next to the return type, as opposed to being on the side. It also no longer produces a modal, it shows the traits on hover, and it can be clicked on to pin the hover bubble. ![image](https://user-images.githubusercontent.com/1617736/86674555-a82d2600-bfad-11ea-9a4a-a1a9ffd66ae5.png) ![image](https://user-images.githubusercontent.com/1617736/86674533-a1061800-bfad-11ea-9e8a-c62ad86ed0d7.png) It also works fine on mobile: ![image](https://user-images.githubusercontent.com/1617736/86674638-bda25000-bfad-11ea-8d8d-1798b608923e.png)
2 parents 0e1964a + c621a54 commit a7c66d9

File tree

23 files changed

+284
-15
lines changed

23 files changed

+284
-15
lines changed

src/doc/rustdoc/src/unstable-features.md

+21
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,27 @@ Book][unstable-doc-cfg] and [its tracking issue][issue-doc-cfg].
150150
[unstable-doc-cfg]: ../unstable-book/language-features/doc-cfg.html
151151
[issue-doc-cfg]: https://github.com/rust-lang/rust/issues/43781
152152

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

155176
The standard library uses several dependencies which, in turn, use several types and traits from the
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# `doc_spotlight`
2+
3+
The tracking issue for this feature is: [#45040]
4+
5+
The `doc_spotlight` feature allows the use of the `spotlight` parameter to the `#[doc]` attribute,
6+
to "spotlight" a specific trait on the return values of functions. Adding a `#[doc(spotlight)]`
7+
attribute to a trait definition will make rustdoc print extra information for functions which return
8+
a type that implements that trait. This attribute is applied to the `Iterator`, `io::Read`, and
9+
`io::Write` traits in the standard library.
10+
11+
You can do this on your own traits, like this:
12+
13+
```
14+
#![feature(doc_spotlight)]
15+
16+
#[doc(spotlight)]
17+
pub trait MyTrait {}
18+
19+
pub struct MyStruct;
20+
impl MyTrait for MyStruct {}
21+
22+
/// The docs for this function will have an extra line about `MyStruct` implementing `MyTrait`,
23+
/// without having to write that yourself!
24+
pub fn my_fn() -> MyStruct { MyStruct }
25+
```
26+
27+
This feature was originally implemented in PR [#45039].
28+
29+
[#45040]: https://github.com/rust-lang/rust/issues/45040
30+
[#45039]: https://github.com/rust-lang/rust/pull/45039

src/libcore/future/future.rs

+1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ use crate::task::{Context, Poll};
2424
/// `.await` the value.
2525
///
2626
/// [`Waker`]: ../task/struct.Waker.html
27+
#[doc(spotlight)]
2728
#[must_use = "futures do nothing unless you `.await` or poll them"]
2829
#[stable(feature = "futures_api", since = "1.36.0")]
2930
#[lang = "future_trait"]

src/libcore/iter/traits/iterator.rs

+1
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ fn _assert_is_object_safe(_: &dyn Iterator<Item = ()>) {}
9292
label = "`{Self}` is not an iterator",
9393
message = "`{Self}` is not an iterator"
9494
)]
95+
#[doc(spotlight)]
9596
#[must_use = "iterators are lazy and do nothing unless consumed"]
9697
pub trait Iterator {
9798
/// The type of the elements being iterated over.

src/libcore/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@
9696
#![feature(custom_inner_attributes)]
9797
#![feature(decl_macro)]
9898
#![feature(doc_cfg)]
99+
#![cfg_attr(not(bootstrap), feature(doc_spotlight))]
99100
#![feature(duration_consts_2)]
100101
#![feature(extern_types)]
101102
#![feature(fundamental)]

src/librustc_ast_passes/feature_gate.rs

+1
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,7 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
253253
include => external_doc
254254
cfg => doc_cfg
255255
masked => doc_masked
256+
spotlight => doc_spotlight
256257
alias => doc_alias
257258
keyword => doc_keyword
258259
);

src/librustc_feature/active.rs

+3
Original file line numberDiff line numberDiff line change
@@ -368,6 +368,9 @@ declare_features! (
368368
/// Allows `#[doc(masked)]`.
369369
(active, doc_masked, "1.21.0", Some(44027), None),
370370

371+
/// Allows `#[doc(spotlight)]`.
372+
(active, doc_spotlight, "1.22.0", Some(45040), None),
373+
371374
/// Allows `#[doc(include = "some-file")]`.
372375
(active, external_doc, "1.22.0", Some(44732), None),
373376

src/librustc_span/symbol.rs

+2
Original file line numberDiff line numberDiff line change
@@ -400,6 +400,7 @@ symbols! {
400400
doc_cfg,
401401
doc_keyword,
402402
doc_masked,
403+
doc_spotlight,
403404
doctest,
404405
document_private_items,
405406
dotdot_in_tuple_patterns,
@@ -968,6 +969,7 @@ symbols! {
968969
soft,
969970
specialization,
970971
speed,
972+
spotlight,
971973
sqrtf32,
972974
sqrtf64,
973975
sse4a_target_feature,

src/librustdoc/clean/inline.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use rustc_metadata::creader::LoadedMacro;
1212
use rustc_middle::ty;
1313
use rustc_mir::const_eval::is_min_const_fn;
1414
use rustc_span::hygiene::MacroKind;
15-
use rustc_span::symbol::Symbol;
15+
use rustc_span::symbol::{sym, Symbol};
1616
use rustc_span::Span;
1717

1818
use crate::clean::{self, GetDefId, ToSource, TypeKind};
@@ -194,13 +194,15 @@ pub fn build_external_trait(cx: &DocContext<'_>, did: DefId) -> clean::Trait {
194194
let generics = (cx.tcx.generics_of(did), predicates).clean(cx);
195195
let generics = filter_non_trait_generics(did, generics);
196196
let (generics, supertrait_bounds) = separate_supertrait_bounds(generics);
197+
let is_spotlight = load_attrs(cx, did).clean(cx).has_doc_flag(sym::spotlight);
197198
let is_auto = cx.tcx.trait_is_auto(did);
198199
clean::Trait {
199200
auto: auto_trait,
200201
unsafety: cx.tcx.trait_def(did).unsafety,
201202
generics,
202203
items: trait_items,
203204
bounds: supertrait_bounds,
205+
is_spotlight,
204206
is_auto,
205207
}
206208
}

src/librustdoc/clean/mod.rs

+2
Original file line numberDiff line numberDiff line change
@@ -1007,6 +1007,7 @@ impl Clean<FnRetTy> for hir::FnRetTy<'_> {
10071007
impl Clean<Item> for doctree::Trait<'_> {
10081008
fn clean(&self, cx: &DocContext<'_>) -> Item {
10091009
let attrs = self.attrs.clean(cx);
1010+
let is_spotlight = attrs.has_doc_flag(sym::spotlight);
10101011
Item {
10111012
name: Some(self.name.clean(cx)),
10121013
attrs,
@@ -1021,6 +1022,7 @@ impl Clean<Item> for doctree::Trait<'_> {
10211022
items: self.items.iter().map(|ti| ti.clean(cx)).collect(),
10221023
generics: self.generics.clean(cx),
10231024
bounds: self.bounds.clean(cx),
1025+
is_spotlight,
10241026
is_auto: self.is_auto.clean(cx),
10251027
}),
10261028
}

src/librustdoc/clean/types.rs

+1
Original file line numberDiff line numberDiff line change
@@ -967,6 +967,7 @@ pub struct Trait {
967967
pub items: Vec<Item>,
968968
pub generics: Generics,
969969
pub bounds: Vec<GenericBound>,
970+
pub is_spotlight: bool,
970971
pub is_auto: bool,
971972
}
972973

src/librustdoc/html/format.rs

+12
Original file line numberDiff line numberDiff line change
@@ -63,10 +63,22 @@ impl Buffer {
6363
Buffer { for_html: false, buffer: String::new() }
6464
}
6565

66+
crate fn is_empty(&self) -> bool {
67+
self.buffer.is_empty()
68+
}
69+
6670
crate fn into_inner(self) -> String {
6771
self.buffer
6872
}
6973

74+
crate fn insert_str(&mut self, idx: usize, s: &str) {
75+
self.buffer.insert_str(idx, s);
76+
}
77+
78+
crate fn push_str(&mut self, s: &str) {
79+
self.buffer.push_str(s);
80+
}
81+
7082
// Intended for consumption by write! and writeln! (std::fmt) but without
7183
// the fmt::Result return type imposed by fmt::Write (and avoiding the trait
7284
// import).

src/librustdoc/html/render.rs

+64-5
Original file line numberDiff line numberDiff line change
@@ -2415,7 +2415,7 @@ fn item_function(w: &mut Buffer, cx: &Context, it: &clean::Item, f: &clean::Func
24152415
write!(
24162416
w,
24172417
"{vis}{constness}{asyncness}{unsafety}{abi}fn \
2418-
{name}{generics}{decl}{where_clause}</pre>",
2418+
{name}{generics}{decl}{spotlight}{where_clause}</pre>",
24192419
vis = it.visibility.print_with_space(),
24202420
constness = f.header.constness.print_with_space(),
24212421
asyncness = f.header.asyncness.print_with_space(),
@@ -2425,7 +2425,8 @@ fn item_function(w: &mut Buffer, cx: &Context, it: &clean::Item, f: &clean::Func
24252425
generics = f.generics.print(),
24262426
where_clause = WhereClause { gens: &f.generics, indent: 0, end_newline: true },
24272427
decl = Function { decl: &f.decl, header_len, indent: 0, asyncness: f.header.asyncness }
2428-
.print()
2428+
.print(),
2429+
spotlight = spotlight_decl(&f.decl),
24292430
);
24302431
document(w, cx, it)
24312432
}
@@ -2612,7 +2613,7 @@ fn item_trait(w: &mut Buffer, cx: &Context, it: &clean::Item, t: &clean::Trait)
26122613
let name = m.name.as_ref().unwrap();
26132614
let item_type = m.type_();
26142615
let id = cx.derive_id(format!("{}.{}", item_type, name));
2615-
write!(w, "<h3 id='{id}' class='method'><code>", id = id);
2616+
write!(w, "<h3 id='{id}' class='method'><code>", id = id,);
26162617
render_assoc_item(w, m, AssocItemLink::Anchor(Some(&id)), ItemType::Impl);
26172618
write!(w, "</code>");
26182619
render_stability_since(w, m, t);
@@ -2926,7 +2927,7 @@ fn render_assoc_item(
29262927
write!(
29272928
w,
29282929
"{}{}{}{}{}{}{}fn <a href='{href}' class='fnname'>{name}</a>\
2929-
{generics}{decl}{where_clause}",
2930+
{generics}{decl}{spotlight}{where_clause}",
29302931
if parent == ItemType::Trait { " " } else { "" },
29312932
meth.visibility.print_with_space(),
29322933
header.constness.print_with_space(),
@@ -2938,6 +2939,7 @@ fn render_assoc_item(
29382939
name = name,
29392940
generics = g.print(),
29402941
decl = Function { decl: d, header_len, indent, asyncness: header.asyncness }.print(),
2942+
spotlight = spotlight_decl(&d),
29412943
where_clause = WhereClause { gens: g, indent, end_newline }
29422944
)
29432945
}
@@ -3559,6 +3561,62 @@ fn should_render_item(item: &clean::Item, deref_mut_: bool) -> bool {
35593561
}
35603562
}
35613563

3564+
fn spotlight_decl(decl: &clean::FnDecl) -> String {
3565+
let mut out = Buffer::html();
3566+
let mut trait_ = String::new();
3567+
3568+
if let Some(did) = decl.output.def_id() {
3569+
let c = cache();
3570+
if let Some(impls) = c.impls.get(&did) {
3571+
for i in impls {
3572+
let impl_ = i.inner_impl();
3573+
if impl_.trait_.def_id().map_or(false, |d| c.traits[&d].is_spotlight) {
3574+
if out.is_empty() {
3575+
out.push_str(&format!(
3576+
"<h3 class=\"important\">Important traits for {}</h3>\
3577+
<code class=\"content\">",
3578+
impl_.for_.print()
3579+
));
3580+
trait_.push_str(&impl_.for_.print().to_string());
3581+
}
3582+
3583+
//use the "where" class here to make it small
3584+
out.push_str(&format!(
3585+
"<span class=\"where fmt-newline\">{}</span>",
3586+
impl_.print()
3587+
));
3588+
let t_did = impl_.trait_.def_id().unwrap();
3589+
for it in &impl_.items {
3590+
if let clean::TypedefItem(ref tydef, _) = it.inner {
3591+
out.push_str("<span class=\"where fmt-newline\"> ");
3592+
assoc_type(
3593+
&mut out,
3594+
it,
3595+
&[],
3596+
Some(&tydef.type_),
3597+
AssocItemLink::GotoSource(t_did, &FxHashSet::default()),
3598+
"",
3599+
);
3600+
out.push_str(";</span>");
3601+
}
3602+
}
3603+
}
3604+
}
3605+
}
3606+
}
3607+
3608+
if !out.is_empty() {
3609+
out.insert_str(
3610+
0,
3611+
"<span class=\"important-traits\"><span class=\"important-traits-tooltip\">ⓘ<div class='important-traits-tooltiptext'><span class=\"docblock\">"
3612+
3613+
);
3614+
out.push_str("</code></span></div></span></span>");
3615+
}
3616+
3617+
out.into_inner()
3618+
}
3619+
35623620
fn render_impl(
35633621
w: &mut Buffer,
35643622
cx: &Context,
@@ -3670,7 +3728,8 @@ fn render_impl(
36703728
// Only render when the method is not static or we allow static methods
36713729
if render_method_item {
36723730
let id = cx.derive_id(format!("{}.{}", item_type, name));
3673-
write!(w, "<h4 id='{}' class=\"{}{}\"><code>", id, item_type, extra_class);
3731+
write!(w, "<h4 id='{}' class=\"{}{}\">", id, item_type, extra_class);
3732+
write!(w, "<code>");
36743733
render_assoc_item(w, item, link.anchor(&id), ItemType::Impl);
36753734
write!(w, "</code>");
36763735
render_stability_since_raw(w, item.stable_since(), outer_version);

src/librustdoc/html/static/main.js

+7
Original file line numberDiff line numberDiff line change
@@ -2636,6 +2636,13 @@ function defocusSearchBar() {
26362636
});
26372637
}());
26382638

2639+
onEachLazy(document.getElementsByClassName("important-traits"), function(e) {
2640+
e.onclick = function() {
2641+
this.getElementsByClassName('important-traits-tooltiptext')[0]
2642+
.classList.toggle("force-tooltip");
2643+
};
2644+
});
2645+
26392646
// In the search display, allows to switch between tabs.
26402647
function printTab(nb) {
26412648
if (nb === 0 || nb === 1 || nb === 2) {

0 commit comments

Comments
 (0)