Skip to content

Commit

Permalink
Copy local resources into doc generated folder
Browse files Browse the repository at this point in the history
  • Loading branch information
GuillaumeGomez committed Feb 3, 2023
1 parent a9985cf commit d23042d
Show file tree
Hide file tree
Showing 5 changed files with 179 additions and 1 deletion.
30 changes: 30 additions & 0 deletions src/librustdoc/formats/cache.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use std::mem;
use std::path::PathBuf;

use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, DefIdSet};
Expand Down Expand Up @@ -121,6 +122,8 @@ pub(crate) struct Cache {
pub(crate) intra_doc_links: FxHashMap<ItemId, Vec<clean::ItemLink>>,
/// Cfg that have been hidden via #![doc(cfg_hide(...))]
pub(crate) hidden_cfg: FxHashSet<clean::cfg::Cfg>,
/// Local resources that are copied into the rustdoc output directory.
pub(crate) local_resources: LocalResources,
}

/// This struct is used to wrap the `cache` and `tcx` in order to run `DocFolder`.
Expand Down Expand Up @@ -516,6 +519,33 @@ impl<'a, 'tcx> DocFolder for CacheBuilder<'a, 'tcx> {
}
}

#[derive(Default)]
pub(crate) struct LocalResources {
/// The key is the original location of the resource. The value is the new name.
pub(crate) resources_to_copy: FxHashMap<PathBuf, String>,
/// This will be used when generating the HTML, once everything is generated, we copy these
/// files into the static folder.
///
/// The key is the depth and the value is hashmap where the key is the path of the resource in
/// the markdown and the value is the new path to the resources in the rustdoc output folder.
pub(crate) resources_correspondance: FxHashMap<usize, FxHashMap<String, String>>,
pub(crate) total_entries: usize,
}

impl LocalResources {
pub(crate) fn add_entry_at_depth(&mut self, depth: usize, key: String, value: String) {
if self
.resources_correspondance
.entry(depth)
.or_insert_with(FxHashMap::default)
.insert(key, value)
.is_none()
{
self.total_entries += 1;
}
}
}

pub(crate) struct OrphanImplItem {
pub(crate) parent: DefId,
pub(crate) item: clean::Item,
Expand Down
2 changes: 2 additions & 0 deletions src/librustdoc/html/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,5 @@ mod url_parts_builder;

#[cfg(test)]
mod tests;

pub(crate) const LOCAL_RESOURCES_FOLDER_NAME: &str = "local_resources";
6 changes: 5 additions & 1 deletion src/librustdoc/html/render/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,10 @@ pub(crate) struct SharedContext<'tcx> {
pub(crate) call_locations: AllCallLocations,
}

pub(crate) fn root_path(depth: usize) -> String {
"../".repeat(depth)
}

impl SharedContext<'_> {
pub(crate) fn ensure_dir(&self, dst: &Path) -> Result<(), Error> {
let mut dirs = self.created_dirs.borrow_mut();
Expand Down Expand Up @@ -165,7 +169,7 @@ impl<'tcx> Context<'tcx> {
/// String representation of how to get back to the root path of the 'doc/'
/// folder in terms of a relative URL.
pub(super) fn root_path(&self) -> String {
"../".repeat(self.current.len())
root_path(self.current.len())
}

fn render_item(&mut self, it: &clean::Item, is_module: bool) -> String {
Expand Down
137 changes: 137 additions & 0 deletions src/librustdoc/passes/collect_local_resources.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
//! This file will go through all doc comments and retrieve local resources to then store them
//! in the rustdoc output directory.
use pulldown_cmark::{Event, Parser, Tag};

use rustc_span::def_id::LOCAL_CRATE;
use rustc_span::FileName;

use crate::clean::{Crate, Item};
use crate::core::DocContext;
use crate::html::markdown::main_body_opts;
use crate::html::render::root_path;
use crate::html::LOCAL_RESOURCES_FOLDER_NAME;
use crate::passes::Pass;
use crate::visit::DocVisitor;

use std::path::{Path, PathBuf};

pub(crate) const COLLECT_LOCAL_RESOURCES: Pass = Pass {
name: "collect-local-resources",
run: collect_local_resources,
description: "resolves intra-doc links",
};

fn span_file_path(cx: &DocContext<'_>, item: &Item) -> Option<PathBuf> {
item.span(cx.tcx).and_then(|span| match span.filename(cx.sess()) {
FileName::Real(ref path) => Some(path.local_path_if_available().into()),
_ => None,
})
}

struct ResourcesCollector<'a, 'tcx> {
cx: &'a mut DocContext<'tcx>,
/// The depth is used to know how many "../" needs to be generated to get the original file
/// path.
depth: usize,
}

fn collect_local_resources(krate: Crate, cx: &mut DocContext<'_>) -> Crate {
let mut collector = ResourcesCollector { cx, depth: 1 };
collector.visit_crate(&krate);
krate
}

impl<'a, 'tcx> ResourcesCollector<'a, 'tcx> {
pub fn handle_event(
&mut self,
event: Event<'_>,
current_path: &mut Option<PathBuf>,
item: &Item,
) {
if let Event::Start(Tag::Image(_, ref ori_path, _)) = event &&
!ori_path.starts_with("http://") &&
!ori_path.starts_with("https://")
{
let ori_path = ori_path.to_string();
if self.cx.cache.local_resources.resources_correspondance
.get(&self.depth)
.and_then(|entry| entry.get(&ori_path))
.is_some()
{
// We already have this entry so nothing to be done!
return;
}
if current_path.is_none() {
*current_path = span_file_path(self.cx, item);
}
let Some(current_path) = current_path else { return };

let path = match current_path.parent()
.unwrap_or_else(|| Path::new("."))
.join(&ori_path)
.canonicalize()
{
Ok(p) => p,
Err(_) => {
self.cx.tcx.sess.struct_span_err(
item.attr_span(self.cx.tcx),
&format!("`{ori_path}`: No such file"),
).emit();
return;
}
};

if !path.is_file() {
self.cx.tcx.sess.struct_span_err(
item.attr_span(self.cx.tcx),
&format!("`{ori_path}`: No such file (expanded into `{}`)", path.display()),
).emit();
return;
}

// We now enter the file into the `resources_to_copy` in case it's not already in
// and then generate a path the file that we store into `resources_correspondance`
// with the `add_entry_at_depth` method.
let current_nb = self.cx.cache.local_resources.resources_to_copy.len();
let file_name = self.cx.cache.local_resources.resources_to_copy
.entry(path.clone())
.or_insert_with(|| {
let extension = path.extension();
let (extension, dot) = match extension.and_then(|e| e.to_str()) {
Some(e) => (e, "."),
None => ("", ""),
};
format!(
"{current_nb}{}{dot}{extension}",
self.cx.render_options.resource_suffix,
)
});
let file = format!(
"{}{LOCAL_RESOURCES_FOLDER_NAME}/{}/{file_name}",
root_path(self.depth),
self.cx.tcx.crate_name(LOCAL_CRATE).as_str(),
);
self.cx.cache.local_resources.add_entry_at_depth(self.depth, ori_path, file);
}
}
}

impl<'a, 'tcx> DocVisitor for ResourcesCollector<'a, 'tcx> {
fn visit_item(&mut self, item: &Item) {
if let Some(md) = item.collapsed_doc_value() {
let mut current_path = None;
for event in Parser::new_ext(&md, main_body_opts()).into_iter() {
self.handle_event(event, &mut current_path, item);
}
}

if item.is_mod() && !item.is_crate() {
self.depth += 1;
self.visit_item_recur(item);
self.depth -= 1;
} else {
self.visit_item_recur(item)
}
}
}
5 changes: 5 additions & 0 deletions src/librustdoc/passes/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ pub(crate) use self::propagate_doc_cfg::PROPAGATE_DOC_CFG;
pub(crate) mod collect_intra_doc_links;
pub(crate) use self::collect_intra_doc_links::COLLECT_INTRA_DOC_LINKS;

pub(crate) mod collect_local_resources;
pub(crate) use self::collect_local_resources::COLLECT_LOCAL_RESOURCES;

mod check_doc_test_visibility;
pub(crate) use self::check_doc_test_visibility::CHECK_DOC_TEST_VISIBILITY;

Expand Down Expand Up @@ -77,6 +80,7 @@ pub(crate) const PASSES: &[Pass] = &[
PROPAGATE_DOC_CFG,
COLLECT_INTRA_DOC_LINKS,
COLLECT_TRAIT_IMPLS,
COLLECT_LOCAL_RESOURCES,
CALCULATE_DOC_COVERAGE,
RUN_LINTS,
];
Expand All @@ -89,6 +93,7 @@ pub(crate) const DEFAULT_PASSES: &[ConditionalPass] = &[
ConditionalPass::new(STRIP_PRIVATE, WhenNotDocumentPrivate),
ConditionalPass::new(STRIP_PRIV_IMPORTS, WhenDocumentPrivate),
ConditionalPass::always(COLLECT_INTRA_DOC_LINKS),
ConditionalPass::always(COLLECT_LOCAL_RESOURCES),
ConditionalPass::always(PROPAGATE_DOC_CFG),
ConditionalPass::always(RUN_LINTS),
];
Expand Down

0 comments on commit d23042d

Please sign in to comment.