diff options
Diffstat (limited to 'src/librustdoc/html/format.rs')
| -rw-r--r-- | src/librustdoc/html/format.rs | 121 |
1 files changed, 82 insertions, 39 deletions
diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index 981eb9589e9..a4cdef9358b 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -19,6 +19,7 @@ use rustc_middle::ty; use rustc_middle::ty::DefIdTree; use rustc_middle::ty::TyCtxt; use rustc_span::def_id::CRATE_DEF_INDEX; +use rustc_span::{sym, Symbol}; use rustc_target::spec::abi::Abi; use crate::clean::{ @@ -502,11 +503,45 @@ crate enum HrefError { NotInExternalCache, } +// This mostly works with sequences of symbols, but sometimes the first item +// comes from a string, and in that case we want to trim any trailing `/`. +// `syms` can be empty. +crate fn join_with_slash(first: Option<&str>, syms: &[Symbol]) -> String { + // 64 bytes covers 99.9%+ of cases. + let mut s = String::with_capacity(64); + if let Some(first) = first { + s.push_str(first.trim_end_matches('/')); + if !syms.is_empty() { + s.push('/'); + } + } + if !syms.is_empty() { + s.push_str(&syms[0].as_str()); + for sym in &syms[1..] { + s.push('/'); + s.push_str(&sym.as_str()); + } + } + s +} + +// Panics if `syms` is empty. +crate fn join_with_double_colon(syms: &[Symbol]) -> String { + // 64 bytes covers 99.9%+ of cases. + let mut s = String::with_capacity(64); + s.push_str(&syms[0].as_str()); + for sym in &syms[1..] { + s.push_str("::"); + s.push_str(&sym.as_str()); + } + s +} + crate fn href_with_root_path( did: DefId, cx: &Context<'_>, root_path: Option<&str>, -) -> Result<(String, ItemType, Vec<String>), HrefError> { +) -> Result<(String, ItemType, Vec<Symbol>), HrefError> { let tcx = cx.tcx(); let def_kind = tcx.def_kind(did); let did = match def_kind { @@ -518,7 +553,7 @@ crate fn href_with_root_path( }; let cache = cx.cache(); let relative_to = &cx.current; - fn to_module_fqp(shortty: ItemType, fqp: &[String]) -> &[String] { + fn to_module_fqp(shortty: ItemType, fqp: &[Symbol]) -> &[Symbol] { if shortty == ItemType::Module { fqp } else { &fqp[..fqp.len() - 1] } } @@ -533,9 +568,9 @@ crate fn href_with_root_path( let mut is_remote = false; let (fqp, shortty, mut url_parts) = match cache.paths.get(&did) { Some(&(ref fqp, shortty)) => (fqp, shortty, { - let module_fqp = to_module_fqp(shortty, fqp); + let module_fqp = to_module_fqp(shortty, fqp.as_slice()); debug!(?fqp, ?shortty, ?module_fqp); - href_relative_parts(module_fqp, relative_to) + href_relative_parts(module_fqp, relative_to).collect() }), None => { if let Some(&(ref fqp, shortty)) = cache.external_paths.get(&did) { @@ -548,10 +583,12 @@ crate fn href_with_root_path( is_remote = true; let s = s.trim_end_matches('/'); let mut builder = UrlPartsBuilder::singleton(s); - builder.extend(module_fqp.iter().map(String::as_str)); + builder.extend(module_fqp.iter().copied()); builder } - ExternalLocation::Local => href_relative_parts(module_fqp, relative_to), + ExternalLocation::Local => { + href_relative_parts(module_fqp, relative_to).collect() + } ExternalLocation::Unknown => return Err(HrefError::DocumentationNotBuilt), }, ) @@ -567,45 +604,50 @@ crate fn href_with_root_path( } } debug!(?url_parts); - let last = &fqp.last().unwrap()[..]; match shortty { ItemType::Module => { url_parts.push("index.html"); } _ => { - let filename = format!("{}.{}.html", shortty.as_str(), last); - url_parts.push(&filename); + let prefix = shortty.as_str(); + let last = fqp.last().unwrap(); + url_parts.push_fmt(format_args!("{}.{}.html", prefix, last)); } } Ok((url_parts.finish(), shortty, fqp.to_vec())) } -crate fn href(did: DefId, cx: &Context<'_>) -> Result<(String, ItemType, Vec<String>), HrefError> { +crate fn href(did: DefId, cx: &Context<'_>) -> Result<(String, ItemType, Vec<Symbol>), HrefError> { href_with_root_path(did, cx, None) } /// Both paths should only be modules. /// This is because modules get their own directories; that is, `std::vec` and `std::vec::Vec` will /// both need `../iter/trait.Iterator.html` to get at the iterator trait. -crate fn href_relative_parts(fqp: &[String], relative_to_fqp: &[String]) -> UrlPartsBuilder { +crate fn href_relative_parts<'fqp>( + fqp: &'fqp [Symbol], + relative_to_fqp: &[Symbol], +) -> Box<dyn Iterator<Item = Symbol> + 'fqp> { for (i, (f, r)) in fqp.iter().zip(relative_to_fqp.iter()).enumerate() { // e.g. linking to std::iter from std::vec (`dissimilar_part_count` will be 1) if f != r { let dissimilar_part_count = relative_to_fqp.len() - i; - let fqp_module = fqp[i..fqp.len()].iter().map(String::as_str); - return iter::repeat("..").take(dissimilar_part_count).chain(fqp_module).collect(); + let fqp_module = &fqp[i..fqp.len()]; + return box iter::repeat(sym::dotdot) + .take(dissimilar_part_count) + .chain(fqp_module.iter().copied()); } } // e.g. linking to std::sync::atomic from std::sync if relative_to_fqp.len() < fqp.len() { - fqp[relative_to_fqp.len()..fqp.len()].iter().map(String::as_str).collect() + box fqp[relative_to_fqp.len()..fqp.len()].iter().copied() // e.g. linking to std::sync from std::sync::atomic } else if fqp.len() < relative_to_fqp.len() { let dissimilar_part_count = relative_to_fqp.len() - fqp.len(); - iter::repeat("..").take(dissimilar_part_count).collect() + box iter::repeat(sym::dotdot).take(dissimilar_part_count) // linking to the same module } else { - UrlPartsBuilder::new() + box iter::empty() } } @@ -632,14 +674,14 @@ fn resolved_path<'cx>( if let Ok((_, _, fqp)) = href(did, cx) { format!( "{}::{}", - fqp[..fqp.len() - 1].join("::"), - anchor(did, fqp.last().unwrap(), cx) + join_with_double_colon(&fqp[..fqp.len() - 1]), + anchor(did, *fqp.last().unwrap(), cx) ) } else { last.name.to_string() } } else { - anchor(did, last.name.as_str(), cx).to_string() + anchor(did, last.name, cx).to_string() }; write!(w, "{}{}", path, last.args.print(cx))?; } @@ -668,30 +710,31 @@ fn primitive_link( needs_termination = true; } Some(&def_id) => { - let cname_sym; let loc = match m.extern_locations[&def_id.krate] { ExternalLocation::Remote(ref s) => { - cname_sym = ExternalCrate { crate_num: def_id.krate }.name(cx.tcx()); - Some(vec![s.trim_end_matches('/'), cname_sym.as_str()]) + let cname_sym = ExternalCrate { crate_num: def_id.krate }.name(cx.tcx()); + let builder: UrlPartsBuilder = + [s.as_str().trim_end_matches('/'), cname_sym.as_str()] + .into_iter() + .collect(); + Some(builder) } ExternalLocation::Local => { - cname_sym = ExternalCrate { crate_num: def_id.krate }.name(cx.tcx()); - Some(if cx.current.first().map(|x| &x[..]) == Some(cname_sym.as_str()) { - iter::repeat("..").take(cx.current.len() - 1).collect() + let cname_sym = ExternalCrate { crate_num: def_id.krate }.name(cx.tcx()); + Some(if cx.current.first() == Some(&cname_sym) { + iter::repeat(sym::dotdot).take(cx.current.len() - 1).collect() } else { - let cname = iter::once(cname_sym.as_str()); - iter::repeat("..").take(cx.current.len()).chain(cname).collect() + iter::repeat(sym::dotdot) + .take(cx.current.len()) + .chain(iter::once(cname_sym)) + .collect() }) } ExternalLocation::Unknown => None, }; - if let Some(loc) = loc { - write!( - f, - "<a class=\"primitive\" href=\"{}/primitive.{}.html\">", - loc.join("/"), - prim.as_sym() - )?; + if let Some(mut loc) = loc { + loc.push_fmt(format_args!("primitive.{}.html", prim.as_sym())); + write!(f, "<a class=\"primitive\" href=\"{}\">", loc.finish())?; needs_termination = true; } } @@ -730,7 +773,7 @@ fn tybounds<'a, 'tcx: 'a>( crate fn anchor<'a, 'cx: 'a>( did: DefId, - text: &'a str, + text: Symbol, cx: &'cx Context<'_>, ) -> impl fmt::Display + 'a { let parts = href(did, cx); @@ -742,8 +785,8 @@ crate fn anchor<'a, 'cx: 'a>( short_ty, url, short_ty, - fqp.join("::"), - text + join_with_double_colon(&fqp), + &*text.as_str() ) } else { write!(f, "{}", text) @@ -960,7 +1003,7 @@ fn fmt_type<'cx>( url = url, shortty = ItemType::AssocType, name = name, - path = path.join("::") + path = join_with_double_colon(path), )?; } _ => write!(f, "{}", name)?, @@ -1270,7 +1313,7 @@ impl clean::Visibility { debug!("path={:?}", path); // modified from `resolved_path()` to work with `DefPathData` let last_name = path.data.last().unwrap().data.get_opt_name().unwrap(); - let anchor = anchor(vis_did, last_name.as_str(), cx).to_string(); + let anchor = anchor(vis_did, last_name, cx).to_string(); let mut s = "pub(in ".to_owned(); for seg in &path.data[..path.data.len() - 1] { |
