Skip to content

Commit fc09817

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 61fccf0 + c621a54 commit fc09817

File tree

23 files changed

+284
-15
lines changed

23 files changed

+284
-15
lines changed

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

Lines changed: 21 additions & 0 deletions
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
Lines changed: 30 additions & 0 deletions
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

Lines changed: 1 addition & 0 deletions
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

Lines changed: 1 addition & 0 deletions
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

Lines changed: 1 addition & 0 deletions
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

Lines changed: 1 addition & 0 deletions
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

Lines changed: 3 additions & 0 deletions
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

Lines changed: 2 additions & 0 deletions
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

Lines changed: 3 additions & 1 deletion
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

Lines changed: 2 additions & 0 deletions
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

Lines changed: 1 addition & 0 deletions
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

Lines changed: 12 additions & 0 deletions
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

Lines changed: 64 additions & 5 deletions
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

Lines changed: 7 additions & 0 deletions
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)