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

rustdoc: Add support for local resources #107640

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
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
4 changes: 4 additions & 0 deletions src/librustdoc/externalfiles.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ impl ExternalHtml {
edition,
playground,
heading_offset: HeadingOffset::H2,
depth: 0,
local_resources: None,
}
.into_string()
);
Expand All @@ -63,6 +65,8 @@ impl ExternalHtml {
edition,
playground,
heading_offset: HeadingOffset::H2,
depth: 0,
local_resources: None,
}
.into_string()
);
Expand Down
28 changes: 28 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,31 @@ impl<'a, 'tcx> DocFolder for CacheBuilder<'a, 'tcx> {
}
}

#[derive(Default)]
pub 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.
Comment on lines +526 to +527
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We shouldn't put per-crate files into static.files/, which is used for toolchain-specific static files. If we go this route, there should be a separate directory for invocation-specific files.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Absolutely. The RFC mentions that there won't be a crate 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>>,
}

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

pub(crate) fn get_at_depth(&self, depth: usize, key: &str) -> Option<&String> {
self.resources_correspondance.get(&depth).and_then(|e| e.get(key))
}
}

pub(crate) struct OrphanImplItem {
pub(crate) parent: DefId,
pub(crate) item: clean::Item,
Expand Down
72 changes: 63 additions & 9 deletions src/librustdoc/html/markdown.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
//! edition: Edition::Edition2015,
//! playground: &None,
//! heading_offset: HeadingOffset::H2,
//! depth: 0,
//! local_resources: None,
//! };
//! let html = md.into_string();
//! // ... something using html
Expand All @@ -42,6 +44,7 @@ use std::str;

use crate::clean::RenderedLink;
use crate::doctest;
use crate::formats::cache::LocalResources;
use crate::html::escape::Escape;
use crate::html::format::Buffer;
use crate::html::highlight;
Expand Down Expand Up @@ -101,21 +104,60 @@ pub struct Markdown<'a> {
/// Offset at which we render headings.
/// E.g. if `heading_offset: HeadingOffset::H2`, then `# something` renders an `<h2>`.
pub heading_offset: HeadingOffset,
pub depth: usize,
pub local_resources: Option<&'a LocalResources>,
}
/// A tuple struct like `Markdown` that renders the markdown with a table of contents.
pub(crate) struct MarkdownWithToc<'a>(
pub(crate) &'a str,
pub(crate) &'a mut IdMap,
pub(crate) ErrorCodes,
pub(crate) Edition,
pub(crate) &'a Option<Playground>,
);
pub(crate) struct MarkdownWithToc<'a> {
pub(crate) content: &'a str,
pub(crate) ids: &'a mut IdMap,
pub(crate) error_codes: ErrorCodes,
pub(crate) edition: Edition,
pub(crate) playground: &'a Option<Playground>,
pub(crate) depth: usize,
pub(crate) local_resources: Option<&'a LocalResources>,
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because I'm afraid that this PR might take awhile to approve, can you please separate 62e7207 into its own PR?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure!

/// A tuple struct like `Markdown` that renders the markdown escaping HTML tags
/// and includes no paragraph tags.
pub(crate) struct MarkdownItemInfo<'a>(pub(crate) &'a str, pub(crate) &'a mut IdMap);
/// A tuple struct like `Markdown` that renders only the first paragraph.
pub(crate) struct MarkdownSummaryLine<'a>(pub &'a str, pub &'a [RenderedLink]);

struct LocalResourcesReplacer<'b, I> {
inner: I,
local_resources: Option<&'b LocalResources>,
depth: usize,
}

impl<'b, I> LocalResourcesReplacer<'b, I> {
fn new(iter: I, local_resources: Option<&'b LocalResources>, depth: usize) -> Self {
Self { inner: iter, local_resources, depth }
}
}

impl<'a, 'b, I: Iterator<Item = Event<'a>>> Iterator for LocalResourcesReplacer<'b, I> {
type Item = Event<'a>;

fn next(&mut self) -> Option<Self::Item> {
let event = self.inner.next()?;
// We only modify
if let Event::Start(Tag::Image(type_, ref path, ref title)) = event &&
!path.starts_with("http://") &&
!path.starts_with("https://") &&
let Some(local_resources) = &self.local_resources &&
let Some(correspondance) = local_resources.get_at_depth(self.depth, &*path)
{
Some(Event::Start(Tag::Image(
type_,
CowStr::Boxed(correspondance.clone().into_boxed_str()),
title.clone(),
)))
} else {
Some(event)
}
}
}

#[derive(Copy, Clone, PartialEq, Debug)]
pub enum ErrorCodes {
Yes,
Expand Down Expand Up @@ -1017,6 +1059,8 @@ impl Markdown<'_> {
edition,
playground,
heading_offset,
depth,
local_resources,
} = self;

// This is actually common enough to special-case
Expand All @@ -1038,6 +1082,7 @@ impl Markdown<'_> {
let p = HeadingLinks::new(p, None, ids, heading_offset);
let p = Footnotes::new(p);
let p = LinkReplacer::new(p.map(|(ev, _)| ev), links);
let p = LocalResourcesReplacer::new(p, local_resources, depth);
let p = TableWrapper::new(p);
let p = CodeBlocks::new(p, codes, edition, playground);
html::push_html(&mut s, p);
Expand All @@ -1048,7 +1093,15 @@ impl Markdown<'_> {

impl MarkdownWithToc<'_> {
pub(crate) fn into_string(self) -> String {
let MarkdownWithToc(md, ids, codes, edition, playground) = self;
let MarkdownWithToc {
content: md,
ids,
error_codes: codes,
edition,
playground,
depth,
local_resources,
} = self;

let p = Parser::new_ext(md, main_body_opts()).into_offset_iter();

Expand All @@ -1059,7 +1112,8 @@ impl MarkdownWithToc<'_> {
{
let p = HeadingLinks::new(p, Some(&mut toc), ids, HeadingOffset::H1);
let p = Footnotes::new(p);
let p = TableWrapper::new(p.map(|(ev, _)| ev));
let p = LocalResourcesReplacer::new(p.map(|(ev, _)| ev), local_resources, depth);
let p = TableWrapper::new(p);
let p = CodeBlocks::new(p, codes, edition, playground);
html::push_html(&mut s, p);
}
Expand Down
6 changes: 6 additions & 0 deletions src/librustdoc/html/markdown/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,8 @@ fn test_header() {
edition: DEFAULT_EDITION,
playground: &None,
heading_offset: HeadingOffset::H2,
depth: 0,
local_resources: None,
}
.into_string();
assert_eq!(output, expect, "original: {}", input);
Expand Down Expand Up @@ -193,6 +195,8 @@ fn test_header_ids_multiple_blocks() {
edition: DEFAULT_EDITION,
playground: &None,
heading_offset: HeadingOffset::H2,
depth: 0,
local_resources: None,
}
.into_string();
assert_eq!(output, expect, "original: {}", input);
Expand Down Expand Up @@ -322,6 +326,8 @@ fn test_ascii_with_prepending_hashtag() {
edition: DEFAULT_EDITION,
playground: &None,
heading_offset: HeadingOffset::H2,
depth: 0,
local_resources: None,
}
.into_string();
assert_eq!(output, expect, "original: {}", input);
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";
22 changes: 21 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 Expand Up @@ -715,6 +719,22 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
}
}

{
// Copying the local resources to the destination folder.
let resources = &shared.cache.local_resources.resources_to_copy;
if !resources.is_empty() {
let dst = self
.dst
.join(crate::html::LOCAL_RESOURCES_FOLDER_NAME)
.join(crate_name.as_str());
shared.ensure_dir(&dst)?;
for (original_path, dest_name) in resources.iter() {
let dst = dst.join(dest_name);
try_err!(std::fs::copy(original_path, &dst), &dst);
}
}
}

// No need for it anymore.
drop(shared);

Expand Down
10 changes: 8 additions & 2 deletions src/librustdoc/html/render/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -404,7 +404,9 @@ fn scrape_examples_help(shared: &SharedContext<'_>) -> String {
error_codes: shared.codes,
edition: shared.edition(),
playground: &shared.playground,
heading_offset: HeadingOffset::H1
heading_offset: HeadingOffset::H1,
depth: 0,
local_resources: None,
}
.into_string()
)
Expand Down Expand Up @@ -447,6 +449,8 @@ fn render_markdown(
edition: cx.shared.edition(),
playground: &cx.shared.playground,
heading_offset,
depth: cx.current.len(),
local_resources: Some(&cx.shared.cache.local_resources),
}
.into_string()
)
Expand Down Expand Up @@ -1755,7 +1759,9 @@ fn render_impl(
error_codes: cx.shared.codes,
edition: cx.shared.edition(),
playground: &cx.shared.playground,
heading_offset: HeadingOffset::H4
heading_offset: HeadingOffset::H4,
depth: cx.current.len(),
local_resources: Some(&cx.shared.cache.local_resources),
}
.into_string()
);
Expand Down
2 changes: 1 addition & 1 deletion src/librustdoc/html/render/write_shared.rs
Original file line number Diff line number Diff line change
Expand Up @@ -329,7 +329,7 @@ if (typeof exports !== 'undefined') {exports.searchIndex = searchIndex};
md_opts.output = cx.dst.clone();
md_opts.external_html = (*cx.shared).layout.external_html.clone();

crate::markdown::render(&index_page, md_opts, cx.shared.edition())
crate::markdown::render(&index_page, md_opts, cx.shared.edition(), Some(&cx))
.map_err(|e| Error::new(e, &index_page))?;
} else {
let shared = Rc::clone(&cx.shared);
Expand Down
2 changes: 1 addition & 1 deletion src/librustdoc/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -749,7 +749,7 @@ fn main_args(at_args: &[String]) -> MainResult {
return wrap_return(
&diag,
interface::run_compiler(config, |_compiler| {
markdown::render(&input, render_options, edition)
markdown::render(&input, render_options, edition, None)
}),
);
}
Expand Down
22 changes: 19 additions & 3 deletions src/librustdoc/markdown.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ use rustc_span::source_map::DUMMY_SP;
use crate::config::{Options, RenderOptions};
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,
self, find_testable_code, ErrorCodes, HeadingOffset, IdMap, Markdown, MarkdownWithToc,
};
use crate::html::render::Context;

/// Separate any lines at the start of the file that begin with `# ` or `%`.
fn extract_leading_metadata(s: &str) -> (Vec<&str>, &str) {
Expand Down Expand Up @@ -41,6 +41,7 @@ pub(crate) fn render<P: AsRef<Path>>(
input: P,
options: RenderOptions,
edition: Edition,
cx: Option<&Context<'_>>,
) -> Result<(), String> {
if let Err(e) = create_dir_all(&options.output) {
return Err(format!("{}: {}", options.output.display(), e));
Expand Down Expand Up @@ -71,8 +72,21 @@ pub(crate) fn render<P: AsRef<Path>>(

let mut ids = IdMap::new();
let error_codes = ErrorCodes::from(options.unstable_features.is_nightly_build());
let (local_resources, depth) = match cx {
Some(cx) => (Some(&cx.shared.cache.local_resources), cx.current.len()),
None => (None, 0),
};
let text = if !options.markdown_no_toc {
MarkdownWithToc(text, &mut ids, error_codes, edition, &playground).into_string()
MarkdownWithToc {
content: text,
ids: &mut ids,
error_codes,
edition,
playground: &playground,
local_resources,
depth,
}
.into_string()
} else {
Markdown {
content: text,
Expand All @@ -82,6 +96,8 @@ pub(crate) fn render<P: AsRef<Path>>(
edition,
playground: &playground,
heading_offset: HeadingOffset::H1,
local_resources,
depth,
}
.into_string()
};
Expand Down
Loading