Skip to content

Commit 4ae25a6

Browse files
authored
Rollup merge of #41280 - QuietMisdreavus:rustdoc-toc, r=GuillaumeGomez
rustdoc: add a list of headings to the sidebar It's another misdreavus rustdoc PR, which means it's time for Bikeshed City once again! `:3` In an effort to aid navigation in long documentation pages, this PR adds a listing of headings to the sidebars of pages where such headings exist. For example, for structs, links to their fields, inherent methods, and trait implementations are available where applicable. Examples: * Modules/Crate roots ![image](https://cloud.githubusercontent.com/assets/5217170/25019930/1000fa3a-2052-11e7-98ff-ddf5af18b3e6.png) * Enums ![image](https://cloud.githubusercontent.com/assets/5217170/25019954/33497f9e-2052-11e7-88cf-df46f1b3b8a3.png) * Primitives ![image](https://cloud.githubusercontent.com/assets/5217170/25019978/4820bbc6-2052-11e7-8b5a-96a864eb2a5b.png) * Traits ![image](https://cloud.githubusercontent.com/assets/5217170/25020093/bd1bc9f2-2052-11e7-9cd1-00a0ad8007bc.png) * Structs ![image](https://cloud.githubusercontent.com/assets/5217170/25020145/d75206b0-2052-11e7-88cc-9e9525084775.png) Open questions: * Right now, these kinds of pages (and also unions) are the only pages that will receive the name header - pages for functions, constants, macros, etc, won't have the corresponding name in their sidebar. Should I print the name regardless and only add table-of-contents links for pages that have them? This would make them match, for example, a struct with no public fields, no methods, and no trait implementations. The latter would still have a "Struct MyStruct" line above the module contents, with no header links to speak of, whereas a function wouldn't even have "Function my\_function". * This is only a header listing, but there has been requests to include a more-complete listing of fields/methods/traits/etc, for example in #41123.
2 parents 914b6f1 + 27bfbd5 commit 4ae25a6

File tree

2 files changed

+225
-4
lines changed

2 files changed

+225
-4
lines changed

src/librustdoc/clean/mod.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -292,7 +292,7 @@ impl Item {
292292
self.type_() == ItemType::Struct
293293
}
294294
pub fn is_enum(&self) -> bool {
295-
self.type_() == ItemType::Module
295+
self.type_() == ItemType::Enum
296296
}
297297
pub fn is_fn(&self) -> bool {
298298
self.type_() == ItemType::Function
@@ -312,6 +312,9 @@ impl Item {
312312
pub fn is_primitive(&self) -> bool {
313313
self.type_() == ItemType::Primitive
314314
}
315+
pub fn is_union(&self) -> bool {
316+
self.type_() == ItemType::Union
317+
}
315318
pub fn is_stripped(&self) -> bool {
316319
match self.inner { StrippedItem(..) => true, _ => false }
317320
}

src/librustdoc/html/render.rs

+221-3
Original file line numberDiff line numberDiff line change
@@ -2430,7 +2430,7 @@ fn item_struct(w: &mut fmt::Formatter, cx: &Context, it: &clean::Item,
24302430
}).peekable();
24312431
if let doctree::Plain = s.struct_type {
24322432
if fields.peek().is_some() {
2433-
write!(w, "<h2 class='fields'>Fields</h2>")?;
2433+
write!(w, "<h2 id='fields' class='fields'>Fields</h2>")?;
24342434
for (field, ty) in fields {
24352435
let id = derive_id(format!("{}.{}",
24362436
ItemType::StructField,
@@ -2478,7 +2478,7 @@ fn item_union(w: &mut fmt::Formatter, cx: &Context, it: &clean::Item,
24782478
}
24792479
}).peekable();
24802480
if fields.peek().is_some() {
2481-
write!(w, "<h2 class='fields'>Fields</h2>")?;
2481+
write!(w, "<h2 id='fields' class='fields'>Fields</h2>")?;
24822482
for (field, ty) in fields {
24832483
write!(w, "<span id='{shortty}.{name}' class=\"{shortty}\"><code>{name}: {ty}</code>
24842484
</span>",
@@ -2550,7 +2550,7 @@ fn item_enum(w: &mut fmt::Formatter, cx: &Context, it: &clean::Item,
25502550

25512551
document(w, cx, it)?;
25522552
if !e.variants.is_empty() {
2553-
write!(w, "<h2 class='variants'>Variants</h2>\n")?;
2553+
write!(w, "<h2 id='variants' class='variants'>Variants</h2>\n")?;
25542554
for variant in &e.variants {
25552555
let id = derive_id(format!("{}.{}",
25562556
ItemType::Variant,
@@ -3074,6 +3074,37 @@ impl<'a> fmt::Display for Sidebar<'a> {
30743074
let it = self.item;
30753075
let parentlen = cx.current.len() - if it.is_mod() {1} else {0};
30763076

3077+
if it.is_struct() || it.is_trait() || it.is_primitive() || it.is_union()
3078+
|| it.is_enum() || it.is_mod()
3079+
{
3080+
write!(fmt, "<p class='location'>")?;
3081+
match it.inner {
3082+
clean::StructItem(..) => write!(fmt, "Struct ")?,
3083+
clean::TraitItem(..) => write!(fmt, "Trait ")?,
3084+
clean::PrimitiveItem(..) => write!(fmt, "Primitive Type ")?,
3085+
clean::UnionItem(..) => write!(fmt, "Union ")?,
3086+
clean::EnumItem(..) => write!(fmt, "Enum ")?,
3087+
clean::ModuleItem(..) => if it.is_crate() {
3088+
write!(fmt, "Crate ")?;
3089+
} else {
3090+
write!(fmt, "Module ")?;
3091+
},
3092+
_ => (),
3093+
}
3094+
write!(fmt, "{}", it.name.as_ref().unwrap())?;
3095+
write!(fmt, "</p>")?;
3096+
3097+
match it.inner {
3098+
clean::StructItem(ref s) => sidebar_struct(fmt, it, s)?,
3099+
clean::TraitItem(ref t) => sidebar_trait(fmt, it, t)?,
3100+
clean::PrimitiveItem(ref p) => sidebar_primitive(fmt, it, p)?,
3101+
clean::UnionItem(ref u) => sidebar_union(fmt, it, u)?,
3102+
clean::EnumItem(ref e) => sidebar_enum(fmt, it, e)?,
3103+
clean::ModuleItem(ref m) => sidebar_module(fmt, it, &m.items)?,
3104+
_ => (),
3105+
}
3106+
}
3107+
30773108
// The sidebar is designed to display sibling functions, modules and
30783109
// other miscellaneous information. since there are lots of sibling
30793110
// items (and that causes quadratic growth in large modules),
@@ -3116,6 +3147,193 @@ impl<'a> fmt::Display for Sidebar<'a> {
31163147
}
31173148
}
31183149

3150+
fn sidebar_assoc_items(it: &clean::Item) -> String {
3151+
let mut out = String::new();
3152+
let c = cache();
3153+
if let Some(v) = c.impls.get(&it.def_id) {
3154+
if v.iter().any(|i| i.inner_impl().trait_.is_none()) {
3155+
out.push_str("<li><a href=\"#methods\">Methods</a></li>");
3156+
}
3157+
3158+
if v.iter().any(|i| i.inner_impl().trait_.is_some()) {
3159+
if let Some(impl_) = v.iter()
3160+
.filter(|i| i.inner_impl().trait_.is_some())
3161+
.find(|i| i.inner_impl().trait_.def_id() == c.deref_trait_did) {
3162+
if let Some(target) = impl_.inner_impl().items.iter().filter_map(|item| {
3163+
match item.inner {
3164+
clean::TypedefItem(ref t, true) => Some(&t.type_),
3165+
_ => None,
3166+
}
3167+
}).next() {
3168+
let inner_impl = target.def_id().or(target.primitive_type().and_then(|prim| {
3169+
c.primitive_locations.get(&prim).cloned()
3170+
})).and_then(|did| c.impls.get(&did));
3171+
if inner_impl.is_some() {
3172+
out.push_str("<li><a href=\"#deref-methods\">");
3173+
out.push_str(&format!("Methods from {:#}&lt;Target={:#}&gt;",
3174+
impl_.inner_impl().trait_.as_ref().unwrap(),
3175+
target));
3176+
out.push_str("</a></li>");
3177+
}
3178+
}
3179+
}
3180+
out.push_str("<li><a href=\"#implementations\">Trait Implementations</a></li>");
3181+
}
3182+
}
3183+
3184+
out
3185+
}
3186+
3187+
fn sidebar_struct(fmt: &mut fmt::Formatter, it: &clean::Item,
3188+
s: &clean::Struct) -> fmt::Result {
3189+
let mut sidebar = String::new();
3190+
3191+
if s.fields.iter()
3192+
.any(|f| if let clean::StructFieldItem(..) = f.inner { true } else { false }) {
3193+
if let doctree::Plain = s.struct_type {
3194+
sidebar.push_str("<li><a href=\"#fields\">Fields</a></li>");
3195+
}
3196+
}
3197+
3198+
sidebar.push_str(&sidebar_assoc_items(it));
3199+
3200+
if !sidebar.is_empty() {
3201+
write!(fmt, "<div class=\"block items\"><ul>{}</ul></div>", sidebar)?;
3202+
}
3203+
Ok(())
3204+
}
3205+
3206+
fn sidebar_trait(fmt: &mut fmt::Formatter, it: &clean::Item,
3207+
t: &clean::Trait) -> fmt::Result {
3208+
let mut sidebar = String::new();
3209+
3210+
let has_types = t.items.iter().any(|m| m.is_associated_type());
3211+
let has_consts = t.items.iter().any(|m| m.is_associated_const());
3212+
let has_required = t.items.iter().any(|m| m.is_ty_method());
3213+
let has_provided = t.items.iter().any(|m| m.is_method());
3214+
3215+
if has_types {
3216+
sidebar.push_str("<li><a href=\"#associated-types\">Associated Types</a></li>");
3217+
}
3218+
if has_consts {
3219+
sidebar.push_str("<li><a href=\"#associated-const\">Associated Constants</a></li>");
3220+
}
3221+
if has_required {
3222+
sidebar.push_str("<li><a href=\"#required-methods\">Required Methods</a></li>");
3223+
}
3224+
if has_provided {
3225+
sidebar.push_str("<li><a href=\"#provided-methods\">Provided Methods</a></li>");
3226+
}
3227+
3228+
sidebar.push_str(&sidebar_assoc_items(it));
3229+
3230+
sidebar.push_str("<li><a href=\"#implementors\">Implementors</a></li>");
3231+
3232+
write!(fmt, "<div class=\"block items\"><ul>{}</ul></div>", sidebar)
3233+
}
3234+
3235+
fn sidebar_primitive(fmt: &mut fmt::Formatter, it: &clean::Item,
3236+
_p: &clean::PrimitiveType) -> fmt::Result {
3237+
let sidebar = sidebar_assoc_items(it);
3238+
3239+
if !sidebar.is_empty() {
3240+
write!(fmt, "<div class=\"block items\"><ul>{}</ul></div>", sidebar)?;
3241+
}
3242+
Ok(())
3243+
}
3244+
3245+
fn sidebar_union(fmt: &mut fmt::Formatter, it: &clean::Item,
3246+
u: &clean::Union) -> fmt::Result {
3247+
let mut sidebar = String::new();
3248+
3249+
if u.fields.iter()
3250+
.any(|f| if let clean::StructFieldItem(..) = f.inner { true } else { false }) {
3251+
sidebar.push_str("<li><a href=\"#fields\">Fields</a></li>");
3252+
}
3253+
3254+
sidebar.push_str(&sidebar_assoc_items(it));
3255+
3256+
if !sidebar.is_empty() {
3257+
write!(fmt, "<div class=\"block items\"><ul>{}</ul></div>", sidebar)?;
3258+
}
3259+
Ok(())
3260+
}
3261+
3262+
fn sidebar_enum(fmt: &mut fmt::Formatter, it: &clean::Item,
3263+
e: &clean::Enum) -> fmt::Result {
3264+
let mut sidebar = String::new();
3265+
3266+
if !e.variants.is_empty() {
3267+
sidebar.push_str("<li><a href=\"#variants\">Variants</a></li>");
3268+
}
3269+
3270+
sidebar.push_str(&sidebar_assoc_items(it));
3271+
3272+
if !sidebar.is_empty() {
3273+
write!(fmt, "<div class=\"block items\"><ul>{}</ul></div>", sidebar)?;
3274+
}
3275+
Ok(())
3276+
}
3277+
3278+
fn sidebar_module(fmt: &mut fmt::Formatter, _it: &clean::Item,
3279+
items: &[clean::Item]) -> fmt::Result {
3280+
let mut sidebar = String::new();
3281+
3282+
if items.iter().any(|it| it.type_() == ItemType::ExternCrate ||
3283+
it.type_() == ItemType::Import) {
3284+
sidebar.push_str(&format!("<li><a href=\"#{id}\">{name}</a></li>",
3285+
id = "reexports",
3286+
name = "Reexports"));
3287+
}
3288+
3289+
// ordering taken from item_module, reorder, where it prioritized elements in a certain order
3290+
// to print its headings
3291+
for &myty in &[ItemType::Primitive, ItemType::Module, ItemType::Macro, ItemType::Struct,
3292+
ItemType::Enum, ItemType::Constant, ItemType::Static, ItemType::Trait,
3293+
ItemType::Function, ItemType::Typedef, ItemType::Union, ItemType::Impl,
3294+
ItemType::TyMethod, ItemType::Method, ItemType::StructField, ItemType::Variant,
3295+
ItemType::AssociatedType, ItemType::AssociatedConst] {
3296+
if items.iter().any(|it| {
3297+
if let clean::DefaultImplItem(..) = it.inner {
3298+
false
3299+
} else {
3300+
!maybe_ignore_item(it) && !it.is_stripped() && it.type_() == myty
3301+
}
3302+
}) {
3303+
let (short, name) = match myty {
3304+
ItemType::ExternCrate |
3305+
ItemType::Import => ("reexports", "Reexports"),
3306+
ItemType::Module => ("modules", "Modules"),
3307+
ItemType::Struct => ("structs", "Structs"),
3308+
ItemType::Union => ("unions", "Unions"),
3309+
ItemType::Enum => ("enums", "Enums"),
3310+
ItemType::Function => ("functions", "Functions"),
3311+
ItemType::Typedef => ("types", "Type Definitions"),
3312+
ItemType::Static => ("statics", "Statics"),
3313+
ItemType::Constant => ("constants", "Constants"),
3314+
ItemType::Trait => ("traits", "Traits"),
3315+
ItemType::Impl => ("impls", "Implementations"),
3316+
ItemType::TyMethod => ("tymethods", "Type Methods"),
3317+
ItemType::Method => ("methods", "Methods"),
3318+
ItemType::StructField => ("fields", "Struct Fields"),
3319+
ItemType::Variant => ("variants", "Variants"),
3320+
ItemType::Macro => ("macros", "Macros"),
3321+
ItemType::Primitive => ("primitives", "Primitive Types"),
3322+
ItemType::AssociatedType => ("associated-types", "Associated Types"),
3323+
ItemType::AssociatedConst => ("associated-consts", "Associated Constants"),
3324+
};
3325+
sidebar.push_str(&format!("<li><a href=\"#{id}\">{name}</a></li>",
3326+
id = short,
3327+
name = name));
3328+
}
3329+
}
3330+
3331+
if !sidebar.is_empty() {
3332+
write!(fmt, "<div class=\"block items\"><ul>{}</ul></div>", sidebar)?;
3333+
}
3334+
Ok(())
3335+
}
3336+
31193337
impl<'a> fmt::Display for Source<'a> {
31203338
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
31213339
let Source(s) = *self;

0 commit comments

Comments
 (0)