diff --git a/library/std/src/net/ip.rs b/library/std/src/net/ip.rs index da2415e361077..9b629e19be53d 100644 --- a/library/std/src/net/ip.rs +++ b/library/std/src/net/ip.rs @@ -334,6 +334,8 @@ impl Ipv4Addr { /// An IPv4 address representing an unspecified address: 0.0.0.0 /// + /// This corresponds to the constant `INADDR_ANY` in other languages. + /// /// # Examples /// /// ``` @@ -342,6 +344,7 @@ impl Ipv4Addr { /// let addr = Ipv4Addr::UNSPECIFIED; /// assert_eq!(addr, Ipv4Addr::new(0, 0, 0, 0)); /// ``` + #[doc(alias = "INADDR_ANY")] #[stable(feature = "ip_constructors", since = "1.30.0")] pub const UNSPECIFIED: Self = Ipv4Addr::new(0, 0, 0, 0); diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index f3c9b987eb02a..2b25c6a26bcc4 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -41,6 +41,7 @@ use crate::core::DocContext; use crate::formats::cache::Cache; use crate::formats::item_type::ItemType; use crate::html::render::cache::ExternalLocation; +use crate::html::render::Context; use self::FnRetTy::*; use self::ItemKind::*; @@ -193,19 +194,18 @@ impl Item { self.attrs.collapsed_doc_value() } - crate fn links(&self, cache: &Cache) -> Vec { + crate fn links(&self, cx: &Context<'_>) -> Vec { use crate::html::format::href; - use crate::html::render::CURRENT_DEPTH; - cache + cx.cache() .intra_doc_links .get(&self.def_id) .map_or(&[][..], |v| v.as_slice()) .iter() - .filter_map(|ItemLink { link: s, link_text, did, fragment }| { + .filter_map(|ItemLink { link: s, link_text, did, ref fragment }| { match *did { Some(did) => { - if let Some((mut href, ..)) = href(did, cache) { + if let Some((mut href, ..)) = href(did, cx) { if let Some(ref fragment) = *fragment { href.push('#'); href.push_str(fragment); @@ -219,16 +219,26 @@ impl Item { None } } + // FIXME(83083): using fragments as a side-channel for + // primitive names is very unfortunate None => { + let relative_to = &cx.current; if let Some(ref fragment) = *fragment { - let url = match cache.extern_locations.get(&self.def_id.krate) { + let url = match cx.cache().extern_locations.get(&self.def_id.krate) { Some(&(_, _, ExternalLocation::Local)) => { - let depth = CURRENT_DEPTH.with(|l| l.get()); - "../".repeat(depth) + if relative_to[0] == "std" { + let depth = relative_to.len() - 1; + "../".repeat(depth) + } else { + let depth = relative_to.len(); + format!("{}std/", "../".repeat(depth)) + } + } + Some(&(_, _, ExternalLocation::Remote(ref s))) => { + format!("{}/std/", s.trim_end_matches('/')) } - Some(&(_, _, ExternalLocation::Remote(ref s))) => s.to_string(), Some(&(_, _, ExternalLocation::Unknown)) | None => format!( - "https://doc.rust-lang.org/{}", + "https://doc.rust-lang.org/{}/std/", crate::doc_rust_lang_org_channel(), ), }; @@ -238,9 +248,8 @@ impl Item { original_text: s.clone(), new_text: link_text.clone(), href: format!( - "{}{}std/primitive.{}.html{}", + "{}primitive.{}.html{}", url, - if !url.ends_with('/') { "/" } else { "" }, &fragment[..tail], &fragment[tail..] ), diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index 29d468e3d231c..ca364b9f10365 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -20,6 +20,7 @@ use crate::formats::cache::Cache; use crate::formats::item_type::ItemType; use crate::html::escape::Escape; use crate::html::render::cache::ExternalLocation; +use crate::html::render::Context; use crate::html::render::CURRENT_DEPTH; crate trait Print { @@ -125,19 +126,18 @@ fn comma_sep(items: impl Iterator) -> impl fmt::Displ crate fn print_generic_bounds<'a, 'tcx: 'a>( bounds: &'a [clean::GenericBound], - cache: &'a Cache, - tcx: TyCtxt<'tcx>, + cx: &'a Context<'tcx>, ) -> impl fmt::Display + 'a + Captures<'tcx> { display_fn(move |f| { let mut bounds_dup = FxHashSet::default(); for (i, bound) in - bounds.iter().filter(|b| bounds_dup.insert(b.print(cache, tcx).to_string())).enumerate() + bounds.iter().filter(|b| bounds_dup.insert(b.print(cx).to_string())).enumerate() { if i > 0 { f.write_str(" + ")?; } - fmt::Display::fmt(&bound.print(cache, tcx), f)?; + fmt::Display::fmt(&bound.print(cx), f)?; } Ok(()) }) @@ -146,8 +146,7 @@ crate fn print_generic_bounds<'a, 'tcx: 'a>( impl clean::GenericParamDef { crate fn print<'a, 'tcx: 'a>( &'a self, - cache: &'a Cache, - tcx: TyCtxt<'tcx>, + cx: &'a Context<'tcx>, ) -> impl fmt::Display + 'a + Captures<'tcx> { display_fn(move |f| match self.kind { clean::GenericParamDefKind::Lifetime => write!(f, "{}", self.name), @@ -156,17 +155,17 @@ impl clean::GenericParamDef { if !bounds.is_empty() { if f.alternate() { - write!(f, ": {:#}", print_generic_bounds(bounds, cache, tcx))?; + write!(f, ": {:#}", print_generic_bounds(bounds, cx))?; } else { - write!(f, ": {}", print_generic_bounds(bounds, cache, tcx))?; + write!(f, ": {}", print_generic_bounds(bounds, cx))?; } } if let Some(ref ty) = default { if f.alternate() { - write!(f, " = {:#}", ty.print(cache, tcx))?; + write!(f, " = {:#}", ty.print(cx))?; } else { - write!(f, " = {}", ty.print(cache, tcx))?; + write!(f, " = {}", ty.print(cx))?; } } @@ -174,9 +173,9 @@ impl clean::GenericParamDef { } clean::GenericParamDefKind::Const { ref ty, .. } => { if f.alternate() { - write!(f, "const {}: {:#}", self.name, ty.print(cache, tcx)) + write!(f, "const {}: {:#}", self.name, ty.print(cx)) } else { - write!(f, "const {}: {}", self.name, ty.print(cache, tcx)) + write!(f, "const {}: {}", self.name, ty.print(cx)) } } }) @@ -186,8 +185,7 @@ impl clean::GenericParamDef { impl clean::Generics { crate fn print<'a, 'tcx: 'a>( &'a self, - cache: &'a Cache, - tcx: TyCtxt<'tcx>, + cx: &'a Context<'tcx>, ) -> impl fmt::Display + 'a + Captures<'tcx> { display_fn(move |f| { let real_params = @@ -196,9 +194,9 @@ impl clean::Generics { return Ok(()); } if f.alternate() { - write!(f, "<{:#}>", comma_sep(real_params.iter().map(|g| g.print(cache, tcx)))) + write!(f, "<{:#}>", comma_sep(real_params.iter().map(|g| g.print(cx)))) } else { - write!(f, "<{}>", comma_sep(real_params.iter().map(|g| g.print(cache, tcx)))) + write!(f, "<{}>", comma_sep(real_params.iter().map(|g| g.print(cx)))) } }) } @@ -209,8 +207,7 @@ impl clean::Generics { /// * Whether the where-clause needs to add a comma and newline after the last bound. crate fn print_where_clause<'a, 'tcx: 'a>( gens: &'a clean::Generics, - cache: &'a Cache, - tcx: TyCtxt<'tcx>, + cx: &'a Context<'tcx>, indent: usize, end_newline: bool, ) -> impl fmt::Display + 'a + Captures<'tcx> { @@ -241,14 +238,14 @@ crate fn print_where_clause<'a, 'tcx: 'a>( if f.alternate() { clause.push_str(&format!( "{:#}: {:#}", - ty.print(cache, tcx), - print_generic_bounds(bounds, cache, tcx) + ty.print(cx), + print_generic_bounds(bounds, cx) )); } else { clause.push_str(&format!( "{}: {}", - ty.print(cache, tcx), - print_generic_bounds(bounds, cache, tcx) + ty.print(cx), + print_generic_bounds(bounds, cx) )); } } @@ -258,24 +255,16 @@ crate fn print_where_clause<'a, 'tcx: 'a>( lifetime.print(), bounds .iter() - .map(|b| b.print(cache, tcx).to_string()) + .map(|b| b.print(cx).to_string()) .collect::>() .join(" + ") )); } clean::WherePredicate::EqPredicate { lhs, rhs } => { if f.alternate() { - clause.push_str(&format!( - "{:#} == {:#}", - lhs.print(cache, tcx), - rhs.print(cache, tcx), - )); + clause.push_str(&format!("{:#} == {:#}", lhs.print(cx), rhs.print(cx),)); } else { - clause.push_str(&format!( - "{} == {}", - lhs.print(cache, tcx), - rhs.print(cache, tcx), - )); + clause.push_str(&format!("{} == {}", lhs.print(cx), rhs.print(cx),)); } } } @@ -327,8 +316,7 @@ impl clean::Constant { impl clean::PolyTrait { fn print<'a, 'tcx: 'a>( &'a self, - cache: &'a Cache, - tcx: TyCtxt<'tcx>, + cx: &'a Context<'tcx>, ) -> impl fmt::Display + 'a + Captures<'tcx> { display_fn(move |f| { if !self.generic_params.is_empty() { @@ -336,20 +324,20 @@ impl clean::PolyTrait { write!( f, "for<{:#}> ", - comma_sep(self.generic_params.iter().map(|g| g.print(cache, tcx))) + comma_sep(self.generic_params.iter().map(|g| g.print(cx))) )?; } else { write!( f, "for<{}> ", - comma_sep(self.generic_params.iter().map(|g| g.print(cache, tcx))) + comma_sep(self.generic_params.iter().map(|g| g.print(cx))) )?; } } if f.alternate() { - write!(f, "{:#}", self.trait_.print(cache, tcx)) + write!(f, "{:#}", self.trait_.print(cx)) } else { - write!(f, "{}", self.trait_.print(cache, tcx)) + write!(f, "{}", self.trait_.print(cx)) } }) } @@ -358,8 +346,7 @@ impl clean::PolyTrait { impl clean::GenericBound { crate fn print<'a, 'tcx: 'a>( &'a self, - cache: &'a Cache, - tcx: TyCtxt<'tcx>, + cx: &'a Context<'tcx>, ) -> impl fmt::Display + 'a + Captures<'tcx> { display_fn(move |f| match self { clean::GenericBound::Outlives(lt) => write!(f, "{}", lt.print()), @@ -370,9 +357,9 @@ impl clean::GenericBound { hir::TraitBoundModifier::MaybeConst => "?const", }; if f.alternate() { - write!(f, "{}{:#}", modifier_str, ty.print(cache, tcx)) + write!(f, "{}{:#}", modifier_str, ty.print(cx)) } else { - write!(f, "{}{}", modifier_str, ty.print(cache, tcx)) + write!(f, "{}{}", modifier_str, ty.print(cx)) } } }) @@ -382,8 +369,7 @@ impl clean::GenericBound { impl clean::GenericArgs { fn print<'a, 'tcx: 'a>( &'a self, - cache: &'a Cache, - tcx: TyCtxt<'tcx>, + cx: &'a Context<'tcx>, ) -> impl fmt::Display + 'a + Captures<'tcx> { display_fn(move |f| { match self { @@ -401,9 +387,9 @@ impl clean::GenericArgs { } comma = true; if f.alternate() { - write!(f, "{:#}", arg.print(cache, tcx))?; + write!(f, "{:#}", arg.print(cx))?; } else { - write!(f, "{}", arg.print(cache, tcx))?; + write!(f, "{}", arg.print(cx))?; } } for binding in bindings { @@ -412,9 +398,9 @@ impl clean::GenericArgs { } comma = true; if f.alternate() { - write!(f, "{:#}", binding.print(cache, tcx))?; + write!(f, "{:#}", binding.print(cx))?; } else { - write!(f, "{}", binding.print(cache, tcx))?; + write!(f, "{}", binding.print(cx))?; } } if f.alternate() { @@ -433,17 +419,17 @@ impl clean::GenericArgs { } comma = true; if f.alternate() { - write!(f, "{:#}", ty.print(cache, tcx))?; + write!(f, "{:#}", ty.print(cx))?; } else { - write!(f, "{}", ty.print(cache, tcx))?; + write!(f, "{}", ty.print(cx))?; } } f.write_str(")")?; if let Some(ref ty) = *output { if f.alternate() { - write!(f, " -> {:#}", ty.print(cache, tcx))?; + write!(f, " -> {:#}", ty.print(cx))?; } else { - write!(f, " -> {}", ty.print(cache, tcx))?; + write!(f, " -> {}", ty.print(cx))?; } } } @@ -453,56 +439,89 @@ impl clean::GenericArgs { } } -crate fn href(did: DefId, cache: &Cache) -> Option<(String, ItemType, Vec)> { +crate fn href(did: DefId, cx: &Context<'_>) -> Option<(String, ItemType, Vec)> { + let cache = &cx.cache(); + let relative_to = &cx.current; + fn to_module_fqp(shortty: ItemType, fqp: &[String]) -> &[String] { + if shortty == ItemType::Module { &fqp[..] } else { &fqp[..fqp.len() - 1] } + } + if !did.is_local() && !cache.access_levels.is_public(did) && !cache.document_private { return None; } - let depth = CURRENT_DEPTH.with(|l| l.get()); - let (fqp, shortty, mut url) = match cache.paths.get(&did) { - Some(&(ref fqp, shortty)) => (fqp, shortty, "../".repeat(depth)), + 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); + href_relative_parts(module_fqp, relative_to) + }), None => { let &(ref fqp, shortty) = cache.external_paths.get(&did)?; + let module_fqp = to_module_fqp(shortty, fqp); ( fqp, shortty, match cache.extern_locations[&did.krate] { - (.., ExternalLocation::Remote(ref s)) => s.to_string(), - (.., ExternalLocation::Local) => "../".repeat(depth), + (.., ExternalLocation::Remote(ref s)) => { + let s = s.trim_end_matches('/'); + let mut s = vec![&s[..]]; + s.extend(module_fqp[..].iter().map(String::as_str)); + s + } + (.., ExternalLocation::Local) => href_relative_parts(module_fqp, relative_to), (.., ExternalLocation::Unknown) => return None, }, ) } }; - for component in &fqp[..fqp.len() - 1] { - url.push_str(component); - url.push('/'); - } + let last = &fqp.last().unwrap()[..]; + let filename; match shortty { ItemType::Module => { - url.push_str(fqp.last().unwrap()); - url.push_str("/index.html"); + url_parts.push("index.html"); } _ => { - url.push_str(shortty.as_str()); - url.push('.'); - url.push_str(fqp.last().unwrap()); - url.push_str(".html"); + filename = format!("{}.{}.html", shortty.as_str(), last); + url_parts.push(&filename); } } - Some((url, shortty, fqp.to_vec())) + Some((url_parts.join("/"), shortty, fqp.to_vec())) +} + +/// 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<'a>(fqp: &'a [String], relative_to_fqp: &'a [String]) -> Vec<&'a str> { + 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 std::iter::repeat("..").take(dissimilar_part_count).chain(fqp_module).collect(); + } + } + // 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() + // 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(); + std::iter::repeat("..").take(dissimilar_part_count).collect() + // linking to the same module + } else { + Vec::new() + } } /// Used when rendering a `ResolvedPath` structure. This invokes the `path` /// rendering function with the necessary arguments for linking to a local path. -fn resolved_path<'a, 'tcx: 'a>( +fn resolved_path<'a, 'cx: 'a>( w: &mut fmt::Formatter<'_>, did: DefId, path: &clean::Path, print_all: bool, use_absolute: bool, - cache: &Cache, - tcx: TyCtxt<'tcx>, + cx: &'cx Context<'_>, ) -> fmt::Result { let last = path.segments.last().unwrap(); @@ -512,22 +531,22 @@ fn resolved_path<'a, 'tcx: 'a>( } } if w.alternate() { - write!(w, "{}{:#}", &last.name, last.args.print(cache, tcx))?; + write!(w, "{}{:#}", &last.name, last.args.print(cx))?; } else { let path = if use_absolute { - if let Some((_, _, fqp)) = href(did, cache) { + if let Some((_, _, fqp)) = href(did, cx) { format!( "{}::{}", fqp[..fqp.len() - 1].join("::"), - anchor(did, fqp.last().unwrap(), cache) + anchor(did, fqp.last().unwrap(), cx) ) } else { last.name.to_string() } } else { - anchor(did, &*last.name.as_str(), cache).to_string() + anchor(did, &*last.name.as_str(), cx).to_string() }; - write!(w, "{}{}", path, last.args.print(cache, tcx))?; + write!(w, "{}{}", path, last.args.print(cx))?; } Ok(()) } @@ -585,14 +604,13 @@ fn primitive_link( /// Helper to render type parameters fn tybounds<'a, 'tcx: 'a>( param_names: &'a Option>, - cache: &'a Cache, - tcx: TyCtxt<'tcx>, + cx: &'a Context<'tcx>, ) -> impl fmt::Display + 'a + Captures<'tcx> { display_fn(move |f| match *param_names { Some(ref params) => { for param in params { write!(f, " + ")?; - fmt::Display::fmt(¶m.print(cache, tcx), f)?; + fmt::Display::fmt(¶m.print(cx), f)?; } Ok(()) } @@ -600,9 +618,14 @@ fn tybounds<'a, 'tcx: 'a>( }) } -crate fn anchor<'a>(did: DefId, text: &'a str, cache: &'a Cache) -> impl fmt::Display + 'a { +crate fn anchor<'a, 'cx: 'a>( + did: DefId, + text: &'a str, + cx: &'cx Context<'_>, +) -> impl fmt::Display + 'a { + let parts = href(did, cx); display_fn(move |f| { - if let Some((url, short_ty, fqp)) = href(did, cache) { + if let Some((url, short_ty, fqp)) = parts { write!( f, r#"{}"#, @@ -618,12 +641,11 @@ crate fn anchor<'a>(did: DefId, text: &'a str, cache: &'a Cache) -> impl fmt::Di }) } -fn fmt_type( +fn fmt_type<'cx>( t: &clean::Type, f: &mut fmt::Formatter<'_>, use_absolute: bool, - cache: &Cache, - tcx: TyCtxt<'_>, + cx: &'cx Context<'_>, ) -> fmt::Result { debug!("fmt_type(t = {:?})", t); @@ -634,69 +656,69 @@ fn fmt_type( f.write_str("dyn ")?; } // Paths like `T::Output` and `Self::Output` should be rendered with all segments. - resolved_path(f, did, path, is_generic, use_absolute, cache, tcx)?; - fmt::Display::fmt(&tybounds(param_names, cache, tcx), f) + resolved_path(f, did, path, is_generic, use_absolute, cx)?; + fmt::Display::fmt(&tybounds(param_names, cx), f) } clean::Infer => write!(f, "_"), - clean::Primitive(prim) => primitive_link(f, prim, prim.as_str(), cache), + clean::Primitive(prim) => primitive_link(f, prim, prim.as_str(), &cx.cache()), clean::BareFunction(ref decl) => { if f.alternate() { write!( f, "{:#}{}{:#}fn{:#}", - decl.print_hrtb_with_space(cache, tcx), + decl.print_hrtb_with_space(cx), decl.unsafety.print_with_space(), print_abi_with_space(decl.abi), - decl.decl.print(cache, tcx), + decl.decl.print(cx), ) } else { write!( f, "{}{}{}", - decl.print_hrtb_with_space(cache, tcx), + decl.print_hrtb_with_space(cx), decl.unsafety.print_with_space(), print_abi_with_space(decl.abi) )?; - primitive_link(f, PrimitiveType::Fn, "fn", cache)?; - write!(f, "{}", decl.decl.print(cache, tcx)) + primitive_link(f, PrimitiveType::Fn, "fn", &cx.cache())?; + write!(f, "{}", decl.decl.print(cx)) } } clean::Tuple(ref typs) => { match &typs[..] { - &[] => primitive_link(f, PrimitiveType::Unit, "()", cache), + &[] => primitive_link(f, PrimitiveType::Unit, "()", &cx.cache()), &[ref one] => { - primitive_link(f, PrimitiveType::Tuple, "(", cache)?; + primitive_link(f, PrimitiveType::Tuple, "(", &cx.cache())?; // Carry `f.alternate()` into this display w/o branching manually. - fmt::Display::fmt(&one.print(cache, tcx), f)?; - primitive_link(f, PrimitiveType::Tuple, ",)", cache) + fmt::Display::fmt(&one.print(cx), f)?; + primitive_link(f, PrimitiveType::Tuple, ",)", &cx.cache()) } many => { - primitive_link(f, PrimitiveType::Tuple, "(", cache)?; + primitive_link(f, PrimitiveType::Tuple, "(", &cx.cache())?; for (i, item) in many.iter().enumerate() { if i != 0 { write!(f, ", ")?; } - fmt::Display::fmt(&item.print(cache, tcx), f)?; + fmt::Display::fmt(&item.print(cx), f)?; } - primitive_link(f, PrimitiveType::Tuple, ")", cache) + primitive_link(f, PrimitiveType::Tuple, ")", &cx.cache()) } } } clean::Slice(ref t) => { - primitive_link(f, PrimitiveType::Slice, "[", cache)?; - fmt::Display::fmt(&t.print(cache, tcx), f)?; - primitive_link(f, PrimitiveType::Slice, "]", cache) + primitive_link(f, PrimitiveType::Slice, "[", &cx.cache())?; + fmt::Display::fmt(&t.print(cx), f)?; + primitive_link(f, PrimitiveType::Slice, "]", &cx.cache()) } clean::Array(ref t, ref n) => { - primitive_link(f, PrimitiveType::Array, "[", cache)?; - fmt::Display::fmt(&t.print(cache, tcx), f)?; + primitive_link(f, PrimitiveType::Array, "[", &cx.cache())?; + fmt::Display::fmt(&t.print(cx), f)?; if f.alternate() { - primitive_link(f, PrimitiveType::Array, &format!("; {}]", n), cache) + primitive_link(f, PrimitiveType::Array, &format!("; {}]", n), &cx.cache()) } else { - primitive_link(f, PrimitiveType::Array, &format!("; {}]", Escape(n)), cache) + primitive_link(f, PrimitiveType::Array, &format!("; {}]", Escape(n)), &cx.cache()) } } - clean::Never => primitive_link(f, PrimitiveType::Never, "!", cache), + clean::Never => primitive_link(f, PrimitiveType::Never, "!", &cx.cache()), clean::RawPointer(m, ref t) => { let m = match m { hir::Mutability::Mut => "mut", @@ -708,15 +730,15 @@ fn fmt_type( primitive_link( f, clean::PrimitiveType::RawPointer, - &format!("*{} {:#}", m, t.print(cache, tcx)), - cache, + &format!("*{} {:#}", m, t.print(cx)), + &cx.cache(), ) } else { primitive_link( f, clean::PrimitiveType::RawPointer, - &format!("*{} {}", m, t.print(cache, tcx)), - cache, + &format!("*{} {}", m, t.print(cx)), + &cx.cache(), ) } } @@ -725,9 +747,9 @@ fn fmt_type( f, clean::PrimitiveType::RawPointer, &format!("*{} ", m), - cache, + &cx.cache(), )?; - fmt::Display::fmt(&t.print(cache, tcx), f) + fmt::Display::fmt(&t.print(cx), f) } } } @@ -747,15 +769,15 @@ fn fmt_type( primitive_link( f, PrimitiveType::Slice, - &format!("{}{}{}[{:#}]", amp, lt, m, bt.print(cache, tcx)), - cache, + &format!("{}{}{}[{:#}]", amp, lt, m, bt.print(cx)), + &cx.cache(), ) } else { primitive_link( f, PrimitiveType::Slice, - &format!("{}{}{}[{}]", amp, lt, m, bt.print(cache, tcx)), - cache, + &format!("{}{}{}[{}]", amp, lt, m, bt.print(cx)), + &cx.cache(), ) } } @@ -764,20 +786,20 @@ fn fmt_type( f, PrimitiveType::Slice, &format!("{}{}{}[", amp, lt, m), - cache, + &cx.cache(), )?; if f.alternate() { - write!(f, "{:#}", bt.print(cache, tcx))?; + write!(f, "{:#}", bt.print(cx))?; } else { - write!(f, "{}", bt.print(cache, tcx))?; + write!(f, "{}", bt.print(cx))?; } - primitive_link(f, PrimitiveType::Slice, "]", cache) + primitive_link(f, PrimitiveType::Slice, "]", &cx.cache()) } } } clean::ResolvedPath { param_names: Some(ref v), .. } if !v.is_empty() => { write!(f, "{}{}{}(", amp, lt, m)?; - fmt_type(&ty, f, use_absolute, cache, tcx)?; + fmt_type(&ty, f, use_absolute, cx)?; write!(f, ")") } clean::Generic(..) => { @@ -785,21 +807,21 @@ fn fmt_type( f, PrimitiveType::Reference, &format!("{}{}{}", amp, lt, m), - cache, + &cx.cache(), )?; - fmt_type(&ty, f, use_absolute, cache, tcx) + fmt_type(&ty, f, use_absolute, cx) } _ => { write!(f, "{}{}{}", amp, lt, m)?; - fmt_type(&ty, f, use_absolute, cache, tcx) + fmt_type(&ty, f, use_absolute, cx) } } } clean::ImplTrait(ref bounds) => { if f.alternate() { - write!(f, "impl {:#}", print_generic_bounds(bounds, cache, tcx)) + write!(f, "impl {:#}", print_generic_bounds(bounds, cx)) } else { - write!(f, "impl {}", print_generic_bounds(bounds, cache, tcx)) + write!(f, "impl {}", print_generic_bounds(bounds, cx)) } } clean::QPath { ref name, ref self_type, ref trait_ } => { @@ -811,25 +833,15 @@ fn fmt_type( }; if f.alternate() { if should_show_cast { - write!( - f, - "<{:#} as {:#}>::", - self_type.print(cache, tcx), - trait_.print(cache, tcx) - )? + write!(f, "<{:#} as {:#}>::", self_type.print(cx), trait_.print(cx))? } else { - write!(f, "{:#}::", self_type.print(cache, tcx))? + write!(f, "{:#}::", self_type.print(cx))? } } else { if should_show_cast { - write!( - f, - "<{} as {}>::", - self_type.print(cache, tcx), - trait_.print(cache, tcx) - )? + write!(f, "<{} as {}>::", self_type.print(cx), trait_.print(cx))? } else { - write!(f, "{}::", self_type.print(cache, tcx))? + write!(f, "{}::", self_type.print(cx))? } }; match *trait_ { @@ -844,7 +856,7 @@ fn fmt_type( // everything comes in as a fully resolved QPath (hard to // look at). box clean::ResolvedPath { did, ref param_names, .. } => { - match href(did, cache) { + match href(did, cx) { Some((ref url, _, ref path)) if !f.alternate() => { write!( f, @@ -872,42 +884,40 @@ fn fmt_type( impl clean::Type { crate fn print<'b, 'a: 'b, 'tcx: 'a>( &'a self, - cache: &'b Cache, - tcx: TyCtxt<'tcx>, + cx: &'a Context<'tcx>, ) -> impl fmt::Display + 'b + Captures<'tcx> { - display_fn(move |f| fmt_type(self, f, false, cache, tcx)) + display_fn(move |f| fmt_type(self, f, false, cx)) } } impl clean::Impl { crate fn print<'a, 'tcx: 'a>( &'a self, - cache: &'a Cache, use_absolute: bool, - tcx: TyCtxt<'tcx>, + cx: &'a Context<'tcx>, ) -> impl fmt::Display + 'a + Captures<'tcx> { display_fn(move |f| { if f.alternate() { - write!(f, "impl{:#} ", self.generics.print(cache, tcx))?; + write!(f, "impl{:#} ", self.generics.print(cx))?; } else { - write!(f, "impl{} ", self.generics.print(cache, tcx))?; + write!(f, "impl{} ", self.generics.print(cx))?; } if let Some(ref ty) = self.trait_ { if self.negative_polarity { write!(f, "!")?; } - fmt::Display::fmt(&ty.print(cache, tcx), f)?; + fmt::Display::fmt(&ty.print(cx), f)?; write!(f, " for ")?; } if let Some(ref ty) = self.blanket_impl { - fmt_type(ty, f, use_absolute, cache, tcx)?; + fmt_type(ty, f, use_absolute, cx)?; } else { - fmt_type(&self.for_, f, use_absolute, cache, tcx)?; + fmt_type(&self.for_, f, use_absolute, cx)?; } - fmt::Display::fmt(&print_where_clause(&self.generics, cache, tcx, 0, true), f)?; + fmt::Display::fmt(&print_where_clause(&self.generics, cx, 0, true), f)?; Ok(()) }) } @@ -916,8 +926,7 @@ impl clean::Impl { impl clean::Arguments { crate fn print<'a, 'tcx: 'a>( &'a self, - cache: &'a Cache, - tcx: TyCtxt<'tcx>, + cx: &'a Context<'tcx>, ) -> impl fmt::Display + 'a + Captures<'tcx> { display_fn(move |f| { for (i, input) in self.values.iter().enumerate() { @@ -925,9 +934,9 @@ impl clean::Arguments { write!(f, "{}: ", input.name)?; } if f.alternate() { - write!(f, "{:#}", input.type_.print(cache, tcx))?; + write!(f, "{:#}", input.type_.print(cx))?; } else { - write!(f, "{}", input.type_.print(cache, tcx))?; + write!(f, "{}", input.type_.print(cx))?; } if i + 1 < self.values.len() { write!(f, ", ")?; @@ -941,13 +950,14 @@ impl clean::Arguments { impl clean::FnRetTy { crate fn print<'a, 'tcx: 'a>( &'a self, - cache: &'a Cache, - tcx: TyCtxt<'tcx>, + cx: &'a Context<'tcx>, ) -> impl fmt::Display + 'a + Captures<'tcx> { display_fn(move |f| match self { clean::Return(clean::Tuple(tys)) if tys.is_empty() => Ok(()), - clean::Return(ty) if f.alternate() => write!(f, " -> {:#}", ty.print(cache, tcx)), - clean::Return(ty) => write!(f, " -> {}", ty.print(cache, tcx)), + clean::Return(ty) if f.alternate() => { + write!(f, " -> {:#}", ty.print(cx)) + } + clean::Return(ty) => write!(f, " -> {}", ty.print(cx)), clean::DefaultReturn => Ok(()), }) } @@ -956,16 +966,11 @@ impl clean::FnRetTy { impl clean::BareFunctionDecl { fn print_hrtb_with_space<'a, 'tcx: 'a>( &'a self, - cache: &'a Cache, - tcx: TyCtxt<'tcx>, + cx: &'a Context<'tcx>, ) -> impl fmt::Display + 'a + Captures<'tcx> { display_fn(move |f| { if !self.generic_params.is_empty() { - write!( - f, - "for<{}> ", - comma_sep(self.generic_params.iter().map(|g| g.print(cache, tcx))) - ) + write!(f, "for<{}> ", comma_sep(self.generic_params.iter().map(|g| g.print(cx)))) } else { Ok(()) } @@ -976,8 +981,7 @@ impl clean::BareFunctionDecl { impl clean::FnDecl { crate fn print<'b, 'a: 'b, 'tcx: 'a>( &'a self, - cache: &'b Cache, - tcx: TyCtxt<'tcx>, + cx: &'a Context<'tcx>, ) -> impl fmt::Display + 'b + Captures<'tcx> { display_fn(move |f| { let ellipsis = if self.c_variadic { ", ..." } else { "" }; @@ -985,17 +989,17 @@ impl clean::FnDecl { write!( f, "({args:#}{ellipsis}){arrow:#}", - args = self.inputs.print(cache, tcx), + args = self.inputs.print(cx), ellipsis = ellipsis, - arrow = self.output.print(cache, tcx) + arrow = self.output.print(cx) ) } else { write!( f, "({args}{ellipsis}){arrow}", - args = self.inputs.print(cache, tcx), + args = self.inputs.print(cx), ellipsis = ellipsis, - arrow = self.output.print(cache, tcx) + arrow = self.output.print(cx) ) } }) @@ -1007,25 +1011,23 @@ impl clean::FnDecl { /// * `indent`: The number of spaces to indent each successive line with, if line-wrapping is /// necessary. /// * `asyncness`: Whether the function is async or not. - crate fn full_print<'b, 'a: 'b, 'tcx: 'a>( + crate fn full_print<'a, 'tcx: 'a>( &'a self, - cache: &'b Cache, - tcx: TyCtxt<'tcx>, header_len: usize, indent: usize, asyncness: hir::IsAsync, - ) -> impl fmt::Display + 'b + Captures<'tcx> { - display_fn(move |f| self.inner_full_print(cache, tcx, header_len, indent, asyncness, f)) + cx: &'a Context<'tcx>, + ) -> impl fmt::Display + 'a + Captures<'tcx> { + display_fn(move |f| self.inner_full_print(header_len, indent, asyncness, f, cx)) } fn inner_full_print( &self, - cache: &Cache, - tcx: TyCtxt<'_>, header_len: usize, indent: usize, asyncness: hir::IsAsync, f: &mut fmt::Formatter<'_>, + cx: &Context<'_>, ) -> fmt::Result { let amp = if f.alternate() { "&" } else { "&" }; let mut args = String::new(); @@ -1060,11 +1062,11 @@ impl clean::FnDecl { } clean::SelfExplicit(ref typ) => { if f.alternate() { - args.push_str(&format!("self: {:#}", typ.print(cache, tcx))); + args.push_str(&format!("self: {:#}", typ.print(cx))); } else { - args.push_str(&format!("self: {}", typ.print(cache, tcx))); + args.push_str(&format!("self: {}", typ.print(cx))); } - args_plain.push_str(&format!("self: {:#}", typ.print(cache, tcx))); + args_plain.push_str(&format!("self: {:#}", typ.print(cx))); } } } else { @@ -1078,11 +1080,11 @@ impl clean::FnDecl { } if f.alternate() { - args.push_str(&format!("{:#}", input.type_.print(cache, tcx))); + args.push_str(&format!("{:#}", input.type_.print(cx))); } else { - args.push_str(&input.type_.print(cache, tcx).to_string()); + args.push_str(&input.type_.print(cx).to_string()); } - args_plain.push_str(&format!("{:#}", input.type_.print(cache, tcx))); + args_plain.push_str(&format!("{:#}", input.type_.print(cx))); } if i + 1 < self.inputs.values.len() { args.push(','); @@ -1100,19 +1102,11 @@ impl clean::FnDecl { let arrow_plain; let arrow = if let hir::IsAsync::Async = asyncness { let output = self.sugared_async_return_type(); - arrow_plain = format!("{:#}", output.print(cache, tcx)); - if f.alternate() { - arrow_plain.clone() - } else { - format!("{}", output.print(cache, tcx)) - } + arrow_plain = format!("{:#}", output.print(cx)); + if f.alternate() { arrow_plain.clone() } else { format!("{}", output.print(cx)) } } else { - arrow_plain = format!("{:#}", self.output.print(cache, tcx)); - if f.alternate() { - arrow_plain.clone() - } else { - format!("{}", self.output.print(cache, tcx)) - } + arrow_plain = format!("{:#}", self.output.print(cx)); + if f.alternate() { arrow_plain.clone() } else { format!("{}", self.output.print(cx)) } }; let declaration_len = header_len + args_plain.len() + arrow_plain.len(); @@ -1140,9 +1134,8 @@ impl clean::FnDecl { impl clean::Visibility { crate fn print_with_space<'a, 'tcx: 'a>( self, - tcx: TyCtxt<'tcx>, item_did: DefId, - cache: &'a Cache, + cx: &'a Context<'tcx>, ) -> impl fmt::Display + 'a + Captures<'tcx> { let to_print = match self { clean::Public => "pub ".to_owned(), @@ -1151,7 +1144,7 @@ impl clean::Visibility { // FIXME(camelid): This may not work correctly if `item_did` is a module. // However, rustdoc currently never displays a module's // visibility, so it shouldn't matter. - let parent_module = find_nearest_parent_module(tcx, item_did); + let parent_module = find_nearest_parent_module(cx.tcx(), item_did); if vis_did.index == CRATE_DEF_INDEX { "pub(crate) ".to_owned() @@ -1160,17 +1153,17 @@ impl clean::Visibility { // is the same as no visibility modifier String::new() } else if parent_module - .map(|parent| find_nearest_parent_module(tcx, parent)) + .map(|parent| find_nearest_parent_module(cx.tcx(), parent)) .flatten() == Some(vis_did) { "pub(super) ".to_owned() } else { - let path = tcx.def_path(vis_did); + let path = cx.tcx().def_path(vis_did); 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(), cache).to_string(); + let anchor = anchor(vis_did, &last_name.as_str(), cx).to_string(); let mut s = "pub(in ".to_owned(); for seg in &path.data[..path.data.len() - 1] { @@ -1263,24 +1256,23 @@ impl PrintWithSpace for hir::Mutability { } impl clean::Import { - crate fn print<'b, 'a: 'b, 'tcx: 'a>( + crate fn print<'a, 'tcx: 'a>( &'a self, - cache: &'b Cache, - tcx: TyCtxt<'tcx>, - ) -> impl fmt::Display + 'b + Captures<'tcx> { + cx: &'a Context<'tcx>, + ) -> impl fmt::Display + 'a + Captures<'tcx> { display_fn(move |f| match self.kind { clean::ImportKind::Simple(name) => { if name == self.source.path.last() { - write!(f, "use {};", self.source.print(cache, tcx)) + write!(f, "use {};", self.source.print(cx)) } else { - write!(f, "use {} as {};", self.source.print(cache, tcx), name) + write!(f, "use {} as {};", self.source.print(cx), name) } } clean::ImportKind::Glob => { if self.source.path.segments.is_empty() { write!(f, "use *;") } else { - write!(f, "use {}::*;", self.source.print(cache, tcx)) + write!(f, "use {}::*;", self.source.print(cx)) } } }) @@ -1288,20 +1280,19 @@ impl clean::Import { } impl clean::ImportSource { - crate fn print<'b, 'a: 'b, 'tcx: 'a>( + crate fn print<'a, 'tcx: 'a>( &'a self, - cache: &'b Cache, - tcx: TyCtxt<'tcx>, - ) -> impl fmt::Display + 'b + Captures<'tcx> { + cx: &'a Context<'tcx>, + ) -> impl fmt::Display + 'a + Captures<'tcx> { display_fn(move |f| match self.did { - Some(did) => resolved_path(f, did, &self.path, true, false, cache, tcx), + Some(did) => resolved_path(f, did, &self.path, true, false, cx), _ => { for seg in &self.path.segments[..self.path.segments.len() - 1] { write!(f, "{}::", seg.name)?; } let name = self.path.last_name(); if let hir::def::Res::PrimTy(p) = self.path.res { - primitive_link(f, PrimitiveType::from(p), &*name, cache)?; + primitive_link(f, PrimitiveType::from(p), &*name, &cx.cache())?; } else { write!(f, "{}", name)?; } @@ -1312,27 +1303,26 @@ impl clean::ImportSource { } impl clean::TypeBinding { - crate fn print<'b, 'a: 'b, 'tcx: 'a>( + crate fn print<'a, 'tcx: 'a>( &'a self, - cache: &'b Cache, - tcx: TyCtxt<'tcx>, - ) -> impl fmt::Display + 'b + Captures<'tcx> { + cx: &'a Context<'tcx>, + ) -> impl fmt::Display + 'a + Captures<'tcx> { display_fn(move |f| { f.write_str(&*self.name.as_str())?; match self.kind { clean::TypeBindingKind::Equality { ref ty } => { if f.alternate() { - write!(f, " = {:#}", ty.print(cache, tcx))?; + write!(f, " = {:#}", ty.print(cx))?; } else { - write!(f, " = {}", ty.print(cache, tcx))?; + write!(f, " = {}", ty.print(cx))?; } } clean::TypeBindingKind::Constraint { ref bounds } => { if !bounds.is_empty() { if f.alternate() { - write!(f, ": {:#}", print_generic_bounds(bounds, cache, tcx))?; + write!(f, ": {:#}", print_generic_bounds(bounds, cx))?; } else { - write!(f, ": {}", print_generic_bounds(bounds, cache, tcx))?; + write!(f, ": {}", print_generic_bounds(bounds, cx))?; } } } @@ -1357,15 +1347,14 @@ crate fn print_default_space<'a>(v: bool) -> &'a str { } impl clean::GenericArg { - crate fn print<'b, 'a: 'b, 'tcx: 'a>( + crate fn print<'a, 'tcx: 'a>( &'a self, - cache: &'b Cache, - tcx: TyCtxt<'tcx>, - ) -> impl fmt::Display + 'b + Captures<'tcx> { + cx: &'a Context<'tcx>, + ) -> impl fmt::Display + 'a + Captures<'tcx> { display_fn(move |f| match self { clean::GenericArg::Lifetime(lt) => fmt::Display::fmt(<.print(), f), - clean::GenericArg::Type(ty) => fmt::Display::fmt(&ty.print(cache, tcx), f), - clean::GenericArg::Const(ct) => fmt::Display::fmt(&ct.print(tcx), f), + clean::GenericArg::Type(ty) => fmt::Display::fmt(&ty.print(cx), f), + clean::GenericArg::Const(ct) => fmt::Display::fmt(&ct.print(cx.tcx()), f), }) } } diff --git a/src/librustdoc/html/layout.rs b/src/librustdoc/html/layout.rs index 68d70f27c8c78..dc67a63d33399 100644 --- a/src/librustdoc/html/layout.rs +++ b/src/librustdoc/html/layout.rs @@ -113,7 +113,8 @@ crate fn render(
\ {after_content}\
+ data-search-index-js=\"{root_path}search-index{suffix}.js\" \ + data-search-js=\"{static_root_path}search{suffix}.js\"> \ {extra_scripts}\ \ diff --git a/src/librustdoc/html/mod.rs b/src/librustdoc/html/mod.rs index 4318be898ceb4..60ebdf5690d0d 100644 --- a/src/librustdoc/html/mod.rs +++ b/src/librustdoc/html/mod.rs @@ -8,3 +8,6 @@ crate mod render; crate mod sources; crate mod static_files; crate mod toc; + +#[cfg(test)] +mod tests; diff --git a/src/librustdoc/html/render/context.rs b/src/librustdoc/html/render/context.rs index df5ff6e106d7c..d866cf4f4cf04 100644 --- a/src/librustdoc/html/render/context.rs +++ b/src/librustdoc/html/render/context.rs @@ -40,7 +40,7 @@ use crate::html::{layout, sources}; crate struct Context<'tcx> { /// Current hierarchy of components leading down to what's currently being /// rendered - pub(super) current: Vec, + pub(crate) current: Vec, /// The current destination folder of where HTML artifacts should be placed. /// This changes as the context descends into the module hierarchy. pub(super) dst: PathBuf, @@ -144,10 +144,14 @@ impl SharedContext<'_> { } impl<'tcx> Context<'tcx> { - pub(super) fn tcx(&self) -> TyCtxt<'tcx> { + pub(crate) fn tcx(&self) -> TyCtxt<'tcx> { self.shared.tcx } + pub(crate) fn cache(&self) -> &Cache { + &self.cache + } + fn sess(&self) -> &'tcx Session { &self.shared.tcx.sess } diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index 9886b5ceeed39..d10b612a73799 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -51,7 +51,6 @@ use rustc_hir::def::CtorKind; use rustc_hir::def_id::DefId; use rustc_hir::Mutability; use rustc_middle::middle::stability; -use rustc_middle::ty::TyCtxt; use rustc_span::symbol::{kw, sym, Symbol}; use serde::ser::SerializeSeq; use serde::{Serialize, Serializer}; @@ -61,7 +60,7 @@ use crate::docfs::PathError; use crate::error::Error; use crate::formats::cache::Cache; use crate::formats::item_type::ItemType; -use crate::formats::{AssocItemRender, FormatRenderer, Impl, RenderMode}; +use crate::formats::{AssocItemRender, Impl, RenderMode}; use crate::html::escape::Escape; use crate::html::format::{ href, print_abi_with_space, print_default_space, print_generic_bounds, print_where_clause, @@ -560,11 +559,10 @@ fn document_short( return; } if let Some(s) = item.doc_value() { - let mut summary_html = MarkdownSummaryLine(&s, &item.links(&cx.cache)).into_string(); + let mut summary_html = MarkdownSummaryLine(&s, &item.links(cx)).into_string(); if s.contains('\n') { - let link = - format!(r#" Read more"#, naive_assoc_href(item, link, cx.cache())); + let link = format!(r#" Read more"#, naive_assoc_href(item, link, cx)); if let Some(idx) = summary_html.rfind("

") { summary_html.insert_str(idx, &link); @@ -599,7 +597,7 @@ fn document_full( ) { if let Some(s) = cx.shared.maybe_collapsed_doc_value(item) { debug!("Doc block: =====\n{}\n=====", s); - render_markdown(w, cx, &*s, item.links(&cx.cache), prefix, is_hidden); + render_markdown(w, cx, &*s, item.links(cx), prefix, is_hidden); } else if !prefix.is_empty() { if is_hidden { w.write_str("
"); @@ -785,7 +783,7 @@ fn render_impls( w.write_str(&impls.join("")); } -fn naive_assoc_href(it: &clean::Item, link: AssocItemLink<'_>, cache: &Cache) -> String { +fn naive_assoc_href(it: &clean::Item, link: AssocItemLink<'_>, cx: &Context<'_>) -> String { use crate::formats::item_type::ItemType::*; let name = it.name.as_ref().unwrap(); @@ -799,7 +797,7 @@ fn naive_assoc_href(it: &clean::Item, link: AssocItemLink<'_>, cache: &Cache) -> AssocItemLink::Anchor(Some(ref id)) => format!("#{}", id), AssocItemLink::Anchor(None) => anchor, AssocItemLink::GotoSource(did, _) => { - href(did, cache).map(|p| format!("{}{}", p.0, anchor)).unwrap_or(anchor) + href(did, cx).map(|p| format!("{}{}", p.0, anchor)).unwrap_or(anchor) } } } @@ -813,16 +811,14 @@ fn assoc_const( extra: &str, cx: &Context<'_>, ) { - let cache = cx.cache(); - let tcx = cx.tcx(); write!( w, "{}{}const {}: {}", extra, - it.visibility.print_with_space(tcx, it.def_id, cache), - naive_assoc_href(it, link, cache), + it.visibility.print_with_space(it.def_id, cx), + naive_assoc_href(it, link, cx), it.name.as_ref().unwrap(), - ty.print(cache, tcx) + ty.print(cx) ); } @@ -833,21 +829,20 @@ fn assoc_type( default: Option<&clean::Type>, link: AssocItemLink<'_>, extra: &str, - cache: &Cache, - tcx: TyCtxt<'_>, + cx: &Context<'_>, ) { write!( w, "{}type {}", extra, - naive_assoc_href(it, link, cache), + naive_assoc_href(it, link, cx), it.name.as_ref().unwrap() ); if !bounds.is_empty() { - write!(w, ": {}", print_generic_bounds(bounds, cache, tcx)) + write!(w, ": {}", print_generic_bounds(bounds, cx)) } if let Some(default) = default { - write!(w, " = {}", default.print(cache, tcx)) + write!(w, " = {}", default.print(cx)) } } @@ -897,8 +892,6 @@ fn render_assoc_item( parent: ItemType, cx: &Context<'_>, ) { - let cache = cx.cache(); - let tcx = cx.tcx(); let name = meth.name.as_ref().unwrap(); let href = match link { AssocItemLink::Anchor(Some(ref id)) => format!("#{}", id), @@ -912,19 +905,19 @@ fn render_assoc_item( ItemType::TyMethod }; - href(did, cache) + href(did, cx) .map(|p| format!("{}#{}.{}", p.0, ty, name)) .unwrap_or_else(|| format!("#{}.{}", ty, name)) } }; - let vis = meth.visibility.print_with_space(tcx, meth.def_id, cache).to_string(); + let vis = meth.visibility.print_with_space(meth.def_id, cx).to_string(); let constness = header.constness.print_with_space(); let asyncness = header.asyncness.print_with_space(); let unsafety = header.unsafety.print_with_space(); let defaultness = print_default_space(meth.is_default()); let abi = print_abi_with_space(header.abi).to_string(); // NOTE: `{:#}` does not print HTML formatting, `{}` does. So `g.print` can't be reused between the length calculation and `write!`. - let generics_len = format!("{:#}", g.print(cache, tcx)).len(); + let generics_len = format!("{:#}", g.print(cx)).len(); let mut header_len = "fn ".len() + vis.len() + constness.len() @@ -958,10 +951,10 @@ fn render_assoc_item( abi, href = href, name = name, - generics = g.print(cache, tcx), - decl = d.full_print(cache, tcx, header_len, indent, header.asyncness), - notable_traits = notable_traits_decl(&d, cache, tcx), - where_clause = print_where_clause(g, cache, tcx, indent, end_newline), + generics = g.print(cx), + decl = d.full_print(header_len, indent, header.asyncness, cx), + notable_traits = notable_traits_decl(&d, cx), + where_clause = print_where_clause(g, cx, indent, end_newline), ) } match *item.kind { @@ -988,8 +981,7 @@ fn render_assoc_item( default.as_ref(), link, if parent == ItemType::Trait { " " } else { "" }, - cx.cache(), - cx.tcx(), + cx, ), _ => panic!("render_assoc_item called on non-associated-item"), } @@ -1076,11 +1068,9 @@ fn render_assoc_items( RenderMode::Normal } AssocItemRender::DerefFor { trait_, type_, deref_mut_ } => { - let id = cx.derive_id(small_url_encode(format!( - "deref-methods-{:#}", - type_.print(cache, tcx) - ))); - debug!("Adding {} to deref id map", type_.print(cache, tcx)); + let id = + cx.derive_id(small_url_encode(format!("deref-methods-{:#}", type_.print(cx)))); + debug!("Adding {} to deref id map", type_.print(cx)); cx.deref_id_map.borrow_mut().insert(type_.def_id_full(cache).unwrap(), id.clone()); write!( w, @@ -1089,8 +1079,8 @@ fn render_assoc_items( \ ", id = id, - trait_ = trait_.print(cache, tcx), - type_ = type_.print(cache, tcx), + trait_ = trait_.print(cx), + type_ = type_.print(cx), ); RenderMode::ForDeref { mut_: deref_mut_ } } @@ -1242,36 +1232,34 @@ fn should_render_item(item: &clean::Item, deref_mut_: bool, cache: &Cache) -> bo } } -fn notable_traits_decl(decl: &clean::FnDecl, cache: &Cache, tcx: TyCtxt<'_>) -> String { +fn notable_traits_decl(decl: &clean::FnDecl, cx: &Context<'_>) -> String { let mut out = Buffer::html(); let mut trait_ = String::new(); - if let Some(did) = decl.output.def_id_full(cache) { - if let Some(impls) = cache.impls.get(&did) { + if let Some(did) = decl.output.def_id_full(cx.cache()) { + if let Some(impls) = cx.cache().impls.get(&did) { for i in impls { let impl_ = i.inner_impl(); - if impl_ - .trait_ - .def_id() - .map_or(false, |d| cache.traits.get(&d).map(|t| t.is_notable).unwrap_or(false)) - { + if impl_.trait_.def_id().map_or(false, |d| { + cx.cache().traits.get(&d).map(|t| t.is_notable).unwrap_or(false) + }) { if out.is_empty() { write!( &mut out, "

Notable traits for {}

\ ", - impl_.for_.print(cache, tcx) + impl_.for_.print(cx) ); - trait_.push_str(&impl_.for_.print(cache, tcx).to_string()); + trait_.push_str(&impl_.for_.print(cx).to_string()); } //use the "where" class here to make it small write!( &mut out, "{}", - impl_.print(cache, false, tcx) + impl_.print(false, cx) ); - let t_did = impl_.trait_.def_id_full(cache).unwrap(); + let t_did = impl_.trait_.def_id_full(cx.cache()).unwrap(); for it in &impl_.items { if let clean::TypedefItem(ref tydef, _) = *it.kind { out.push_str(" "); @@ -1282,8 +1270,7 @@ fn notable_traits_decl(decl: &clean::FnDecl, cache: &Cache, tcx: TyCtxt<'_>) -> Some(&tydef.type_), AssocItemLink::GotoSource(t_did, &FxHashSet::default()), "", - cache, - tcx, + cx, ); out.push_str(";"); } @@ -1322,18 +1309,18 @@ fn render_impl( // in documentation pages for trait with automatic implementations like "Send" and "Sync". aliases: &[String], ) { - let traits = &cx.cache.traits; let tcx = cx.tcx(); let cache = cx.cache(); + let traits = &cache.traits; let trait_ = i.trait_did_full(cache).map(|did| &traits[&did]); if render_mode == RenderMode::Normal { let id = cx.derive_id(match i.inner_impl().trait_ { Some(ref t) => { if is_on_foreign_type { - get_id_for_impl_on_foreign_type(&i.inner_impl().for_, t, cache, tcx) + get_id_for_impl_on_foreign_type(&i.inner_impl().for_, t, cx) } else { - format!("impl-{}", small_url_encode(format!("{:#}", t.print(cache, tcx)))) + format!("impl-{}", small_url_encode(format!("{:#}", t.print(cx)))) } } None => "impl".to_string(), @@ -1345,7 +1332,7 @@ fn render_impl( }; if let Some(use_absolute) = use_absolute { write!(w, "

", id, aliases); - write!(w, "{}", i.inner_impl().print(cache, use_absolute, tcx)); + write!(w, "{}", i.inner_impl().print(use_absolute, cx)); if show_def_docs { for it in &i.inner_impl().items { if let clean::TypedefItem(ref tydef, _) = *it.kind { @@ -1357,8 +1344,7 @@ fn render_impl( Some(&tydef.type_), AssocItemLink::Anchor(None), "", - cache, - tcx, + cx, ); w.write_str(";"); } @@ -1371,7 +1357,7 @@ fn render_impl( "

{}", id, aliases, - i.inner_impl().print(cache, false, tcx) + i.inner_impl().print(false, cx) ); } write!(w, "", id); @@ -1398,7 +1384,7 @@ fn render_impl( "
{}
", Markdown( &*dox, - &i.impl_item.links(&cx.cache), + &i.impl_item.links(cx), &mut ids, cx.shared.codes, cx.shared.edition, @@ -1495,8 +1481,7 @@ fn render_impl( Some(&tydef.type_), link.anchor(if trait_.is_some() { &source_id } else { &id }), "", - cx.cache(), - tcx, + cx, ); w.write_str("
"); write!(w, "", id); @@ -1546,8 +1531,7 @@ fn render_impl( default.as_ref(), link.anchor(if trait_.is_some() { &source_id } else { &id }), "", - cx.cache(), - tcx, + cx, ); w.write_str("
"); write!(w, "", id); @@ -1853,7 +1837,6 @@ fn small_url_encode(s: String) -> String { fn sidebar_assoc_items(cx: &Context<'_>, out: &mut Buffer, it: &clean::Item) { if let Some(v) = cx.cache.impls.get(&it.def_id) { let mut used_links = FxHashSet::default(); - let tcx = cx.tcx(); let cache = cx.cache(); { @@ -1888,9 +1871,9 @@ fn sidebar_assoc_items(cx: &Context<'_>, out: &mut Buffer, it: &clean::Item) { .iter() .filter_map(|it| { if let Some(ref i) = it.inner_impl().trait_ { - let i_display = format!("{:#}", i.print(cache, tcx)); + let i_display = format!("{:#}", i.print(cx)); let out = Escape(&i_display); - let encoded = small_url_encode(format!("{:#}", i.print(cache, tcx))); + let encoded = small_url_encode(format!("{:#}", i.print(cx))); let generated = format!( "{}{}", encoded, @@ -1962,7 +1945,6 @@ fn sidebar_assoc_items(cx: &Context<'_>, out: &mut Buffer, it: &clean::Item) { fn sidebar_deref_methods(cx: &Context<'_>, out: &mut Buffer, impl_: &Impl, v: &Vec) { let c = cx.cache(); - let tcx = cx.tcx(); debug!("found Deref: {:?}", impl_); if let Some((target, real_target)) = @@ -2011,11 +1993,8 @@ fn sidebar_deref_methods(cx: &Context<'_>, out: &mut Buffer, impl_: &Impl, v: &V out, "Methods from {}<Target={}>", id, - Escape(&format!( - "{:#}", - impl_.inner_impl().trait_.as_ref().unwrap().print(c, tcx) - )), - Escape(&format!("{:#}", real_target.print(c, tcx))), + Escape(&format!("{:#}", impl_.inner_impl().trait_.as_ref().unwrap().print(cx))), + Escape(&format!("{:#}", real_target.print(cx))), ); // We want links' order to be reproducible so we don't use unstable sort. ret.sort(); @@ -2071,27 +2050,20 @@ fn sidebar_struct(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item, s: &clea fn get_id_for_impl_on_foreign_type( for_: &clean::Type, trait_: &clean::Type, - cache: &Cache, - tcx: TyCtxt<'_>, + cx: &Context<'_>, ) -> String { - small_url_encode(format!( - "impl-{:#}-for-{:#}", - trait_.print(cache, tcx), - for_.print(cache, tcx) - )) + small_url_encode(format!("impl-{:#}-for-{:#}", trait_.print(cx), for_.print(cx),)) } -fn extract_for_impl_name( - item: &clean::Item, - cache: &Cache, - tcx: TyCtxt<'_>, -) -> Option<(String, String)> { +fn extract_for_impl_name(item: &clean::Item, cx: &Context<'_>) -> Option<(String, String)> { match *item.kind { clean::ItemKind::ImplItem(ref i) => { if let Some(ref trait_) = i.trait_ { + // Alternative format produces no URLs, + // so this parameter does nothing. Some(( - format!("{:#}", i.for_.print(cache, tcx)), - get_id_for_impl_on_foreign_type(&i.for_, trait_, cache, tcx), + format!("{:#}", i.for_.print(cx)), + get_id_for_impl_on_foreign_type(&i.for_, trait_, cx), )) } else { None @@ -2172,7 +2144,6 @@ fn sidebar_trait(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item, t: &clean if let Some(implementors) = cx.cache.implementors.get(&it.def_id) { let cache = cx.cache(); - let tcx = cx.tcx(); let mut res = implementors .iter() .filter(|i| { @@ -2181,7 +2152,7 @@ fn sidebar_trait(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item, t: &clean .def_id_full(cache) .map_or(false, |d| !cx.cache.paths.contains_key(&d)) }) - .filter_map(|i| extract_for_impl_name(&i.impl_item, cache, tcx)) + .filter_map(|i| extract_for_impl_name(&i.impl_item, cx)) .collect::>(); if !res.is_empty() { diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs index ff9e4d031da18..42b795030171b 100644 --- a/src/librustdoc/html/render/print_item.rs +++ b/src/librustdoc/html/render/print_item.rs @@ -15,9 +15,8 @@ use super::{ render_impl, render_stability_since_raw, write_srclink, AssocItemLink, Context, }; use crate::clean::{self, GetDefId}; -use crate::formats::cache::Cache; use crate::formats::item_type::ItemType; -use crate::formats::{AssocItemRender, FormatRenderer, Impl, RenderMode}; +use crate::formats::{AssocItemRender, Impl, RenderMode}; use crate::html::escape::Escape; use crate::html::format::{print_abi_with_space, print_where_clause, Buffer, PrintWithSpace}; use crate::html::highlight; @@ -268,15 +267,15 @@ fn item_module(w: &mut Buffer, cx: &Context<'_>, item: &clean::Item, items: &[cl Some(ref src) => write!( w, "{}extern crate {} as {};", - myitem.visibility.print_with_space(cx.tcx(), myitem.def_id, cx.cache()), - anchor(myitem.def_id, &*src.as_str(), cx.cache()), + myitem.visibility.print_with_space(myitem.def_id, cx), + anchor(myitem.def_id, &*src.as_str(), cx), myitem.name.as_ref().unwrap(), ), None => write!( w, "{}extern crate {};", - myitem.visibility.print_with_space(cx.tcx(), myitem.def_id, cx.cache()), - anchor(myitem.def_id, &*myitem.name.as_ref().unwrap().as_str(), cx.cache()), + myitem.visibility.print_with_space(myitem.def_id, cx), + anchor(myitem.def_id, &*myitem.name.as_ref().unwrap().as_str(), cx), ), } w.write_str(""); @@ -286,8 +285,8 @@ fn item_module(w: &mut Buffer, cx: &Context<'_>, item: &clean::Item, items: &[cl write!( w, "{}{}", - myitem.visibility.print_with_space(cx.tcx(), myitem.def_id, cx.cache()), - import.print(cx.cache(), cx.tcx()), + myitem.visibility.print_with_space(myitem.def_id, cx), + import.print(cx), ); } @@ -318,7 +317,7 @@ fn item_module(w: &mut Buffer, cx: &Context<'_>, item: &clean::Item, items: &[cl ", name = *myitem.name.as_ref().unwrap(), stab_tags = extra_info_tags(myitem, item, cx.tcx()), - docs = MarkdownSummaryLine(&doc_value, &myitem.links(&cx.cache)).into_string(), + docs = MarkdownSummaryLine(&doc_value, &myitem.links(cx)).into_string(), class = myitem.type_(), add = add, stab = stab.unwrap_or_else(String::new), @@ -387,13 +386,13 @@ fn extra_info_tags(item: &clean::Item, parent: &clean::Item, tcx: TyCtxt<'_>) -> fn item_function(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, f: &clean::Function) { let header_len = format!( "{}{}{}{}{:#}fn {}{:#}", - it.visibility.print_with_space(cx.tcx(), it.def_id, cx.cache()), + it.visibility.print_with_space(it.def_id, cx), f.header.constness.print_with_space(), f.header.asyncness.print_with_space(), f.header.unsafety.print_with_space(), print_abi_with_space(f.header.abi), it.name.as_ref().unwrap(), - f.generics.print(cx.cache(), cx.tcx()) + f.generics.print(cx), ) .len(); w.write_str("
");
@@ -402,22 +401,22 @@ fn item_function(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, f: &clean::
         w,
         "{vis}{constness}{asyncness}{unsafety}{abi}fn \
          {name}{generics}{decl}{notable_traits}{where_clause}
", - vis = it.visibility.print_with_space(cx.tcx(), it.def_id, cx.cache()), + vis = it.visibility.print_with_space(it.def_id, cx), constness = f.header.constness.print_with_space(), asyncness = f.header.asyncness.print_with_space(), unsafety = f.header.unsafety.print_with_space(), abi = print_abi_with_space(f.header.abi), name = it.name.as_ref().unwrap(), - generics = f.generics.print(cx.cache(), cx.tcx()), - where_clause = print_where_clause(&f.generics, cx.cache(), cx.tcx(), 0, true), - decl = f.decl.full_print(cx.cache(), cx.tcx(), header_len, 0, f.header.asyncness), - notable_traits = notable_traits_decl(&f.decl, cx.cache(), cx.tcx()), + generics = f.generics.print(cx), + where_clause = print_where_clause(&f.generics, cx, 0, true), + decl = f.decl.full_print(header_len, 0, f.header.asyncness, cx), + notable_traits = notable_traits_decl(&f.decl, cx), ); document(w, cx, it, None) } fn item_trait(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::Trait) { - let bounds = bounds(&t.bounds, false, cx.cache(), cx.tcx()); + let bounds = bounds(&t.bounds, false, cx); let types = t.items.iter().filter(|m| m.is_associated_type()).collect::>(); let consts = t.items.iter().filter(|m| m.is_associated_const()).collect::>(); let required = t.items.iter().filter(|m| m.is_ty_method()).collect::>(); @@ -430,16 +429,16 @@ fn item_trait(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::Tra write!( w, "{}{}{}trait {}{}{}", - it.visibility.print_with_space(cx.tcx(), it.def_id, cx.cache()), + it.visibility.print_with_space(it.def_id, cx), t.unsafety.print_with_space(), if t.is_auto { "auto " } else { "" }, it.name.as_ref().unwrap(), - t.generics.print(cx.cache(), cx.tcx()), + t.generics.print(cx), bounds ); if !t.generics.where_predicates.is_empty() { - write!(w, "{}", print_where_clause(&t.generics, cx.cache(), cx.tcx(), 0, true)); + write!(w, "{}", print_where_clause(&t.generics, cx, 0, true)); } else { w.write_str(" "); } @@ -634,8 +633,8 @@ fn item_trait(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::Tra let (mut synthetic, mut concrete): (Vec<&&Impl>, Vec<&&Impl>) = local.iter().partition(|i| i.inner_impl().synthetic); - synthetic.sort_by(|a, b| compare_impl(a, b, cx.cache(), cx.tcx())); - concrete.sort_by(|a, b| compare_impl(a, b, cx.cache(), cx.tcx())); + synthetic.sort_by(|a, b| compare_impl(a, b, cx)); + concrete.sort_by(|a, b| compare_impl(a, b, cx)); if !foreign.is_empty() { write_small_section_header(w, "foreign-impls", "Implementations on Foreign Types", ""); @@ -740,9 +739,9 @@ fn item_trait_alias(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clea w, "trait {}{}{} = {};", it.name.as_ref().unwrap(), - t.generics.print(cx.cache(), cx.tcx()), - print_where_clause(&t.generics, cx.cache(), cx.tcx(), 0, true), - bounds(&t.bounds, true, cx.cache(), cx.tcx()) + t.generics.print(cx), + print_where_clause(&t.generics, cx, 0, true), + bounds(&t.bounds, true, cx) ); document(w, cx, it, None); @@ -761,9 +760,9 @@ fn item_opaque_ty(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean: w, "type {}{}{where_clause} = impl {bounds};", it.name.as_ref().unwrap(), - t.generics.print(cx.cache(), cx.tcx()), - where_clause = print_where_clause(&t.generics, cx.cache(), cx.tcx(), 0, true), - bounds = bounds(&t.bounds, false, cx.cache(), cx.tcx()), + t.generics.print(cx), + where_clause = print_where_clause(&t.generics, cx, 0, true), + bounds = bounds(&t.bounds, false, cx), ); document(w, cx, it, None); @@ -782,9 +781,9 @@ fn item_typedef(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::T w, "type {}{}{where_clause} = {type_};", it.name.as_ref().unwrap(), - t.generics.print(cx.cache(), cx.tcx()), - where_clause = print_where_clause(&t.generics, cx.cache(), cx.tcx(), 0, true), - type_ = t.type_.print(cx.cache(), cx.tcx()), + t.generics.print(cx), + where_clause = print_where_clause(&t.generics, cx, 0, true), + type_ = t.type_.print(cx), ); document(w, cx, it, None); @@ -831,7 +830,7 @@ fn item_union(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, s: &clean::Uni id = id, name = name, shortty = ItemType::StructField, - ty = ty.print(cx.cache(), cx.tcx()), + ty = ty.print(cx), ); if let Some(stability_class) = field.stability_class(cx.tcx()) { write!(w, "", stab = stability_class); @@ -849,10 +848,10 @@ fn item_enum(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, e: &clean::Enum write!( w, "{}enum {}{}{}", - it.visibility.print_with_space(cx.tcx(), it.def_id, cx.cache()), + it.visibility.print_with_space(it.def_id, cx), it.name.as_ref().unwrap(), - e.generics.print(cx.cache(), cx.tcx()), - print_where_clause(&e.generics, cx.cache(), cx.tcx(), 0, true), + e.generics.print(cx), + print_where_clause(&e.generics, cx, 0, true), ); if e.variants.is_empty() && !e.variants_stripped { w.write_str(" {}"); @@ -874,7 +873,7 @@ fn item_enum(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, e: &clean::Enum if i > 0 { w.write_str(", ") } - write!(w, "{}", ty.print(cx.cache(), cx.tcx())); + write!(w, "{}", ty.print(cx)); } w.write_str(")"); } @@ -924,7 +923,7 @@ fn item_enum(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, e: &clean::Enum if i > 0 { w.write_str(", "); } - write!(w, "{}", ty.print(cx.cache(), cx.tcx())); + write!(w, "{}", ty.print(cx)); } w.write_str(")"); } @@ -961,7 +960,7 @@ fn item_enum(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, e: &clean::Enum ", id = id, f = field.name.as_ref().unwrap(), - t = ty.print(cx.cache(), cx.tcx()) + t = ty.print(cx) ); document(w, cx, field, Some(variant)); } @@ -1030,9 +1029,9 @@ fn item_constant(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, c: &clean:: write!( w, "{vis}const {name}: {typ}", - vis = it.visibility.print_with_space(cx.tcx(), it.def_id, cx.cache()), + vis = it.visibility.print_with_space(it.def_id, cx), name = it.name.as_ref().unwrap(), - typ = c.type_.print(cx.cache(), cx.tcx()), + typ = c.type_.print(cx), ); let value = c.value(cx.tcx()); @@ -1102,7 +1101,7 @@ fn item_struct(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, s: &clean::St item_type = ItemType::StructField, id = id, name = field.name.as_ref().unwrap(), - ty = ty.print(cx.cache(), cx.tcx()) + ty = ty.print(cx) ); document(w, cx, field, Some(it)); } @@ -1117,10 +1116,10 @@ fn item_static(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, s: &clean::St write!( w, "{vis}static {mutability}{name}: {typ}", - vis = it.visibility.print_with_space(cx.tcx(), it.def_id, cx.cache()), + vis = it.visibility.print_with_space(it.def_id, cx), mutability = s.mutability.print_with_space(), name = it.name.as_ref().unwrap(), - typ = s.type_.print(cx.cache(), cx.tcx()) + typ = s.type_.print(cx) ); document(w, cx, it, None) } @@ -1131,7 +1130,7 @@ fn item_foreign_type(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item) { write!( w, " {}type {};\n}}", - it.visibility.print_with_space(cx.tcx(), it.def_id, cx.cache()), + it.visibility.print_with_space(it.def_id, cx), it.name.as_ref().unwrap(), ); @@ -1195,12 +1194,7 @@ pub(super) fn item_path(ty: ItemType, name: &str) -> String { } } -fn bounds( - t_bounds: &[clean::GenericBound], - trait_alias: bool, - cache: &Cache, - tcx: TyCtxt<'_>, -) -> String { +fn bounds(t_bounds: &[clean::GenericBound], trait_alias: bool, cx: &Context<'_>) -> String { let mut bounds = String::new(); if !t_bounds.is_empty() { if !trait_alias { @@ -1210,7 +1204,7 @@ fn bounds( if i > 0 { bounds.push_str(" + "); } - bounds.push_str(&p.print(cache, tcx).to_string()); + bounds.push_str(&p.print(cx).to_string()); } } bounds @@ -1240,17 +1234,12 @@ fn render_stability_since( ) } -fn compare_impl<'a, 'b>( - lhs: &'a &&Impl, - rhs: &'b &&Impl, - cache: &Cache, - tcx: TyCtxt<'_>, -) -> Ordering { - let lhs = format!("{}", lhs.inner_impl().print(cache, false, tcx)); - let rhs = format!("{}", rhs.inner_impl().print(cache, false, tcx)); +fn compare_impl<'a, 'b>(lhs: &'a &&Impl, rhs: &'b &&Impl, cx: &Context<'_>) -> Ordering { + let lhss = format!("{}", lhs.inner_impl().print(false, cx)); + let rhss = format!("{}", rhs.inner_impl().print(false, cx)); // lhs and rhs are formatted as HTML, which may be unnecessary - compare_names(&lhs, &rhs) + compare_names(&lhss, &rhss) } fn render_implementor( @@ -1300,13 +1289,13 @@ fn render_union( write!( w, "{}{}{}", - it.visibility.print_with_space(cx.tcx(), it.def_id, cx.cache()), + it.visibility.print_with_space(it.def_id, cx), if structhead { "union " } else { "" }, it.name.as_ref().unwrap() ); if let Some(g) = g { - write!(w, "{}", g.print(cx.cache(), cx.tcx())); - write!(w, "{}", print_where_clause(&g, cx.cache(), cx.tcx(), 0, true)); + write!(w, "{}", g.print(cx)); + write!(w, "{}", print_where_clause(&g, cx, 0, true)); } write!(w, " {{\n{}", tab); @@ -1322,9 +1311,9 @@ fn render_union( write!( w, " {}{}: {},\n{}", - field.visibility.print_with_space(cx.tcx(), field.def_id, cx.cache()), + field.visibility.print_with_space(field.def_id, cx), field.name.as_ref().unwrap(), - ty.print(cx.cache(), cx.tcx()), + ty.print(cx), tab ); } @@ -1352,17 +1341,17 @@ fn render_struct( write!( w, "{}{}{}", - it.visibility.print_with_space(cx.tcx(), it.def_id, cx.cache()), + it.visibility.print_with_space(it.def_id, cx), if structhead { "struct " } else { "" }, it.name.as_ref().unwrap() ); if let Some(g) = g { - write!(w, "{}", g.print(cx.cache(), cx.tcx())) + write!(w, "{}", g.print(cx)) } match ty { CtorKind::Fictive => { if let Some(g) = g { - write!(w, "{}", print_where_clause(g, cx.cache(), cx.tcx(), 0, true),) + write!(w, "{}", print_where_clause(g, cx, 0, true),) } w.write_str(" {"); let count_fields = @@ -1378,9 +1367,9 @@ fn render_struct( w, "\n{} {}{}: {},", tab, - field.visibility.print_with_space(cx.tcx(), field.def_id, cx.cache()), + field.visibility.print_with_space(field.def_id, cx), field.name.as_ref().unwrap(), - ty.print(cx.cache(), cx.tcx()), + ty.print(cx), ); } } @@ -1412,8 +1401,8 @@ fn render_struct( write!( w, "{}{}", - field.visibility.print_with_space(cx.tcx(), field.def_id, cx.cache()), - ty.print(cx.cache(), cx.tcx()), + field.visibility.print_with_space(field.def_id, cx), + ty.print(cx), ) } _ => unreachable!(), @@ -1421,14 +1410,14 @@ fn render_struct( } w.write_str(")"); if let Some(g) = g { - write!(w, "{}", print_where_clause(g, cx.cache(), cx.tcx(), 0, false),) + write!(w, "{}", print_where_clause(g, cx, 0, false),) } w.write_str(";"); } CtorKind::Const => { // Needed for PhantomData. if let Some(g) = g { - write!(w, "{}", print_where_clause(g, cx.cache(), cx.tcx(), 0, false),) + write!(w, "{}", print_where_clause(g, cx, 0, false),) } w.write_str(";"); } diff --git a/src/librustdoc/html/render/write_shared.rs b/src/librustdoc/html/render/write_shared.rs index 8fb6d68f3c6bc..78bcd40af7538 100644 --- a/src/librustdoc/html/render/write_shared.rs +++ b/src/librustdoc/html/render/write_shared.rs @@ -16,7 +16,6 @@ use crate::clean::Crate; use crate::config::{EmitType, RenderOptions}; use crate::docfs::PathError; use crate::error::Error; -use crate::formats::FormatRenderer; use crate::html::{layout, static_files}; crate static FILES_UNVERSIONED: Lazy> = Lazy::new(|| { @@ -223,6 +222,7 @@ pub(super) fn write_shared( &format!(" = {}", serde_json::to_string(&themes).unwrap()), ), )?; + write_minify("search.js", static_files::SEARCH_JS)?; write_minify("settings.js", static_files::SETTINGS_JS)?; if cx.shared.include_sources { write_minify("source-script.js", static_files::sidebar::SOURCE_SCRIPT)?; @@ -410,7 +410,7 @@ pub(super) fn write_shared( write_crate("search-index.js", &|| { let mut v = String::from("var searchIndex = JSON.parse('{\\\n"); v.push_str(&all_indexes.join(",\\\n")); - v.push_str("\\\n}');\ninitSearch(searchIndex);"); + v.push_str("\\\n}');\nif (window.initSearch) {window.initSearch(searchIndex)};"); Ok(v.into_bytes()) })?; @@ -500,7 +500,7 @@ pub(super) fn write_shared( None } else { Some(Implementor { - text: imp.inner_impl().print(cx.cache(), false, cx.tcx()).to_string(), + text: imp.inner_impl().print(false, cx).to_string(), synthetic: imp.inner_impl().synthetic, types: collect_paths_for_type(imp.inner_impl().for_.clone(), cx.cache()), }) diff --git a/src/librustdoc/html/static/main.js b/src/librustdoc/html/static/main.js index d71cc15a4576f..f017fd846b1db 100644 --- a/src/librustdoc/html/static/main.js +++ b/src/librustdoc/html/static/main.js @@ -1,4 +1,3 @@ -// ignore-tidy-filelength // Local js definitions: /* global addClass, getSettingValue, hasClass */ /* global onEach, onEachLazy, hasOwnProperty, removeClass, updateLocalStorage */ @@ -44,6 +43,7 @@ if (!DOMTokenList.prototype.remove) { window.rootPath = rustdocVars.attributes["data-root-path"].value; window.currentCrate = rustdocVars.attributes["data-current-crate"].value; window.searchJS = rustdocVars.attributes["data-search-js"].value; + window.searchIndexJS = rustdocVars.attributes["data-search-index-js"].value; } var sidebarVars = document.getElementById("sidebar-vars"); if (sidebarVars) { @@ -77,14 +77,6 @@ function getVirtualKey(ev) { return String.fromCharCode(c); } -function getSearchInput() { - return document.getElementsByClassName("search-input")[0]; -} - -function getSearchElement() { - return document.getElementById("search"); -} - var THEME_PICKER_ELEMENT_ID = "theme-picker"; var THEMES_ELEMENT_ID = "theme-choices"; @@ -101,16 +93,6 @@ function getNakedUrl() { return window.location.href.split("?")[0].split("#")[0]; } -// Sets the focus on the search bar at the top of the page -function focusSearchBar() { - getSearchInput().focus(); -} - -// Removes the focus from the search bar. -function defocusSearchBar() { - getSearchInput().blur(); -} - function showThemeButtonState() { var themePicker = getThemePickerElement(); var themeChoices = getThemesElement(); @@ -173,67 +155,154 @@ function hideThemeButtonState() { (function() { "use strict"; - // This mapping table should match the discriminants of - // `rustdoc::html::item_type::ItemType` type in Rust. - var itemTypes = ["mod", - "externcrate", - "import", - "struct", - "enum", - "fn", - "type", - "static", - "trait", - "impl", - "tymethod", - "method", - "structfield", - "variant", - "macro", - "primitive", - "associatedtype", - "constant", - "associatedconstant", - "union", - "foreigntype", - "keyword", - "existential", - "attr", - "derive", - "traitalias"]; + window.searchState = { + loadingText: "Loading search results...", + input: document.getElementsByClassName("search-input")[0], + outputElement: function() { + return document.getElementById("search"); + }, + title: null, + titleBeforeSearch: document.title, + timeout: null, + // On the search screen, so you remain on the last tab you opened. + // + // 0 for "In Names" + // 1 for "In Parameters" + // 2 for "In Return Types" + currentTab: 0, + mouseMovedAfterSearch: true, + clearInputTimeout: function() { + if (searchState.timeout !== null) { + clearTimeout(searchState.timeout); + searchState.timeout = null; + } + }, + // Sets the focus on the search bar at the top of the page + focus: function() { + searchState.input.focus(); + }, + // Removes the focus from the search bar. + defocus: function() { + searchState.input.blur(); + }, + showResults: function(search) { + if (search === null || typeof search === 'undefined') { + search = searchState.outputElement(); + } + addClass(main, "hidden"); + removeClass(search, "hidden"); + searchState.mouseMovedAfterSearch = false; + document.title = searchState.title; + }, + hideResults: function(search) { + if (search === null || typeof search === 'undefined') { + search = searchState.outputElement(); + } + addClass(search, "hidden"); + removeClass(main, "hidden"); + document.title = searchState.titleBeforeSearch; + // We also remove the query parameter from the URL. + if (searchState.browserSupportsHistoryApi()) { + history.replaceState("", window.currentCrate + " - Rust", + getNakedUrl() + window.location.hash); + } + }, + getQueryStringParams: function() { + var params = {}; + window.location.search.substring(1).split("&"). + map(function(s) { + var pair = s.split("="); + params[decodeURIComponent(pair[0])] = + typeof pair[1] === "undefined" ? null : decodeURIComponent(pair[1]); + }); + return params; + }, + putBackSearch: function(search_input) { + var search = searchState.outputElement(); + if (search_input.value !== "" && hasClass(search, "hidden")) { + searchState.showResults(search); + if (searchState.browserSupportsHistoryApi()) { + var extra = "?search=" + encodeURIComponent(search_input.value); + history.replaceState(search_input.value, "", + getNakedUrl() + extra + window.location.hash); + } + document.title = searchState.title; + } + }, + browserSupportsHistoryApi: function() { + return window.history && typeof window.history.pushState === "function"; + }, + setup: function() { + var search_input = searchState.input; + if (!searchState.input) { + return; + } + function loadScript(url) { + var script = document.createElement('script'); + script.src = url; + document.head.append(script); + } - var disableShortcuts = getSettingValue("disable-shortcuts") === "true"; - var search_input = getSearchInput(); - var searchTimeout = null; - var toggleAllDocsId = "toggle-all-docs"; + var searchLoaded = false; + function loadSearch() { + if (!searchLoaded) { + searchLoaded = true; + loadScript(window.searchJS); + loadScript(window.searchIndexJS); + } + } - // On the search screen, so you remain on the last tab you opened. - // - // 0 for "In Names" - // 1 for "In Parameters" - // 2 for "In Return Types" - var currentTab = 0; + search_input.addEventListener("focus", function() { + searchState.putBackSearch(this); + search_input.origPlaceholder = searchState.input.placeholder; + search_input.placeholder = "Type your search here."; + loadSearch(); + }); + search_input.addEventListener("blur", function() { + search_input.placeholder = searchState.input.origPlaceholder; + }); - var mouseMovedAfterSearch = true; + document.addEventListener("mousemove", function() { + searchState.mouseMovedAfterSearch = true; + }); - var titleBeforeSearch = document.title; - var searchTitle = null; + search_input.removeAttribute('disabled'); - function removeEmptyStringsFromArray(x) { - for (var i = 0, len = x.length; i < len; ++i) { - if (x[i] === "") { - x.splice(i, 1); - i -= 1; - } + // `crates{version}.js` should always be loaded before this script, so we can use it safely. + searchState.addCrateDropdown(window.ALL_CRATES); + var params = searchState.getQueryStringParams(); + if (params.search !== undefined) { + var search = searchState.outputElement(); + search.innerHTML = "

" + + searchState.loadingText + "

"; + searchState.showResults(search); + loadSearch(); } - } + }, + addCrateDropdown: function(crates) { + var elem = document.getElementById("crate-search"); - function clearInputTimeout() { - if (searchTimeout !== null) { - clearTimeout(searchTimeout); - searchTimeout = null; + if (!elem) { + return; } - } + var savedCrate = getSettingValue("saved-filter-crate"); + for (var i = 0, len = crates.length; i < len; ++i) { + var option = document.createElement("option"); + option.value = crates[i]; + option.innerText = crates[i]; + elem.appendChild(option); + // Set the crate filter from saved storage, if the current page has the saved crate + // filter. + // + // If not, ignore the crate filter -- we want to support filtering for crates on sites + // like doc.rust-lang.org where the crates may differ from page to page while on the + // same domain. + if (crates[i] === savedCrate) { + elem.value = savedCrate; + } + } + }, + }; function getPageId() { if (window.location.hash) { @@ -276,65 +345,23 @@ function hideThemeButtonState() { document.getElementsByTagName("body")[0].style.marginTop = ""; } - function showSearchResults(search) { - if (search === null || typeof search === 'undefined') { - search = getSearchElement(); - } - addClass(main, "hidden"); - removeClass(search, "hidden"); - mouseMovedAfterSearch = false; - document.title = searchTitle; - } - - function hideSearchResults(search) { - if (search === null || typeof search === 'undefined') { - search = getSearchElement(); - } - addClass(search, "hidden"); - removeClass(main, "hidden"); - document.title = titleBeforeSearch; - // We also remove the query parameter from the URL. - if (browserSupportsHistoryApi()) { - history.replaceState("", window.currentCrate + " - Rust", - getNakedUrl() + window.location.hash); - } - } - - // used for special search precedence - var TY_PRIMITIVE = itemTypes.indexOf("primitive"); - var TY_KEYWORD = itemTypes.indexOf("keyword"); - - function getQueryStringParams() { - var params = {}; - window.location.search.substring(1).split("&"). - map(function(s) { - var pair = s.split("="); - params[decodeURIComponent(pair[0])] = - typeof pair[1] === "undefined" ? null : decodeURIComponent(pair[1]); - }); - return params; - } - - function browserSupportsHistoryApi() { - return window.history && typeof window.history.pushState === "function"; - } - function isHidden(elem) { return elem.offsetHeight === 0; } + var toggleAllDocsId = "toggle-all-docs"; var main = document.getElementById("main"); var savedHash = ""; function handleHashes(ev) { var elem; - var search = getSearchElement(); + var search = searchState.outputElement(); if (ev !== null && search && !hasClass(search, "hidden") && ev.newURL) { // This block occurs when clicking on an element in the navbar while // in a search. - hideSearchResults(search); + searchState.hideResults(search); var hash = ev.newURL.slice(ev.newURL.indexOf("#") + 1); - if (browserSupportsHistoryApi()) { + if (searchState.browserSupportsHistoryApi()) { // `window.location.search`` contains all the query parameters, not just `search`. history.replaceState(hash, "", getNakedUrl() + window.location.search + "#" + hash); @@ -475,18 +502,19 @@ function hideThemeButtonState() { function handleEscape(ev) { var help = getHelpElement(false); - var search = getSearchElement(); + var search = searchState.outputElement(); if (hasClass(help, "hidden") === false) { displayHelp(false, ev, help); } else if (hasClass(search, "hidden") === false) { - clearInputTimeout(); + searchState.clearInputTimeout(); ev.preventDefault(); - hideSearchResults(search); + searchState.hideResults(search); } - defocusSearchBar(); + searchState.defocus(); hideThemeButtonState(); } + var disableShortcuts = getSettingValue("disable-shortcuts") === "true"; function handleShortcut(ev) { // Don't interfere with browser shortcuts if (ev.ctrlKey || ev.altKey || ev.metaKey || disableShortcuts === true) { @@ -509,7 +537,7 @@ function hideThemeButtonState() { case "S": displayHelp(false, ev); ev.preventDefault(); - focusSearchBar(); + searchState.focus(); break; case "+": @@ -596,15 +624,13 @@ function hideThemeButtonState() { document.addEventListener("keypress", handleShortcut); document.addEventListener("keydown", handleShortcut); - document.addEventListener("mousemove", function() { mouseMovedAfterSearch = true; }); - var handleSourceHighlight = (function() { var prev_line_id = 0; var set_fragment = function(name) { var x = window.scrollX, y = window.scrollY; - if (browserSupportsHistoryApi()) { + if (searchState.browserSupportsHistoryApi()) { history.replaceState(null, null, "#" + name); highlightSourceLines(); } else { @@ -686,1444 +712,6 @@ function hideThemeButtonState() { } }()); - /** - * A function to compute the Levenshtein distance between two strings - * Licensed under the Creative Commons Attribution-ShareAlike 3.0 Unported - * Full License can be found at http://creativecommons.org/licenses/by-sa/3.0/legalcode - * This code is an unmodified version of the code written by Marco de Wit - * and was found at http://stackoverflow.com/a/18514751/745719 - */ - var levenshtein_row2 = []; - function levenshtein(s1, s2) { - if (s1 === s2) { - return 0; - } - var s1_len = s1.length, s2_len = s2.length; - if (s1_len && s2_len) { - var i1 = 0, i2 = 0, a, b, c, c2, row = levenshtein_row2; - while (i1 < s1_len) { - row[i1] = ++i1; - } - while (i2 < s2_len) { - c2 = s2.charCodeAt(i2); - a = i2; - ++i2; - b = i2; - for (i1 = 0; i1 < s1_len; ++i1) { - c = a + (s1.charCodeAt(i1) !== c2 ? 1 : 0); - a = row[i1]; - b = b < a ? (b < c ? b + 1 : c) : (a < c ? a + 1 : c); - row[i1] = b; - } - } - return b; - } - return s1_len + s2_len; - } - - window.initSearch = function(rawSearchIndex) { - var MAX_LEV_DISTANCE = 3; - var MAX_RESULTS = 200; - var GENERICS_DATA = 1; - var NAME = 0; - var INPUTS_DATA = 0; - var OUTPUT_DATA = 1; - var NO_TYPE_FILTER = -1; - var currentResults, index, searchIndex; - var ALIASES = {}; - var params = getQueryStringParams(); - - // Populate search bar with query string search term when provided, - // but only if the input bar is empty. This avoid the obnoxious issue - // where you start trying to do a search, and the index loads, and - // suddenly your search is gone! - if (search_input.value === "") { - search_input.value = params.search || ""; - } - - /** - * Executes the query and builds an index of results - * @param {[Object]} query [The user query] - * @param {[type]} searchWords [The list of search words to query - * against] - * @param {[type]} filterCrates [Crate to search in if defined] - * @return {[type]} [A search index of results] - */ - function execQuery(query, searchWords, filterCrates) { - function itemTypeFromName(typename) { - for (var i = 0, len = itemTypes.length; i < len; ++i) { - if (itemTypes[i] === typename) { - return i; - } - } - return NO_TYPE_FILTER; - } - - var valLower = query.query.toLowerCase(), - val = valLower, - typeFilter = itemTypeFromName(query.type), - results = {}, results_in_args = {}, results_returned = {}, - split = valLower.split("::"); - - removeEmptyStringsFromArray(split); - - function transformResults(results, isType) { - var out = []; - for (var i = 0, len = results.length; i < len; ++i) { - if (results[i].id > -1) { - var obj = searchIndex[results[i].id]; - obj.lev = results[i].lev; - if (isType !== true || obj.type) { - var res = buildHrefAndPath(obj); - obj.displayPath = pathSplitter(res[0]); - obj.fullPath = obj.displayPath + obj.name; - // To be sure than it some items aren't considered as duplicate. - obj.fullPath += "|" + obj.ty; - obj.href = res[1]; - out.push(obj); - if (out.length >= MAX_RESULTS) { - break; - } - } - } - } - return out; - } - - function sortResults(results, isType) { - var ar = []; - for (var entry in results) { - if (hasOwnProperty(results, entry)) { - ar.push(results[entry]); - } - } - results = ar; - var i, len, result; - for (i = 0, len = results.length; i < len; ++i) { - result = results[i]; - result.word = searchWords[result.id]; - result.item = searchIndex[result.id] || {}; - } - // if there are no results then return to default and fail - if (results.length === 0) { - return []; - } - - results.sort(function(aaa, bbb) { - var a, b; - - // sort by exact match with regard to the last word (mismatch goes later) - a = (aaa.word !== val); - b = (bbb.word !== val); - if (a !== b) { return a - b; } - - // Sort by non levenshtein results and then levenshtein results by the distance - // (less changes required to match means higher rankings) - a = (aaa.lev); - b = (bbb.lev); - if (a !== b) { return a - b; } - - // sort by crate (non-current crate goes later) - a = (aaa.item.crate !== window.currentCrate); - b = (bbb.item.crate !== window.currentCrate); - if (a !== b) { return a - b; } - - // sort by item name length (longer goes later) - a = aaa.word.length; - b = bbb.word.length; - if (a !== b) { return a - b; } - - // sort by item name (lexicographically larger goes later) - a = aaa.word; - b = bbb.word; - if (a !== b) { return (a > b ? +1 : -1); } - - // sort by index of keyword in item name (no literal occurrence goes later) - a = (aaa.index < 0); - b = (bbb.index < 0); - if (a !== b) { return a - b; } - // (later literal occurrence, if any, goes later) - a = aaa.index; - b = bbb.index; - if (a !== b) { return a - b; } - - // special precedence for primitive and keyword pages - if ((aaa.item.ty === TY_PRIMITIVE && bbb.item.ty !== TY_KEYWORD) || - (aaa.item.ty === TY_KEYWORD && bbb.item.ty !== TY_PRIMITIVE)) { - return -1; - } - if ((bbb.item.ty === TY_PRIMITIVE && aaa.item.ty !== TY_PRIMITIVE) || - (bbb.item.ty === TY_KEYWORD && aaa.item.ty !== TY_KEYWORD)) { - return 1; - } - - // sort by description (no description goes later) - a = (aaa.item.desc === ""); - b = (bbb.item.desc === ""); - if (a !== b) { return a - b; } - - // sort by type (later occurrence in `itemTypes` goes later) - a = aaa.item.ty; - b = bbb.item.ty; - if (a !== b) { return a - b; } - - // sort by path (lexicographically larger goes later) - a = aaa.item.path; - b = bbb.item.path; - if (a !== b) { return (a > b ? +1 : -1); } - - // que sera, sera - return 0; - }); - - for (i = 0, len = results.length; i < len; ++i) { - var result = results[i]; - - // this validation does not make sense when searching by types - if (result.dontValidate) { - continue; - } - var name = result.item.name.toLowerCase(), - path = result.item.path.toLowerCase(), - parent = result.item.parent; - - if (isType !== true && - validateResult(name, path, split, parent) === false) - { - result.id = -1; - } - } - return transformResults(results); - } - - function extractGenerics(val) { - val = val.toLowerCase(); - if (val.indexOf("<") !== -1) { - var values = val.substring(val.indexOf("<") + 1, val.lastIndexOf(">")); - return { - name: val.substring(0, val.indexOf("<")), - generics: values.split(/\s*,\s*/), - }; - } - return { - name: val, - generics: [], - }; - } - - function getObjectNameFromId(id) { - if (typeof id === "number") { - return searchIndex[id].name; - } - return id; - } - - function checkGenerics(obj, val) { - // The names match, but we need to be sure that all generics kinda - // match as well. - var tmp_lev, elem_name; - if (val.generics.length > 0) { - if (obj.length > GENERICS_DATA && - obj[GENERICS_DATA].length >= val.generics.length) { - var elems = Object.create(null); - var elength = object[GENERICS_DATA].length; - for (var x = 0; x < elength; ++x) { - elems[getObjectNameFromId(obj[GENERICS_DATA][x])] += 1; - } - var total = 0; - var done = 0; - // We need to find the type that matches the most to remove it in order - // to move forward. - var vlength = val.generics.length; - for (x = 0; x < vlength; ++x) { - var lev = MAX_LEV_DISTANCE + 1; - var firstGeneric = getObjectNameFromId(val.generics[x]); - var match = null; - if (elems[firstGeneric]) { - match = firstGeneric; - lev = 0; - } else { - for (elem_name in elems) { - tmp_lev = levenshtein(elem_name, firstGeneric); - if (tmp_lev < lev) { - lev = tmp_lev; - match = elem_name; - } - } - } - if (match !== null) { - elems[match] -= 1; - if (elems[match] == 0) { - delete elems[match]; - } - total += lev; - done += 1; - } else { - return MAX_LEV_DISTANCE + 1; - } - } - return Math.ceil(total / done); - } - } - return MAX_LEV_DISTANCE + 1; - } - - // Check for type name and type generics (if any). - function checkType(obj, val, literalSearch) { - var lev_distance = MAX_LEV_DISTANCE + 1; - var len, x, firstGeneric; - if (obj[NAME] === val.name) { - if (literalSearch === true) { - if (val.generics && val.generics.length !== 0) { - if (obj.length > GENERICS_DATA && - obj[GENERICS_DATA].length >= val.generics.length) { - var elems = Object.create(null); - len = obj[GENERICS_DATA].length; - for (x = 0; x < len; ++x) { - elems[getObjectNameFromId(obj[GENERICS_DATA][x])] += 1; - } - - var allFound = true; - len = val.generics.length; - for (x = 0; x < len; ++x) { - firstGeneric = getObjectNameFromId(val.generics[x]); - if (elems[firstGeneric]) { - elems[firstGeneric] -= 1; - } else { - allFound = false; - break; - } - } - if (allFound === true) { - return true; - } - } else { - return false; - } - } - return true; - } - // If the type has generics but don't match, then it won't return at this point. - // Otherwise, `checkGenerics` will return 0 and it'll return. - if (obj.length > GENERICS_DATA && obj[GENERICS_DATA].length !== 0) { - var tmp_lev = checkGenerics(obj, val); - if (tmp_lev <= MAX_LEV_DISTANCE) { - return tmp_lev; - } - } else { - return 0; - } - } - // Names didn't match so let's check if one of the generic types could. - if (literalSearch === true) { - if (obj.length > GENERICS_DATA && obj[GENERICS_DATA].length > 0) { - return obj[GENERICS_DATA].some( - function(name) { - return name === val.name; - }); - } - return false; - } - lev_distance = Math.min(levenshtein(obj[NAME], val.name), lev_distance); - if (lev_distance <= MAX_LEV_DISTANCE) { - // The generics didn't match but the name kinda did so we give it - // a levenshtein distance value that isn't *this* good so it goes - // into the search results but not too high. - lev_distance = Math.ceil((checkGenerics(obj, val) + lev_distance) / 2); - } else if (obj.length > GENERICS_DATA && obj[GENERICS_DATA].length > 0) { - // We can check if the type we're looking for is inside the generics! - var olength = obj[GENERICS_DATA].length; - for (x = 0; x < olength; ++x) { - lev_distance = Math.min(levenshtein(obj[GENERICS_DATA][x], val.name), - lev_distance); - } - } - // Now whatever happens, the returned distance is "less good" so we should mark it - // as such, and so we add 1 to the distance to make it "less good". - return lev_distance + 1; - } - - function findArg(obj, val, literalSearch, typeFilter) { - var lev_distance = MAX_LEV_DISTANCE + 1; - - if (obj && obj.type && obj.type[INPUTS_DATA] && obj.type[INPUTS_DATA].length > 0) { - var length = obj.type[INPUTS_DATA].length; - for (var i = 0; i < length; i++) { - var tmp = obj.type[INPUTS_DATA][i]; - if (typePassesFilter(typeFilter, tmp[1]) === false) { - continue; - } - tmp = checkType(tmp, val, literalSearch); - if (literalSearch === true) { - if (tmp === true) { - return true; - } - continue; - } - lev_distance = Math.min(tmp, lev_distance); - if (lev_distance === 0) { - return 0; - } - } - } - return literalSearch === true ? false : lev_distance; - } - - function checkReturned(obj, val, literalSearch, typeFilter) { - var lev_distance = MAX_LEV_DISTANCE + 1; - - if (obj && obj.type && obj.type.length > OUTPUT_DATA) { - var ret = obj.type[OUTPUT_DATA]; - if (typeof ret[0] === "string") { - ret = [ret]; - } - for (var x = 0, len = ret.length; x < len; ++x) { - var tmp = ret[x]; - if (typePassesFilter(typeFilter, tmp[1]) === false) { - continue; - } - tmp = checkType(tmp, val, literalSearch); - if (literalSearch === true) { - if (tmp === true) { - return true; - } - continue; - } - lev_distance = Math.min(tmp, lev_distance); - if (lev_distance === 0) { - return 0; - } - } - } - return literalSearch === true ? false : lev_distance; - } - - function checkPath(contains, lastElem, ty) { - if (contains.length === 0) { - return 0; - } - var ret_lev = MAX_LEV_DISTANCE + 1; - var path = ty.path.split("::"); - - if (ty.parent && ty.parent.name) { - path.push(ty.parent.name.toLowerCase()); - } - - var length = path.length; - var clength = contains.length; - if (clength > length) { - return MAX_LEV_DISTANCE + 1; - } - for (var i = 0; i < length; ++i) { - if (i + clength > length) { - break; - } - var lev_total = 0; - var aborted = false; - for (var x = 0; x < clength; ++x) { - var lev = levenshtein(path[i + x], contains[x]); - if (lev > MAX_LEV_DISTANCE) { - aborted = true; - break; - } - lev_total += lev; - } - if (aborted === false) { - ret_lev = Math.min(ret_lev, Math.round(lev_total / clength)); - } - } - return ret_lev; - } - - function typePassesFilter(filter, type) { - // No filter - if (filter <= NO_TYPE_FILTER) return true; - - // Exact match - if (filter === type) return true; - - // Match related items - var name = itemTypes[type]; - switch (itemTypes[filter]) { - case "constant": - return name === "associatedconstant"; - case "fn": - return name === "method" || name === "tymethod"; - case "type": - return name === "primitive" || name === "associatedtype"; - case "trait": - return name === "traitalias"; - } - - // No match - return false; - } - - function createAliasFromItem(item) { - return { - crate: item.crate, - name: item.name, - path: item.path, - desc: item.desc, - ty: item.ty, - parent: item.parent, - type: item.type, - is_alias: true, - }; - } - - function handleAliases(ret, query, filterCrates) { - // We separate aliases and crate aliases because we want to have current crate - // aliases to be before the others in the displayed results. - var aliases = []; - var crateAliases = []; - if (filterCrates !== undefined) { - if (ALIASES[filterCrates] && ALIASES[filterCrates][query.search]) { - var query_aliases = ALIASES[filterCrates][query.search]; - var len = query_aliases.length; - for (var i = 0; i < len; ++i) { - aliases.push(createAliasFromItem(searchIndex[query_aliases[i]])); - } - } - } else { - Object.keys(ALIASES).forEach(function(crate) { - if (ALIASES[crate][query.search]) { - var pushTo = crate === window.currentCrate ? crateAliases : aliases; - var query_aliases = ALIASES[crate][query.search]; - var len = query_aliases.length; - for (var i = 0; i < len; ++i) { - pushTo.push(createAliasFromItem(searchIndex[query_aliases[i]])); - } - } - }); - } - - var sortFunc = function(aaa, bbb) { - if (aaa.path < bbb.path) { - return 1; - } else if (aaa.path === bbb.path) { - return 0; - } - return -1; - }; - crateAliases.sort(sortFunc); - aliases.sort(sortFunc); - - var pushFunc = function(alias) { - alias.alias = query.raw; - var res = buildHrefAndPath(alias); - alias.displayPath = pathSplitter(res[0]); - alias.fullPath = alias.displayPath + alias.name; - alias.href = res[1]; - - ret.others.unshift(alias); - if (ret.others.length > MAX_RESULTS) { - ret.others.pop(); - } - }; - onEach(aliases, pushFunc); - onEach(crateAliases, pushFunc); - } - - // quoted values mean literal search - var nSearchWords = searchWords.length; - var i, it; - var ty; - var fullId; - var returned; - var in_args; - var len; - if ((val.charAt(0) === "\"" || val.charAt(0) === "'") && - val.charAt(val.length - 1) === val.charAt(0)) - { - val = extractGenerics(val.substr(1, val.length - 2)); - for (i = 0; i < nSearchWords; ++i) { - if (filterCrates !== undefined && searchIndex[i].crate !== filterCrates) { - continue; - } - in_args = findArg(searchIndex[i], val, true, typeFilter); - returned = checkReturned(searchIndex[i], val, true, typeFilter); - ty = searchIndex[i]; - fullId = ty.id; - - if (searchWords[i] === val.name - && typePassesFilter(typeFilter, searchIndex[i].ty) - && results[fullId] === undefined) { - results[fullId] = { - id: i, - index: -1, - dontValidate: true, - }; - } - if (in_args === true && results_in_args[fullId] === undefined) { - results_in_args[fullId] = { - id: i, - index: -1, - dontValidate: true, - }; - } - if (returned === true && results_returned[fullId] === undefined) { - results_returned[fullId] = { - id: i, - index: -1, - dontValidate: true, - }; - } - } - query.inputs = [val]; - query.output = val; - query.search = val; - // searching by type - } else if (val.search("->") > -1) { - var trimmer = function(s) { return s.trim(); }; - var parts = val.split("->").map(trimmer); - var input = parts[0]; - // sort inputs so that order does not matter - var inputs = input.split(",").map(trimmer).sort(); - for (i = 0, len = inputs.length; i < len; ++i) { - inputs[i] = extractGenerics(inputs[i]); - } - var output = extractGenerics(parts[1]); - - for (i = 0; i < nSearchWords; ++i) { - if (filterCrates !== undefined && searchIndex[i].crate !== filterCrates) { - continue; - } - var type = searchIndex[i].type; - ty = searchIndex[i]; - if (!type) { - continue; - } - fullId = ty.id; - - returned = checkReturned(ty, output, true, NO_TYPE_FILTER); - if (output.name === "*" || returned === true) { - in_args = false; - var is_module = false; - - if (input === "*") { - is_module = true; - } else { - var allFound = true; - for (it = 0, len = inputs.length; allFound === true && it < len; it++) { - allFound = checkType(type, inputs[it], true); - } - in_args = allFound; - } - if (in_args === true) { - results_in_args[fullId] = { - id: i, - index: -1, - dontValidate: true, - }; - } - if (returned === true) { - results_returned[fullId] = { - id: i, - index: -1, - dontValidate: true, - }; - } - if (is_module === true) { - results[fullId] = { - id: i, - index: -1, - dontValidate: true, - }; - } - } - } - query.inputs = inputs.map(function(input) { - return input.name; - }); - query.output = output.name; - } else { - query.inputs = [val]; - query.output = val; - query.search = val; - // gather matching search results up to a certain maximum - val = val.replace(/\_/g, ""); - - var valGenerics = extractGenerics(val); - - var paths = valLower.split("::"); - removeEmptyStringsFromArray(paths); - val = paths[paths.length - 1]; - var contains = paths.slice(0, paths.length > 1 ? paths.length - 1 : 1); - - var lev, j; - for (j = 0; j < nSearchWords; ++j) { - ty = searchIndex[j]; - if (!ty || (filterCrates !== undefined && ty.crate !== filterCrates)) { - continue; - } - var lev_add = 0; - if (paths.length > 1) { - lev = checkPath(contains, paths[paths.length - 1], ty); - if (lev > MAX_LEV_DISTANCE) { - continue; - } else if (lev > 0) { - lev_add = lev / 10; - } - } - - returned = MAX_LEV_DISTANCE + 1; - in_args = MAX_LEV_DISTANCE + 1; - var index = -1; - // we want lev results to go lower than others - lev = MAX_LEV_DISTANCE + 1; - fullId = ty.id; - - if (searchWords[j].indexOf(split[i]) > -1 || - searchWords[j].indexOf(val) > -1 || - ty.normalizedName.indexOf(val) > -1) - { - // filter type: ... queries - if (typePassesFilter(typeFilter, ty.ty) && results[fullId] === undefined) { - index = ty.normalizedName.indexOf(val); - } - } - if ((lev = levenshtein(searchWords[j], val)) <= MAX_LEV_DISTANCE) { - if (typePassesFilter(typeFilter, ty.ty) === false) { - lev = MAX_LEV_DISTANCE + 1; - } else { - lev += 1; - } - } - in_args = findArg(ty, valGenerics, false, typeFilter); - returned = checkReturned(ty, valGenerics, false, typeFilter); - - lev += lev_add; - if (lev > 0 && val.length > 3 && searchWords[j].indexOf(val) > -1) { - if (val.length < 6) { - lev -= 1; - } else { - lev = 0; - } - } - if (in_args <= MAX_LEV_DISTANCE) { - if (results_in_args[fullId] === undefined) { - results_in_args[fullId] = { - id: j, - index: index, - lev: in_args, - }; - } - results_in_args[fullId].lev = - Math.min(results_in_args[fullId].lev, in_args); - } - if (returned <= MAX_LEV_DISTANCE) { - if (results_returned[fullId] === undefined) { - results_returned[fullId] = { - id: j, - index: index, - lev: returned, - }; - } - results_returned[fullId].lev = - Math.min(results_returned[fullId].lev, returned); - } - if (index !== -1 || lev <= MAX_LEV_DISTANCE) { - if (index !== -1 && paths.length < 2) { - lev = 0; - } - if (results[fullId] === undefined) { - results[fullId] = { - id: j, - index: index, - lev: lev, - }; - } - results[fullId].lev = Math.min(results[fullId].lev, lev); - } - } - } - - var ret = { - "in_args": sortResults(results_in_args, true), - "returned": sortResults(results_returned, true), - "others": sortResults(results), - }; - handleAliases(ret, query, filterCrates); - return ret; - } - - /** - * Validate performs the following boolean logic. For example: - * "File::open" will give IF A PARENT EXISTS => ("file" && "open") - * exists in (name || path || parent) OR => ("file" && "open") exists in - * (name || path ) - * - * This could be written functionally, but I wanted to minimise - * functions on stack. - * - * @param {[string]} name [The name of the result] - * @param {[string]} path [The path of the result] - * @param {[string]} keys [The keys to be used (["file", "open"])] - * @param {[object]} parent [The parent of the result] - * @return {[boolean]} [Whether the result is valid or not] - */ - function validateResult(name, path, keys, parent) { - for (var i = 0, len = keys.length; i < len; ++i) { - // each check is for validation so we negate the conditions and invalidate - if (!( - // check for an exact name match - name.indexOf(keys[i]) > -1 || - // then an exact path match - path.indexOf(keys[i]) > -1 || - // next if there is a parent, check for exact parent match - (parent !== undefined && parent.name !== undefined && - parent.name.toLowerCase().indexOf(keys[i]) > -1) || - // lastly check to see if the name was a levenshtein match - levenshtein(name, keys[i]) <= MAX_LEV_DISTANCE)) { - return false; - } - } - return true; - } - - function getQuery(raw) { - var matches, type, query; - query = raw; - - matches = query.match(/^(fn|mod|struct|enum|trait|type|const|macro)\s*:\s*/i); - if (matches) { - type = matches[1].replace(/^const$/, "constant"); - query = query.substring(matches[0].length); - } - - return { - raw: raw, - query: query, - type: type, - id: query + type - }; - } - - function initSearchNav() { - var hoverTimeout; - - var click_func = function(e) { - var el = e.target; - // to retrieve the real "owner" of the event. - while (el.tagName !== "TR") { - el = el.parentNode; - } - var dst = e.target.getElementsByTagName("a"); - if (dst.length < 1) { - return; - } - dst = dst[0]; - if (window.location.pathname === dst.pathname) { - hideSearchResults(); - document.location.href = dst.href; - } - }; - var mouseover_func = function(e) { - if (mouseMovedAfterSearch) { - var el = e.target; - // to retrieve the real "owner" of the event. - while (el.tagName !== "TR") { - el = el.parentNode; - } - clearTimeout(hoverTimeout); - hoverTimeout = setTimeout(function() { - onEachLazy(document.getElementsByClassName("search-results"), function(e) { - onEachLazy(e.getElementsByClassName("result"), function(i_e) { - removeClass(i_e, "highlighted"); - }); - }); - addClass(el, "highlighted"); - }, 20); - } - }; - onEachLazy(document.getElementsByClassName("search-results"), function(e) { - onEachLazy(e.getElementsByClassName("result"), function(i_e) { - i_e.onclick = click_func; - i_e.onmouseover = mouseover_func; - }); - }); - - search_input.onkeydown = function(e) { - // "actives" references the currently highlighted item in each search tab. - // Each array in "actives" represents a tab. - var actives = [[], [], []]; - // "current" is used to know which tab we're looking into. - var current = 0; - onEachLazy(document.getElementById("results").childNodes, function(e) { - onEachLazy(e.getElementsByClassName("highlighted"), function(h_e) { - actives[current].push(h_e); - }); - current += 1; - }); - - if (e.which === 38) { // up - if (e.ctrlKey) { // Going through result tabs. - printTab(currentTab > 0 ? currentTab - 1 : 2); - } else { - if (!actives[currentTab].length || - !actives[currentTab][0].previousElementSibling) { - return; - } - addClass(actives[currentTab][0].previousElementSibling, "highlighted"); - removeClass(actives[currentTab][0], "highlighted"); - } - e.preventDefault(); - } else if (e.which === 40) { // down - if (e.ctrlKey) { // Going through result tabs. - printTab(currentTab > 1 ? 0 : currentTab + 1); - } else if (!actives[currentTab].length) { - var results = document.getElementById("results").childNodes; - if (results.length > 0) { - var res = results[currentTab].getElementsByClassName("result"); - if (res.length > 0) { - addClass(res[0], "highlighted"); - } - } - } else if (actives[currentTab][0].nextElementSibling) { - addClass(actives[currentTab][0].nextElementSibling, "highlighted"); - removeClass(actives[currentTab][0], "highlighted"); - } - e.preventDefault(); - } else if (e.which === 13) { // return - if (actives[currentTab].length) { - document.location.href = - actives[currentTab][0].getElementsByTagName("a")[0].href; - } - } else if (e.which === 16) { // shift - // Does nothing, it's just to avoid losing "focus" on the highlighted element. - } else if (actives[currentTab].length > 0) { - removeClass(actives[currentTab][0], "highlighted"); - } - }; - } - - function buildHrefAndPath(item) { - var displayPath; - var href; - var type = itemTypes[item.ty]; - var name = item.name; - var path = item.path; - - if (type === "mod") { - displayPath = path + "::"; - href = window.rootPath + path.replace(/::/g, "/") + "/" + - name + "/index.html"; - } else if (type === "primitive" || type === "keyword") { - displayPath = ""; - href = window.rootPath + path.replace(/::/g, "/") + - "/" + type + "." + name + ".html"; - } else if (type === "externcrate") { - displayPath = ""; - href = window.rootPath + name + "/index.html"; - } else if (item.parent !== undefined) { - var myparent = item.parent; - var anchor = "#" + type + "." + name; - var parentType = itemTypes[myparent.ty]; - var pageType = parentType; - var pageName = myparent.name; - - if (parentType === "primitive") { - displayPath = myparent.name + "::"; - } else if (type === "structfield" && parentType === "variant") { - // Structfields belonging to variants are special: the - // final path element is the enum name. - var enumNameIdx = item.path.lastIndexOf("::"); - var enumName = item.path.substr(enumNameIdx + 2); - path = item.path.substr(0, enumNameIdx); - displayPath = path + "::" + enumName + "::" + myparent.name + "::"; - anchor = "#variant." + myparent.name + ".field." + name; - pageType = "enum"; - pageName = enumName; - } else { - displayPath = path + "::" + myparent.name + "::"; - } - href = window.rootPath + path.replace(/::/g, "/") + - "/" + pageType + - "." + pageName + - ".html" + anchor; - } else { - displayPath = item.path + "::"; - href = window.rootPath + item.path.replace(/::/g, "/") + - "/" + type + "." + name + ".html"; - } - return [displayPath, href]; - } - - function escape(content) { - var h1 = document.createElement("h1"); - h1.textContent = content; - return h1.innerHTML; - } - - function pathSplitter(path) { - var tmp = "" + path.replace(/::/g, "::"); - if (tmp.endsWith("")) { - return tmp.slice(0, tmp.length - 6); - } - return tmp; - } - - function addTab(array, query, display) { - var extraStyle = ""; - if (display === false) { - extraStyle = " style=\"display: none;\""; - } - - var output = ""; - var duplicates = {}; - var length = 0; - if (array.length > 0) { - output = ""; - - array.forEach(function(item) { - var name, type; - - name = item.name; - type = itemTypes[item.ty]; - - if (item.is_alias !== true) { - if (duplicates[item.fullPath]) { - return; - } - duplicates[item.fullPath] = true; - } - length += 1; - - output += ""; - }); - output += "
" + - "" + - (item.is_alias === true ? - ("" + item.alias + "  - see ") : "") + - item.displayPath + "" + - name + "" + - "" + - "" + item.desc + - " 
"; - } else { - output = "
No results :(
" + - "Try on DuckDuckGo?

" + - "Or try looking in one of these:
"; - } - return [output, length]; - } - - function makeTabHeader(tabNb, text, nbElems) { - if (currentTab === tabNb) { - return ""; - } - return ""; - } - - function showResults(results) { - var search = getSearchElement(); - if (results.others.length === 1 - && getSettingValue("go-to-only-result") === "true" - // By default, the search DOM element is "empty" (meaning it has no children not - // text content). Once a search has been run, it won't be empty, even if you press - // ESC or empty the search input (which also "cancels" the search). - && (!search.firstChild || search.firstChild.innerText !== getSearchLoadingText())) - { - var elem = document.createElement("a"); - elem.href = results.others[0].href; - elem.style.display = "none"; - // For firefox, we need the element to be in the DOM so it can be clicked. - document.body.appendChild(elem); - elem.click(); - return; - } - var query = getQuery(search_input.value); - - currentResults = query.id; - - var ret_others = addTab(results.others, query); - var ret_in_args = addTab(results.in_args, query, false); - var ret_returned = addTab(results.returned, query, false); - - // Navigate to the relevant tab if the current tab is empty, like in case users search - // for "-> String". If they had selected another tab previously, they have to click on - // it again. - if ((currentTab === 0 && ret_others[1] === 0) || - (currentTab === 1 && ret_in_args[1] === 0) || - (currentTab === 2 && ret_returned[1] === 0)) { - if (ret_others[1] !== 0) { - currentTab = 0; - } else if (ret_in_args[1] !== 0) { - currentTab = 1; - } else if (ret_returned[1] !== 0) { - currentTab = 2; - } - } - - var output = "

Results for " + escape(query.query) + - (query.type ? " (type: " + escape(query.type) + ")" : "") + "

" + - "
" + - makeTabHeader(0, "In Names", ret_others[1]) + - makeTabHeader(1, "In Parameters", ret_in_args[1]) + - makeTabHeader(2, "In Return Types", ret_returned[1]) + - "
" + - ret_others[0] + ret_in_args[0] + ret_returned[0] + "
"; - - search.innerHTML = output; - showSearchResults(search); - initSearchNav(); - var elems = document.getElementById("titles").childNodes; - elems[0].onclick = function() { printTab(0); }; - elems[1].onclick = function() { printTab(1); }; - elems[2].onclick = function() { printTab(2); }; - printTab(currentTab); - } - - function execSearch(query, searchWords, filterCrates) { - function getSmallest(arrays, positions, notDuplicates) { - var start = null; - - for (var it = 0, len = positions.length; it < len; ++it) { - if (arrays[it].length > positions[it] && - (start === null || start > arrays[it][positions[it]].lev) && - !notDuplicates[arrays[it][positions[it]].fullPath]) { - start = arrays[it][positions[it]].lev; - } - } - return start; - } - - function mergeArrays(arrays) { - var ret = []; - var positions = []; - var notDuplicates = {}; - - for (var x = 0, arrays_len = arrays.length; x < arrays_len; ++x) { - positions.push(0); - } - while (ret.length < MAX_RESULTS) { - var smallest = getSmallest(arrays, positions, notDuplicates); - - if (smallest === null) { - break; - } - for (x = 0; x < arrays_len && ret.length < MAX_RESULTS; ++x) { - if (arrays[x].length > positions[x] && - arrays[x][positions[x]].lev === smallest && - !notDuplicates[arrays[x][positions[x]].fullPath]) { - ret.push(arrays[x][positions[x]]); - notDuplicates[arrays[x][positions[x]].fullPath] = true; - positions[x] += 1; - } - } - } - return ret; - } - - var queries = query.raw.split(","); - var results = { - "in_args": [], - "returned": [], - "others": [], - }; - - for (var i = 0, len = queries.length; i < len; ++i) { - query = queries[i].trim(); - if (query.length !== 0) { - var tmp = execQuery(getQuery(query), searchWords, filterCrates); - - results.in_args.push(tmp.in_args); - results.returned.push(tmp.returned); - results.others.push(tmp.others); - } - } - if (queries.length > 1) { - return { - "in_args": mergeArrays(results.in_args), - "returned": mergeArrays(results.returned), - "others": mergeArrays(results.others), - }; - } - return { - "in_args": results.in_args[0], - "returned": results.returned[0], - "others": results.others[0], - }; - } - - function getFilterCrates() { - var elem = document.getElementById("crate-search"); - - if (elem && elem.value !== "All crates" && hasOwnProperty(rawSearchIndex, elem.value)) { - return elem.value; - } - return undefined; - } - - function search(e, forced) { - var params = getQueryStringParams(); - var query = getQuery(search_input.value.trim()); - - if (e) { - e.preventDefault(); - } - - if (query.query.length === 0) { - return; - } - if (forced !== true && query.id === currentResults) { - if (query.query.length > 0) { - putBackSearch(search_input); - } - return; - } - - // Update document title to maintain a meaningful browser history - searchTitle = "Results for " + query.query + " - Rust"; - - // Because searching is incremental by character, only the most - // recent search query is added to the browser history. - if (browserSupportsHistoryApi()) { - var newURL = getNakedUrl() + "?search=" + encodeURIComponent(query.raw) + - window.location.hash; - if (!history.state && !params.search) { - history.pushState(query, "", newURL); - } else { - history.replaceState(query, "", newURL); - } - } - - var filterCrates = getFilterCrates(); - showResults(execSearch(query, index, filterCrates)); - } - - function buildIndex(rawSearchIndex) { - searchIndex = []; - var searchWords = []; - var i, word; - var currentIndex = 0; - var id = 0; - - for (var crate in rawSearchIndex) { - if (!hasOwnProperty(rawSearchIndex, crate)) { continue; } - - var crateSize = 0; - - searchWords.push(crate); - var normalizedName = crate.indexOf("_") === -1 - ? crate - : crate.replace(/_/g, ""); - // This object should have exactly the same set of fields as the "row" - // object defined below. Your JavaScript runtime will thank you. - // https://mathiasbynens.be/notes/shapes-ics - var crateRow = { - crate: crate, - ty: 1, // == ExternCrate - name: crate, - path: "", - desc: rawSearchIndex[crate].doc, - parent: undefined, - type: null, - id: id, - normalizedName: normalizedName, - }; - id += 1; - searchIndex.push(crateRow); - currentIndex += 1; - - // an array of (Number) item types - var itemTypes = rawSearchIndex[crate].t; - // an array of (String) item names - var itemNames = rawSearchIndex[crate].n; - // an array of (String) full paths (or empty string for previous path) - var itemPaths = rawSearchIndex[crate].q; - // an array of (String) descriptions - var itemDescs = rawSearchIndex[crate].d; - // an array of (Number) the parent path index + 1 to `paths`, or 0 if none - var itemParentIdxs = rawSearchIndex[crate].i; - // an array of (Object | null) the type of the function, if any - var itemFunctionSearchTypes = rawSearchIndex[crate].f; - // an array of [(Number) item type, - // (String) name] - var paths = rawSearchIndex[crate].p; - // a array of [(String) alias name - // [Number] index to items] - var aliases = rawSearchIndex[crate].a; - - // convert `rawPaths` entries into object form - var len = paths.length; - for (i = 0; i < len; ++i) { - paths[i] = {ty: paths[i][0], name: paths[i][1]}; - } - - // convert `item*` into an object form, and construct word indices. - // - // before any analysis is performed lets gather the search terms to - // search against apart from the rest of the data. This is a quick - // operation that is cached for the life of the page state so that - // all other search operations have access to this cached data for - // faster analysis operations - len = itemTypes.length; - var lastPath = ""; - for (i = 0; i < len; ++i) { - // This object should have exactly the same set of fields as the "crateRow" - // object defined above. - if (typeof itemNames[i] === "string") { - word = itemNames[i].toLowerCase(); - searchWords.push(word); - } else { - word = ""; - searchWords.push(""); - } - var normalizedName = word.indexOf("_") === -1 - ? word - : word.replace(/_/g, ""); - var row = { - crate: crate, - ty: itemTypes[i], - name: itemNames[i], - path: itemPaths[i] ? itemPaths[i] : lastPath, - desc: itemDescs[i], - parent: itemParentIdxs[i] > 0 ? paths[itemParentIdxs[i] - 1] : undefined, - type: itemFunctionSearchTypes[i], - id: id, - normalizedName: normalizedName, - }; - id += 1; - searchIndex.push(row); - lastPath = row.path; - crateSize += 1; - } - - if (aliases) { - ALIASES[crate] = {}; - var j, local_aliases; - for (var alias_name in aliases) { - if (!aliases.hasOwnProperty(alias_name)) { continue; } - - if (!ALIASES[crate].hasOwnProperty(alias_name)) { - ALIASES[crate][alias_name] = []; - } - local_aliases = aliases[alias_name]; - for (j = 0, len = local_aliases.length; j < len; ++j) { - ALIASES[crate][alias_name].push(local_aliases[j] + currentIndex); - } - } - } - currentIndex += crateSize; - } - return searchWords; - } - - function registerSearchEvents() { - var searchAfter500ms = function() { - clearInputTimeout(); - if (search_input.value.length === 0) { - if (browserSupportsHistoryApi()) { - history.replaceState("", window.currentCrate + " - Rust", - getNakedUrl() + window.location.hash); - } - hideSearchResults(); - } else { - searchTimeout = setTimeout(search, 500); - } - }; - search_input.onkeyup = searchAfter500ms; - search_input.oninput = searchAfter500ms; - document.getElementsByClassName("search-form")[0].onsubmit = function(e) { - e.preventDefault(); - clearInputTimeout(); - search(); - }; - search_input.onchange = function(e) { - if (e.target !== document.activeElement) { - // To prevent doing anything when it's from a blur event. - return; - } - // Do NOT e.preventDefault() here. It will prevent pasting. - clearInputTimeout(); - // zero-timeout necessary here because at the time of event handler execution the - // pasted content is not in the input field yet. Shouldn’t make any difference for - // change, though. - setTimeout(search, 0); - }; - search_input.onpaste = search_input.onchange; - - var selectCrate = document.getElementById("crate-search"); - if (selectCrate) { - selectCrate.onchange = function() { - updateLocalStorage("rustdoc-saved-filter-crate", selectCrate.value); - search(undefined, true); - }; - } - - // Push and pop states are used to add search results to the browser - // history. - if (browserSupportsHistoryApi()) { - // Store the previous so we can revert back to it later. - var previousTitle = document.title; - - window.addEventListener("popstate", function(e) { - var params = getQueryStringParams(); - // Revert to the previous title manually since the History - // API ignores the title parameter. - document.title = previousTitle; - // When browsing forward to search results the previous - // search will be repeated, so the currentResults are - // cleared to ensure the search is successful. - currentResults = null; - // Synchronize search bar with query string state and - // perform the search. This will empty the bar if there's - // nothing there, which lets you really go back to a - // previous state with nothing in the bar. - if (params.search && params.search.length > 0) { - search_input.value = params.search; - // Some browsers fire "onpopstate" for every page load - // (Chrome), while others fire the event only when actually - // popping a state (Firefox), which is why search() is - // called both here and at the end of the startSearch() - // function. - search(e); - } else { - search_input.value = ""; - // When browsing back from search results the main page - // visibility must be reset. - hideSearchResults(); - } - }); - } - - // This is required in firefox to avoid this problem: Navigating to a search result - // with the keyboard, hitting enter, and then hitting back would take you back to - // the doc page, rather than the search that should overlay it. - // This was an interaction between the back-forward cache and our handlers - // that try to sync state between the URL and the search input. To work around it, - // do a small amount of re-init on page show. - window.onpageshow = function(){ - var qSearch = getQueryStringParams().search; - if (search_input.value === "" && qSearch) { - search_input.value = qSearch; - } - search(); - }; - } - - index = buildIndex(rawSearchIndex); - registerSearchEvents(); - // If there's a search term in the URL, execute the search now. - if (getQueryStringParams().search) { - search(); - } - }; - function addSidebarCrates(crates) { // Draw a convenient sidebar of known crates if we have a listing if (window.rootPath === "../" || window.rootPath === "./") { @@ -2216,6 +804,9 @@ function hideThemeButtonState() { block("foreigntype", "Foreign Types"); block("keyword", "Keywords"); block("traitalias", "Trait Aliases"); + + // `crates{version}.js` should always be loaded before this script, so we can use it safely. + addSidebarCrates(window.ALL_CRATES); }; window.register_implementors = function(imp) { @@ -2813,60 +1404,6 @@ function hideThemeButtonState() { }; }); - // In the search display, allows to switch between tabs. - function printTab(nb) { - if (nb === 0 || nb === 1 || nb === 2) { - currentTab = nb; - } - var nb_copy = nb; - onEachLazy(document.getElementById("titles").childNodes, function(elem) { - if (nb_copy === 0) { - addClass(elem, "selected"); - } else { - removeClass(elem, "selected"); - } - nb_copy -= 1; - }); - onEachLazy(document.getElementById("results").childNodes, function(elem) { - if (nb === 0) { - elem.style.display = ""; - } else { - elem.style.display = "none"; - } - nb -= 1; - }); - } - - function putBackSearch(search_input) { - var search = getSearchElement(); - if (search_input.value !== "" && hasClass(search, "hidden")) { - showSearchResults(search); - if (browserSupportsHistoryApi()) { - var extra = "?search=" + encodeURIComponent(search_input.value); - history.replaceState(search_input.value, "", - getNakedUrl() + extra + window.location.hash); - } - document.title = searchTitle; - } - } - - function getSearchLoadingText() { - return "Loading search results..."; - } - - if (search_input) { - search_input.onfocus = function() { - putBackSearch(this); - }; - } - - var params = getQueryStringParams(); - if (params && params.search) { - var search = getSearchElement(); - search.innerHTML = "<h3 style=\"text-align: center;\">" + getSearchLoadingText() + "</h3>"; - showSearchResults(search); - } - var sidebar_menu = document.getElementsByClassName("sidebar-menu")[0]; if (sidebar_menu) { sidebar_menu.onclick = function() { @@ -2899,30 +1436,6 @@ function hideThemeButtonState() { }); } - function addSearchOptions(crates) { - var elem = document.getElementById("crate-search"); - - if (!elem) { - return; - } - var savedCrate = getSettingValue("saved-filter-crate"); - for (var i = 0, len = crates.length; i < len; ++i) { - var option = document.createElement("option"); - option.value = crates[i]; - option.innerText = crates[i]; - elem.appendChild(option); - // Set the crate filter from saved storage, if the current page has the saved crate - // filter. - // - // If not, ignore the crate filter -- we want to support filtering for crates on sites - // like doc.rust-lang.org where the crates may differ from page to page while on the - // same domain. - if (crates[i] === savedCrate) { - elem.value = savedCrate; - } - } - }; - function buildHelperPopup() { var popup = document.createElement("aside"); addClass(popup, "hidden"); @@ -2980,55 +1493,14 @@ function hideThemeButtonState() { container.appendChild(div_infos); popup.appendChild(container); - insertAfter(popup, getSearchElement()); + insertAfter(popup, searchState.outputElement()); // So that it's only built once and then it'll do nothing when called! buildHelperPopup = function() {}; } - function loadScript(url) { - var script = document.createElement('script'); - script.src = url; - document.head.append(script); - } - - function setupSearchLoader() { - var searchLoaded = false; - function loadSearch() { - if (!searchLoaded) { - searchLoaded = true; - loadScript(window.searchJS); - } - } - - // `crates{version}.js` should always be loaded before this script, so we can use it safely. - addSearchOptions(window.ALL_CRATES); - addSidebarCrates(window.ALL_CRATES); - - search_input.addEventListener("focus", function() { - search_input.origPlaceholder = search_input.placeholder; - search_input.placeholder = "Type your search here."; - loadSearch(); - }); - search_input.addEventListener("blur", function() { - search_input.placeholder = search_input.origPlaceholder; - }); - search_input.removeAttribute('disabled'); - - var crateSearchDropDown = document.getElementById("crate-search"); - // `crateSearchDropDown` can be null in case there is only crate because in that case, the - // crate filter dropdown is removed. - if (crateSearchDropDown) { - crateSearchDropDown.addEventListener("focus", loadSearch); - } - var params = getQueryStringParams(); - if (params.search !== undefined) { - loadSearch(); - } - } - onHashChange(null); window.onhashchange = onHashChange; - setupSearchLoader(); + searchState.setup(); }()); function copy_path(but) { diff --git a/src/librustdoc/html/static/search.js b/src/librustdoc/html/static/search.js new file mode 100644 index 0000000000000..538c811c71097 --- /dev/null +++ b/src/librustdoc/html/static/search.js @@ -0,0 +1,1512 @@ +(function() { +// This mapping table should match the discriminants of +// `rustdoc::html::item_type::ItemType` type in Rust. +var itemTypes = ["mod", + "externcrate", + "import", + "struct", + "enum", + "fn", + "type", + "static", + "trait", + "impl", + "tymethod", + "method", + "structfield", + "variant", + "macro", + "primitive", + "associatedtype", + "constant", + "associatedconstant", + "union", + "foreigntype", + "keyword", + "existential", + "attr", + "derive", + "traitalias"]; + +// used for special search precedence +var TY_PRIMITIVE = itemTypes.indexOf("primitive"); +var TY_KEYWORD = itemTypes.indexOf("keyword"); + +// In the search display, allows to switch between tabs. +function printTab(nb) { + if (nb === 0 || nb === 1 || nb === 2) { + searchState.currentTab = nb; + } + var nb_copy = nb; + onEachLazy(document.getElementById("titles").childNodes, function(elem) { + if (nb_copy === 0) { + addClass(elem, "selected"); + } else { + removeClass(elem, "selected"); + } + nb_copy -= 1; + }); + onEachLazy(document.getElementById("results").childNodes, function(elem) { + if (nb === 0) { + elem.style.display = ""; + } else { + elem.style.display = "none"; + } + nb -= 1; + }); +} + +function removeEmptyStringsFromArray(x) { + for (var i = 0, len = x.length; i < len; ++i) { + if (x[i] === "") { + x.splice(i, 1); + i -= 1; + } + } +} + +/** + * A function to compute the Levenshtein distance between two strings + * Licensed under the Creative Commons Attribution-ShareAlike 3.0 Unported + * Full License can be found at http://creativecommons.org/licenses/by-sa/3.0/legalcode + * This code is an unmodified version of the code written by Marco de Wit + * and was found at http://stackoverflow.com/a/18514751/745719 + */ +var levenshtein_row2 = []; +function levenshtein(s1, s2) { + if (s1 === s2) { + return 0; + } + var s1_len = s1.length, s2_len = s2.length; + if (s1_len && s2_len) { + var i1 = 0, i2 = 0, a, b, c, c2, row = levenshtein_row2; + while (i1 < s1_len) { + row[i1] = ++i1; + } + while (i2 < s2_len) { + c2 = s2.charCodeAt(i2); + a = i2; + ++i2; + b = i2; + for (i1 = 0; i1 < s1_len; ++i1) { + c = a + (s1.charCodeAt(i1) !== c2 ? 1 : 0); + a = row[i1]; + b = b < a ? (b < c ? b + 1 : c) : (a < c ? a + 1 : c); + row[i1] = b; + } + } + return b; + } + return s1_len + s2_len; +} + +window.initSearch = function(rawSearchIndex) { + var MAX_LEV_DISTANCE = 3; + var MAX_RESULTS = 200; + var GENERICS_DATA = 1; + var NAME = 0; + var INPUTS_DATA = 0; + var OUTPUT_DATA = 1; + var NO_TYPE_FILTER = -1; + var currentResults, index, searchIndex; + var ALIASES = {}; + var params = searchState.getQueryStringParams(); + + // Populate search bar with query string search term when provided, + // but only if the input bar is empty. This avoid the obnoxious issue + // where you start trying to do a search, and the index loads, and + // suddenly your search is gone! + if (searchState.input.value === "") { + searchState.input.value = params.search || ""; + } + + /** + * Executes the query and builds an index of results + * @param {[Object]} query [The user query] + * @param {[type]} searchWords [The list of search words to query + * against] + * @param {[type]} filterCrates [Crate to search in if defined] + * @return {[type]} [A search index of results] + */ + function execQuery(query, searchWords, filterCrates) { + function itemTypeFromName(typename) { + for (var i = 0, len = itemTypes.length; i < len; ++i) { + if (itemTypes[i] === typename) { + return i; + } + } + return NO_TYPE_FILTER; + } + + var valLower = query.query.toLowerCase(), + val = valLower, + typeFilter = itemTypeFromName(query.type), + results = {}, results_in_args = {}, results_returned = {}, + split = valLower.split("::"); + + removeEmptyStringsFromArray(split); + + function transformResults(results, isType) { + var out = []; + for (var i = 0, len = results.length; i < len; ++i) { + if (results[i].id > -1) { + var obj = searchIndex[results[i].id]; + obj.lev = results[i].lev; + if (isType !== true || obj.type) { + var res = buildHrefAndPath(obj); + obj.displayPath = pathSplitter(res[0]); + obj.fullPath = obj.displayPath + obj.name; + // To be sure than it some items aren't considered as duplicate. + obj.fullPath += "|" + obj.ty; + obj.href = res[1]; + out.push(obj); + if (out.length >= MAX_RESULTS) { + break; + } + } + } + } + return out; + } + + function sortResults(results, isType) { + var ar = []; + for (var entry in results) { + if (hasOwnProperty(results, entry)) { + ar.push(results[entry]); + } + } + results = ar; + var i, len, result; + for (i = 0, len = results.length; i < len; ++i) { + result = results[i]; + result.word = searchWords[result.id]; + result.item = searchIndex[result.id] || {}; + } + // if there are no results then return to default and fail + if (results.length === 0) { + return []; + } + + results.sort(function(aaa, bbb) { + var a, b; + + // sort by exact match with regard to the last word (mismatch goes later) + a = (aaa.word !== val); + b = (bbb.word !== val); + if (a !== b) { return a - b; } + + // Sort by non levenshtein results and then levenshtein results by the distance + // (less changes required to match means higher rankings) + a = (aaa.lev); + b = (bbb.lev); + if (a !== b) { return a - b; } + + // sort by crate (non-current crate goes later) + a = (aaa.item.crate !== window.currentCrate); + b = (bbb.item.crate !== window.currentCrate); + if (a !== b) { return a - b; } + + // sort by item name length (longer goes later) + a = aaa.word.length; + b = bbb.word.length; + if (a !== b) { return a - b; } + + // sort by item name (lexicographically larger goes later) + a = aaa.word; + b = bbb.word; + if (a !== b) { return (a > b ? +1 : -1); } + + // sort by index of keyword in item name (no literal occurrence goes later) + a = (aaa.index < 0); + b = (bbb.index < 0); + if (a !== b) { return a - b; } + // (later literal occurrence, if any, goes later) + a = aaa.index; + b = bbb.index; + if (a !== b) { return a - b; } + + // special precedence for primitive and keyword pages + if ((aaa.item.ty === TY_PRIMITIVE && bbb.item.ty !== TY_KEYWORD) || + (aaa.item.ty === TY_KEYWORD && bbb.item.ty !== TY_PRIMITIVE)) { + return -1; + } + if ((bbb.item.ty === TY_PRIMITIVE && aaa.item.ty !== TY_PRIMITIVE) || + (bbb.item.ty === TY_KEYWORD && aaa.item.ty !== TY_KEYWORD)) { + return 1; + } + + // sort by description (no description goes later) + a = (aaa.item.desc === ""); + b = (bbb.item.desc === ""); + if (a !== b) { return a - b; } + + // sort by type (later occurrence in `itemTypes` goes later) + a = aaa.item.ty; + b = bbb.item.ty; + if (a !== b) { return a - b; } + + // sort by path (lexicographically larger goes later) + a = aaa.item.path; + b = bbb.item.path; + if (a !== b) { return (a > b ? +1 : -1); } + + // que sera, sera + return 0; + }); + + for (i = 0, len = results.length; i < len; ++i) { + var result = results[i]; + + // this validation does not make sense when searching by types + if (result.dontValidate) { + continue; + } + var name = result.item.name.toLowerCase(), + path = result.item.path.toLowerCase(), + parent = result.item.parent; + + if (isType !== true && + validateResult(name, path, split, parent) === false) + { + result.id = -1; + } + } + return transformResults(results); + } + + function extractGenerics(val) { + val = val.toLowerCase(); + if (val.indexOf("<") !== -1) { + var values = val.substring(val.indexOf("<") + 1, val.lastIndexOf(">")); + return { + name: val.substring(0, val.indexOf("<")), + generics: values.split(/\s*,\s*/), + }; + } + return { + name: val, + generics: [], + }; + } + + function getObjectNameFromId(id) { + if (typeof id === "number") { + return searchIndex[id].name; + } + return id; + } + + function checkGenerics(obj, val) { + // The names match, but we need to be sure that all generics kinda + // match as well. + var tmp_lev, elem_name; + if (val.generics.length > 0) { + if (obj.length > GENERICS_DATA && + obj[GENERICS_DATA].length >= val.generics.length) { + var elems = Object.create(null); + var elength = object[GENERICS_DATA].length; + for (var x = 0; x < elength; ++x) { + elems[getObjectNameFromId(obj[GENERICS_DATA][x])] += 1; + } + var total = 0; + var done = 0; + // We need to find the type that matches the most to remove it in order + // to move forward. + var vlength = val.generics.length; + for (x = 0; x < vlength; ++x) { + var lev = MAX_LEV_DISTANCE + 1; + var firstGeneric = getObjectNameFromId(val.generics[x]); + var match = null; + if (elems[firstGeneric]) { + match = firstGeneric; + lev = 0; + } else { + for (elem_name in elems) { + tmp_lev = levenshtein(elem_name, firstGeneric); + if (tmp_lev < lev) { + lev = tmp_lev; + match = elem_name; + } + } + } + if (match !== null) { + elems[match] -= 1; + if (elems[match] == 0) { + delete elems[match]; + } + total += lev; + done += 1; + } else { + return MAX_LEV_DISTANCE + 1; + } + } + return Math.ceil(total / done); + } + } + return MAX_LEV_DISTANCE + 1; + } + + // Check for type name and type generics (if any). + function checkType(obj, val, literalSearch) { + var lev_distance = MAX_LEV_DISTANCE + 1; + var len, x, firstGeneric; + if (obj[NAME] === val.name) { + if (literalSearch === true) { + if (val.generics && val.generics.length !== 0) { + if (obj.length > GENERICS_DATA && + obj[GENERICS_DATA].length >= val.generics.length) { + var elems = Object.create(null); + len = obj[GENERICS_DATA].length; + for (x = 0; x < len; ++x) { + elems[getObjectNameFromId(obj[GENERICS_DATA][x])] += 1; + } + + var allFound = true; + len = val.generics.length; + for (x = 0; x < len; ++x) { + firstGeneric = getObjectNameFromId(val.generics[x]); + if (elems[firstGeneric]) { + elems[firstGeneric] -= 1; + } else { + allFound = false; + break; + } + } + if (allFound === true) { + return true; + } + } else { + return false; + } + } + return true; + } + // If the type has generics but don't match, then it won't return at this point. + // Otherwise, `checkGenerics` will return 0 and it'll return. + if (obj.length > GENERICS_DATA && obj[GENERICS_DATA].length !== 0) { + var tmp_lev = checkGenerics(obj, val); + if (tmp_lev <= MAX_LEV_DISTANCE) { + return tmp_lev; + } + } else { + return 0; + } + } + // Names didn't match so let's check if one of the generic types could. + if (literalSearch === true) { + if (obj.length > GENERICS_DATA && obj[GENERICS_DATA].length > 0) { + return obj[GENERICS_DATA].some( + function(name) { + return name === val.name; + }); + } + return false; + } + lev_distance = Math.min(levenshtein(obj[NAME], val.name), lev_distance); + if (lev_distance <= MAX_LEV_DISTANCE) { + // The generics didn't match but the name kinda did so we give it + // a levenshtein distance value that isn't *this* good so it goes + // into the search results but not too high. + lev_distance = Math.ceil((checkGenerics(obj, val) + lev_distance) / 2); + } else if (obj.length > GENERICS_DATA && obj[GENERICS_DATA].length > 0) { + // We can check if the type we're looking for is inside the generics! + var olength = obj[GENERICS_DATA].length; + for (x = 0; x < olength; ++x) { + lev_distance = Math.min(levenshtein(obj[GENERICS_DATA][x], val.name), + lev_distance); + } + } + // Now whatever happens, the returned distance is "less good" so we should mark it + // as such, and so we add 1 to the distance to make it "less good". + return lev_distance + 1; + } + + function findArg(obj, val, literalSearch, typeFilter) { + var lev_distance = MAX_LEV_DISTANCE + 1; + + if (obj && obj.type && obj.type[INPUTS_DATA] && obj.type[INPUTS_DATA].length > 0) { + var length = obj.type[INPUTS_DATA].length; + for (var i = 0; i < length; i++) { + var tmp = obj.type[INPUTS_DATA][i]; + if (typePassesFilter(typeFilter, tmp[1]) === false) { + continue; + } + tmp = checkType(tmp, val, literalSearch); + if (literalSearch === true) { + if (tmp === true) { + return true; + } + continue; + } + lev_distance = Math.min(tmp, lev_distance); + if (lev_distance === 0) { + return 0; + } + } + } + return literalSearch === true ? false : lev_distance; + } + + function checkReturned(obj, val, literalSearch, typeFilter) { + var lev_distance = MAX_LEV_DISTANCE + 1; + + if (obj && obj.type && obj.type.length > OUTPUT_DATA) { + var ret = obj.type[OUTPUT_DATA]; + if (typeof ret[0] === "string") { + ret = [ret]; + } + for (var x = 0, len = ret.length; x < len; ++x) { + var tmp = ret[x]; + if (typePassesFilter(typeFilter, tmp[1]) === false) { + continue; + } + tmp = checkType(tmp, val, literalSearch); + if (literalSearch === true) { + if (tmp === true) { + return true; + } + continue; + } + lev_distance = Math.min(tmp, lev_distance); + if (lev_distance === 0) { + return 0; + } + } + } + return literalSearch === true ? false : lev_distance; + } + + function checkPath(contains, lastElem, ty) { + if (contains.length === 0) { + return 0; + } + var ret_lev = MAX_LEV_DISTANCE + 1; + var path = ty.path.split("::"); + + if (ty.parent && ty.parent.name) { + path.push(ty.parent.name.toLowerCase()); + } + + var length = path.length; + var clength = contains.length; + if (clength > length) { + return MAX_LEV_DISTANCE + 1; + } + for (var i = 0; i < length; ++i) { + if (i + clength > length) { + break; + } + var lev_total = 0; + var aborted = false; + for (var x = 0; x < clength; ++x) { + var lev = levenshtein(path[i + x], contains[x]); + if (lev > MAX_LEV_DISTANCE) { + aborted = true; + break; + } + lev_total += lev; + } + if (aborted === false) { + ret_lev = Math.min(ret_lev, Math.round(lev_total / clength)); + } + } + return ret_lev; + } + + function typePassesFilter(filter, type) { + // No filter + if (filter <= NO_TYPE_FILTER) return true; + + // Exact match + if (filter === type) return true; + + // Match related items + var name = itemTypes[type]; + switch (itemTypes[filter]) { + case "constant": + return name === "associatedconstant"; + case "fn": + return name === "method" || name === "tymethod"; + case "type": + return name === "primitive" || name === "associatedtype"; + case "trait": + return name === "traitalias"; + } + + // No match + return false; + } + + function createAliasFromItem(item) { + return { + crate: item.crate, + name: item.name, + path: item.path, + desc: item.desc, + ty: item.ty, + parent: item.parent, + type: item.type, + is_alias: true, + }; + } + + function handleAliases(ret, query, filterCrates) { + // We separate aliases and crate aliases because we want to have current crate + // aliases to be before the others in the displayed results. + var aliases = []; + var crateAliases = []; + if (filterCrates !== undefined) { + if (ALIASES[filterCrates] && ALIASES[filterCrates][query.search]) { + var query_aliases = ALIASES[filterCrates][query.search]; + var len = query_aliases.length; + for (var i = 0; i < len; ++i) { + aliases.push(createAliasFromItem(searchIndex[query_aliases[i]])); + } + } + } else { + Object.keys(ALIASES).forEach(function(crate) { + if (ALIASES[crate][query.search]) { + var pushTo = crate === window.currentCrate ? crateAliases : aliases; + var query_aliases = ALIASES[crate][query.search]; + var len = query_aliases.length; + for (var i = 0; i < len; ++i) { + pushTo.push(createAliasFromItem(searchIndex[query_aliases[i]])); + } + } + }); + } + + var sortFunc = function(aaa, bbb) { + if (aaa.path < bbb.path) { + return 1; + } else if (aaa.path === bbb.path) { + return 0; + } + return -1; + }; + crateAliases.sort(sortFunc); + aliases.sort(sortFunc); + + var pushFunc = function(alias) { + alias.alias = query.raw; + var res = buildHrefAndPath(alias); + alias.displayPath = pathSplitter(res[0]); + alias.fullPath = alias.displayPath + alias.name; + alias.href = res[1]; + + ret.others.unshift(alias); + if (ret.others.length > MAX_RESULTS) { + ret.others.pop(); + } + }; + onEach(aliases, pushFunc); + onEach(crateAliases, pushFunc); + } + + // quoted values mean literal search + var nSearchWords = searchWords.length; + var i, it; + var ty; + var fullId; + var returned; + var in_args; + var len; + if ((val.charAt(0) === "\"" || val.charAt(0) === "'") && + val.charAt(val.length - 1) === val.charAt(0)) + { + val = extractGenerics(val.substr(1, val.length - 2)); + for (i = 0; i < nSearchWords; ++i) { + if (filterCrates !== undefined && searchIndex[i].crate !== filterCrates) { + continue; + } + in_args = findArg(searchIndex[i], val, true, typeFilter); + returned = checkReturned(searchIndex[i], val, true, typeFilter); + ty = searchIndex[i]; + fullId = ty.id; + + if (searchWords[i] === val.name + && typePassesFilter(typeFilter, searchIndex[i].ty) + && results[fullId] === undefined) { + results[fullId] = { + id: i, + index: -1, + dontValidate: true, + }; + } + if (in_args === true && results_in_args[fullId] === undefined) { + results_in_args[fullId] = { + id: i, + index: -1, + dontValidate: true, + }; + } + if (returned === true && results_returned[fullId] === undefined) { + results_returned[fullId] = { + id: i, + index: -1, + dontValidate: true, + }; + } + } + query.inputs = [val]; + query.output = val; + query.search = val; + // searching by type + } else if (val.search("->") > -1) { + var trimmer = function(s) { return s.trim(); }; + var parts = val.split("->").map(trimmer); + var input = parts[0]; + // sort inputs so that order does not matter + var inputs = input.split(",").map(trimmer).sort(); + for (i = 0, len = inputs.length; i < len; ++i) { + inputs[i] = extractGenerics(inputs[i]); + } + var output = extractGenerics(parts[1]); + + for (i = 0; i < nSearchWords; ++i) { + if (filterCrates !== undefined && searchIndex[i].crate !== filterCrates) { + continue; + } + var type = searchIndex[i].type; + ty = searchIndex[i]; + if (!type) { + continue; + } + fullId = ty.id; + + returned = checkReturned(ty, output, true, NO_TYPE_FILTER); + if (output.name === "*" || returned === true) { + in_args = false; + var is_module = false; + + if (input === "*") { + is_module = true; + } else { + var allFound = true; + for (it = 0, len = inputs.length; allFound === true && it < len; it++) { + allFound = checkType(type, inputs[it], true); + } + in_args = allFound; + } + if (in_args === true) { + results_in_args[fullId] = { + id: i, + index: -1, + dontValidate: true, + }; + } + if (returned === true) { + results_returned[fullId] = { + id: i, + index: -1, + dontValidate: true, + }; + } + if (is_module === true) { + results[fullId] = { + id: i, + index: -1, + dontValidate: true, + }; + } + } + } + query.inputs = inputs.map(function(input) { + return input.name; + }); + query.output = output.name; + } else { + query.inputs = [val]; + query.output = val; + query.search = val; + // gather matching search results up to a certain maximum + val = val.replace(/\_/g, ""); + + var valGenerics = extractGenerics(val); + + var paths = valLower.split("::"); + removeEmptyStringsFromArray(paths); + val = paths[paths.length - 1]; + var contains = paths.slice(0, paths.length > 1 ? paths.length - 1 : 1); + + var lev, j; + for (j = 0; j < nSearchWords; ++j) { + ty = searchIndex[j]; + if (!ty || (filterCrates !== undefined && ty.crate !== filterCrates)) { + continue; + } + var lev_add = 0; + if (paths.length > 1) { + lev = checkPath(contains, paths[paths.length - 1], ty); + if (lev > MAX_LEV_DISTANCE) { + continue; + } else if (lev > 0) { + lev_add = lev / 10; + } + } + + returned = MAX_LEV_DISTANCE + 1; + in_args = MAX_LEV_DISTANCE + 1; + var index = -1; + // we want lev results to go lower than others + lev = MAX_LEV_DISTANCE + 1; + fullId = ty.id; + + if (searchWords[j].indexOf(split[i]) > -1 || + searchWords[j].indexOf(val) > -1 || + ty.normalizedName.indexOf(val) > -1) + { + // filter type: ... queries + if (typePassesFilter(typeFilter, ty.ty) && results[fullId] === undefined) { + index = ty.normalizedName.indexOf(val); + } + } + if ((lev = levenshtein(searchWords[j], val)) <= MAX_LEV_DISTANCE) { + if (typePassesFilter(typeFilter, ty.ty) === false) { + lev = MAX_LEV_DISTANCE + 1; + } else { + lev += 1; + } + } + in_args = findArg(ty, valGenerics, false, typeFilter); + returned = checkReturned(ty, valGenerics, false, typeFilter); + + lev += lev_add; + if (lev > 0 && val.length > 3 && searchWords[j].indexOf(val) > -1) { + if (val.length < 6) { + lev -= 1; + } else { + lev = 0; + } + } + if (in_args <= MAX_LEV_DISTANCE) { + if (results_in_args[fullId] === undefined) { + results_in_args[fullId] = { + id: j, + index: index, + lev: in_args, + }; + } + results_in_args[fullId].lev = + Math.min(results_in_args[fullId].lev, in_args); + } + if (returned <= MAX_LEV_DISTANCE) { + if (results_returned[fullId] === undefined) { + results_returned[fullId] = { + id: j, + index: index, + lev: returned, + }; + } + results_returned[fullId].lev = + Math.min(results_returned[fullId].lev, returned); + } + if (index !== -1 || lev <= MAX_LEV_DISTANCE) { + if (index !== -1 && paths.length < 2) { + lev = 0; + } + if (results[fullId] === undefined) { + results[fullId] = { + id: j, + index: index, + lev: lev, + }; + } + results[fullId].lev = Math.min(results[fullId].lev, lev); + } + } + } + + var ret = { + "in_args": sortResults(results_in_args, true), + "returned": sortResults(results_returned, true), + "others": sortResults(results), + }; + handleAliases(ret, query, filterCrates); + return ret; + } + + /** + * Validate performs the following boolean logic. For example: + * "File::open" will give IF A PARENT EXISTS => ("file" && "open") + * exists in (name || path || parent) OR => ("file" && "open") exists in + * (name || path ) + * + * This could be written functionally, but I wanted to minimise + * functions on stack. + * + * @param {[string]} name [The name of the result] + * @param {[string]} path [The path of the result] + * @param {[string]} keys [The keys to be used (["file", "open"])] + * @param {[object]} parent [The parent of the result] + * @return {[boolean]} [Whether the result is valid or not] + */ + function validateResult(name, path, keys, parent) { + for (var i = 0, len = keys.length; i < len; ++i) { + // each check is for validation so we negate the conditions and invalidate + if (!( + // check for an exact name match + name.indexOf(keys[i]) > -1 || + // then an exact path match + path.indexOf(keys[i]) > -1 || + // next if there is a parent, check for exact parent match + (parent !== undefined && parent.name !== undefined && + parent.name.toLowerCase().indexOf(keys[i]) > -1) || + // lastly check to see if the name was a levenshtein match + levenshtein(name, keys[i]) <= MAX_LEV_DISTANCE)) { + return false; + } + } + return true; + } + + function getQuery(raw) { + var matches, type, query; + query = raw; + + matches = query.match(/^(fn|mod|struct|enum|trait|type|const|macro)\s*:\s*/i); + if (matches) { + type = matches[1].replace(/^const$/, "constant"); + query = query.substring(matches[0].length); + } + + return { + raw: raw, + query: query, + type: type, + id: query + type + }; + } + + function initSearchNav() { + var hoverTimeout; + + var click_func = function(e) { + var el = e.target; + // to retrieve the real "owner" of the event. + while (el.tagName !== "TR") { + el = el.parentNode; + } + var dst = e.target.getElementsByTagName("a"); + if (dst.length < 1) { + return; + } + dst = dst[0]; + if (window.location.pathname === dst.pathname) { + searchState.hideResults(); + document.location.href = dst.href; + } + }; + var mouseover_func = function(e) { + if (searchState.mouseMovedAfterSearch) { + var el = e.target; + // to retrieve the real "owner" of the event. + while (el.tagName !== "TR") { + el = el.parentNode; + } + clearTimeout(hoverTimeout); + hoverTimeout = setTimeout(function() { + onEachLazy(document.getElementsByClassName("search-results"), function(e) { + onEachLazy(e.getElementsByClassName("result"), function(i_e) { + removeClass(i_e, "highlighted"); + }); + }); + addClass(el, "highlighted"); + }, 20); + } + }; + onEachLazy(document.getElementsByClassName("search-results"), function(e) { + onEachLazy(e.getElementsByClassName("result"), function(i_e) { + i_e.onclick = click_func; + i_e.onmouseover = mouseover_func; + }); + }); + + searchState.input.onkeydown = function(e) { + // "actives" references the currently highlighted item in each search tab. + // Each array in "actives" represents a tab. + var actives = [[], [], []]; + // "current" is used to know which tab we're looking into. + var current = 0; + onEachLazy(document.getElementById("results").childNodes, function(e) { + onEachLazy(e.getElementsByClassName("highlighted"), function(h_e) { + actives[current].push(h_e); + }); + current += 1; + }); + + var currentTab = searchState.currentTab; + if (e.which === 38) { // up + if (e.ctrlKey) { // Going through result tabs. + printTab(currentTab > 0 ? currentTab - 1 : 2); + } else { + if (!actives[currentTab].length || + !actives[currentTab][0].previousElementSibling) { + return; + } + addClass(actives[currentTab][0].previousElementSibling, "highlighted"); + removeClass(actives[currentTab][0], "highlighted"); + } + e.preventDefault(); + } else if (e.which === 40) { // down + if (e.ctrlKey) { // Going through result tabs. + printTab(currentTab > 1 ? 0 : currentTab + 1); + } else if (!actives[currentTab].length) { + var results = document.getElementById("results").childNodes; + if (results.length > 0) { + var res = results[currentTab].getElementsByClassName("result"); + if (res.length > 0) { + addClass(res[0], "highlighted"); + } + } + } else if (actives[currentTab][0].nextElementSibling) { + addClass(actives[currentTab][0].nextElementSibling, "highlighted"); + removeClass(actives[currentTab][0], "highlighted"); + } + e.preventDefault(); + } else if (e.which === 13) { // return + if (actives[currentTab].length) { + document.location.href = + actives[currentTab][0].getElementsByTagName("a")[0].href; + } + } else if (e.which === 16) { // shift + // Does nothing, it's just to avoid losing "focus" on the highlighted element. + } else if (actives[currentTab].length > 0) { + removeClass(actives[currentTab][0], "highlighted"); + } + }; + } + + function buildHrefAndPath(item) { + var displayPath; + var href; + var type = itemTypes[item.ty]; + var name = item.name; + var path = item.path; + + if (type === "mod") { + displayPath = path + "::"; + href = window.rootPath + path.replace(/::/g, "/") + "/" + + name + "/index.html"; + } else if (type === "primitive" || type === "keyword") { + displayPath = ""; + href = window.rootPath + path.replace(/::/g, "/") + + "/" + type + "." + name + ".html"; + } else if (type === "externcrate") { + displayPath = ""; + href = window.rootPath + name + "/index.html"; + } else if (item.parent !== undefined) { + var myparent = item.parent; + var anchor = "#" + type + "." + name; + var parentType = itemTypes[myparent.ty]; + var pageType = parentType; + var pageName = myparent.name; + + if (parentType === "primitive") { + displayPath = myparent.name + "::"; + } else if (type === "structfield" && parentType === "variant") { + // Structfields belonging to variants are special: the + // final path element is the enum name. + var enumNameIdx = item.path.lastIndexOf("::"); + var enumName = item.path.substr(enumNameIdx + 2); + path = item.path.substr(0, enumNameIdx); + displayPath = path + "::" + enumName + "::" + myparent.name + "::"; + anchor = "#variant." + myparent.name + ".field." + name; + pageType = "enum"; + pageName = enumName; + } else { + displayPath = path + "::" + myparent.name + "::"; + } + href = window.rootPath + path.replace(/::/g, "/") + + "/" + pageType + + "." + pageName + + ".html" + anchor; + } else { + displayPath = item.path + "::"; + href = window.rootPath + item.path.replace(/::/g, "/") + + "/" + type + "." + name + ".html"; + } + return [displayPath, href]; + } + + function escape(content) { + var h1 = document.createElement("h1"); + h1.textContent = content; + return h1.innerHTML; + } + + function pathSplitter(path) { + var tmp = "<span>" + path.replace(/::/g, "::</span><span>"); + if (tmp.endsWith("<span>")) { + return tmp.slice(0, tmp.length - 6); + } + return tmp; + } + + function addTab(array, query, display) { + var extraStyle = ""; + if (display === false) { + extraStyle = " style=\"display: none;\""; + } + + var output = ""; + var duplicates = {}; + var length = 0; + if (array.length > 0) { + output = "<table class=\"search-results\"" + extraStyle + ">"; + + array.forEach(function(item) { + var name, type; + + name = item.name; + type = itemTypes[item.ty]; + + if (item.is_alias !== true) { + if (duplicates[item.fullPath]) { + return; + } + duplicates[item.fullPath] = true; + } + length += 1; + + output += "<tr class=\"" + type + " result\"><td>" + + "<a href=\"" + item.href + "\">" + + (item.is_alias === true ? + ("<span class=\"alias\"><b>" + item.alias + " </b></span><span " + + "class=\"grey\"><i> - see </i></span>") : "") + + item.displayPath + "<span class=\"" + type + "\">" + + name + "</span></a></td><td>" + + "<a href=\"" + item.href + "\">" + + "<span class=\"desc\">" + item.desc + + " </span></a></td></tr>"; + }); + output += "</table>"; + } else { + output = "<div class=\"search-failed\"" + extraStyle + ">No results :(<br/>" + + "Try on <a href=\"https://duckduckgo.com/?q=" + + encodeURIComponent("rust " + query.query) + + "\">DuckDuckGo</a>?<br/><br/>" + + "Or try looking in one of these:<ul><li>The <a " + + "href=\"https://doc.rust-lang.org/reference/index.html\">Rust Reference</a> " + + " for technical details about the language.</li><li><a " + + "href=\"https://doc.rust-lang.org/rust-by-example/index.html\">Rust By " + + "Example</a> for expository code examples.</a></li><li>The <a " + + "href=\"https://doc.rust-lang.org/book/index.html\">Rust Book</a> for " + + "introductions to language features and the language itself.</li><li><a " + + "href=\"https://docs.rs\">Docs.rs</a> for documentation of crates released on" + + " <a href=\"https://crates.io/\">crates.io</a>.</li></ul></div>"; + } + return [output, length]; + } + + function makeTabHeader(tabNb, text, nbElems) { + if (searchState.currentTab === tabNb) { + return "<button class=\"selected\">" + text + + " <div class=\"count\">(" + nbElems + ")</div></button>"; + } + return "<button>" + text + " <div class=\"count\">(" + nbElems + ")</div></button>"; + } + + function showResults(results) { + var search = searchState.outputElement(); + if (results.others.length === 1 + && getSettingValue("go-to-only-result") === "true" + // By default, the search DOM element is "empty" (meaning it has no children not + // text content). Once a search has been run, it won't be empty, even if you press + // ESC or empty the search input (which also "cancels" the search). + && (!search.firstChild || search.firstChild.innerText !== searchState.loadingText)) + { + var elem = document.createElement("a"); + elem.href = results.others[0].href; + elem.style.display = "none"; + // For firefox, we need the element to be in the DOM so it can be clicked. + document.body.appendChild(elem); + elem.click(); + return; + } + var query = getQuery(searchState.input.value); + + currentResults = query.id; + + var ret_others = addTab(results.others, query); + var ret_in_args = addTab(results.in_args, query, false); + var ret_returned = addTab(results.returned, query, false); + + // Navigate to the relevant tab if the current tab is empty, like in case users search + // for "-> String". If they had selected another tab previously, they have to click on + // it again. + var currentTab = searchState.currentTab; + if ((currentTab === 0 && ret_others[1] === 0) || + (currentTab === 1 && ret_in_args[1] === 0) || + (currentTab === 2 && ret_returned[1] === 0)) { + if (ret_others[1] !== 0) { + currentTab = 0; + } else if (ret_in_args[1] !== 0) { + currentTab = 1; + } else if (ret_returned[1] !== 0) { + currentTab = 2; + } + } + + var output = "<h1>Results for " + escape(query.query) + + (query.type ? " (type: " + escape(query.type) + ")" : "") + "</h1>" + + "<div id=\"titles\">" + + makeTabHeader(0, "In Names", ret_others[1]) + + makeTabHeader(1, "In Parameters", ret_in_args[1]) + + makeTabHeader(2, "In Return Types", ret_returned[1]) + + "</div><div id=\"results\">" + + ret_others[0] + ret_in_args[0] + ret_returned[0] + "</div>"; + + search.innerHTML = output; + searchState.showResults(search); + initSearchNav(); + var elems = document.getElementById("titles").childNodes; + elems[0].onclick = function() { printTab(0); }; + elems[1].onclick = function() { printTab(1); }; + elems[2].onclick = function() { printTab(2); }; + printTab(currentTab); + } + + function execSearch(query, searchWords, filterCrates) { + function getSmallest(arrays, positions, notDuplicates) { + var start = null; + + for (var it = 0, len = positions.length; it < len; ++it) { + if (arrays[it].length > positions[it] && + (start === null || start > arrays[it][positions[it]].lev) && + !notDuplicates[arrays[it][positions[it]].fullPath]) { + start = arrays[it][positions[it]].lev; + } + } + return start; + } + + function mergeArrays(arrays) { + var ret = []; + var positions = []; + var notDuplicates = {}; + + for (var x = 0, arrays_len = arrays.length; x < arrays_len; ++x) { + positions.push(0); + } + while (ret.length < MAX_RESULTS) { + var smallest = getSmallest(arrays, positions, notDuplicates); + + if (smallest === null) { + break; + } + for (x = 0; x < arrays_len && ret.length < MAX_RESULTS; ++x) { + if (arrays[x].length > positions[x] && + arrays[x][positions[x]].lev === smallest && + !notDuplicates[arrays[x][positions[x]].fullPath]) { + ret.push(arrays[x][positions[x]]); + notDuplicates[arrays[x][positions[x]].fullPath] = true; + positions[x] += 1; + } + } + } + return ret; + } + + var queries = query.raw.split(","); + var results = { + "in_args": [], + "returned": [], + "others": [], + }; + + for (var i = 0, len = queries.length; i < len; ++i) { + query = queries[i].trim(); + if (query.length !== 0) { + var tmp = execQuery(getQuery(query), searchWords, filterCrates); + + results.in_args.push(tmp.in_args); + results.returned.push(tmp.returned); + results.others.push(tmp.others); + } + } + if (queries.length > 1) { + return { + "in_args": mergeArrays(results.in_args), + "returned": mergeArrays(results.returned), + "others": mergeArrays(results.others), + }; + } + return { + "in_args": results.in_args[0], + "returned": results.returned[0], + "others": results.others[0], + }; + } + + function getFilterCrates() { + var elem = document.getElementById("crate-search"); + + if (elem && elem.value !== "All crates" && hasOwnProperty(rawSearchIndex, elem.value)) { + return elem.value; + } + return undefined; + } + + function search(e, forced) { + var params = searchState.getQueryStringParams(); + var query = getQuery(searchState.input.value.trim()); + + if (e) { + e.preventDefault(); + } + + if (query.query.length === 0) { + return; + } + if (forced !== true && query.id === currentResults) { + if (query.query.length > 0) { + searchState.putBackSearch(searchState.input); + } + return; + } + + // Update document title to maintain a meaningful browser history + searchState.title = "Results for " + query.query + " - Rust"; + + // Because searching is incremental by character, only the most + // recent search query is added to the browser history. + if (searchState.browserSupportsHistoryApi()) { + var newURL = getNakedUrl() + "?search=" + encodeURIComponent(query.raw) + + window.location.hash; + if (!history.state && !params.search) { + history.pushState(query, "", newURL); + } else { + history.replaceState(query, "", newURL); + } + } + + var filterCrates = getFilterCrates(); + showResults(execSearch(query, index, filterCrates)); + } + + function buildIndex(rawSearchIndex) { + searchIndex = []; + var searchWords = []; + var i, word; + var currentIndex = 0; + var id = 0; + + for (var crate in rawSearchIndex) { + if (!hasOwnProperty(rawSearchIndex, crate)) { continue; } + + var crateSize = 0; + + searchWords.push(crate); + var normalizedName = crate.indexOf("_") === -1 + ? crate + : crate.replace(/_/g, ""); + // This object should have exactly the same set of fields as the "row" + // object defined below. Your JavaScript runtime will thank you. + // https://mathiasbynens.be/notes/shapes-ics + var crateRow = { + crate: crate, + ty: 1, // == ExternCrate + name: crate, + path: "", + desc: rawSearchIndex[crate].doc, + parent: undefined, + type: null, + id: id, + normalizedName: normalizedName, + }; + id += 1; + searchIndex.push(crateRow); + currentIndex += 1; + + // an array of (Number) item types + var itemTypes = rawSearchIndex[crate].t; + // an array of (String) item names + var itemNames = rawSearchIndex[crate].n; + // an array of (String) full paths (or empty string for previous path) + var itemPaths = rawSearchIndex[crate].q; + // an array of (String) descriptions + var itemDescs = rawSearchIndex[crate].d; + // an array of (Number) the parent path index + 1 to `paths`, or 0 if none + var itemParentIdxs = rawSearchIndex[crate].i; + // an array of (Object | null) the type of the function, if any + var itemFunctionSearchTypes = rawSearchIndex[crate].f; + // an array of [(Number) item type, + // (String) name] + var paths = rawSearchIndex[crate].p; + // a array of [(String) alias name + // [Number] index to items] + var aliases = rawSearchIndex[crate].a; + + // convert `rawPaths` entries into object form + var len = paths.length; + for (i = 0; i < len; ++i) { + paths[i] = {ty: paths[i][0], name: paths[i][1]}; + } + + // convert `item*` into an object form, and construct word indices. + // + // before any analysis is performed lets gather the search terms to + // search against apart from the rest of the data. This is a quick + // operation that is cached for the life of the page state so that + // all other search operations have access to this cached data for + // faster analysis operations + len = itemTypes.length; + var lastPath = ""; + for (i = 0; i < len; ++i) { + // This object should have exactly the same set of fields as the "crateRow" + // object defined above. + if (typeof itemNames[i] === "string") { + word = itemNames[i].toLowerCase(); + searchWords.push(word); + } else { + word = ""; + searchWords.push(""); + } + var normalizedName = word.indexOf("_") === -1 + ? word + : word.replace(/_/g, ""); + var row = { + crate: crate, + ty: itemTypes[i], + name: itemNames[i], + path: itemPaths[i] ? itemPaths[i] : lastPath, + desc: itemDescs[i], + parent: itemParentIdxs[i] > 0 ? paths[itemParentIdxs[i] - 1] : undefined, + type: itemFunctionSearchTypes[i], + id: id, + normalizedName: normalizedName, + }; + id += 1; + searchIndex.push(row); + lastPath = row.path; + crateSize += 1; + } + + if (aliases) { + ALIASES[crate] = {}; + var j, local_aliases; + for (var alias_name in aliases) { + if (!aliases.hasOwnProperty(alias_name)) { continue; } + + if (!ALIASES[crate].hasOwnProperty(alias_name)) { + ALIASES[crate][alias_name] = []; + } + local_aliases = aliases[alias_name]; + for (j = 0, len = local_aliases.length; j < len; ++j) { + ALIASES[crate][alias_name].push(local_aliases[j] + currentIndex); + } + } + } + currentIndex += crateSize; + } + return searchWords; + } + + function registerSearchEvents() { + var searchAfter500ms = function() { + searchState.clearInputTimeout(); + if (searchState.input.value.length === 0) { + if (searchState.browserSupportsHistoryApi()) { + history.replaceState("", window.currentCrate + " - Rust", + getNakedUrl() + window.location.hash); + } + searchState.hideResults(); + } else { + searchState.timeout = setTimeout(search, 500); + } + }; + searchState.input.onkeyup = searchAfter500ms; + searchState.input.oninput = searchAfter500ms; + document.getElementsByClassName("search-form")[0].onsubmit = function(e) { + e.preventDefault(); + searchState.clearInputTimeout(); + search(); + }; + searchState.input.onchange = function(e) { + if (e.target !== document.activeElement) { + // To prevent doing anything when it's from a blur event. + return; + } + // Do NOT e.preventDefault() here. It will prevent pasting. + searchState.clearInputTimeout(); + // zero-timeout necessary here because at the time of event handler execution the + // pasted content is not in the input field yet. Shouldn’t make any difference for + // change, though. + setTimeout(search, 0); + }; + searchState.input.onpaste = searchState.input.onchange; + + var selectCrate = document.getElementById("crate-search"); + if (selectCrate) { + selectCrate.onchange = function() { + updateLocalStorage("rustdoc-saved-filter-crate", selectCrate.value); + search(undefined, true); + }; + } + + // Push and pop states are used to add search results to the browser + // history. + if (searchState.browserSupportsHistoryApi()) { + // Store the previous <title> so we can revert back to it later. + var previousTitle = document.title; + + window.addEventListener("popstate", function(e) { + var params = searchState.getQueryStringParams(); + // Revert to the previous title manually since the History + // API ignores the title parameter. + document.title = previousTitle; + // When browsing forward to search results the previous + // search will be repeated, so the currentResults are + // cleared to ensure the search is successful. + currentResults = null; + // Synchronize search bar with query string state and + // perform the search. This will empty the bar if there's + // nothing there, which lets you really go back to a + // previous state with nothing in the bar. + if (params.search && params.search.length > 0) { + searchState.input.value = params.search; + // Some browsers fire "onpopstate" for every page load + // (Chrome), while others fire the event only when actually + // popping a state (Firefox), which is why search() is + // called both here and at the end of the startSearch() + // function. + search(e); + } else { + searchState.input.value = ""; + // When browsing back from search results the main page + // visibility must be reset. + searchState.hideResults(); + } + }); + } + + // This is required in firefox to avoid this problem: Navigating to a search result + // with the keyboard, hitting enter, and then hitting back would take you back to + // the doc page, rather than the search that should overlay it. + // This was an interaction between the back-forward cache and our handlers + // that try to sync state between the URL and the search input. To work around it, + // do a small amount of re-init on page show. + window.onpageshow = function(){ + var qSearch = searchState.getQueryStringParams().search; + if (searchState.input.value === "" && qSearch) { + searchState.input.value = qSearch; + } + search(); + }; + } + + index = buildIndex(rawSearchIndex); + registerSearchEvents(); + // If there's a search term in the URL, execute the search now. + if (searchState.getQueryStringParams().search) { + search(); + } +}; + +if (window.searchIndex !== undefined) { + initSearch(window.searchIndex); +} + +})(); diff --git a/src/librustdoc/html/static_files.rs b/src/librustdoc/html/static_files.rs index b3ac865d55ea1..2b73bd5d52ee6 100644 --- a/src/librustdoc/html/static_files.rs +++ b/src/librustdoc/html/static_files.rs @@ -24,6 +24,9 @@ crate static NORMALIZE_CSS: &str = include_str!("static/normalize.css"); /// including search behavior and docblock folding, among others. crate static MAIN_JS: &str = include_str!("static/main.js"); +/// The file contents of `search.js`, which contains the search behavior. +crate static SEARCH_JS: &str = include_str!("static/search.js"); + /// The file contents of `settings.js`, which contains the JavaScript used to handle the settings /// page. crate static SETTINGS_JS: &str = include_str!("static/settings.js"); diff --git a/src/librustdoc/html/tests.rs b/src/librustdoc/html/tests.rs new file mode 100644 index 0000000000000..5d537dabd0c07 --- /dev/null +++ b/src/librustdoc/html/tests.rs @@ -0,0 +1,44 @@ +use crate::html::format::href_relative_parts; + +fn assert_relative_path(expected: &[&str], relative_to_fqp: &[&str], fqp: &[&str]) { + let relative_to_fqp: Vec<String> = relative_to_fqp.iter().copied().map(String::from).collect(); + let fqp: Vec<String> = fqp.iter().copied().map(String::from).collect(); + assert_eq!(expected, href_relative_parts(&fqp, &relative_to_fqp)); +} + +#[test] +fn href_relative_parts_basic() { + let relative_to_fqp = &["std", "vec"]; + let fqp = &["std", "iter"]; + assert_relative_path(&["..", "iter"], relative_to_fqp, fqp); +} +#[test] +fn href_relative_parts_parent_module() { + let relative_to_fqp = &["std", "vec"]; + let fqp = &["std"]; + assert_relative_path(&[".."], relative_to_fqp, fqp); +} +#[test] +fn href_relative_parts_different_crate() { + let relative_to_fqp = &["std", "vec"]; + let fqp = &["core", "iter"]; + assert_relative_path(&["..", "..", "core", "iter"], relative_to_fqp, fqp); +} +#[test] +fn href_relative_parts_same_module() { + let relative_to_fqp = &["std", "vec"]; + let fqp = &["std", "vec"]; + assert_relative_path(&[], relative_to_fqp, fqp); +} +#[test] +fn href_relative_parts_child_module() { + let relative_to_fqp = &["std"]; + let fqp = &["std", "vec"]; + assert_relative_path(&["vec"], relative_to_fqp, fqp); +} +#[test] +fn href_relative_parts_root() { + let relative_to_fqp = &[]; + let fqp = &["std"]; + assert_relative_path(&["std"], relative_to_fqp, fqp); +} diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index b9c4bbdceb276..2a51d78f64a39 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -289,7 +289,13 @@ fn opts() -> Vec<RustcOptGroup> { stable("cfg", |o| o.optmulti("", "cfg", "pass a --cfg to rustc", "")), stable("extern", |o| o.optmulti("", "extern", "pass an --extern to rustc", "NAME[=PATH]")), unstable("extern-html-root-url", |o| { - o.optmulti("", "extern-html-root-url", "base URL to use for dependencies", "NAME=URL") + o.optmulti( + "", + "extern-html-root-url", + "base URL to use for dependencies; for example, \ + \"std=/doc\" links std::vec::Vec to /doc/std/vec/struct.Vec.html", + "NAME=URL", + ) }), stable("plugin-path", |o| o.optmulti("", "plugin-path", "removed", "DIR")), stable("C", |o| { diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs index 4ce7c70d4b57e..837cd034b655c 100644 --- a/src/librustdoc/passes/collect_intra_doc_links.rs +++ b/src/librustdoc/passes/collect_intra_doc_links.rs @@ -1957,20 +1957,28 @@ fn resolution_failure( /// Report an anchor failure. fn anchor_failure(cx: &DocContext<'_>, diag_info: DiagnosticInfo<'_>, failure: AnchorFailure) { - let msg = match failure { + let (msg, anchor_idx) = match failure { AnchorFailure::MultipleAnchors => { - format!("`{}` contains multiple anchors", diag_info.ori_link) + (format!("`{}` contains multiple anchors", diag_info.ori_link), 1) } - AnchorFailure::RustdocAnchorConflict(res) => format!( - "`{}` contains an anchor, but links to {kind}s are already anchored", - diag_info.ori_link, - kind = res.descr(), + AnchorFailure::RustdocAnchorConflict(res) => ( + format!( + "`{}` contains an anchor, but links to {kind}s are already anchored", + diag_info.ori_link, + kind = res.descr(), + ), + 0, ), }; report_diagnostic(cx.tcx, BROKEN_INTRA_DOC_LINKS, &msg, &diag_info, |diag, sp| { - if let Some(sp) = sp { - diag.span_label(sp, "contains invalid anchor"); + if let Some(mut sp) = sp { + if let Some((fragment_offset, _)) = + diag_info.ori_link.char_indices().filter(|(_, x)| *x == '#').nth(anchor_idx) + { + sp = sp.with_lo(sp.lo() + rustc_span::BytePos(fragment_offset as _)); + } + diag.span_label(sp, "invalid anchor"); } if let AnchorFailure::RustdocAnchorConflict(Res::Primitive(_)) = failure { diag.note("this restriction may be lifted in a future release"); diff --git a/src/test/rustdoc-ui/intra-doc/anchors.stderr b/src/test/rustdoc-ui/intra-doc/anchors.stderr index 42a8832185ae6..d63e1ee60b3c5 100644 --- a/src/test/rustdoc-ui/intra-doc/anchors.stderr +++ b/src/test/rustdoc-ui/intra-doc/anchors.stderr @@ -2,7 +2,9 @@ error: `prim@usize#x` contains an anchor, but links to builtin types are already --> $DIR/anchors.rs:47:6 | LL | /// [prim@usize#x] - | ^^^^^^^^^^^^ contains invalid anchor + | ^^^^^^^^^^-- + | | + | invalid anchor | note: the lint level is defined here --> $DIR/anchors.rs:1:9 @@ -16,25 +18,33 @@ error: `Foo::f#hola` contains an anchor, but links to fields are already anchore --> $DIR/anchors.rs:25:15 | LL | /// Or maybe [Foo::f#hola]. - | ^^^^^^^^^^^ contains invalid anchor + | ^^^^^^----- + | | + | invalid anchor error: `hello#people#!` contains multiple anchors --> $DIR/anchors.rs:31:28 | LL | /// Another anchor error: [hello#people#!]. - | ^^^^^^^^^^^^^^ contains invalid anchor + | ^^^^^^^^^^^^-- + | | + | invalid anchor error: `Enum::A#whatever` contains an anchor, but links to variants are already anchored --> $DIR/anchors.rs:37:28 | LL | /// Damn enum's variants: [Enum::A#whatever]. - | ^^^^^^^^^^^^^^^^ contains invalid anchor + | ^^^^^^^--------- + | | + | invalid anchor error: `u32#hello` contains an anchor, but links to builtin types are already anchored --> $DIR/anchors.rs:43:6 | LL | /// [u32#hello] - | ^^^^^^^^^ contains invalid anchor + | ^^^------ + | | + | invalid anchor | = note: this restriction may be lifted in a future release = note: see https://github.com/rust-lang/rust/issues/83083 for more information diff --git a/src/test/rustdoc-ui/intra-doc/double-anchor.stderr b/src/test/rustdoc-ui/intra-doc/double-anchor.stderr index c0241b98b78c1..6addb010e078f 100644 --- a/src/test/rustdoc-ui/intra-doc/double-anchor.stderr +++ b/src/test/rustdoc-ui/intra-doc/double-anchor.stderr @@ -2,7 +2,9 @@ warning: `with#anchor#error` contains multiple anchors --> $DIR/double-anchor.rs:5:18 | LL | /// docs [label][with#anchor#error] - | ^^^^^^^^^^^^^^^^^ contains invalid anchor + | ^^^^^^^^^^^------ + | | + | invalid anchor | = note: `#[warn(rustdoc::broken_intra_doc_links)]` on by default diff --git a/src/test/rustdoc/assoc-types.rs b/src/test/rustdoc/assoc-types.rs index 82fa7cf9e6057..8fda171002ba0 100644 --- a/src/test/rustdoc/assoc-types.rs +++ b/src/test/rustdoc/assoc-types.rs @@ -6,14 +6,14 @@ pub trait Index<I: ?Sized> { type Output: ?Sized; // @has - '//*[@id="tymethod.index"]//code' \ // "fn index<'a>(&'a self, index: I) -> &'a Self::Output" - // @has - '//*[@id="tymethod.index"]//code//a[@href="../assoc_types/trait.Index.html#associatedtype.Output"]' \ + // @has - '//*[@id="tymethod.index"]//code//a[@href="trait.Index.html#associatedtype.Output"]' \ // "Output" fn index<'a>(&'a self, index: I) -> &'a Self::Output; } // @has assoc_types/fn.use_output.html // @has - '//*[@class="rust fn"]' '-> &T::Output' -// @has - '//*[@class="rust fn"]//a[@href="../assoc_types/trait.Index.html#associatedtype.Output"]' 'Output' +// @has - '//*[@class="rust fn"]//a[@href="trait.Index.html#associatedtype.Output"]' 'Output' pub fn use_output<T: Index<usize>>(obj: &T, index: usize) -> &T::Output { obj.index(index) } @@ -24,12 +24,12 @@ pub trait Feed { // @has assoc_types/fn.use_input.html // @has - '//*[@class="rust fn"]' 'T::Input' -// @has - '//*[@class="rust fn"]//a[@href="../assoc_types/trait.Feed.html#associatedtype.Input"]' 'Input' +// @has - '//*[@class="rust fn"]//a[@href="trait.Feed.html#associatedtype.Input"]' 'Input' pub fn use_input<T: Feed>(_feed: &T, _element: T::Input) { } // @has assoc_types/fn.cmp_input.html // @has - '//*[@class="rust fn"]' 'where T::Input: PartialEq<U::Input>' -// @has - '//*[@class="rust fn"]//a[@href="../assoc_types/trait.Feed.html#associatedtype.Input"]' 'Input' +// @has - '//*[@class="rust fn"]//a[@href="trait.Feed.html#associatedtype.Input"]' 'Input' pub fn cmp_input<T: Feed, U: Feed>(a: &T::Input, b: &U::Input) -> bool where T::Input: PartialEq<U::Input> { diff --git a/src/test/rustdoc/auxiliary/primitive-doc.rs b/src/test/rustdoc/auxiliary/primitive-doc.rs new file mode 100644 index 0000000000000..a5b69740dd447 --- /dev/null +++ b/src/test/rustdoc/auxiliary/primitive-doc.rs @@ -0,0 +1,6 @@ +// compile-flags: --crate-type lib --edition 2018 + +#[doc(primitive = "usize")] +/// This is the built-in type `usize`. +mod usize { +} diff --git a/src/test/rustdoc/check-styled-link.rs b/src/test/rustdoc/check-styled-link.rs index b479820dfb2ec..ed4a5ea21374b 100644 --- a/src/test/rustdoc/check-styled-link.rs +++ b/src/test/rustdoc/check-styled-link.rs @@ -2,7 +2,7 @@ pub struct Foo; -// @has foo/struct.Bar.html '//a[@href="../foo/struct.Foo.html"]' 'Foo' +// @has foo/struct.Bar.html '//a[@href="struct.Foo.html"]' 'Foo' /// Code-styled reference to [`Foo`]. pub struct Bar; diff --git a/src/test/rustdoc/cross-crate-primitive-doc.rs b/src/test/rustdoc/cross-crate-primitive-doc.rs new file mode 100644 index 0000000000000..05376e4680ec4 --- /dev/null +++ b/src/test/rustdoc/cross-crate-primitive-doc.rs @@ -0,0 +1,9 @@ +// aux-build:primitive-doc.rs +// compile-flags: --extern-html-root-url=primitive_doc=../ -Z unstable-options + +#![no_std] + +extern crate primitive_doc; + +// @has 'cross_crate_primitive_doc/fn.foo.html' '//a[@href="../primitive_doc/primitive.usize.html"]' 'usize' +pub fn foo() -> usize { 0 } diff --git a/src/test/rustdoc/default-trait-method-link.rs b/src/test/rustdoc/default-trait-method-link.rs index e4f0bdab16292..7bcd2a3c149e9 100644 --- a/src/test/rustdoc/default-trait-method-link.rs +++ b/src/test/rustdoc/default-trait-method-link.rs @@ -1,7 +1,7 @@ #![crate_name = "foo"] -// @has foo/trait.Foo.html '//a[@href="../foo/trait.Foo.html#tymethod.req"]' 'req' -// @has foo/trait.Foo.html '//a[@href="../foo/trait.Foo.html#method.prov"]' 'prov' +// @has foo/trait.Foo.html '//a[@href="trait.Foo.html#tymethod.req"]' 'req' +// @has foo/trait.Foo.html '//a[@href="trait.Foo.html#method.prov"]' 'prov' /// Always make sure to implement [`req`], but you don't have to implement [`prov`]. /// diff --git a/src/test/rustdoc/intra-doc-crate/self.rs b/src/test/rustdoc/intra-doc-crate/self.rs index 4db63b12b6bb7..8c36a7fa002d8 100644 --- a/src/test/rustdoc/intra-doc-crate/self.rs +++ b/src/test/rustdoc/intra-doc-crate/self.rs @@ -3,7 +3,7 @@ extern crate cross_crate_self; -// @has self/struct.S.html '//a[@href="../self/struct.S.html#method.f"]' "Self::f" -// @has self/struct.S.html '//a[@href="../self/struct.S.html"]' "Self" +// @has self/struct.S.html '//a[@href="struct.S.html#method.f"]' "Self::f" +// @has self/struct.S.html '//a[@href="struct.S.html"]' "Self" // @has self/struct.S.html '//a[@href="../cross_crate_self/index.html"]' "crate" pub use cross_crate_self::S; diff --git a/src/test/rustdoc/intra-doc/anchors.rs b/src/test/rustdoc/intra-doc/anchors.rs index e4f0c737bdd33..8ec1a7b4f9056 100644 --- a/src/test/rustdoc/intra-doc/anchors.rs +++ b/src/test/rustdoc/intra-doc/anchors.rs @@ -4,7 +4,7 @@ pub struct Something; // @has anchors/struct.SomeOtherType.html -// @has - '//a/@href' '../anchors/struct.Something.html#Anchor!' +// @has - '//a/@href' 'struct.Something.html#Anchor!' /// I want... /// diff --git a/src/test/rustdoc/intra-doc/associated-defaults.rs b/src/test/rustdoc/intra-doc/associated-defaults.rs index 28dc7073a3ecc..68647127fe880 100644 --- a/src/test/rustdoc/intra-doc/associated-defaults.rs +++ b/src/test/rustdoc/intra-doc/associated-defaults.rs @@ -9,14 +9,14 @@ pub trait TraitWithDefault { } /// Link to [UsesDefaults::T] and [UsesDefaults::f] -// @has 'associated_defaults/struct.UsesDefaults.html' '//a[@href="../associated_defaults/struct.UsesDefaults.html#associatedtype.T"]' 'UsesDefaults::T' -// @has 'associated_defaults/struct.UsesDefaults.html' '//a[@href="../associated_defaults/struct.UsesDefaults.html#method.f"]' 'UsesDefaults::f' +// @has 'associated_defaults/struct.UsesDefaults.html' '//a[@href="struct.UsesDefaults.html#associatedtype.T"]' 'UsesDefaults::T' +// @has 'associated_defaults/struct.UsesDefaults.html' '//a[@href="struct.UsesDefaults.html#method.f"]' 'UsesDefaults::f' pub struct UsesDefaults; impl TraitWithDefault for UsesDefaults {} /// Link to [OverridesDefaults::T] and [OverridesDefaults::f] -// @has 'associated_defaults/struct.OverridesDefaults.html' '//a[@href="../associated_defaults/struct.OverridesDefaults.html#associatedtype.T"]' 'OverridesDefaults::T' -// @has 'associated_defaults/struct.OverridesDefaults.html' '//a[@href="../associated_defaults/struct.OverridesDefaults.html#method.f"]' 'OverridesDefaults::f' +// @has 'associated_defaults/struct.OverridesDefaults.html' '//a[@href="struct.OverridesDefaults.html#associatedtype.T"]' 'OverridesDefaults::T' +// @has 'associated_defaults/struct.OverridesDefaults.html' '//a[@href="struct.OverridesDefaults.html#method.f"]' 'OverridesDefaults::f' pub struct OverridesDefaults; impl TraitWithDefault for OverridesDefaults { type T = bool; diff --git a/src/test/rustdoc/intra-doc/associated-items.rs b/src/test/rustdoc/intra-doc/associated-items.rs index 43a43a79738b3..2757418bc64e5 100644 --- a/src/test/rustdoc/intra-doc/associated-items.rs +++ b/src/test/rustdoc/intra-doc/associated-items.rs @@ -9,10 +9,10 @@ pub fn foo() {} /// Link to [MyStruct], [link from struct][MyStruct::method], [MyStruct::clone], [MyStruct::Input] -// @has 'associated_items/struct.MyStruct.html' '//a[@href="../associated_items/struct.MyStruct.html"]' 'MyStruct' -// @has 'associated_items/struct.MyStruct.html' '//a[@href="../associated_items/struct.MyStruct.html#method.method"]' 'link from struct' -// @has 'associated_items/struct.MyStruct.html' '//a[@href="../associated_items/struct.MyStruct.html#method.clone"]' 'MyStruct::clone' -// @has 'associated_items/struct.MyStruct.html' '//a[@href="../associated_items/struct.MyStruct.html#associatedtype.Input"]' 'MyStruct::Input' +// @has 'associated_items/struct.MyStruct.html' '//a[@href="struct.MyStruct.html"]' 'MyStruct' +// @has 'associated_items/struct.MyStruct.html' '//a[@href="struct.MyStruct.html#method.method"]' 'link from struct' +// @has 'associated_items/struct.MyStruct.html' '//a[@href="struct.MyStruct.html#method.clone"]' 'MyStruct::clone' +// @has 'associated_items/struct.MyStruct.html' '//a[@href="struct.MyStruct.html#associatedtype.Input"]' 'MyStruct::Input' pub struct MyStruct { foo: () } impl Clone for MyStruct { @@ -30,7 +30,7 @@ impl T for MyStruct { type Input = usize; /// [link from method][MyStruct::method] on method - // @has 'associated_items/struct.MyStruct.html' '//a[@href="../associated_items/struct.MyStruct.html#method.method"]' 'link from method' + // @has 'associated_items/struct.MyStruct.html' '//a[@href="struct.MyStruct.html#method.method"]' 'link from method' fn method(i: usize) { } } diff --git a/src/test/rustdoc/intra-doc/basic.rs b/src/test/rustdoc/intra-doc/basic.rs index 7760546e1fa1c..39f5c298bc4a1 100644 --- a/src/test/rustdoc/intra-doc/basic.rs +++ b/src/test/rustdoc/intra-doc/basic.rs @@ -1,21 +1,21 @@ // @has basic/index.html -// @has - '//a/@href' '../basic/struct.ThisType.html' -// @has - '//a/@href' '../basic/struct.ThisType.html#method.this_method' -// @has - '//a/@href' '../basic/enum.ThisEnum.html' -// @has - '//a/@href' '../basic/enum.ThisEnum.html#variant.ThisVariant' -// @has - '//a/@href' '../basic/trait.ThisTrait.html' -// @has - '//a/@href' '../basic/trait.ThisTrait.html#tymethod.this_associated_method' -// @has - '//a/@href' '../basic/trait.ThisTrait.html#associatedtype.ThisAssociatedType' -// @has - '//a/@href' '../basic/trait.ThisTrait.html#associatedconstant.THIS_ASSOCIATED_CONST' -// @has - '//a/@href' '../basic/trait.ThisTrait.html' -// @has - '//a/@href' '../basic/type.ThisAlias.html' -// @has - '//a/@href' '../basic/union.ThisUnion.html' -// @has - '//a/@href' '../basic/fn.this_function.html' -// @has - '//a/@href' '../basic/constant.THIS_CONST.html' -// @has - '//a/@href' '../basic/static.THIS_STATIC.html' -// @has - '//a/@href' '../basic/macro.this_macro.html' -// @has - '//a/@href' '../basic/trait.SoAmbiguous.html' -// @has - '//a/@href' '../basic/fn.SoAmbiguous.html' +// @has - '//a/@href' 'struct.ThisType.html' +// @has - '//a/@href' 'struct.ThisType.html#method.this_method' +// @has - '//a/@href' 'enum.ThisEnum.html' +// @has - '//a/@href' 'enum.ThisEnum.html#variant.ThisVariant' +// @has - '//a/@href' 'trait.ThisTrait.html' +// @has - '//a/@href' 'trait.ThisTrait.html#tymethod.this_associated_method' +// @has - '//a/@href' 'trait.ThisTrait.html#associatedtype.ThisAssociatedType' +// @has - '//a/@href' 'trait.ThisTrait.html#associatedconstant.THIS_ASSOCIATED_CONST' +// @has - '//a/@href' 'trait.ThisTrait.html' +// @has - '//a/@href' 'type.ThisAlias.html' +// @has - '//a/@href' 'union.ThisUnion.html' +// @has - '//a/@href' 'fn.this_function.html' +// @has - '//a/@href' 'constant.THIS_CONST.html' +// @has - '//a/@href' 'static.THIS_STATIC.html' +// @has - '//a/@href' 'macro.this_macro.html' +// @has - '//a/@href' 'trait.SoAmbiguous.html' +// @has - '//a/@href' 'fn.SoAmbiguous.html' //! In this crate we would like to link to: //! //! * [`ThisType`](ThisType) @@ -46,7 +46,7 @@ macro_rules! this_macro { () => {}; } -// @has basic/struct.ThisType.html '//a/@href' '../basic/macro.this_macro.html' +// @has basic/struct.ThisType.html '//a/@href' 'macro.this_macro.html' /// another link to [`this_macro!()`] pub struct ThisType; @@ -72,10 +72,10 @@ pub trait SoAmbiguous {} pub fn SoAmbiguous() {} -// @has basic/struct.SomeOtherType.html '//a/@href' '../basic/struct.ThisType.html' -// @has - '//a/@href' '../basic/struct.ThisType.html#method.this_method' -// @has - '//a/@href' '../basic/enum.ThisEnum.html' -// @has - '//a/@href' '../basic/enum.ThisEnum.html#variant.ThisVariant' +// @has basic/struct.SomeOtherType.html '//a/@href' 'struct.ThisType.html' +// @has - '//a/@href' 'struct.ThisType.html#method.this_method' +// @has - '//a/@href' 'enum.ThisEnum.html' +// @has - '//a/@href' 'enum.ThisEnum.html#variant.ThisVariant' /// Shortcut links for: /// * [`ThisType`] /// * [`ThisType::this_method`] diff --git a/src/test/rustdoc/intra-doc/cross-crate/additional_doc.rs b/src/test/rustdoc/intra-doc/cross-crate/additional_doc.rs index 837390b3c7161..85c5866ca7ecf 100644 --- a/src/test/rustdoc/intra-doc/cross-crate/additional_doc.rs +++ b/src/test/rustdoc/intra-doc/cross-crate/additional_doc.rs @@ -4,7 +4,7 @@ extern crate my_rand; -// @has 'additional_doc/trait.Rng.html' '//a[@href="../additional_doc/trait.Rng.html"]' 'Rng' +// @has 'additional_doc/trait.Rng.html' '//a[@href="trait.Rng.html"]' 'Rng' // @has 'additional_doc/trait.Rng.html' '//a[@href="../my_rand/trait.RngCore.html"]' 'RngCore' /// This is an [`Rng`]. pub use my_rand::Rng; diff --git a/src/test/rustdoc/intra-doc/cross-crate/hidden.rs b/src/test/rustdoc/intra-doc/cross-crate/hidden.rs index 9c9d4c649455e..31337f20f18dc 100644 --- a/src/test/rustdoc/intra-doc/cross-crate/hidden.rs +++ b/src/test/rustdoc/intra-doc/cross-crate/hidden.rs @@ -6,5 +6,5 @@ extern crate hidden_dep; -// @has 'hidden/struct.Ready.html' '//a/@href' '../hidden/fn.ready.html' +// @has 'hidden/struct.Ready.html' '//a/@href' 'fn.ready.html' pub use hidden_dep::future::{ready, Ready}; diff --git a/src/test/rustdoc/intra-doc/cross-crate/submodule-outer.rs b/src/test/rustdoc/intra-doc/cross-crate/submodule-outer.rs index 45f561328f279..db7952b5aced0 100644 --- a/src/test/rustdoc/intra-doc/cross-crate/submodule-outer.rs +++ b/src/test/rustdoc/intra-doc/cross-crate/submodule-outer.rs @@ -11,6 +11,6 @@ pub mod bar { // NOTE: we re-exported both `Foo` and `Bar` here, // NOTE: so they are inlined and therefore we link to the current module. -// @has 'submodule_outer/trait.Foo.html' '//a[@href="../submodule_outer/bar/trait.Bar.html"]' 'Bar' -// @has 'submodule_outer/trait.Foo.html' '//a[@href="../submodule_outer/trait.Baz.html"]' 'Baz' +// @has 'submodule_outer/trait.Foo.html' '//a[@href="bar/trait.Bar.html"]' 'Bar' +// @has 'submodule_outer/trait.Foo.html' '//a[@href="trait.Baz.html"]' 'Baz' pub use ::bar_::{Foo, Baz}; diff --git a/src/test/rustdoc/intra-doc/disambiguators-removed.rs b/src/test/rustdoc/intra-doc/disambiguators-removed.rs index 12c3cee29c3ac..d782c5cf5dc84 100644 --- a/src/test/rustdoc/intra-doc/disambiguators-removed.rs +++ b/src/test/rustdoc/intra-doc/disambiguators-removed.rs @@ -2,26 +2,26 @@ // first try backticks /// Trait: [`trait@Name`], fn: [`fn@Name`], [`Name`][`macro@Name`] // @has disambiguators_removed/struct.AtDisambiguator.html -// @has - '//a[@href="../disambiguators_removed/trait.Name.html"][code]' "Name" -// @has - '//a[@href="../disambiguators_removed/fn.Name.html"][code]' "Name" -// @has - '//a[@href="../disambiguators_removed/macro.Name.html"][code]' "Name" +// @has - '//a[@href="trait.Name.html"][code]' "Name" +// @has - '//a[@href="fn.Name.html"][code]' "Name" +// @has - '//a[@href="macro.Name.html"][code]' "Name" pub struct AtDisambiguator; /// fn: [`Name()`], macro: [`Name!`] // @has disambiguators_removed/struct.SymbolDisambiguator.html -// @has - '//a[@href="../disambiguators_removed/fn.Name.html"][code]' "Name()" -// @has - '//a[@href="../disambiguators_removed/macro.Name.html"][code]' "Name!" +// @has - '//a[@href="fn.Name.html"][code]' "Name()" +// @has - '//a[@href="macro.Name.html"][code]' "Name!" pub struct SymbolDisambiguator; // Now make sure that backticks aren't added if they weren't already there /// [fn@Name] // @has disambiguators_removed/trait.Name.html -// @has - '//a[@href="../disambiguators_removed/fn.Name.html"]' "Name" -// @!has - '//a[@href="../disambiguators_removed/fn.Name.html"][code]' "Name" +// @has - '//a[@href="fn.Name.html"]' "Name" +// @!has - '//a[@href="fn.Name.html"][code]' "Name" // FIXME: this will turn !() into ! alone /// [Name!()] -// @has - '//a[@href="../disambiguators_removed/macro.Name.html"]' "Name!" +// @has - '//a[@href="macro.Name.html"]' "Name!" pub trait Name {} #[allow(non_snake_case)] @@ -29,22 +29,22 @@ pub trait Name {} // Try collapsed reference links /// [macro@Name][] // @has disambiguators_removed/fn.Name.html -// @has - '//a[@href="../disambiguators_removed/macro.Name.html"]' "Name" +// @has - '//a[@href="macro.Name.html"]' "Name" // Try links that have the same text as a generated URL -/// Weird URL aligned [../disambiguators_removed/macro.Name.html][trait@Name] -// @has - '//a[@href="../disambiguators_removed/trait.Name.html"]' "../disambiguators_removed/macro.Name.html" +/// Weird URL aligned [macro.Name.html][trait@Name] +// @has - '//a[@href="trait.Name.html"]' "macro.Name.html" pub fn Name() {} #[macro_export] // Rustdoc doesn't currently handle links that have weird interspersing of inline code blocks. /// [fn@Na`m`e] // @has disambiguators_removed/macro.Name.html -// @has - '//a[@href="../disambiguators_removed/fn.Name.html"]' "fn@Name" +// @has - '//a[@href="fn.Name.html"]' "fn@Name" // It also doesn't handle any case where the code block isn't the whole link text: /// [trait@`Name`] -// @has - '//a[@href="../disambiguators_removed/trait.Name.html"]' "trait@Name" +// @has - '//a[@href="trait.Name.html"]' "trait@Name" macro_rules! Name { () => () } diff --git a/src/test/rustdoc/intra-doc/enum-struct-field.rs b/src/test/rustdoc/intra-doc/enum-struct-field.rs index 70bf343a9a5e5..2270a1fafa1cb 100644 --- a/src/test/rustdoc/intra-doc/enum-struct-field.rs +++ b/src/test/rustdoc/intra-doc/enum-struct-field.rs @@ -11,4 +11,4 @@ pub enum Foo { /// I want [Foo::X::y]. pub fn foo() {} -// @has foo/fn.foo.html '//a/@href' '../foo/enum.Foo.html#variant.X.field.y' +// @has foo/fn.foo.html '//a/@href' 'enum.Foo.html#variant.X.field.y' diff --git a/src/test/rustdoc/intra-doc/extern-type.rs b/src/test/rustdoc/intra-doc/extern-type.rs index e1934698d1fad..f37ae62dde1aa 100644 --- a/src/test/rustdoc/intra-doc/extern-type.rs +++ b/src/test/rustdoc/intra-doc/extern-type.rs @@ -12,6 +12,6 @@ impl ExternType { // @has 'extern_type/foreigntype.ExternType.html' // @has 'extern_type/fn.links_to_extern_type.html' \ -// 'href="../extern_type/foreigntype.ExternType.html#method.f"' +// 'href="foreigntype.ExternType.html#method.f"' /// See also [ExternType::f] pub fn links_to_extern_type() {} diff --git a/src/test/rustdoc/intra-doc/issue-82209.rs b/src/test/rustdoc/intra-doc/issue-82209.rs index 76618cdce4cd2..68a5672a8d209 100644 --- a/src/test/rustdoc/intra-doc/issue-82209.rs +++ b/src/test/rustdoc/intra-doc/issue-82209.rs @@ -8,4 +8,4 @@ pub enum Foo { }, } -// @has foo/enum.Foo.html '//a/@href' '../foo/enum.Foo.html#variant.Bar.field.abc' +// @has foo/enum.Foo.html '//a/@href' 'enum.Foo.html#variant.Bar.field.abc' diff --git a/src/test/rustdoc/intra-doc/mod-ambiguity.rs b/src/test/rustdoc/intra-doc/mod-ambiguity.rs index feb013b22be65..24b9dc30a9e49 100644 --- a/src/test/rustdoc/intra-doc/mod-ambiguity.rs +++ b/src/test/rustdoc/intra-doc/mod-ambiguity.rs @@ -6,11 +6,11 @@ pub fn foo() { } pub mod foo {} -// @has mod_ambiguity/struct.A.html '//a/@href' '../mod_ambiguity/foo/index.html' +// @has mod_ambiguity/struct.A.html '//a/@href' 'foo/index.html' /// Module is [`module@foo`] pub struct A; -// @has mod_ambiguity/struct.B.html '//a/@href' '../mod_ambiguity/fn.foo.html' +// @has mod_ambiguity/struct.B.html '//a/@href' 'fn.foo.html' /// Function is [`fn@foo`] pub struct B; diff --git a/src/test/rustdoc/intra-doc/prim-precedence.rs b/src/test/rustdoc/intra-doc/prim-precedence.rs index ab6e3da17f4ed..478b40b0b516f 100644 --- a/src/test/rustdoc/intra-doc/prim-precedence.rs +++ b/src/test/rustdoc/intra-doc/prim-precedence.rs @@ -11,6 +11,6 @@ pub mod char { pub struct MyString; /// See also [crate::char] and [mod@char] -// @has prim_precedence/struct.MyString2.html '//*[@href="../prim_precedence/char/index.html"]' 'crate::char' -// @has - '//*[@href="../prim_precedence/char/index.html"]' 'mod@char' +// @has prim_precedence/struct.MyString2.html '//*[@href="char/index.html"]' 'crate::char' +// @has - '//*[@href="char/index.html"]' 'mod@char' pub struct MyString2; diff --git a/src/test/rustdoc/intra-doc/private.rs b/src/test/rustdoc/intra-doc/private.rs index 337102d6ab3fa..2756a7998e8ea 100644 --- a/src/test/rustdoc/intra-doc/private.rs +++ b/src/test/rustdoc/intra-doc/private.rs @@ -4,9 +4,9 @@ // make sure to update `rustdoc-ui/intra-doc/private.rs` if you update this file /// docs [DontDocMe] [DontDocMe::f] [DontDocMe::x] -// @has private/struct.DocMe.html '//*a[@href="../private/struct.DontDocMe.html"]' 'DontDocMe' -// @has private/struct.DocMe.html '//*a[@href="../private/struct.DontDocMe.html#method.f"]' 'DontDocMe::f' -// @has private/struct.DocMe.html '//*a[@href="../private/struct.DontDocMe.html#structfield.x"]' 'DontDocMe::x' +// @has private/struct.DocMe.html '//*a[@href="struct.DontDocMe.html"]' 'DontDocMe' +// @has private/struct.DocMe.html '//*a[@href="struct.DontDocMe.html#method.f"]' 'DontDocMe::f' +// @has private/struct.DocMe.html '//*a[@href="struct.DontDocMe.html#structfield.x"]' 'DontDocMe::x' pub struct DocMe; struct DontDocMe { x: usize, diff --git a/src/test/rustdoc/intra-doc/proc-macro.rs b/src/test/rustdoc/intra-doc/proc-macro.rs index ab4626ccfc3ab..fce10a130be65 100644 --- a/src/test/rustdoc/intra-doc/proc-macro.rs +++ b/src/test/rustdoc/intra-doc/proc-macro.rs @@ -9,17 +9,17 @@ pub use proc_macro_macro::{DeriveA, attr_a}; use proc_macro_macro::{DeriveB, attr_b}; // @has proc_macro/struct.Foo.html -// @has - '//a/@href' '../proc_macro/derive.DeriveA.html' -// @has - '//a/@href' '../proc_macro/attr.attr_a.html' -// @has - '//a/@href' '../proc_macro/trait.DeriveTrait.html' +// @has - '//a/@href' 'derive.DeriveA.html' +// @has - '//a/@href' 'attr.attr_a.html' +// @has - '//a/@href' 'trait.DeriveTrait.html' // @has - '//a/@href' '../proc_macro_macro/derive.DeriveB.html' // @has - '//a/@href' '../proc_macro_macro/attr.attr_b.html' /// Link to [DeriveA], [attr_a], [DeriveB], [attr_b], [DeriveTrait] pub struct Foo; // @has proc_macro/struct.Bar.html -// @has - '//a/@href' '../proc_macro/derive.DeriveA.html' -// @has - '//a/@href' '../proc_macro/attr.attr_a.html' +// @has - '//a/@href' 'derive.DeriveA.html' +// @has - '//a/@href' 'attr.attr_a.html' /// Link to [deriveA](derive@DeriveA) [attr](macro@attr_a) pub struct Bar; diff --git a/src/test/rustdoc/intra-doc/pub-use.rs b/src/test/rustdoc/intra-doc/pub-use.rs index dd52249abc6d0..579fa68cee8be 100644 --- a/src/test/rustdoc/intra-doc/pub-use.rs +++ b/src/test/rustdoc/intra-doc/pub-use.rs @@ -13,7 +13,7 @@ extern crate inner; // @has outer/index.html // @ has - '//a[@href="https://doc.rust-lang.org/nightly/std/env/fn.var.html"]' "std::env" -// @ has - '//a[@href="../outer/fn.f.html"]' "g" +// @ has - '//a[@href="fn.f.html"]' "g" pub use f as g; // FIXME: same as above diff --git a/src/test/rustdoc/intra-doc/raw-ident-self.rs b/src/test/rustdoc/intra-doc/raw-ident-self.rs index d289797f14652..177c3016fb151 100644 --- a/src/test/rustdoc/intra-doc/raw-ident-self.rs +++ b/src/test/rustdoc/intra-doc/raw-ident-self.rs @@ -5,7 +5,7 @@ pub mod r#impl { impl S { /// See [Self::b]. // @has raw_ident_self/impl/struct.S.html - // @has - '//a[@href="../../raw_ident_self/impl/struct.S.html#method.b"]' 'Self::b' + // @has - '//a[@href="struct.S.html#method.b"]' 'Self::b' pub fn a() {} pub fn b() {} diff --git a/src/test/rustdoc/intra-doc/reexport-additional-docs.rs b/src/test/rustdoc/intra-doc/reexport-additional-docs.rs index 96f3580f3055e..64683bacd6513 100644 --- a/src/test/rustdoc/intra-doc/reexport-additional-docs.rs +++ b/src/test/rustdoc/intra-doc/reexport-additional-docs.rs @@ -3,13 +3,13 @@ #![crate_name = "foo"] extern crate inner; -// @has foo/struct.Inner.html '//a[@href="../foo/fn.with_code.html"]' 'crate::with_code' +// @has foo/struct.Inner.html '//a[@href="fn.with_code.html"]' 'crate::with_code' /// [crate::with_code] -// @has - '//a[@href="../foo/fn.with_code.html"]' 'different text' +// @has - '//a[@href="fn.with_code.html"]' 'different text' /// [different text][with_code] -// @has - '//a[@href="../foo/fn.me_too.html"]' 'me_too' +// @has - '//a[@href="fn.me_too.html"]' 'me_too' #[doc = "[me_too]"] -// @has - '//a[@href="../foo/fn.me_three.html"]' 'reference link' +// @has - '//a[@href="fn.me_three.html"]' 'reference link' /// This [reference link] #[doc = "has an attr in the way"] /// diff --git a/src/test/rustdoc/intra-doc/self.rs b/src/test/rustdoc/intra-doc/self.rs index b2b75127b3122..0ba7df8a78ad7 100644 --- a/src/test/rustdoc/intra-doc/self.rs +++ b/src/test/rustdoc/intra-doc/self.rs @@ -1,8 +1,8 @@ #![crate_name = "foo"] -// @has foo/index.html '//a/@href' '../foo/struct.Foo.html#method.new' -// @has foo/struct.Foo.html '//a/@href' '../foo/struct.Foo.html#method.new' +// @has foo/index.html '//a/@href' 'struct.Foo.html#method.new' +// @has foo/struct.Foo.html '//a/@href' 'struct.Foo.html#method.new' /// Use [`new`] to create a new instance. /// @@ -15,8 +15,8 @@ impl Foo { } } -// @has foo/index.html '//a/@href' '../foo/struct.Bar.html#method.new2' -// @has foo/struct.Bar.html '//a/@href' '../foo/struct.Bar.html#method.new2' +// @has foo/index.html '//a/@href' 'struct.Bar.html#method.new2' +// @has foo/struct.Bar.html '//a/@href' 'struct.Bar.html#method.new2' /// Use [`new2`] to create a new instance. /// @@ -30,7 +30,7 @@ impl Bar { } pub struct MyStruct { - // @has foo/struct.MyStruct.html '//a/@href' '../foo/struct.MyStruct.html#structfield.struct_field' + // @has foo/struct.MyStruct.html '//a/@href' 'struct.MyStruct.html#structfield.struct_field' /// [`struct_field`] /// @@ -39,7 +39,7 @@ pub struct MyStruct { } pub enum MyEnum { - // @has foo/enum.MyEnum.html '//a/@href' '../foo/enum.MyEnum.html#variant.EnumVariant' + // @has foo/enum.MyEnum.html '//a/@href' 'enum.MyEnum.html#variant.EnumVariant' /// [`EnumVariant`] /// @@ -48,7 +48,7 @@ pub enum MyEnum { } pub union MyUnion { - // @has foo/union.MyUnion.html '//a/@href' '../foo/union.MyUnion.html#structfield.union_field' + // @has foo/union.MyUnion.html '//a/@href' 'union.MyUnion.html#structfield.union_field' /// [`union_field`] /// @@ -57,21 +57,21 @@ pub union MyUnion { } pub trait MyTrait { - // @has foo/trait.MyTrait.html '//a/@href' '../foo/trait.MyTrait.html#associatedtype.AssoType' + // @has foo/trait.MyTrait.html '//a/@href' 'trait.MyTrait.html#associatedtype.AssoType' /// [`AssoType`] /// /// [`AssoType`]: Self::AssoType type AssoType; - // @has foo/trait.MyTrait.html '//a/@href' '../foo/trait.MyTrait.html#associatedconstant.ASSO_CONST' + // @has foo/trait.MyTrait.html '//a/@href' 'trait.MyTrait.html#associatedconstant.ASSO_CONST' /// [`ASSO_CONST`] /// /// [`ASSO_CONST`]: Self::ASSO_CONST const ASSO_CONST: i32 = 1; - // @has foo/trait.MyTrait.html '//a/@href' '../foo/trait.MyTrait.html#method.asso_fn' + // @has foo/trait.MyTrait.html '//a/@href' 'trait.MyTrait.html#method.asso_fn' /// [`asso_fn`] /// @@ -80,7 +80,7 @@ pub trait MyTrait { } impl MyStruct { - // @has foo/struct.MyStruct.html '//a/@href' '../foo/struct.MyStruct.html#method.for_impl' + // @has foo/struct.MyStruct.html '//a/@href' 'struct.MyStruct.html#method.for_impl' /// [`for_impl`] /// @@ -91,21 +91,21 @@ impl MyStruct { } impl MyTrait for MyStruct { - // @has foo/struct.MyStruct.html '//a/@href' '../foo/struct.MyStruct.html#associatedtype.AssoType' + // @has foo/struct.MyStruct.html '//a/@href' 'struct.MyStruct.html#associatedtype.AssoType' /// [`AssoType`] /// /// [`AssoType`]: Self::AssoType type AssoType = u32; - // @has foo/struct.MyStruct.html '//a/@href' '../foo/struct.MyStruct.html#associatedconstant.ASSO_CONST' + // @has foo/struct.MyStruct.html '//a/@href' 'struct.MyStruct.html#associatedconstant.ASSO_CONST' /// [`ASSO_CONST`] /// /// [`ASSO_CONST`]: Self::ASSO_CONST const ASSO_CONST: i32 = 10; - // @has foo/struct.MyStruct.html '//a/@href' '../foo/struct.MyStruct.html#method.asso_fn' + // @has foo/struct.MyStruct.html '//a/@href' 'struct.MyStruct.html#method.asso_fn' /// [`asso_fn`] /// diff --git a/src/test/rustdoc/intra-doc/trait-impl.rs b/src/test/rustdoc/intra-doc/trait-impl.rs index ef1987a829ad2..cf60dc1dbd50e 100644 --- a/src/test/rustdoc/intra-doc/trait-impl.rs +++ b/src/test/rustdoc/intra-doc/trait-impl.rs @@ -5,21 +5,21 @@ pub struct MyStruct; impl MyTrait for MyStruct { -// @has foo/struct.MyStruct.html '//a/@href' '../foo/struct.MyStruct.html#associatedtype.AssoType' +// @has foo/struct.MyStruct.html '//a/@href' 'struct.MyStruct.html#associatedtype.AssoType' /// [`AssoType`] /// /// [`AssoType`]: MyStruct::AssoType type AssoType = u32; -// @has foo/struct.MyStruct.html '//a/@href' '../foo/struct.MyStruct.html#associatedconstant.ASSO_CONST' +// @has foo/struct.MyStruct.html '//a/@href' 'struct.MyStruct.html#associatedconstant.ASSO_CONST' /// [`ASSO_CONST`] /// /// [`ASSO_CONST`]: MyStruct::ASSO_CONST const ASSO_CONST: i32 = 10; -// @has foo/struct.MyStruct.html '//a/@href' '../foo/struct.MyStruct.html#method.trait_fn' +// @has foo/struct.MyStruct.html '//a/@href' 'struct.MyStruct.html#method.trait_fn' /// [`trait_fn`] /// diff --git a/src/test/rustdoc/intra-doc/trait-item.rs b/src/test/rustdoc/intra-doc/trait-item.rs index affd2aaec2d22..7602aced56416 100644 --- a/src/test/rustdoc/intra-doc/trait-item.rs +++ b/src/test/rustdoc/intra-doc/trait-item.rs @@ -2,7 +2,7 @@ /// Link to [S::assoc_fn()] /// Link to [Default::default()] -// @has trait_item/struct.S.html '//*[@href="../trait_item/struct.S.html#method.assoc_fn"]' 'S::assoc_fn()' +// @has trait_item/struct.S.html '//*[@href="struct.S.html#method.assoc_fn"]' 'S::assoc_fn()' // @has - '//*[@href="https://doc.rust-lang.org/nightly/core/default/trait.Default.html#tymethod.default"]' 'Default::default()' pub struct S; diff --git a/src/test/rustdoc/intra-link-self-cache.rs b/src/test/rustdoc/intra-link-self-cache.rs index add1530a5a675..63bf7fa5768a7 100644 --- a/src/test/rustdoc/intra-link-self-cache.rs +++ b/src/test/rustdoc/intra-link-self-cache.rs @@ -1,12 +1,12 @@ #![crate_name = "foo"] -// @has foo/enum.E1.html '//a/@href' '../foo/enum.E1.html#variant.A' +// @has foo/enum.E1.html '//a/@href' 'enum.E1.html#variant.A' /// [Self::A::b] pub enum E1 { A { b: usize } } -// @has foo/enum.E2.html '//a/@href' '../foo/enum.E2.html#variant.A' +// @has foo/enum.E2.html '//a/@href' 'enum.E2.html#variant.A' /// [Self::A::b] pub enum E2 { diff --git a/src/test/rustdoc/issue-28478.rs b/src/test/rustdoc/issue-28478.rs index 4cc4056025463..497276e6826a1 100644 --- a/src/test/rustdoc/issue-28478.rs +++ b/src/test/rustdoc/issue-28478.rs @@ -23,9 +23,9 @@ impl Foo { } impl Bar for Foo { - // @has - '//*[@href="../issue_28478/trait.Bar.html#associatedtype.Bar"]' 'Bar' - // @has - '//*[@href="../issue_28478/trait.Bar.html#associatedconstant.Baz"]' 'Baz' - // @has - '//*[@href="../issue_28478/trait.Bar.html#tymethod.bar"]' 'bar' + // @has - '//*[@href="trait.Bar.html#associatedtype.Bar"]' 'Bar' + // @has - '//*[@href="trait.Bar.html#associatedconstant.Baz"]' 'Baz' + // @has - '//*[@href="trait.Bar.html#tymethod.bar"]' 'bar' fn bar() {} - // @has - '//*[@href="../issue_28478/trait.Bar.html#method.baz"]' 'baz' + // @has - '//*[@href="trait.Bar.html#method.baz"]' 'baz' } diff --git a/src/test/rustdoc/issue-55364.rs b/src/test/rustdoc/issue-55364.rs index 4aa553f779339..f156d225bd79b 100644 --- a/src/test/rustdoc/issue-55364.rs +++ b/src/test/rustdoc/issue-55364.rs @@ -2,19 +2,19 @@ // @has issue_55364/subone/index.html // These foo/bar links in the module's documentation should refer inside `subone` -// @has - '//section[@id="main"]/div[@class="docblock"]//a[@href="../../issue_55364/subone/fn.foo.html"]' 'foo' -// @has - '//section[@id="main"]/div[@class="docblock"]//a[@href="../../issue_55364/subone/fn.bar.html"]' 'bar' +// @has - '//section[@id="main"]/div[@class="docblock"]//a[@href="fn.foo.html"]' 'foo' +// @has - '//section[@id="main"]/div[@class="docblock"]//a[@href="fn.bar.html"]' 'bar' pub mod subone { //! See either [foo] or [bar]. // This should refer to subone's `bar` // @has issue_55364/subone/fn.foo.html - // @has - '//section[@id="main"]/div[@class="docblock"]//a[@href="../../issue_55364/subone/fn.bar.html"]' 'bar' + // @has - '//section[@id="main"]/div[@class="docblock"]//a[@href="fn.bar.html"]' 'bar' /// See [bar] pub fn foo() {} // This should refer to subone's `foo` // @has issue_55364/subone/fn.bar.html - // @has - '//section[@id="main"]/div[@class="docblock"]//a[@href="../../issue_55364/subone/fn.foo.html"]' 'foo' + // @has - '//section[@id="main"]/div[@class="docblock"]//a[@href="fn.foo.html"]' 'foo' /// See [foo] pub fn bar() {} } @@ -23,11 +23,11 @@ pub mod subone { // @has issue_55364/subtwo/index.html // These foo/bar links in the module's documentation should not reference inside `subtwo` -// @!has - '//section[@id="main"]/div[@class="docblock"]//a[@href="../../issue_55364/subtwo/fn.foo.html"]' 'foo' -// @!has - '//section[@id="main"]/div[@class="docblock"]//a[@href="../../issue_55364/subtwo/fn.bar.html"]' 'bar' +// @!has - '//section[@id="main"]/div[@class="docblock"]//a[@href="fn.foo.html"]' 'foo' +// @!has - '//section[@id="main"]/div[@class="docblock"]//a[@href="fn.bar.html"]' 'bar' // Instead it should be referencing the top level functions -// @has - '//section[@id="main"]/div[@class="docblock"]//a[@href="../../issue_55364/fn.foo.html"]' 'foo' -// @has - '//section[@id="main"]/div[@class="docblock"]//a[@href="../../issue_55364/fn.bar.html"]' 'bar' +// @has - '//section[@id="main"]/div[@class="docblock"]//a[@href="../fn.foo.html"]' 'foo' +// @has - '//section[@id="main"]/div[@class="docblock"]//a[@href="../fn.bar.html"]' 'bar' // Though there should be such links later // @has - '//section[@id="main"]/table//tr[@class="module-item"]/td/a[@class="fn"][@href="fn.foo.html"]' 'foo' // @has - '//section[@id="main"]/table//tr[@class="module-item"]/td/a[@class="fn"][@href="fn.bar.html"]' 'bar' @@ -37,13 +37,13 @@ pub mod subtwo { // Despite the module's docs referring to the top level foo/bar, // this should refer to subtwo's `bar` // @has issue_55364/subtwo/fn.foo.html - // @has - '//section[@id="main"]/div[@class="docblock"]//a[@href="../../issue_55364/subtwo/fn.bar.html"]' 'bar' + // @has - '//section[@id="main"]/div[@class="docblock"]//a[@href="fn.bar.html"]' 'bar' /// See [bar] pub fn foo() {} // Despite the module's docs referring to the top level foo/bar, // this should refer to subtwo's `foo` // @has issue_55364/subtwo/fn.bar.html - // @has - '//section[@id="main"]/div[@class="docblock"]//a[@href="../../issue_55364/subtwo/fn.foo.html"]' 'foo' + // @has - '//section[@id="main"]/div[@class="docblock"]//a[@href="fn.foo.html"]' 'foo' /// See [foo] pub fn bar() {} } @@ -59,8 +59,8 @@ pub fn bar() {} // @has issue_55364/subthree/index.html // This module should also refer to the top level foo/bar -// @has - '//section[@id="main"]/div[@class="docblock"]//a[@href="../../issue_55364/fn.foo.html"]' 'foo' -// @has - '//section[@id="main"]/div[@class="docblock"]//a[@href="../../issue_55364/fn.bar.html"]' 'bar' +// @has - '//section[@id="main"]/div[@class="docblock"]//a[@href="../fn.foo.html"]' 'foo' +// @has - '//section[@id="main"]/div[@class="docblock"]//a[@href="../fn.bar.html"]' 'bar' pub mod subthree { //! See either [foo][super::foo] or [bar][super::bar] } @@ -68,8 +68,8 @@ pub mod subthree { // Next we go *deeper* - In order to ensure it's not just "this or parent" // we test `crate::` and a `super::super::...` chain // @has issue_55364/subfour/subfive/subsix/subseven/subeight/index.html -// @has - '//section[@id="main"]/table//tr[@class="module-item"]/td[@class="docblock-short"]//a[@href="../../../../../../issue_55364/subone/fn.foo.html"]' 'other foo' -// @has - '//section[@id="main"]/table//tr[@class="module-item"]/td[@class="docblock-short"]//a[@href="../../../../../../issue_55364/subtwo/fn.bar.html"]' 'other bar' +// @has - '//section[@id="main"]/table//tr[@class="module-item"]/td[@class="docblock-short"]//a[@href="../../../../../subone/fn.foo.html"]' 'other foo' +// @has - '//section[@id="main"]/table//tr[@class="module-item"]/td[@class="docblock-short"]//a[@href="../../../../../subtwo/fn.bar.html"]' 'other bar' pub mod subfour { pub mod subfive { pub mod subsix { diff --git a/src/test/rustdoc/issue-72340.rs b/src/test/rustdoc/issue-72340.rs index 6ed3bfbe3e54b..64044cfe94720 100644 --- a/src/test/rustdoc/issue-72340.rs +++ b/src/test/rustdoc/issue-72340.rs @@ -10,7 +10,7 @@ impl Body { } impl Default for Body { - // @has foo/struct.Body.html '//a/@href' '../foo/struct.Body.html#method.empty' + // @has foo/struct.Body.html '//a/@href' 'struct.Body.html#method.empty' /// Returns [`Body::empty()`](Body::empty). fn default() -> Body { diff --git a/src/test/rustdoc/link-assoc-const.rs b/src/test/rustdoc/link-assoc-const.rs index f9eb2b722d6e6..75a2531a308f7 100644 --- a/src/test/rustdoc/link-assoc-const.rs +++ b/src/test/rustdoc/link-assoc-const.rs @@ -1,7 +1,7 @@ #![crate_name = "foo"] -// @has foo/index.html '//a[@href="../foo/foo/constant.FIRSTCONST.html"]' 'foo::FIRSTCONST' -// @has foo/index.html '//a[@href="../foo/struct.Bar.html#associatedconstant.CONST"]' 'Bar::CONST' +// @has foo/index.html '//a[@href="foo/constant.FIRSTCONST.html"]' 'foo::FIRSTCONST' +// @has foo/index.html '//a[@href="struct.Bar.html#associatedconstant.CONST"]' 'Bar::CONST' //! We have here [`foo::FIRSTCONST`] and [`Bar::CONST`]. diff --git a/src/test/rustdoc/proc-macro.rs b/src/test/rustdoc/proc-macro.rs index 82196e413e94b..f6d1f2cf91b5f 100644 --- a/src/test/rustdoc/proc-macro.rs +++ b/src/test/rustdoc/proc-macro.rs @@ -61,12 +61,12 @@ pub fn some_derive(_item: TokenStream) -> TokenStream { // @has some_macros/foo/index.html mod foo { // @has - '//code' 'pub use some_proc_macro;' - // @has - '//a/@href' '../../some_macros/macro.some_proc_macro.html' + // @has - '//a/@href' '../macro.some_proc_macro.html' pub use some_proc_macro; // @has - '//code' 'pub use some_proc_attr;' - // @has - '//a/@href' '../../some_macros/attr.some_proc_attr.html' + // @has - '//a/@href' '../attr.some_proc_attr.html' pub use some_proc_attr; // @has - '//code' 'pub use some_derive;' - // @has - '//a/@href' '../../some_macros/derive.SomeDerive.html' + // @has - '//a/@href' '../derive.SomeDerive.html' pub use some_derive; } diff --git a/src/test/rustdoc/raw-ident-eliminate-r-hashtag.rs b/src/test/rustdoc/raw-ident-eliminate-r-hashtag.rs index 3ecf434c39e45..ad19036126760 100644 --- a/src/test/rustdoc/raw-ident-eliminate-r-hashtag.rs +++ b/src/test/rustdoc/raw-ident-eliminate-r-hashtag.rs @@ -8,13 +8,13 @@ pub mod internal { /// /// [name]: mod /// [other name]: crate::internal::mod - // @has 'raw_ident_eliminate_r_hashtag/internal/struct.B.html' '//*a[@href="../../raw_ident_eliminate_r_hashtag/internal/struct.mod.html"]' 'name' - // @has 'raw_ident_eliminate_r_hashtag/internal/struct.B.html' '//*a[@href="../../raw_ident_eliminate_r_hashtag/internal/struct.mod.html"]' 'other name' + // @has 'raw_ident_eliminate_r_hashtag/internal/struct.B.html' '//*a[@href="struct.mod.html"]' 'name' + // @has 'raw_ident_eliminate_r_hashtag/internal/struct.B.html' '//*a[@href="struct.mod.html"]' 'other name' pub struct B; } /// See [name]. /// /// [name]: internal::mod -// @has 'raw_ident_eliminate_r_hashtag/struct.A.html' '//*a[@href="../raw_ident_eliminate_r_hashtag/internal/struct.mod.html"]' 'name' +// @has 'raw_ident_eliminate_r_hashtag/struct.A.html' '//*a[@href="internal/struct.mod.html"]' 'name' pub struct A; diff --git a/src/test/rustdoc/struct-field.rs b/src/test/rustdoc/struct-field.rs index 974b863bb1604..998683bdde7f2 100644 --- a/src/test/rustdoc/struct-field.rs +++ b/src/test/rustdoc/struct-field.rs @@ -1,9 +1,9 @@ #![crate_name = "foo"] -// @has foo/index.html '//*[@class="docblock"]/p/a[@href="../foo/struct.Foo.html#structfield.bar"]' 'Foo::bar' -// @has foo/index.html '//*[@class="docblock"]/p/a[@href="../foo/union.Bar.html#structfield.foo"]' 'Bar::foo' -// @has foo/index.html '//*[@class="docblock"]/p/a[@href="../foo/enum.Uniooon.html#variant.X"]' 'Uniooon::X' +// @has foo/index.html '//*[@class="docblock"]/p/a[@href="struct.Foo.html#structfield.bar"]' 'Foo::bar' +// @has foo/index.html '//*[@class="docblock"]/p/a[@href="union.Bar.html#structfield.foo"]' 'Bar::foo' +// @has foo/index.html '//*[@class="docblock"]/p/a[@href="enum.Uniooon.html#variant.X"]' 'Uniooon::X' //! Test with [Foo::bar], [Bar::foo], [Uniooon::X] diff --git a/src/test/rustdoc/trait-impl-items-links-and-anchors.rs b/src/test/rustdoc/trait-impl-items-links-and-anchors.rs index 6c09be1144a83..c6a9313e821c9 100644 --- a/src/test/rustdoc/trait-impl-items-links-and-anchors.rs +++ b/src/test/rustdoc/trait-impl-items-links-and-anchors.rs @@ -40,25 +40,25 @@ impl MyTrait for Vec<u8> { impl MyTrait for MyStruct { // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//h4[@id="associatedtype.Assoc-3"]//a[@class="type"]/@href' #associatedtype.Assoc // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//h4[@id="associatedtype.Assoc-3"]//a[@class="anchor"]/@href' #associatedtype.Assoc-3 - // @has trait_impl_items_links_and_anchors/struct.MyStruct.html '//h4[@id="associatedtype.Assoc"]//a[@class="type"]/@href' ../trait_impl_items_links_and_anchors/trait.MyTrait.html#associatedtype.Assoc + // @has trait_impl_items_links_and_anchors/struct.MyStruct.html '//h4[@id="associatedtype.Assoc"]//a[@class="type"]/@href' trait.MyTrait.html#associatedtype.Assoc // @has trait_impl_items_links_and_anchors/struct.MyStruct.html '//h4[@id="associatedtype.Assoc"]//a[@class="anchor"]/@href' #associatedtype.Assoc type Assoc = bool; // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//h4[@id="associatedconstant.VALUE-3"]//a[@class="constant"]/@href' #associatedconstant.VALUE // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//h4[@id="associatedconstant.VALUE-3"]//a[@class="anchor"]/@href' #associatedconstant.VALUE-3 - // @has trait_impl_items_links_and_anchors/struct.MyStruct.html '//h4[@id="associatedconstant.VALUE"]//a[@class="constant"]/@href' ../trait_impl_items_links_and_anchors/trait.MyTrait.html#associatedconstant.VALUE + // @has trait_impl_items_links_and_anchors/struct.MyStruct.html '//h4[@id="associatedconstant.VALUE"]//a[@class="constant"]/@href' trait.MyTrait.html#associatedconstant.VALUE // @has trait_impl_items_links_and_anchors/struct.MyStruct.html '//h4[@id="associatedconstant.VALUE"]//a[@class="anchor"]/@href' #associatedconstant.VALUE const VALUE: u32 = 20; // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//h4[@id="method.trait_function-2"]//a[@class="fnname"]/@href' #tymethod.trait_function // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//h4[@id="method.trait_function-2"]//a[@class="anchor"]/@href' #method.trait_function-2 - // @has trait_impl_items_links_and_anchors/struct.MyStruct.html '//h4[@id="method.trait_function"]//a[@class="fnname"]/@href' ../trait_impl_items_links_and_anchors/trait.MyTrait.html#tymethod.trait_function + // @has trait_impl_items_links_and_anchors/struct.MyStruct.html '//h4[@id="method.trait_function"]//a[@class="fnname"]/@href' trait.MyTrait.html#tymethod.trait_function // @has trait_impl_items_links_and_anchors/struct.MyStruct.html '//h4[@id="method.trait_function"]//a[@class="anchor"]/@href' #method.trait_function fn trait_function(&self) {} // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//h4[@id="method.defaulted_override-3"]//a[@class="fnname"]/@href' #method.defaulted_override // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//h4[@id="method.defaulted_override-3"]//a[@class="anchor"]/@href' #method.defaulted_override-3 - // @has trait_impl_items_links_and_anchors/struct.MyStruct.html '//h4[@id="method.defaulted_override"]//a[@class="fnname"]/@href' ../trait_impl_items_links_and_anchors/trait.MyTrait.html#method.defaulted_override + // @has trait_impl_items_links_and_anchors/struct.MyStruct.html '//h4[@id="method.defaulted_override"]//a[@class="fnname"]/@href' trait.MyTrait.html#method.defaulted_override // @has trait_impl_items_links_and_anchors/struct.MyStruct.html '//h4[@id="method.defaulted_override"]//a[@class="anchor"]/@href' #method.defaulted_override fn defaulted_override(&self) {} - // @has trait_impl_items_links_and_anchors/struct.MyStruct.html '//h4[@id="method.defaulted"]//a[@class="fnname"]/@href' ../trait_impl_items_links_and_anchors/trait.MyTrait.html#method.defaulted + // @has trait_impl_items_links_and_anchors/struct.MyStruct.html '//h4[@id="method.defaulted"]//a[@class="fnname"]/@href' trait.MyTrait.html#method.defaulted // @has trait_impl_items_links_and_anchors/struct.MyStruct.html '//h4[@id="method.defaulted"]//a[@class="anchor"]/@href' #method.defaulted } diff --git a/src/test/rustdoc/trait-self-link.rs b/src/test/rustdoc/trait-self-link.rs index bac28b44012cc..e311dadff0e63 100644 --- a/src/test/rustdoc/trait-self-link.rs +++ b/src/test/rustdoc/trait-self-link.rs @@ -1,4 +1,4 @@ -// @has trait_self_link/trait.Foo.html //a/@href ../trait_self_link/trait.Foo.html +// @has trait_self_link/trait.Foo.html //a/@href trait.Foo.html pub trait Foo {} pub struct Bar; diff --git a/src/tools/rustdoc-js/tester.js b/src/tools/rustdoc-js/tester.js index a551a97bda55d..e583bd225a9eb 100644 --- a/src/tools/rustdoc-js/tester.js +++ b/src/tools/rustdoc-js/tester.js @@ -246,7 +246,7 @@ function lookForEntry(entry, data) { return null; } -function loadMainJsAndIndex(mainJs, searchIndex, storageJs, crate) { +function loadSearchJsAndIndex(searchJs, searchIndex, storageJs, crate) { if (searchIndex[searchIndex.length - 1].length === 0) { searchIndex.pop(); } @@ -270,9 +270,9 @@ function loadMainJsAndIndex(mainJs, searchIndex, storageJs, crate) { ALIASES = {}; finalJS += 'window = { "currentCrate": "' + crate + '", rootPath: "../" };\n'; finalJS += loadThings(["hasOwnProperty", "onEach"], 'function', extractFunction, storageJs); - finalJS += loadThings(arraysToLoad, 'array', extractArrayVariable, mainJs); - finalJS += loadThings(variablesToLoad, 'variable', extractVariable, mainJs); - finalJS += loadThings(functionsToLoad, 'function', extractFunction, mainJs); + finalJS += loadThings(arraysToLoad, 'array', extractArrayVariable, searchJs); + finalJS += loadThings(variablesToLoad, 'variable', extractVariable, searchJs); + finalJS += loadThings(functionsToLoad, 'function', extractFunction, searchJs); var loaded = loadContent(finalJS); var index = loaded.buildIndex(searchIndex.rawSearchIndex); @@ -382,12 +382,12 @@ function runChecks(testFile, loaded, index) { } function load_files(doc_folder, resource_suffix, crate) { - var mainJs = readFile(path.join(doc_folder, "main" + resource_suffix + ".js")); + var searchJs = readFile(path.join(doc_folder, "search" + resource_suffix + ".js")); var storageJs = readFile(path.join(doc_folder, "storage" + resource_suffix + ".js")); var searchIndex = readFile( path.join(doc_folder, "search-index" + resource_suffix + ".js")).split("\n"); - return loadMainJsAndIndex(mainJs, searchIndex, storageJs, crate); + return loadSearchJsAndIndex(searchJs, searchIndex, storageJs, crate); } function showHelp() {