diff --git a/src/librustdoc/externalfiles.rs b/src/librustdoc/externalfiles.rs index 6ed911b8d2409..02d9609dab3f0 100644 --- a/src/librustdoc/externalfiles.rs +++ b/src/librustdoc/externalfiles.rs @@ -1,4 +1,4 @@ -use crate::html::markdown::{ErrorCodes, HeadingOffset, IdMap, Markdown, Playground}; +use crate::html::markdown::{ErrorCodes, HeadingOffset, IdMap, IdPrefix, Markdown, Playground}; use crate::rustc_span::edition::Edition; use std::fs; use std::path::Path; @@ -47,6 +47,7 @@ impl ExternalHtml { edition, playground, heading_offset: HeadingOffset::H2, + id_prefix: IdPrefix::without_parent(), } .into_string() ); @@ -63,6 +64,7 @@ impl ExternalHtml { edition, playground, heading_offset: HeadingOffset::H2, + id_prefix: IdPrefix::without_parent(), } .into_string() ); diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs index 545b409175ee6..87023ac4b05fb 100644 --- a/src/librustdoc/html/markdown.rs +++ b/src/librustdoc/html/markdown.rs @@ -8,7 +8,7 @@ //! extern crate rustc_span; //! //! use rustc_span::edition::Edition; -//! use rustdoc::html::markdown::{HeadingOffset, IdMap, Markdown, ErrorCodes}; +//! use rustdoc::html::markdown::{HeadingOffset, IdMap, IdPrefix, Markdown, ErrorCodes}; //! //! let s = "My *markdown* _text_"; //! let mut id_map = IdMap::new(); @@ -20,6 +20,7 @@ //! edition: Edition::Edition2015, //! playground: &None, //! heading_offset: HeadingOffset::H2, +//! id_prefix: IdPrefix::none(), //! }; //! let html = md.into_string(); //! // ... something using html @@ -40,7 +41,7 @@ use std::fmt::Write; use std::ops::{ControlFlow, Range}; use std::str; -use crate::clean::RenderedLink; +use crate::clean::{self, RenderedLink}; use crate::doctest; use crate::html::escape::Escape; use crate::html::format::Buffer; @@ -101,6 +102,7 @@ pub struct Markdown<'a> { /// Offset at which we render headings. /// E.g. if `heading_offset: HeadingOffset::H2`, then `# something` renders an `

`. pub heading_offset: HeadingOffset, + pub id_prefix: IdPrefix<'a>, } /// A tuple struct like `Markdown` that renders the markdown with a table of contents. crate struct MarkdownWithToc<'a>( @@ -109,6 +111,7 @@ crate struct MarkdownWithToc<'a>( crate ErrorCodes, crate Edition, crate &'a Option, + crate IdPrefix<'a>, ); /// A tuple struct like `Markdown` that renders the markdown escaping HTML tags. crate struct MarkdownHtml<'a>( @@ -117,6 +120,7 @@ crate struct MarkdownHtml<'a>( crate ErrorCodes, crate Edition, crate &'a Option, + crate IdPrefix<'a>, ); /// A tuple struct like `Markdown` that renders only the first paragraph. crate struct MarkdownSummaryLine<'a>(pub &'a str, pub &'a [RenderedLink]); @@ -508,27 +512,36 @@ impl<'a, I: Iterator>> Iterator for TableWrapper<'a, I> { type SpannedEvent<'a> = (Event<'a>, Range); /// Make headings links with anchor IDs and build up TOC. -struct HeadingLinks<'a, 'b, 'ids, I> { +struct HeadingLinks<'a, 'b, 'ids, 'c, I> { inner: I, toc: Option<&'b mut TocBuilder>, buf: VecDeque>, id_map: &'ids mut IdMap, heading_offset: HeadingOffset, + id_prefix: IdPrefix<'c>, } -impl<'a, 'b, 'ids, I> HeadingLinks<'a, 'b, 'ids, I> { +impl<'a, 'b, 'ids, 'c, I> HeadingLinks<'a, 'b, 'ids, 'c, I> { fn new( iter: I, toc: Option<&'b mut TocBuilder>, ids: &'ids mut IdMap, heading_offset: HeadingOffset, + id_prefix: IdPrefix<'c>, ) -> Self { - HeadingLinks { inner: iter, toc, buf: VecDeque::new(), id_map: ids, heading_offset } + HeadingLinks { + inner: iter, + toc, + buf: VecDeque::new(), + id_map: ids, + heading_offset, + id_prefix, + } } } -impl<'a, 'b, 'ids, I: Iterator>> Iterator - for HeadingLinks<'a, 'b, 'ids, I> +impl<'a, 'b, 'ids, 'c, I: Iterator>> Iterator + for HeadingLinks<'a, 'b, 'ids, 'c, I> { type Item = SpannedEvent<'a>; @@ -551,7 +564,7 @@ impl<'a, 'b, 'ids, I: Iterator>> Iterator _ => self.buf.push_back(event), } } - let id = self.id_map.derive(id); + let id = self.id_map.derive(id, self.id_prefix); if let Some(ref mut builder) = self.toc { let mut html_header = String::new(); @@ -1044,6 +1057,7 @@ impl Markdown<'_> { edition, playground, heading_offset, + id_prefix, } = self; // This is actually common enough to special-case @@ -1062,7 +1076,7 @@ impl Markdown<'_> { let mut s = String::with_capacity(md.len() * 3 / 2); - let p = HeadingLinks::new(p, None, &mut ids, heading_offset); + let p = HeadingLinks::new(p, None, &mut ids, heading_offset, id_prefix); let p = Footnotes::new(p); let p = LinkReplacer::new(p.map(|(ev, _)| ev), links); let p = TableWrapper::new(p); @@ -1075,7 +1089,7 @@ impl Markdown<'_> { impl MarkdownWithToc<'_> { crate fn into_string(self) -> String { - let MarkdownWithToc(md, mut ids, codes, edition, playground) = self; + let MarkdownWithToc(md, mut ids, codes, edition, playground, id_prefix) = self; let p = Parser::new_ext(md, main_body_opts()).into_offset_iter(); @@ -1084,7 +1098,7 @@ impl MarkdownWithToc<'_> { let mut toc = TocBuilder::new(); { - let p = HeadingLinks::new(p, Some(&mut toc), &mut ids, HeadingOffset::H1); + let p = HeadingLinks::new(p, Some(&mut toc), &mut ids, HeadingOffset::H1, id_prefix); let p = Footnotes::new(p); let p = TableWrapper::new(p.map(|(ev, _)| ev)); let p = CodeBlocks::new(p, codes, edition, playground); @@ -1097,7 +1111,7 @@ impl MarkdownWithToc<'_> { impl MarkdownHtml<'_> { crate fn into_string(self) -> String { - let MarkdownHtml(md, mut ids, codes, edition, playground) = self; + let MarkdownHtml(md, mut ids, codes, edition, playground, id_prefix) = self; // This is actually common enough to special-case if md.is_empty() { @@ -1113,7 +1127,7 @@ impl MarkdownHtml<'_> { let mut s = String::with_capacity(md.len() * 3 / 2); - let p = HeadingLinks::new(p, None, &mut ids, HeadingOffset::H1); + let p = HeadingLinks::new(p, None, &mut ids, HeadingOffset::H1, id_prefix); let p = Footnotes::new(p); let p = TableWrapper::new(p.map(|(ev, _)| ev)); let p = CodeBlocks::new(p, codes, edition, playground); @@ -1325,7 +1339,8 @@ crate fn markdown_links(md: &str) -> Vec { // There's no need to thread an IdMap through to here because // the IDs generated aren't going to be emitted anywhere. let mut ids = IdMap::new(); - let iter = Footnotes::new(HeadingLinks::new(p, None, &mut ids, HeadingOffset::H1)); + let iter = + Footnotes::new(HeadingLinks::new(p, None, &mut ids, HeadingOffset::H1, IdPrefix::none())); for ev in iter { if let Event::Start(Tag::Link(kind, dest, _)) = ev.0 { @@ -1430,68 +1445,70 @@ crate fn rust_code_blocks(md: &str, extra_info: &ExtraInfo<'_>) -> Vec(IdPrefixInner<'a>); + +impl<'a> IdPrefix<'a> { + crate fn without_parent() -> Self { + Self(IdPrefixInner::WithoutParent) + } + + pub fn none() -> Self { + Self(IdPrefixInner::None) + } + + crate fn some(item: &'a clean::Item) -> Self { + Self(IdPrefixInner::Some(item)) + } +} + +/// Depending if we're generating a link for a markdown file or on a Rust type, we don't want the +/// same behaviour: +/// +/// In Rust, to reduce the number of "conflict" between HTML IDs, we add a prefix to them based on +/// their parent. However, it is useless if rustdoc is used to render a markdown file. +#[derive(Debug, Clone, Copy)] +crate enum IdPrefixInner<'a> { + /// Contains the "parent" of the ID. It's used to generate the prefix. + Some(&'a clean::Item), + /// This is used in case markdown is inserted using `--markdown-before-content` or using + /// `--markdown-after-content`. + WithoutParent, + None, +} + #[derive(Clone, Default, Debug)] pub struct IdMap { map: FxHashMap, } -fn init_id_map() -> FxHashMap { - let mut map = FxHashMap::default(); - // This is the list of IDs used in Javascript. - map.insert("help".to_owned(), 1); - // This is the list of IDs used in HTML generated in Rust (including the ones - // used in tera template files). - map.insert("mainThemeStyle".to_owned(), 1); - map.insert("themeStyle".to_owned(), 1); - map.insert("theme-picker".to_owned(), 1); - map.insert("theme-choices".to_owned(), 1); - map.insert("settings-menu".to_owned(), 1); - map.insert("help-button".to_owned(), 1); - map.insert("main-content".to_owned(), 1); - map.insert("search".to_owned(), 1); - map.insert("crate-search".to_owned(), 1); - map.insert("render-detail".to_owned(), 1); - map.insert("toggle-all-docs".to_owned(), 1); - map.insert("all-types".to_owned(), 1); - map.insert("default-settings".to_owned(), 1); - map.insert("rustdoc-vars".to_owned(), 1); - map.insert("sidebar-vars".to_owned(), 1); - map.insert("copy-path".to_owned(), 1); - map.insert("TOC".to_owned(), 1); - // This is the list of IDs used by rustdoc sections (but still generated by - // rustdoc). - map.insert("fields".to_owned(), 1); - map.insert("variants".to_owned(), 1); - map.insert("implementors-list".to_owned(), 1); - map.insert("synthetic-implementors-list".to_owned(), 1); - map.insert("foreign-impls".to_owned(), 1); - map.insert("implementations".to_owned(), 1); - map.insert("trait-implementations".to_owned(), 1); - map.insert("synthetic-implementations".to_owned(), 1); - map.insert("blanket-implementations".to_owned(), 1); - map.insert("associated-types".to_owned(), 1); - map.insert("associated-const".to_owned(), 1); - map.insert("required-methods".to_owned(), 1); - map.insert("provided-methods".to_owned(), 1); - map.insert("implementors".to_owned(), 1); - map.insert("synthetic-implementors".to_owned(), 1); - map.insert("trait-implementations-list".to_owned(), 1); - map.insert("synthetic-implementations-list".to_owned(), 1); - map.insert("blanket-implementations-list".to_owned(), 1); - map.insert("deref-methods".to_owned(), 1); - map -} - impl IdMap { pub fn new() -> Self { - IdMap { map: init_id_map() } + IdMap { map: FxHashMap::default() } } - crate fn derive + ToString>(&mut self, candidate: S) -> String { - let id = match self.map.get_mut(candidate.as_ref()) { - None => candidate.to_string(), + crate fn derive + ToString>( + &mut self, + candidate: S, + id_prefix: IdPrefix<'_>, + ) -> String { + let candidate = match id_prefix.0 { + IdPrefixInner::None => candidate.to_string(), + IdPrefixInner::WithoutParent => format!("top.{}", candidate.as_ref()), + IdPrefixInner::Some(item) => { + let prefix = if let Some(name) = item.name { + format!("{}.{}", item.type_(), name) + } else { + format!("{}.unknown", item.type_()) + }; + format!("{}.{}", prefix, candidate.as_ref()) + } + }; + let id = match self.map.get_mut(&candidate) { + None => candidate, Some(a) => { - let id = format!("{}-{}", candidate.as_ref(), *a); + let id = format!("{}-{}", candidate, *a); *a += 1; id } diff --git a/src/librustdoc/html/markdown/tests.rs b/src/librustdoc/html/markdown/tests.rs index d4af3663b624a..7269bcad08001 100644 --- a/src/librustdoc/html/markdown/tests.rs +++ b/src/librustdoc/html/markdown/tests.rs @@ -1,5 +1,7 @@ use super::{find_testable_code, plain_text_summary, short_markdown_summary}; -use super::{ErrorCodes, HeadingOffset, IdMap, Ignore, LangString, Markdown, MarkdownHtml}; +use super::{ + ErrorCodes, HeadingOffset, IdMap, IdPrefix, Ignore, LangString, Markdown, MarkdownHtml, +}; use rustc_span::edition::{Edition, DEFAULT_EDITION}; #[test] @@ -28,8 +30,8 @@ fn test_unique_id() { "examples-2", "method.into_iter-1", "foo-1", - "main-content-1", - "search-1", + "main-content", + "search", "methods", "examples-3", "method.into_iter-2", @@ -38,7 +40,8 @@ fn test_unique_id() { ]; let mut map = IdMap::new(); - let actual: Vec = input.iter().map(|s| map.derive(s.to_string())).collect(); + let actual: Vec = + input.iter().map(|s| map.derive(s.to_string(), IdPrefix::none())).collect(); assert_eq!(&actual[..], expected); } @@ -155,6 +158,7 @@ fn test_header() { edition: DEFAULT_EDITION, playground: &None, heading_offset: HeadingOffset::H2, + id_prefix: IdPrefix::none(), } .into_string(); assert_eq!(output, expect, "original: {}", input); @@ -197,6 +201,7 @@ fn test_header_ids_multiple_blocks() { edition: DEFAULT_EDITION, playground: &None, heading_offset: HeadingOffset::H2, + id_prefix: IdPrefix::none(), } .into_string(); assert_eq!(output, expect, "original: {}", input); @@ -220,7 +225,7 @@ fn test_header_ids_multiple_blocks() { t( &mut map, "# Search", - "

Search

", + "

Search

", ); t( &mut map, @@ -307,8 +312,15 @@ fn test_plain_text_summary() { fn test_markdown_html_escape() { fn t(input: &str, expect: &str) { let mut idmap = IdMap::new(); - let output = - MarkdownHtml(input, &mut idmap, ErrorCodes::Yes, DEFAULT_EDITION, &None).into_string(); + let output = MarkdownHtml( + input, + &mut idmap, + ErrorCodes::Yes, + DEFAULT_EDITION, + &None, + IdPrefix::none(), + ) + .into_string(); assert_eq!(output, expect, "original: {}", input); } diff --git a/src/librustdoc/html/render/context.rs b/src/librustdoc/html/render/context.rs index c4d326e7711a3..1644f9a3a6d39 100644 --- a/src/librustdoc/html/render/context.rs +++ b/src/librustdoc/html/render/context.rs @@ -31,7 +31,7 @@ use crate::formats::item_type::ItemType; use crate::formats::FormatRenderer; use crate::html::escape::Escape; use crate::html::format::Buffer; -use crate::html::markdown::{self, plain_text_summary, ErrorCodes, IdMap}; +use crate::html::markdown::{self, plain_text_summary, ErrorCodes, IdMap, IdPrefix}; use crate::html::{layout, sources}; use crate::scrape_examples::AllCallLocations; use crate::try_err; @@ -168,7 +168,7 @@ impl<'tcx> Context<'tcx> { pub(super) fn derive_id(&self, id: String) -> String { let mut map = self.id_map.borrow_mut(); - map.derive(id) + map.derive(id, IdPrefix::none()) } /// String representation of how to get back to the root path of the 'doc/' diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index cd89164a25470..6814832a1debc 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -74,7 +74,7 @@ use crate::html::format::{ print_generic_bounds, print_where_clause, Buffer, HrefError, PrintWithSpace, }; use crate::html::highlight; -use crate::html::markdown::{HeadingOffset, Markdown, MarkdownHtml, MarkdownSummaryLine}; +use crate::html::markdown::{HeadingOffset, IdPrefix, Markdown, MarkdownHtml, MarkdownSummaryLine}; use crate::html::sources; use crate::scrape_examples::{CallData, CallLocation}; use crate::try_none; @@ -493,6 +493,7 @@ fn render_markdown( md_text: &str, links: Vec, heading_offset: HeadingOffset, + item: &clean::Item, ) { let mut ids = cx.id_map.borrow_mut(); write!( @@ -506,6 +507,7 @@ fn render_markdown( edition: cx.shared.edition(), playground: &cx.shared.playground, heading_offset, + id_prefix: IdPrefix::some(item), } .into_string() ) @@ -576,10 +578,10 @@ fn document_full_inner( Expand description\ ", ); - render_markdown(w, cx, &s, item.links(cx), heading_offset); + render_markdown(w, cx, &s, item.links(cx), heading_offset, item); w.write_str(""); } else { - render_markdown(w, cx, &s, item.links(cx), heading_offset); + render_markdown(w, cx, &s, item.links(cx), heading_offset, item); } } @@ -663,6 +665,7 @@ fn short_item_info( error_codes, cx.shared.edition(), &cx.shared.playground, + IdPrefix::some(item), ); message.push_str(&format!(": {}", html.into_string())); } @@ -1624,7 +1627,8 @@ fn render_impl( error_codes: cx.shared.codes, edition: cx.shared.edition(), playground: &cx.shared.playground, - heading_offset: HeadingOffset::H4 + heading_offset: HeadingOffset::H4, + id_prefix: IdPrefix::some(&i.impl_item), } .into_string() ); @@ -2570,7 +2574,7 @@ fn render_call_locations(w: &mut Buffer, cx: &Context<'_>, item: &clean::Item) { }; // Generate a unique ID so users can link to this section for a given method - let id = cx.id_map.borrow_mut().derive("scraped-examples"); + let id = cx.id_map.borrow_mut().derive("scraped-examples", IdPrefix::some(item)); write!( w, "
\ diff --git a/src/librustdoc/markdown.rs b/src/librustdoc/markdown.rs index 906b8f8a24570..ae3d5fe1be9eb 100644 --- a/src/librustdoc/markdown.rs +++ b/src/librustdoc/markdown.rs @@ -11,7 +11,7 @@ use crate::doctest::{Collector, GlobalTestOptions}; use crate::html::escape::Escape; use crate::html::markdown; use crate::html::markdown::{ - find_testable_code, ErrorCodes, HeadingOffset, IdMap, Markdown, MarkdownWithToc, + find_testable_code, ErrorCodes, HeadingOffset, IdMap, IdPrefix, Markdown, MarkdownWithToc, }; /// Separate any lines at the start of the file that begin with `# ` or `%`. @@ -70,7 +70,8 @@ crate fn render>( let mut ids = IdMap::new(); let error_codes = ErrorCodes::from(options.unstable_features.is_nightly_build()); let text = if !options.markdown_no_toc { - MarkdownWithToc(text, &mut ids, error_codes, edition, &playground).into_string() + MarkdownWithToc(text, &mut ids, error_codes, edition, &playground, IdPrefix::none()) + .into_string() } else { Markdown { content: text, @@ -80,6 +81,7 @@ crate fn render>( edition, playground: &playground, heading_offset: HeadingOffset::H1, + id_prefix: IdPrefix::none(), } .into_string() }; diff --git a/src/test/rustdoc-gui/anchors.goml b/src/test/rustdoc-gui/anchors.goml index 2216b3f1c9728..caf3a140e353f 100644 --- a/src/test/rustdoc-gui/anchors.goml +++ b/src/test/rustdoc-gui/anchors.goml @@ -16,7 +16,7 @@ assert-css: (".fqn .in-band a:nth-of-type(2)", {"color": "rgb(173, 68, 142)"}) assert-css: (".srclink", {"color": "rgb(0, 0, 0)"}) assert-css: (".srclink", {"color": "rgb(0, 0, 0)"}) -assert-css: ("#top-doc-prose-title", {"color": "rgb(0, 0, 0)"}) +assert-css: ("#struct\.HeavilyDocumentedStruct\.top-doc-prose-title", {"color": "rgb(0, 0, 0)"}) assert-css: (".sidebar a", {"color": "rgb(0, 0, 0)"}) assert-css: (".in-band a", {"color": "rgb(0, 0, 0)"}) @@ -48,19 +48,19 @@ assert-css: (".top-doc .docblock .section-header:not(:first-child)", {"margin-le // Now let's check some other docblock headings... // First the impl block docs. -move-cursor-to: "#title-for-struct-impl-doc" +move-cursor-to: "#impl\.unknown\.title-for-struct-impl-doc" assert-css: ( - "#title-for-struct-impl-doc > a::before", + "#impl\.unknown\.title-for-struct-impl-doc > a::before", {"left": "-25px", "padding-right": "10px"}, ) -assert-css: ("#title-for-struct-impl-doc", {"margin-left": "0px"}) +assert-css: ("#impl\.unknown\.title-for-struct-impl-doc", {"margin-left": "0px"}) // Now a method docs. -move-cursor-to: "#title-for-struct-impl-item-doc" +move-cursor-to: "#method\.do_nothing\.title-for-struct-impl-item-doc" assert-css: ( - "#title-for-struct-impl-item-doc > a::before", + "#method\.do_nothing\.title-for-struct-impl-item-doc > a::before", {"left": "-25px", "padding-right": "10px"}, ) -assert-css: ("#title-for-struct-impl-item-doc", {"margin-left": "0px"}) +assert-css: ("#method\.do_nothing\.title-for-struct-impl-item-doc", {"margin-left": "0px"}) // Finally, we want to ensure that if the first element of the doc block isn't a heading, // if there is a heading afterwards, it won't have the indent. diff --git a/src/test/rustdoc-gui/headings.goml b/src/test/rustdoc-gui/headings.goml index 87c512468e05f..8797ece5f5c99 100644 --- a/src/test/rustdoc-gui/headings.goml +++ b/src/test/rustdoc-gui/headings.goml @@ -17,19 +17,37 @@ goto: file://|DOC_PATH|/test_docs/struct.HeavilyDocumentedStruct.html assert-css: ("h1.fqn", {"font-size": "24px"}) assert-css: ("h1.fqn", {"border-bottom-width": "1px"}) -assert-css: ("h2#top-doc-prose-title", {"font-size": "20.8px"}) -assert-css: ("h2#top-doc-prose-title", {"border-bottom-width": "1px"}) -assert-css: ("h3#top-doc-prose-sub-heading", {"font-size": "18.4px"}) -assert-css: ("h3#top-doc-prose-sub-heading", {"border-bottom-width": "1px"}) -assert-css: ("h4#top-doc-prose-sub-sub-heading", {"font-size": "17.6px"}) -assert-css: ("h4#top-doc-prose-sub-sub-heading", {"border-bottom-width": "1px"}) +assert-css: ( + "h2#struct\.HeavilyDocumentedStruct\.top-doc-prose-title", + {"font-size": "20.8px"}, +) +assert-css: ( + "h2#struct\.HeavilyDocumentedStruct\.top-doc-prose-title", + {"border-bottom-width": "1px"}, +) +assert-css: ( + "h3#struct\.HeavilyDocumentedStruct\.top-doc-prose-sub-heading", + {"font-size": "18.4px"}, +) +assert-css: ( + "h3#struct\.HeavilyDocumentedStruct\.top-doc-prose-sub-heading", + {"border-bottom-width": "1px"}, +) +assert-css: ( + "h4#struct\.HeavilyDocumentedStruct\.top-doc-prose-sub-sub-heading", + {"font-size": "17.6px"}, +) +assert-css: ( + "h4#struct\.HeavilyDocumentedStruct\.top-doc-prose-sub-sub-heading", + {"border-bottom-width": "1px"}, +) assert-css: ("h2#fields", {"font-size": "22.4px"}) assert-css: ("h2#fields", {"border-bottom-width": "1px"}) -assert-css: ("h3#title-for-field", {"font-size": "20.8px"}) -assert-css: ("h3#title-for-field", {"border-bottom-width": "0px"}) -assert-css: ("h4#sub-heading-for-field", {"font-size": "16px"}) -assert-css: ("h4#sub-heading-for-field", {"border-bottom-width": "0px"}) +assert-css: ("h3#structfield\.nothing\.title-for-field", {"font-size": "20.8px"}) +assert-css: ("h3#structfield\.nothing\.title-for-field", {"border-bottom-width": "0px"}) +assert-css: ("h4#structfield\.nothing\.sub-heading-for-field", {"font-size": "16px"}) +assert-css: ("h4#structfield\.nothing\.sub-heading-for-field", {"border-bottom-width": "0px"}) assert-css: ("h2#implementations", {"font-size": "22.4px"}) assert-css: ("h2#implementations", {"border-bottom-width": "1px"}) @@ -39,53 +57,104 @@ assert-css: ("#impl > h3.code-header", {"border-bottom-width": "0px"}) assert-css: ("#method\.do_nothing > h4.code-header", {"font-size": "16px"}) assert-css: ("#method\.do_nothing > h4.code-header", {"border-bottom-width": "0px"}) -assert-css: ("h4#title-for-struct-impl-doc", {"font-size": "16px"}) -assert-css: ("h4#title-for-struct-impl-doc", {"border-bottom-width": "0px"}) -assert-css: ("h5#sub-heading-for-struct-impl-doc", {"font-size": "16px"}) -assert-css: ("h5#sub-heading-for-struct-impl-doc", {"border-bottom-width": "0px"}) -assert-css: ("h6#sub-sub-heading-for-struct-impl-doc", {"font-size": "15.2px"}) -assert-css: ("h6#sub-sub-heading-for-struct-impl-doc", {"border-bottom-width": "0px"}) +assert-css: ( + "h4#impl\.unknown\.title-for-struct-impl-doc", + {"font-size": "16px"}, +) +assert-css: ( + "h4#impl\.unknown\.title-for-struct-impl-doc", + {"border-bottom-width": "0px"}, +) +assert-css: ( + "h5#impl\.unknown\.sub-heading-for-struct-impl-doc", + {"font-size": "16px"}, +) +assert-css: ( + "h5#impl\.unknown\.sub-heading-for-struct-impl-doc", + {"border-bottom-width": "0px"}, +) +assert-css: ( + "h6#impl\.unknown\.sub-sub-heading-for-struct-impl-doc", + {"font-size": "15.2px"}, +) +assert-css: ( + "h6#impl\.unknown\.sub-sub-heading-for-struct-impl-doc", + {"border-bottom-width": "0px"}, +) -assert-css: ("h5#title-for-struct-impl-item-doc", {"font-size": "16px"}) -assert-css: ("h5#title-for-struct-impl-item-doc", {"border-bottom-width": "0px"}) -assert-css: ("h6#sub-heading-for-struct-impl-item-doc", {"font-size": "15.2px"}) -assert-css: ("h6#sub-heading-for-struct-impl-item-doc", {"border-bottom-width": "0px"}) -assert-css: ("h6#sub-sub-heading-for-struct-impl-item-doc", {"font-size": "15.2px"}) +assert-css: ( + "h5#method\.do_nothing\.title-for-struct-impl-item-doc", + {"font-size": "16px"}, +) +assert-css: ( + "h5#method\.do_nothing\.title-for-struct-impl-item-doc", + {"border-bottom-width": "0px"}, +) +assert-css: ( + "h6#method\.do_nothing\.sub-heading-for-struct-impl-item-doc", + {"font-size": "15.2px"}, +) +assert-css: ( + "h6#method\.do_nothing\.sub-heading-for-struct-impl-item-doc", + {"border-bottom-width": "0px"}, +) +assert-css: ( + "h6#method\.do_nothing\.sub-sub-heading-for-struct-impl-item-doc", + {"font-size": "15.2px"}, +) goto: file://|DOC_PATH|/test_docs/enum.HeavilyDocumentedEnum.html assert-css: ("h1.fqn", {"font-size": "24px"}) assert-css: ("h1.fqn", {"border-bottom-width": "1px"}) -assert-css: ("h2#top-doc-prose-title", {"font-size": "20.8px"}) -assert-css: ("h2#top-doc-prose-title", {"border-bottom-width": "1px"}) -assert-css: ("h3#top-doc-prose-sub-heading", {"font-size": "18.4px"}) -assert-css: ("h3#top-doc-prose-sub-heading", {"border-bottom-width": "1px"}) -assert-css: ("h4#top-doc-prose-sub-sub-heading", {"font-size": "17.6px"}) -assert-css: ("h4#top-doc-prose-sub-sub-heading", {"border-bottom-width": "1px"}) +assert-css: ( + "h2#enum\.HeavilyDocumentedEnum\.top-doc-prose-title", + {"font-size": "20.8px"}, +) +assert-css: ( + "h2#enum\.HeavilyDocumentedEnum\.top-doc-prose-title", + {"border-bottom-width": "1px"}, +) +assert-css: ( + "h3#enum\.HeavilyDocumentedEnum\.top-doc-prose-sub-heading", + {"font-size": "18.4px"}, +) +assert-css: ( + "h3#enum\.HeavilyDocumentedEnum\.top-doc-prose-sub-heading", + {"border-bottom-width": "1px"}, +) +assert-css: ( + "h4#enum\.HeavilyDocumentedEnum\.top-doc-prose-sub-sub-heading", + {"font-size": "17.6px"}, +) +assert-css: ( + "h4#enum\.HeavilyDocumentedEnum\.top-doc-prose-sub-sub-heading", + {"border-bottom-width": "1px"}, +) assert-css: ("h2#variants", {"font-size": "22.4px"}) assert-css: ("h2#variants", {"border-bottom-width": "1px"}) -assert-css: ("h4#none-prose-title", {"font-size": "16px"}) -assert-css: ("h4#none-prose-title", {"border-bottom-width": "0px"}) -assert-css: ("h5#none-prose-sub-heading", {"font-size": "16px"}) -assert-css: ("h5#none-prose-sub-heading", {"border-bottom-width": "0px"}) +assert-css: ("h4#variant\.None\.none-prose-title", {"font-size": "16px"}) +assert-css: ("h4#variant\.None\.none-prose-title", {"border-bottom-width": "0px"}) +assert-css: ("h5#variant\.None\.none-prose-sub-heading", {"font-size": "16px"}) +assert-css: ("h5#variant\.None\.none-prose-sub-heading", {"border-bottom-width": "0px"}) -assert-css: ("h4#wrapped-prose-title", {"font-size": "16px"}) -assert-css: ("h4#wrapped-prose-title", {"border-bottom-width": "0px"}) -assert-css: ("h5#wrapped-prose-sub-heading", {"font-size": "16px"}) -assert-css: ("h5#wrapped-prose-sub-heading", {"border-bottom-width": "0px"}) +assert-css: ("h4#variant\.Wrapped\.wrapped-prose-title", {"font-size": "16px"}) +assert-css: ("h4#variant\.Wrapped\.wrapped-prose-title", {"border-bottom-width": "0px"}) +assert-css: ("h5#variant\.Wrapped\.wrapped-prose-sub-heading", {"font-size": "16px"}) +assert-css: ("h5#variant\.Wrapped\.wrapped-prose-sub-heading", {"border-bottom-width": "0px"}) -assert-css: ("h5#wrapped0-prose-title", {"font-size": "16px"}) -assert-css: ("h5#wrapped0-prose-title", {"border-bottom-width": "0px"}) -assert-css: ("h6#wrapped0-prose-sub-heading", {"font-size": "15.2px"}) -assert-css: ("h6#wrapped0-prose-sub-heading", {"border-bottom-width": "0px"}) +assert-css: ("h5#structfield\.0\.wrapped0-prose-title", {"font-size": "16px"}) +assert-css: ("h5#structfield\.0\.wrapped0-prose-title", {"border-bottom-width": "0px"}) +assert-css: ("h6#structfield\.0\.wrapped0-prose-sub-heading", {"font-size": "15.2px"}) +assert-css: ("h6#structfield\.0\.wrapped0-prose-sub-heading", {"border-bottom-width": "0px"}) -assert-css: ("h5#structy-prose-title", {"font-size": "16px"}) -assert-css: ("h5#structy-prose-title", {"border-bottom-width": "0px"}) -assert-css: ("h6#structy-prose-sub-heading", {"font-size": "15.2px"}) -assert-css: ("h6#structy-prose-sub-heading", {"border-bottom-width": "0px"}) +assert-css: ("h5#structfield\.alpha\.structy-prose-title", {"font-size": "16px"}) +assert-css: ("h5#structfield\.alpha\.structy-prose-title", {"border-bottom-width": "0px"}) +assert-css: ("h6#structfield\.alpha\.structy-prose-sub-heading", {"font-size": "15.2px"}) +assert-css: ("h6#structfield\.alpha\.structy-prose-sub-heading", {"border-bottom-width": "0px"}) assert-css: ("h2#implementations", {"font-size": "22.4px"}) assert-css: ("h2#implementations", {"border-bottom-width": "1px"}) @@ -95,19 +164,37 @@ assert-css: ("#impl > h3.code-header", {"border-bottom-width": "0px"}) assert-css: ("#method\.do_nothing > h4.code-header", {"font-size": "16px"}) assert-css: ("#method\.do_nothing > h4.code-header", {"border-bottom-width": "0px"}) -assert-css: ("h4#title-for-enum-impl-doc", {"font-size": "16px"}) -assert-css: ("h4#title-for-enum-impl-doc", {"border-bottom-width": "0px"}) -assert-css: ("h5#sub-heading-for-enum-impl-doc", {"font-size": "16px"}) -assert-css: ("h5#sub-heading-for-enum-impl-doc", {"border-bottom-width": "0px"}) -assert-css: ("h6#sub-sub-heading-for-enum-impl-doc", {"font-size": "15.2px"}) -assert-css: ("h6#sub-sub-heading-for-enum-impl-doc", {"border-bottom-width": "0px"}) +assert-css: ("h4#impl\.unknown\.title-for-enum-impl-doc", {"font-size": "16px"}) +assert-css: ("h4#impl\.unknown\.title-for-enum-impl-doc", {"border-bottom-width": "0px"}) +assert-css: ("h5#impl\.unknown\.sub-heading-for-enum-impl-doc", {"font-size": "16px"}) +assert-css: ("h5#impl\.unknown\.sub-heading-for-enum-impl-doc", {"border-bottom-width": "0px"}) +assert-css: ("h6#impl\.unknown\.sub-sub-heading-for-enum-impl-doc", {"font-size": "15.2px"}) +assert-css: ("h6#impl\.unknown\.sub-sub-heading-for-enum-impl-doc", {"border-bottom-width": "0px"}) -assert-css: ("h5#title-for-enum-impl-item-doc", {"font-size": "16px"}) -assert-css: ("h5#title-for-enum-impl-item-doc", {"border-bottom-width": "0px"}) -assert-css: ("h6#sub-heading-for-enum-impl-item-doc", {"font-size": "15.2px"}) -assert-css: ("h6#sub-heading-for-enum-impl-item-doc", {"border-bottom-width": "0px"}) -assert-css: ("h6#sub-sub-heading-for-enum-impl-item-doc", {"font-size": "15.2px"}) -assert-css: ("h6#sub-sub-heading-for-enum-impl-item-doc", {"border-bottom-width": "0px"}) +assert-css: ( + "h5#method\.do_nothing\.title-for-enum-impl-item-doc", + {"font-size": "16px"}, +) +assert-css: ( + "h5#method\.do_nothing\.title-for-enum-impl-item-doc", + {"border-bottom-width": "0px"}, +) +assert-css: ( + "h6#method\.do_nothing\.sub-heading-for-enum-impl-item-doc", + {"font-size": "15.2px"}, +) +assert-css: ( + "h6#method\.do_nothing\.sub-heading-for-enum-impl-item-doc", + {"border-bottom-width": "0px"}, +) +assert-css: ( + "h6#method\.do_nothing\.sub-sub-heading-for-enum-impl-item-doc", + {"font-size": "15.2px"}, +) +assert-css: ( + "h6#method\.do_nothing\.sub-sub-heading-for-enum-impl-item-doc", + {"border-bottom-width": "0px"}, +) assert-text: (".sidebar .others h3", "Modules") assert-css: (".sidebar .others h3", {"border-bottom-width": "1px"}, ALL) @@ -117,40 +204,88 @@ goto: file://|DOC_PATH|/test_docs/union.HeavilyDocumentedUnion.html assert-css: ("h1.fqn", {"font-size": "24px"}) assert-css: ("h1.fqn", {"border-bottom-width": "1px"}) -assert-css: ("h2#top-doc-prose-title", {"font-size": "20.8px"}) -assert-css: ("h2#top-doc-prose-title", {"border-bottom-width": "1px"}) -assert-css: ("h3#top-doc-prose-sub-heading", {"font-size": "18.4px"}) -assert-css: ("h3#top-doc-prose-sub-heading", {"border-bottom-width": "1px"}) +assert-css: ( + "h2#union\.HeavilyDocumentedUnion\.top-doc-prose-title", + {"font-size": "20.8px"}, +) +assert-css: ( + "h2#union\.HeavilyDocumentedUnion\.top-doc-prose-title", + {"border-bottom-width": "1px"}, +) +assert-css: ( + "h3#union\.HeavilyDocumentedUnion\.top-doc-prose-sub-heading", + {"font-size": "18.4px"}, +) +assert-css: ( + "h3#union\.HeavilyDocumentedUnion\.top-doc-prose-sub-heading", + {"border-bottom-width": "1px"}, +) assert-css: ("h2#fields", {"font-size": "22.4px"}) assert-css: ("h2#fields", {"border-bottom-width": "1px"}) -assert-css: ("h3#title-for-union-variant", {"font-size": "20.8px"}) -assert-css: ("h3#title-for-union-variant", {"border-bottom-width": "0px"}) -assert-css: ("h4#sub-heading-for-union-variant", {"font-size": "16px"}) -assert-css: ("h4#sub-heading-for-union-variant", {"border-bottom-width": "0px"}) +assert-css: ( + "h3#structfield\.nothing\.title-for-union-variant", + {"font-size": "20.8px"}, +) +assert-css: ( + "h3#structfield\.nothing\.title-for-union-variant", + {"border-bottom-width": "0px"}, +) +assert-css: ( + "h4#structfield\.nothing\.sub-heading-for-union-variant", + {"font-size": "16px"}, +) +assert-css: ( + "h4#structfield\.nothing\.sub-heading-for-union-variant", + {"border-bottom-width": "0px"}, +) assert-css: ("h2#implementations", {"font-size": "22.4px"}) assert-css: ("h2#implementations", {"border-bottom-width": "1px"}) assert-css: ("#impl > h3.code-header", {"font-size": "17.6px"}) assert-css: ("#impl > h3.code-header", {"border-bottom-width": "0px"}) -assert-css: ("h4#title-for-union-impl-doc", {"font-size": "16px"}) -assert-css: ("h4#title-for-union-impl-doc", {"border-bottom-width": "0px"}) -assert-css: ("h5#sub-heading-for-union-impl-doc", {"font-size": "16px"}) -assert-css: ("h5#sub-heading-for-union-impl-doc", {"border-bottom-width": "0px"}) +assert-css: ("h4#impl\.unknown\.title-for-union-impl-doc", {"font-size": "16px"}) +assert-css: ("h4#impl\.unknown\.title-for-union-impl-doc", {"border-bottom-width": "0px"}) +assert-css: ("h5#impl\.unknown\.sub-heading-for-union-impl-doc", {"font-size": "16px"}) +assert-css: ("h5#impl\.unknown\.sub-heading-for-union-impl-doc", {"border-bottom-width": "0px"}) -assert-css: ("h5#title-for-union-impl-item-doc", {"font-size": "16px"}) -assert-css: ("h5#title-for-union-impl-item-doc", {"border-bottom-width": "0px"}) -assert-css: ("h6#sub-heading-for-union-impl-item-doc", {"font-size": "15.2px"}) -assert-css: ("h6#sub-heading-for-union-impl-item-doc", {"border-bottom-width": "0px"}) +assert-css: ( + "h5#method\.do_nothing\.title-for-union-impl-item-doc", + {"font-size": "16px"}, +) +assert-css: ( + "h5#method\.do_nothing\.title-for-union-impl-item-doc", + {"border-bottom-width": "0px"}, +) +assert-css: ( + "h6#method\.do_nothing\.sub-heading-for-union-impl-item-doc", + {"font-size": "15.2px"}, +) +assert-css: ( + "h6#method\.do_nothing\.sub-heading-for-union-impl-item-doc", + {"border-bottom-width": "0px"}, +) goto: file://|DOC_PATH|/test_docs/macro.heavily_documented_macro.html assert-css: ("h1.fqn", {"font-size": "24px"}) assert-css: ("h1.fqn", {"border-bottom-width": "1px"}) -assert-css: ("h2#top-doc-prose-title", {"font-size": "20.8px"}) -assert-css: ("h2#top-doc-prose-title", {"border-bottom-width": "1px"}) -assert-css: ("h3#top-doc-prose-sub-heading", {"font-size": "18.4px"}) -assert-css: ("h3#top-doc-prose-sub-heading", {"border-bottom-width": "1px"}) +assert-css: ( + "h2#macro\.heavily_documented_macro\.top-doc-prose-title", + {"font-size": "20.8px"}, +) +assert-css: ( + "h2#macro\.heavily_documented_macro\.top-doc-prose-title", + {"border-bottom-width": "1px"}, +) +assert-css: ( + "h3#macro\.heavily_documented_macro\.top-doc-prose-sub-heading", + {"font-size": "18.4px"}, +) +assert-css: ( + "h3#macro\.heavily_documented_macro\.top-doc-prose-sub-heading", + {"border-bottom-width": "1px"}, +) diff --git a/src/test/rustdoc/enum-headings.rs b/src/test/rustdoc/enum-headings.rs index 2e5c34391c4af..95e23b83a188b 100644 --- a/src/test/rustdoc/enum-headings.rs +++ b/src/test/rustdoc/enum-headings.rs @@ -3,23 +3,23 @@ /// A token! /// # First /// Some following text... -// @has - '//h2[@id="first"]' "First" +// @has - '//h2[@id="enum.Token.first"]' "First" pub enum Token { /// A declaration! /// # Variant-First /// Some following text... - // @has - '//h4[@id="variant-first"]' "Variant-First" + // @has - '//h4[@id="variant.Declaration.variant-first"]' "Variant-First" Declaration { /// A version! /// # Variant-Field-First /// Some following text... - // @has - '//h5[@id="variant-field-first"]' "Variant-Field-First" + // @has - '//h5[@id="structfield.version.variant-field-first"]' "Variant-Field-First" version: String, }, /// A Zoople! /// # Variant-First Zoople( - // @has - '//h5[@id="variant-tuple-field-first"]' "Variant-Tuple-Field-First" + // @has - '//h5[@id="structfield.0.variant-tuple-field-first"]' "Variant-Tuple-Field-First" /// Zoople's first variant! /// # Variant-Tuple-Field-First /// Some following text... @@ -28,13 +28,13 @@ pub enum Token { /// Unfinished business! /// # Non-Exhaustive-First /// Some following text... - // @has - '//h4[@id="non-exhaustive-first"]' "Non-Exhaustive-First" + // @has - '//h4[@id="variant.Unfinished.non-exhaustive-first"]' "Non-Exhaustive-First" #[non_exhaustive] Unfinished { /// This is x. /// # X-First /// Some following text... - // @has - '//h5[@id="x-first"]' "X-First" + // @has - '//h5[@id="structfield.x.x-first"]' "X-First" x: usize, }, } diff --git a/src/test/rustdoc/issue-29449.rs b/src/test/rustdoc/issue-29449.rs index 0d829cf6fcffe..9fc65ad6aa160 100644 --- a/src/test/rustdoc/issue-29449.rs +++ b/src/test/rustdoc/issue-29449.rs @@ -2,18 +2,20 @@ pub struct Foo; impl Foo { - // @has - '//*[@id="examples"]//a' 'Examples' - // @has - '//*[@id="panics"]//a' 'Panics' + // @has - '//*[@id="method.bar.examples"]//a' 'Examples' + // @has - '//*[@id="method.bar.panics"]//a' 'Panics' /// # Examples /// # Panics pub fn bar() {} - // @has - '//*[@id="examples-1"]//a' 'Examples' + // @has - '//*[@id="method.bar_1.examples"]//a' 'Examples' + // @has - '//*[@id="method.bar_1.examples-1"]//a' 'Examples' + /// # Examples /// # Examples pub fn bar_1() {} - // @has - '//*[@id="examples-2"]//a' 'Examples' - // @has - '//*[@id="panics-1"]//a' 'Panics' + // @has - '//*[@id="method.bar_2.examples"]//a' 'Examples' + // @has - '//*[@id="method.bar_2.panics"]//a' 'Panics' /// # Examples /// # Panics pub fn bar_2() {} diff --git a/src/test/rustdoc/remove-url-from-headings.rs b/src/test/rustdoc/remove-url-from-headings.rs index e2b232a6efb93..0c7706f200682 100644 --- a/src/test/rustdoc/remove-url-from-headings.rs +++ b/src/test/rustdoc/remove-url-from-headings.rs @@ -2,8 +2,8 @@ // @has foo/fn.foo.html // @!has - '//a[@href="http://a.a"]' -// @has - '//a[@href="#implementing-stuff-somewhere"]' 'Implementing stuff somewhere' -// @has - '//a[@href="#another-one-urg"]' 'Another one urg' +// @has - '//a[@href="#fn.foo.implementing-stuff-somewhere"]' 'Implementing stuff somewhere' +// @has - '//a[@href="#fn.foo.another-one-urg"]' 'Another one urg' /// fooo /// diff --git a/src/test/rustdoc/short-docblock.rs b/src/test/rustdoc/short-docblock.rs index 17c44eab091a6..1bf66bedf9318 100644 --- a/src/test/rustdoc/short-docblock.rs +++ b/src/test/rustdoc/short-docblock.rs @@ -2,7 +2,7 @@ // @has foo/index.html '//*[@class="item-right docblock-short"]/p' 'fooo' // @!has foo/index.html '//*[@class="item-right docblock-short"]/p/h1' 'fooo' -// @has foo/fn.foo.html '//h2[@id="fooo"]/a[@href="#fooo"]' 'fooo' +// @has foo/fn.foo.html '//h2[@id="fn.foo.fooo"]/a[@href="#fn.foo.fooo"]' 'fooo' /// # fooo /// @@ -11,7 +11,7 @@ pub fn foo() {} // @has foo/index.html '//*[@class="item-right docblock-short"]/p' 'mooood' // @!has foo/index.html '//*[@class="item-right docblock-short"]/p/h2' 'mooood' -// @has foo/foo/index.html '//h3[@id="mooood"]/a[@href="#mooood"]' 'mooood' +// @has foo/foo/index.html '//h3[@id="mod.foo.mooood"]/a[@href="#mod.foo.mooood"]' 'mooood' /// ## mooood /// diff --git a/src/tools/error_index_generator/main.rs b/src/tools/error_index_generator/main.rs index 39498c99e64d6..b4c692548876b 100644 --- a/src/tools/error_index_generator/main.rs +++ b/src/tools/error_index_generator/main.rs @@ -14,7 +14,7 @@ use std::path::PathBuf; use rustc_span::edition::DEFAULT_EDITION; -use rustdoc::html::markdown::{ErrorCodes, HeadingOffset, IdMap, Markdown, Playground}; +use rustdoc::html::markdown::{ErrorCodes, HeadingOffset, IdMap, IdPrefix, Markdown, Playground}; pub struct ErrorMetadata { pub description: Option, @@ -127,6 +127,7 @@ impl Formatter for HTMLFormatter { edition: DEFAULT_EDITION, playground: &Some(playground), heading_offset: HeadingOffset::H1, + id_prefix: IdPrefix::none(), } .into_string() )?