Skip to content

Commit 52d6fa3

Browse files
Add new example disambiguator for intra-doc links
1 parent 9f5e17f commit 52d6fa3

File tree

7 files changed

+226
-99
lines changed

7 files changed

+226
-99
lines changed

src/librustdoc/clean/types.rs

Lines changed: 44 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -487,21 +487,38 @@ impl Item {
487487
let Some(links) = cx.cache().intra_doc_links.get(&self.item_id) else { return vec![] };
488488
links
489489
.iter()
490-
.filter_map(|ItemLink { link: s, link_text, page_id: id, ref fragment }| {
491-
debug!(?id);
492-
if let Ok((mut href, ..)) = href(*id, cx) {
493-
debug!(?href);
494-
if let Some(ref fragment) = *fragment {
495-
fragment.render(&mut href, cx.tcx())
490+
.filter_map(|ItemLink { link: s, link_text, ref kind, ref fragment }| match kind {
491+
ItemLinkKind::Item { page_id: id } => {
492+
debug!(?id);
493+
if let Some(id) = id {
494+
if let Ok((mut href, ..)) = href(*id, cx) {
495+
debug!(?href);
496+
if let Some(ref fragment) = *fragment {
497+
fragment.render(&mut href, cx.tcx())
498+
}
499+
return Some(RenderedLink {
500+
original_text: s.clone(),
501+
new_text: link_text.clone(),
502+
tooltip: link_tooltip(*id, fragment, cx),
503+
href,
504+
});
505+
}
496506
}
507+
None
508+
}
509+
ItemLinkKind::Example { file_path } => {
510+
let example_name = file_path.split('/').next().unwrap_or(file_path);
511+
let mut href =
512+
std::iter::repeat("../").take(cx.current.len()).collect::<String>();
513+
href.push_str("src/");
514+
href.push_str(file_path);
515+
href.push_str(".html");
497516
Some(RenderedLink {
498517
original_text: s.clone(),
499518
new_text: link_text.clone(),
500-
tooltip: link_tooltip(*id, fragment, cx),
519+
tooltip: format!("Example {example_name}"),
501520
href,
502521
})
503-
} else {
504-
None
505522
}
506523
})
507524
.collect()
@@ -1110,6 +1127,23 @@ impl<I: Iterator<Item = ast::MetaItemInner>> NestedAttributesExt for I {
11101127
}
11111128
}
11121129

1130+
/// The kind of a link that has not yet been rendered.
1131+
///
1132+
/// It is used in [`ItemLink`].
1133+
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
1134+
pub(crate) enum ItemLinkKind {
1135+
Item {
1136+
/// The `DefId` of the Item whose **HTML Page** contains the item being
1137+
/// linked to. This will be different to `item_id` on item's that don't
1138+
/// have their own page, such as struct fields and enum variants.
1139+
page_id: Option<DefId>,
1140+
},
1141+
Example {
1142+
/// The path of the example file.
1143+
file_path: String,
1144+
},
1145+
}
1146+
11131147
/// A link that has not yet been rendered.
11141148
///
11151149
/// This link will be turned into a rendered link by [`Item::links`].
@@ -1122,12 +1156,9 @@ pub(crate) struct ItemLink {
11221156
/// This may not be the same as `link` if there was a disambiguator
11231157
/// in an intra-doc link (e.g. \[`fn@f`\])
11241158
pub(crate) link_text: Box<str>,
1125-
/// The `DefId` of the Item whose **HTML Page** contains the item being
1126-
/// linked to. This will be different to `item_id` on item's that don't
1127-
/// have their own page, such as struct fields and enum variants.
1128-
pub(crate) page_id: DefId,
11291159
/// The url fragment to append to the link
11301160
pub(crate) fragment: Option<UrlFragment>,
1161+
pub(crate) kind: ItemLinkKind,
11311162
}
11321163

11331164
pub struct RenderedLink {

src/librustdoc/config.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ use crate::html::markdown::IdMap;
2525
use crate::html::render::StylePath;
2626
use crate::html::static_files;
2727
use crate::passes::{self, Condition};
28-
use crate::scrape_examples::{AllCallLocations, ScrapeExamplesOptions};
28+
use crate::scrape_examples::{AllCallLocations, AllExampleFiles, ScrapeExamplesOptions};
2929
use crate::{html, opts, theme};
3030

3131
#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)]
@@ -287,6 +287,8 @@ pub(crate) struct RenderOptions {
287287
pub(crate) generate_link_to_definition: bool,
288288
/// Set of function-call locations to include as examples
289289
pub(crate) call_locations: AllCallLocations,
290+
/// Set of function-call locations to include as examples
291+
pub(crate) examples_files: AllExampleFiles,
290292
/// If `true`, Context::init will not emit shared files.
291293
pub(crate) no_emit_shared: bool,
292294
/// If `true`, HTML source code pages won't be generated.
@@ -773,7 +775,8 @@ impl Options {
773775

774776
let scrape_examples_options = ScrapeExamplesOptions::new(matches, dcx);
775777
let with_examples = matches.opt_strs("with-examples");
776-
let call_locations = crate::scrape_examples::load_call_locations(with_examples, dcx);
778+
let (call_locations, examples_files) =
779+
crate::scrape_examples::load_call_locations(with_examples, dcx);
777780

778781
let unstable_features =
779782
rustc_feature::UnstableFeatures::from_environment(crate_name.as_deref());
@@ -846,6 +849,7 @@ impl Options {
846849
emit,
847850
generate_link_to_definition,
848851
call_locations,
852+
examples_files,
849853
no_emit_shared: false,
850854
html_no_source,
851855
output_to_stdout,

src/librustdoc/html/render/context.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,8 @@ pub(crate) struct SharedContext<'tcx> {
132132
/// Controls whether we read / write to cci files in the doc root. Defaults read=true,
133133
/// write=true
134134
should_merge: ShouldMerge,
135+
/// Paths of generated files.
136+
pub(crate) emitted_local_sources: RefCell<FxIndexSet<String>>,
135137
}
136138

137139
impl SharedContext<'_> {
@@ -554,6 +556,7 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
554556
cache,
555557
call_locations,
556558
should_merge: options.should_merge,
559+
emitted_local_sources: Default::default(),
557560
};
558561

559562
let dst = output;

src/librustdoc/html/sources.rs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ pub(crate) fn render(cx: &mut Context<'_>, krate: &clean::Crate) -> Result<(), E
3030
let crate_name = crate_name.as_str();
3131

3232
let mut collector =
33-
SourceCollector { dst, cx, emitted_local_sources: FxHashSet::default(), crate_name };
33+
SourceCollector { dst, cx, crate_name, emitted_paths: FxHashSet::default() };
3434
collector.visit_crate(krate);
3535
Ok(())
3636
}
@@ -117,9 +117,10 @@ struct SourceCollector<'a, 'tcx> {
117117

118118
/// Root destination to place all HTML output into
119119
dst: PathBuf,
120-
emitted_local_sources: FxHashSet<PathBuf>,
121120

122121
crate_name: &'a str,
122+
123+
emitted_paths: FxHashSet<PathBuf>,
123124
}
124125

125126
impl DocVisitor<'_> for SourceCollector<'_, '_> {
@@ -182,7 +183,7 @@ impl SourceCollector<'_, '_> {
182183
}
183184
_ => return Ok(()),
184185
};
185-
if self.emitted_local_sources.contains(&*p) {
186+
if self.emitted_paths.contains(&*p) {
186187
// We've already emitted this source
187188
return Ok(());
188189
}
@@ -245,6 +246,7 @@ impl SourceCollector<'_, '_> {
245246
resource_suffix: &shared.resource_suffix,
246247
rust_logo: has_doc_flag(self.cx.tcx(), LOCAL_CRATE.as_def_id(), sym::rust_logo),
247248
};
249+
let file_path_s = file_path.display().to_string();
248250
let v = layout::render(
249251
&shared.layout,
250252
&page,
@@ -264,7 +266,8 @@ impl SourceCollector<'_, '_> {
264266
&shared.style_files,
265267
);
266268
shared.fs.write(cur, v)?;
267-
self.emitted_local_sources.insert(p);
269+
self.emitted_paths.insert(p);
270+
self.cx.shared.emitted_local_sources.borrow_mut().insert(file_path_s);
268271
Ok(())
269272
}
270273
}

src/librustdoc/json/conversions.rs

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ use rustc_span::{Pos, Symbol, sym};
1515
use rustdoc_json_types::*;
1616

1717
use super::FullItemId;
18-
use crate::clean::{self, ItemId};
18+
use crate::clean::{self, ItemId, ItemLink, ItemLinkKind};
1919
use crate::formats::FormatRenderer;
2020
use crate::formats::item_type::ItemType;
2121
use crate::json::JsonRenderer;
@@ -30,14 +30,20 @@ impl JsonRenderer<'_> {
3030
.get(&item.item_id)
3131
.into_iter()
3232
.flatten()
33-
.map(|clean::ItemLink { link, page_id, fragment, .. }| {
34-
let id = match fragment {
35-
Some(UrlFragment::Item(frag_id)) => *frag_id,
36-
// FIXME: Pass the `UserWritten` segment to JSON consumer.
37-
Some(UrlFragment::UserWritten(_)) | None => *page_id,
38-
};
39-
40-
(String::from(&**link), self.id_from_item_default(id.into()))
33+
.filter_map(|ItemLink { link, kind, fragment, .. }| {
34+
if let ItemLinkKind::Item { page_id } = kind
35+
&& let Some(page_id) = page_id
36+
{
37+
let id = match fragment {
38+
Some(UrlFragment::Item(frag_id)) => *frag_id,
39+
// FIXME: Pass the `UserWritten` segment to JSON consumer.
40+
Some(UrlFragment::UserWritten(_)) | None => *page_id,
41+
};
42+
43+
Some((String::from(&**link), self.id_from_item_default(id.into())))
44+
} else {
45+
None
46+
}
4147
})
4248
.collect();
4349
let docs = item.opt_doc_value();

0 commit comments

Comments
 (0)