Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Resolve intra-doc links in summary desc #86451

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions src/librustdoc/clean/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -523,6 +523,33 @@ impl Item {
.collect()
}

/// Find a list of all link names, without finding their href.
///
/// This is used for generating summary text, which does not include
/// the link text, but does need to know which `[]`-bracketed names
/// are actually links.
crate fn link_names(&self, cache: &Cache) -> Vec<RenderedLink> {
cache
.intra_doc_links
.get(&self.def_id)
.map_or(&[][..], |v| v.as_slice())
.iter()
.filter_map(|ItemLink { link: s, link_text, did, fragment }| {
// FIXME(83083): using fragments as a side-channel for
// primitive names is very unfortunate
if did.is_some() || fragment.is_some() {
Some(RenderedLink {
original_text: s.clone(),
new_text: link_text.clone(),
href: String::new(),
})
} else {
None
}
})
.collect()
}

crate fn is_crate(&self) -> bool {
self.is_mod() && self.def_id.as_real().map_or(false, |did| did.index == CRATE_DEF_INDEX)
}
Expand Down
7 changes: 4 additions & 3 deletions src/librustdoc/formats/cache.rs
Original file line number Diff line number Diff line change
Expand Up @@ -292,13 +292,14 @@ impl<'a, 'tcx> DocFolder for CacheBuilder<'a, 'tcx> {
// which should not be indexed. The crate-item itself is
// inserted later on when serializing the search-index.
if item.def_id.index().map_or(false, |idx| idx != CRATE_DEF_INDEX) {
let desc = item.doc_value().map_or_else(String::new, |x| {
short_markdown_summary(&x.as_str(), &item.link_names(&self.cache))
});
self.cache.search_index.push(IndexItem {
ty: item.type_(),
name: s.to_string(),
path: path.join("::"),
desc: item
.doc_value()
.map_or_else(String::new, |x| short_markdown_summary(&x.as_str())),
desc,
parent,
parent_idx: None,
search_type: get_index_search_type(&item, &self.empty_cache, self.tcx),
Expand Down
25 changes: 21 additions & 4 deletions src/librustdoc/html/markdown.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1051,7 +1051,11 @@ impl MarkdownSummaryLine<'_> {
///
/// Returns a tuple of the rendered HTML string and whether the output was shortened
/// due to the provided `length_limit`.
fn markdown_summary_with_limit(md: &str, length_limit: usize) -> (String, bool) {
fn markdown_summary_with_limit(
md: &str,
link_names: &[RenderedLink],
length_limit: usize,
) -> (String, bool) {
if md.is_empty() {
return (String::new(), false);
}
Expand All @@ -1065,7 +1069,20 @@ fn markdown_summary_with_limit(md: &str, length_limit: usize) -> (String, bool)
*text_length += text.len();
}

'outer: for event in Parser::new_ext(md, summary_opts()) {
let mut replacer = |broken_link: BrokenLink<'_>| {
if let Some(link) =
link_names.iter().find(|link| &*link.original_text == broken_link.reference)
{
Some((link.href.as_str().into(), link.new_text.as_str().into()))
} else {
None
}
};

let p = Parser::new_with_broken_link_callback(md, opts(), Some(&mut replacer));
let p = LinkReplacer::new(p, link_names);

'outer: for event in p {
match &event {
Event::Text(text) => {
for word in text.split_inclusive(char::is_whitespace) {
Expand Down Expand Up @@ -1121,8 +1138,8 @@ fn markdown_summary_with_limit(md: &str, length_limit: usize) -> (String, bool)
/// Will shorten to 59 or 60 characters, including an ellipsis (…) if it was shortened.
///
/// See [`markdown_summary_with_limit`] for details about what is rendered and what is not.
crate fn short_markdown_summary(markdown: &str) -> String {
let (mut s, was_shortened) = markdown_summary_with_limit(markdown, 59);
crate fn short_markdown_summary(markdown: &str, link_names: &[RenderedLink]) -> String {
let (mut s, was_shortened) = markdown_summary_with_limit(markdown, link_names, 59);

if was_shortened {
s.push('…');
Expand Down
4 changes: 3 additions & 1 deletion src/librustdoc/html/markdown/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,7 @@ fn test_header_ids_multiple_blocks() {
#[test]
fn test_short_markdown_summary() {
fn t(input: &str, expect: &str) {
let output = short_markdown_summary(input);
let output = short_markdown_summary(input, &[][..]);
assert_eq!(output, expect, "original: {}", input);
}

Expand All @@ -232,6 +232,7 @@ fn test_short_markdown_summary() {
t("Hard-break \nsummary", "Hard-break summary");
t("hello [Rust] :)\n\n[Rust]: https://www.rust-lang.org", "hello Rust :)");
t("hello [Rust](https://www.rust-lang.org \"Rust\") :)", "hello Rust :)");
t("dud [link]", "dud [link]");
t("code `let x = i32;` ...", "code <code>let x = i32;</code> …");
t("type `Type<'static>` ...", "type <code>Type<'static></code> …");
t("# top header", "top header");
Expand Down Expand Up @@ -259,6 +260,7 @@ fn test_plain_text_summary() {
t("Hard-break \nsummary", "Hard-break summary");
t("hello [Rust] :)\n\n[Rust]: https://www.rust-lang.org", "hello Rust :)");
t("hello [Rust](https://www.rust-lang.org \"Rust\") :)", "hello Rust :)");
t("dud [link]", "dud [link]");
t("code `let x = i32;` ...", "code `let x = i32;` …");
t("type `Type<'static>` ...", "type `Type<'static>` …");
t("# top header", "top header");
Expand Down
13 changes: 9 additions & 4 deletions src/librustdoc/html/render/cache.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,14 @@ crate fn build_index<'tcx>(krate: &clean::Crate, cache: &mut Cache, tcx: TyCtxt<
// has since been learned.
for &(did, ref item) in &cache.orphan_impl_items {
if let Some(&(ref fqp, _)) = cache.paths.get(&did) {
let desc = item
.doc_value()
.map_or_else(String::new, |s| short_markdown_summary(&s, &item.link_names(&cache)));
cache.search_index.push(IndexItem {
ty: item.type_(),
name: item.name.unwrap().to_string(),
path: fqp[..fqp.len() - 1].join("::"),
desc: item.doc_value().map_or_else(String::new, |s| short_markdown_summary(&s)),
desc,
parent: Some(did.into()),
parent_idx: None,
search_type: get_index_search_type(&item, cache, tcx),
Expand All @@ -47,6 +50,11 @@ crate fn build_index<'tcx>(krate: &clean::Crate, cache: &mut Cache, tcx: TyCtxt<
}
}

let crate_doc = krate
.module
.doc_value()
.map_or_else(String::new, |s| short_markdown_summary(&s, &krate.module.link_names(&cache)));

let Cache { ref mut search_index, ref paths, .. } = *cache;

// Aliases added through `#[doc(alias = "...")]`. Since a few items can have the same alias,
Expand Down Expand Up @@ -100,9 +108,6 @@ crate fn build_index<'tcx>(krate: &clean::Crate, cache: &mut Cache, tcx: TyCtxt<
crate_items.push(&*item);
}

let crate_doc =
krate.module.doc_value().map_or_else(String::new, |s| short_markdown_summary(&s));

struct CrateData<'a> {
doc: String,
items: Vec<&'a IndexItem>,
Expand Down
2 changes: 1 addition & 1 deletion src/test/rustdoc-js/summaries.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ const QUERY = ['summaries', 'summaries::Sidebar', 'summaries::Sidebar2'];
const EXPECTED = [
{
'others': [
{ 'path': '', 'name': 'summaries', 'desc': 'This <em>summary</em> has a link and <code>code</code>.' },
{ 'path': '', 'name': 'summaries', 'desc': 'This <em>summary</em> has a link, [<code>code</code>], and <code>Sidebar2</code> intra-doc.' },
],
},
{
Expand Down
6 changes: 4 additions & 2 deletions src/test/rustdoc-js/summaries.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
#![crate_type = "lib"]
#![crate_name = "summaries"]

//! This *summary* has a [link] and `code`.
//! This *summary* has a [link], [`code`], and [`Sidebar2`] intra-doc.
//!
//! This is the second paragraph.
//! This is the second paragraph. It should not be rendered.
//! To test that intra-doc links are resolved properly, [`code`] should render
//! the square brackets, and [`Sidebar2`] should not.
//!
//! [link]: https://example.com

Expand Down