-
Notifications
You must be signed in to change notification settings - Fork 13.7k
Rustdoc: generate unique ID attributes for each page #30036
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
Changes from 3 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -342,6 +342,55 @@ impl fmt::Display for IndexItemFunctionType { | |
thread_local!(static CACHE_KEY: RefCell<Arc<Cache>> = Default::default()); | ||
thread_local!(pub static CURRENT_LOCATION_KEY: RefCell<Vec<String>> = | ||
RefCell::new(Vec::new())); | ||
thread_local!(static USED_ID_MAP: RefCell<HashMap<String, usize>> = | ||
RefCell::new(init_ids())); | ||
|
||
fn init_ids() -> HashMap<String, usize> { | ||
[ | ||
"main", | ||
"search", | ||
"help", | ||
"TOC", | ||
"render-detail", | ||
"associated-types", | ||
"associated-const", | ||
"required-methods", | ||
"provided-methods", | ||
"implementors", | ||
"implementors-list", | ||
"methods", | ||
"deref-methods", | ||
"implementations", | ||
"derived_implementations" | ||
].into_iter().map(|id| (String::from(*id), 1)).collect::<HashMap<_, _>>() | ||
} | ||
|
||
/// This method resets the local table of used ID attributes. This is typically | ||
/// used at the beginning of rendering an entire HTML page to reset from the | ||
/// previous state (if any). | ||
pub fn reset_ids() { | ||
USED_ID_MAP.with(|s| *s.borrow_mut() = init_ids()); | ||
} | ||
|
||
pub fn with_unique_id<T, F: FnOnce(&str) -> T>(candidate: String, f: F) -> T { | ||
USED_ID_MAP.with(|map| { | ||
let (id, ret) = match map.borrow_mut().get_mut(&candidate) { | ||
None => { | ||
let ret = f(&candidate); | ||
(candidate, ret) | ||
}, | ||
Some(a) => { | ||
let id = format!("{}-{}", candidate, *a); | ||
let ret = f(&id); | ||
*a += 1; | ||
(id, ret) | ||
} | ||
}; | ||
|
||
map.borrow_mut().insert(id, 1); | ||
ret | ||
}) | ||
} | ||
|
||
/// Generates the documentation for `crate` into the directory `dst` | ||
pub fn run(mut krate: clean::Crate, | ||
|
@@ -1274,7 +1323,7 @@ impl Context { | |
keywords: &keywords, | ||
}; | ||
|
||
markdown::reset_headers(); | ||
reset_ids(); | ||
|
||
// We have a huge number of calls to write, so try to alleviate some | ||
// of the pain by using a buffered writer instead of invoking the | ||
|
@@ -1696,10 +1745,10 @@ fn item_module(w: &mut fmt::Formatter, cx: &Context, | |
ItemType::AssociatedType => ("associated-types", "Associated Types"), | ||
ItemType::AssociatedConst => ("associated-consts", "Associated Constants"), | ||
}; | ||
try!(write!(w, | ||
"<h2 id='{id}' class='section-header'>\ | ||
<a href=\"#{id}\">{name}</a></h2>\n<table>", | ||
id = short, name = name)); | ||
try!(with_unique_id(short.to_owned(), |id| | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Typically multi-line closures are surrounded with braces rather than newlines |
||
write!(w, "<h2 id='{id}' class='section-header'>\ | ||
<a href=\"#{id}\">{name}</a></h2>\n<table>", | ||
id = id, name = name))); | ||
} | ||
|
||
match myitem.inner { | ||
|
@@ -1920,10 +1969,11 @@ fn item_trait(w: &mut fmt::Formatter, cx: &Context, it: &clean::Item, | |
|
||
fn trait_item(w: &mut fmt::Formatter, cx: &Context, m: &clean::Item) | ||
-> fmt::Result { | ||
try!(write!(w, "<h3 id='{ty}.{name}' class='method stab {stab}'><code>", | ||
ty = shortty(m), | ||
name = *m.name.as_ref().unwrap(), | ||
stab = m.stability_class())); | ||
let name = m.name.as_ref().unwrap(); | ||
try!(with_unique_id(format!("{}.{}", shortty(m), name), |id| | ||
write!(w, "<h3 id='{id}' class='method stab {stab}'><code>", | ||
id = id, | ||
stab = m.stability_class()))); | ||
try!(render_assoc_item(w, m, AssocItemLink::Anchor)); | ||
try!(write!(w, "</code></h3>")); | ||
try!(document(w, cx, m)); | ||
|
@@ -2112,11 +2162,12 @@ fn item_struct(w: &mut fmt::Formatter, cx: &Context, it: &clean::Item, | |
if fields.peek().is_some() { | ||
try!(write!(w, "<h2 class='fields'>Fields</h2>\n<table>")); | ||
for field in fields { | ||
try!(write!(w, "<tr class='stab {stab}'> | ||
<td id='structfield.{name}'>\ | ||
<code>{name}</code></td><td>", | ||
stab = field.stability_class(), | ||
name = field.name.as_ref().unwrap())); | ||
let name = field.name.as_ref().unwrap(); | ||
try!(with_unique_id(format!("structfield.{}", name), |id| | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think that ids like this don't need to be altered because there shouldn't ever be more than one struct on a page? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. (same for some around this) |
||
write!(w, "<tr class='stab {}'><td id='{}'><code>{}</code></td><td>", | ||
field.stability_class(), | ||
id, | ||
name))); | ||
try!(document(w, cx, field)); | ||
try!(write!(w, "</td></tr>")); | ||
} | ||
|
@@ -2183,8 +2234,9 @@ fn item_enum(w: &mut fmt::Formatter, cx: &Context, it: &clean::Item, | |
if !e.variants.is_empty() { | ||
try!(write!(w, "<h2 class='variants'>Variants</h2>\n<table>")); | ||
for variant in &e.variants { | ||
try!(write!(w, "<tr><td id='variant.{name}'><code>{name}</code></td><td>", | ||
name = variant.name.as_ref().unwrap())); | ||
let name = variant.name.as_ref().unwrap(); | ||
try!(with_unique_id(format!("variant.{}", name), |id| | ||
write!(w, "<tr><td id='{}'><code>{}</code></td><td>", id, name))); | ||
try!(document(w, cx, variant)); | ||
match variant.inner { | ||
clean::VariantItem(ref var) => { | ||
|
@@ -2202,11 +2254,10 @@ fn item_enum(w: &mut fmt::Formatter, cx: &Context, it: &clean::Item, | |
try!(write!(w, "<h3 class='fields'>Fields</h3>\n | ||
<table>")); | ||
for field in fields { | ||
try!(write!(w, "<tr><td \ | ||
id='variant.{v}.field.{f}'>\ | ||
<code>{f}</code></td><td>", | ||
v = variant.name.as_ref().unwrap(), | ||
f = field.name.as_ref().unwrap())); | ||
let v = variant.name.as_ref().unwrap(); | ||
let f = field.name.as_ref().unwrap(); | ||
try!(with_unique_id(format!("variant.{}.field.{}", v, f), |id| | ||
write!(w, "<tr><td id='{}'><code>{}</code></td><td>", id, f))); | ||
try!(document(w, cx, field)); | ||
try!(write!(w, "</td></tr>")); | ||
} | ||
|
@@ -2418,44 +2469,38 @@ fn render_impl(w: &mut fmt::Formatter, cx: &Context, i: &Impl, link: AssocItemLi | |
|
||
fn doctraititem(w: &mut fmt::Formatter, cx: &Context, item: &clean::Item, | ||
link: AssocItemLink, render_static: bool) -> fmt::Result { | ||
let name = item.name.as_ref().unwrap(); | ||
match item.inner { | ||
clean::MethodItem(..) | clean::TyMethodItem(..) => { | ||
// Only render when the method is not static or we allow static methods | ||
if !is_static_method(item) || render_static { | ||
try!(write!(w, "<h4 id='method.{}' class='{}'><code>", | ||
*item.name.as_ref().unwrap(), | ||
shortty(item))); | ||
try!(with_unique_id(format!("method.{}", name), |id| | ||
write!(w, "<h4 id='{}' class='{}'><code>", id, shortty(item)))); | ||
try!(render_assoc_item(w, item, link)); | ||
try!(write!(w, "</code></h4>\n")); | ||
} | ||
} | ||
clean::TypedefItem(ref tydef, _) => { | ||
let name = item.name.as_ref().unwrap(); | ||
try!(write!(w, "<h4 id='assoc_type.{}' class='{}'><code>", | ||
*name, | ||
shortty(item))); | ||
try!(with_unique_id(format!("assoc_type.{}", name), |id| | ||
write!(w, "<h4 id='{}' class='{}'><code>", id, shortty(item)))); | ||
try!(write!(w, "type {} = {}", name, tydef.type_)); | ||
try!(write!(w, "</code></h4>\n")); | ||
} | ||
clean::AssociatedConstItem(ref ty, ref default) => { | ||
let name = item.name.as_ref().unwrap(); | ||
try!(write!(w, "<h4 id='assoc_const.{}' class='{}'><code>", | ||
*name, shortty(item))); | ||
try!(with_unique_id(format!("assoc_const.{}", name), |id| | ||
write!(w, "<h4 id='{}' class='{}'><code>", id, shortty(item)))); | ||
try!(assoc_const(w, item, ty, default.as_ref())); | ||
try!(write!(w, "</code></h4>\n")); | ||
} | ||
clean::ConstantItem(ref c) => { | ||
let name = item.name.as_ref().unwrap(); | ||
try!(write!(w, "<h4 id='assoc_const.{}' class='{}'><code>", | ||
*name, shortty(item))); | ||
try!(with_unique_id(format!("assoc_const.{}", name), |id| | ||
write!(w, "<h4 id='{}' class='{}'><code>", id, shortty(item)))); | ||
try!(assoc_const(w, item, &c.type_, Some(&c.expr))); | ||
try!(write!(w, "</code></h4>\n")); | ||
} | ||
clean::AssociatedTypeItem(ref bounds, ref default) => { | ||
let name = item.name.as_ref().unwrap(); | ||
try!(write!(w, "<h4 id='assoc_type.{}' class='{}'><code>", | ||
*name, | ||
shortty(item))); | ||
try!(with_unique_id(format!("assoc_type.{}", name), |id| | ||
write!(w, "<h4 id='{}' class='{}'><code>", id, shortty(item)))); | ||
try!(assoc_type(w, item, bounds, default)); | ||
try!(write!(w, "</code></h4>\n")); | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can this return a unique id instead of taking a closure?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure, I felt ambivalent about this anyway. (The intention was to avoid the extra heap allocation)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah yeah don't worry too much about allocations, it happens a lot in rustdoc anyway :)