Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

rustdoc: add a list of headings to the sidebar #41280

Merged
merged 1 commit into from
Apr 17, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion src/librustdoc/clean/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -292,7 +292,7 @@ impl Item {
self.type_() == ItemType::Struct
}
pub fn is_enum(&self) -> bool {
self.type_() == ItemType::Module
self.type_() == ItemType::Enum
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice catch!

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was so confused why enums weren't getting the sidebar stuff, until I decided to trace back through what is_enum was doing. Apparently it's been like this for a while, but there's also no other users for this method, so it's not harming anything.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But now it's working correctly, which is great.

}
pub fn is_fn(&self) -> bool {
self.type_() == ItemType::Function
Expand All @@ -312,6 +312,9 @@ impl Item {
pub fn is_primitive(&self) -> bool {
self.type_() == ItemType::Primitive
}
pub fn is_union(&self) -> bool {
self.type_() == ItemType::Union
}
pub fn is_stripped(&self) -> bool {
match self.inner { StrippedItem(..) => true, _ => false }
}
Expand Down
224 changes: 221 additions & 3 deletions src/librustdoc/html/render.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2411,7 +2411,7 @@ fn item_struct(w: &mut fmt::Formatter, cx: &Context, it: &clean::Item,
}).peekable();
if let doctree::Plain = s.struct_type {
if fields.peek().is_some() {
write!(w, "<h2 class='fields'>Fields</h2>")?;
write!(w, "<h2 id='fields' class='fields'>Fields</h2>")?;
for (field, ty) in fields {
let id = derive_id(format!("{}.{}",
ItemType::StructField,
Expand Down Expand Up @@ -2459,7 +2459,7 @@ fn item_union(w: &mut fmt::Formatter, cx: &Context, it: &clean::Item,
}
}).peekable();
if fields.peek().is_some() {
write!(w, "<h2 class='fields'>Fields</h2>")?;
write!(w, "<h2 id='fields' class='fields'>Fields</h2>")?;
for (field, ty) in fields {
write!(w, "<span id='{shortty}.{name}' class=\"{shortty}\"><code>{name}: {ty}</code>
</span>",
Expand Down Expand Up @@ -2535,7 +2535,7 @@ fn item_enum(w: &mut fmt::Formatter, cx: &Context, it: &clean::Item,

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

if it.is_struct() || it.is_trait() || it.is_primitive() || it.is_union()
|| it.is_enum() || it.is_mod()
{
write!(fmt, "<p class='location'>")?;
match it.inner {
clean::StructItem(..) => write!(fmt, "Struct ")?,
clean::TraitItem(..) => write!(fmt, "Trait ")?,
clean::PrimitiveItem(..) => write!(fmt, "Primitive Type ")?,
clean::UnionItem(..) => write!(fmt, "Union ")?,
clean::EnumItem(..) => write!(fmt, "Enum ")?,
clean::ModuleItem(..) => if it.is_crate() {
write!(fmt, "Crate ")?;
} else {
write!(fmt, "Module ")?;
},
_ => (),
}
write!(fmt, "{}", it.name.as_ref().unwrap())?;
write!(fmt, "</p>")?;

match it.inner {
clean::StructItem(ref s) => sidebar_struct(fmt, it, s)?,
clean::TraitItem(ref t) => sidebar_trait(fmt, it, t)?,
clean::PrimitiveItem(ref p) => sidebar_primitive(fmt, it, p)?,
clean::UnionItem(ref u) => sidebar_union(fmt, it, u)?,
clean::EnumItem(ref e) => sidebar_enum(fmt, it, e)?,
clean::ModuleItem(ref m) => sidebar_module(fmt, it, &m.items)?,
_ => (),
}
}

// The sidebar is designed to display sibling functions, modules and
// other miscellaneous information. since there are lots of sibling
// items (and that causes quadratic growth in large modules),
Expand Down Expand Up @@ -3119,6 +3150,193 @@ impl<'a> fmt::Display for Sidebar<'a> {
}
}

fn sidebar_assoc_items(it: &clean::Item) -> String {
let mut out = String::new();
let c = cache();
if let Some(v) = c.impls.get(&it.def_id) {
if v.iter().any(|i| i.inner_impl().trait_.is_none()) {
out.push_str("<li><a href=\"#methods\">Methods</a></li>");
}

if v.iter().any(|i| i.inner_impl().trait_.is_some()) {
if let Some(impl_) = v.iter()
.filter(|i| i.inner_impl().trait_.is_some())
.find(|i| i.inner_impl().trait_.def_id() == c.deref_trait_did) {
if let Some(target) = impl_.inner_impl().items.iter().filter_map(|item| {
match item.inner {
clean::TypedefItem(ref t, true) => Some(&t.type_),
_ => None,
}
}).next() {
let inner_impl = target.def_id().or(target.primitive_type().and_then(|prim| {
c.primitive_locations.get(&prim).cloned()
})).and_then(|did| c.impls.get(&did));
if inner_impl.is_some() {
out.push_str("<li><a href=\"#deref-methods\">");
out.push_str(&format!("Methods from {:#}&lt;Target={:#}&gt;",
impl_.inner_impl().trait_.as_ref().unwrap(),
target));
out.push_str("</a></li>");
}
}
}
out.push_str("<li><a href=\"#implementations\">Trait Implementations</a></li>");
}
}

out
}

fn sidebar_struct(fmt: &mut fmt::Formatter, it: &clean::Item,
s: &clean::Struct) -> fmt::Result {
let mut sidebar = String::new();

if s.fields.iter()
.any(|f| if let clean::StructFieldItem(..) = f.inner { true } else { false }) {
if let doctree::Plain = s.struct_type {
sidebar.push_str("<li><a href=\"#fields\">Fields</a></li>");
}
}

sidebar.push_str(&sidebar_assoc_items(it));

if !sidebar.is_empty() {
write!(fmt, "<div class=\"block items\"><ul>{}</ul></div>", sidebar)?;
}
Ok(())
}

fn sidebar_trait(fmt: &mut fmt::Formatter, it: &clean::Item,
t: &clean::Trait) -> fmt::Result {
let mut sidebar = String::new();

let has_types = t.items.iter().any(|m| m.is_associated_type());
let has_consts = t.items.iter().any(|m| m.is_associated_const());
let has_required = t.items.iter().any(|m| m.is_ty_method());
let has_provided = t.items.iter().any(|m| m.is_method());

if has_types {
sidebar.push_str("<li><a href=\"#associated-types\">Associated Types</a></li>");
}
if has_consts {
sidebar.push_str("<li><a href=\"#associated-const\">Associated Constants</a></li>");
}
if has_required {
sidebar.push_str("<li><a href=\"#required-methods\">Required Methods</a></li>");
}
if has_provided {
sidebar.push_str("<li><a href=\"#provided-methods\">Provided Methods</a></li>");
}

sidebar.push_str(&sidebar_assoc_items(it));

sidebar.push_str("<li><a href=\"#implementors\">Implementors</a></li>");

write!(fmt, "<div class=\"block items\"><ul>{}</ul></div>", sidebar)
}

fn sidebar_primitive(fmt: &mut fmt::Formatter, it: &clean::Item,
_p: &clean::PrimitiveType) -> fmt::Result {
let sidebar = sidebar_assoc_items(it);

if !sidebar.is_empty() {
write!(fmt, "<div class=\"block items\"><ul>{}</ul></div>", sidebar)?;
}
Ok(())
}

fn sidebar_union(fmt: &mut fmt::Formatter, it: &clean::Item,
u: &clean::Union) -> fmt::Result {
let mut sidebar = String::new();

if u.fields.iter()
.any(|f| if let clean::StructFieldItem(..) = f.inner { true } else { false }) {
sidebar.push_str("<li><a href=\"#fields\">Fields</a></li>");
}

sidebar.push_str(&sidebar_assoc_items(it));

if !sidebar.is_empty() {
write!(fmt, "<div class=\"block items\"><ul>{}</ul></div>", sidebar)?;
}
Ok(())
}

fn sidebar_enum(fmt: &mut fmt::Formatter, it: &clean::Item,
e: &clean::Enum) -> fmt::Result {
let mut sidebar = String::new();

if !e.variants.is_empty() {
sidebar.push_str("<li><a href=\"#variants\">Variants</a></li>");
}

sidebar.push_str(&sidebar_assoc_items(it));

if !sidebar.is_empty() {
write!(fmt, "<div class=\"block items\"><ul>{}</ul></div>", sidebar)?;
}
Ok(())
}

fn sidebar_module(fmt: &mut fmt::Formatter, _it: &clean::Item,
items: &[clean::Item]) -> fmt::Result {
let mut sidebar = String::new();

if items.iter().any(|it| it.type_() == ItemType::ExternCrate ||
it.type_() == ItemType::Import) {
sidebar.push_str(&format!("<li><a href=\"#{id}\">{name}</a></li>",
id = "reexports",
name = "Reexports"));
}

// ordering taken from item_module, reorder, where it prioritized elements in a certain order
// to print its headings
for &myty in &[ItemType::Primitive, ItemType::Module, ItemType::Macro, ItemType::Struct,
ItemType::Enum, ItemType::Constant, ItemType::Static, ItemType::Trait,
ItemType::Function, ItemType::Typedef, ItemType::Union, ItemType::Impl,
ItemType::TyMethod, ItemType::Method, ItemType::StructField, ItemType::Variant,
ItemType::AssociatedType, ItemType::AssociatedConst] {
if items.iter().any(|it| {
if let clean::DefaultImplItem(..) = it.inner {
false
} else {
!maybe_ignore_item(it) && !it.is_stripped() && it.type_() == myty
}
}) {
let (short, name) = match myty {
ItemType::ExternCrate |
ItemType::Import => ("reexports", "Reexports"),
ItemType::Module => ("modules", "Modules"),
ItemType::Struct => ("structs", "Structs"),
ItemType::Union => ("unions", "Unions"),
ItemType::Enum => ("enums", "Enums"),
ItemType::Function => ("functions", "Functions"),
ItemType::Typedef => ("types", "Type Definitions"),
ItemType::Static => ("statics", "Statics"),
ItemType::Constant => ("constants", "Constants"),
ItemType::Trait => ("traits", "Traits"),
ItemType::Impl => ("impls", "Implementations"),
ItemType::TyMethod => ("tymethods", "Type Methods"),
ItemType::Method => ("methods", "Methods"),
ItemType::StructField => ("fields", "Struct Fields"),
ItemType::Variant => ("variants", "Variants"),
ItemType::Macro => ("macros", "Macros"),
ItemType::Primitive => ("primitives", "Primitive Types"),
ItemType::AssociatedType => ("associated-types", "Associated Types"),
ItemType::AssociatedConst => ("associated-consts", "Associated Constants"),
};
sidebar.push_str(&format!("<li><a href=\"#{id}\">{name}</a></li>",
id = short,
name = name));
}
}

if !sidebar.is_empty() {
write!(fmt, "<div class=\"block items\"><ul>{}</ul></div>", sidebar)?;
}
Ok(())
}

impl<'a> fmt::Display for Source<'a> {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
let Source(s) = *self;
Expand Down