Move source HTML generation to own module

This commit is contained in:
Mark Rousskov
2019-08-11 16:11:53 -04:00
parent 9b91b9c10e
commit a77247acb1
3 changed files with 194 additions and 173 deletions

View File

@@ -72,6 +72,7 @@ use crate::html::format::fmt_impl_for_trait_page;
use crate::html::item_type::ItemType;
use crate::html::markdown::{self, Markdown, MarkdownHtml, MarkdownSummaryLine, ErrorCodes, IdMap};
use crate::html::{highlight, layout, static_files};
use crate::html::sources;
use minifier;
@@ -173,7 +174,7 @@ struct Context {
playground: Option<markdown::Playground>,
}
struct SharedContext {
crate struct SharedContext {
/// The path to the crate root source minus the file name.
/// Used for simplifying paths to the highlighted source code files.
pub src_root: PathBuf,
@@ -218,7 +219,7 @@ struct SharedContext {
}
impl SharedContext {
fn ensure_dir(&self, dst: &Path) -> Result<(), Error> {
crate fn ensure_dir(&self, dst: &Path) -> Result<(), Error> {
let mut dirs = self.created_dirs.borrow_mut();
if !dirs.contains(dst) {
try_err!(self.fs.create_dir_all(dst), dst);
@@ -389,18 +390,6 @@ pub struct RenderInfo {
pub owned_box_did: Option<DefId>,
}
/// Helper struct to render all source code to HTML pages
struct SourceCollector<'a> {
scx: &'a mut SharedContext,
/// Root destination to place all HTML output into
dst: PathBuf,
}
/// Wrapper struct to render the source code of a file. This will do things like
/// adding line numbers to the left-hand side.
struct Source<'a>(&'a str);
// Helper structs for rendering items/sidebars and carrying along contextual
// information
@@ -612,7 +601,7 @@ pub fn run(mut krate: clean::Crate,
}
let dst = output;
scx.ensure_dir(&dst)?;
krate = render_sources(&dst, &mut scx, krate)?;
krate = sources::render(&dst, &mut scx, krate)?;
let mut cx = Context {
current: Vec::new(),
dst,
@@ -1293,18 +1282,6 @@ themePicker.onblur = handleThemeButtonsBlur;
Ok(())
}
fn render_sources(dst: &Path, scx: &mut SharedContext,
krate: clean::Crate) -> Result<clean::Crate, Error> {
info!("emitting source files");
let dst = dst.join("src").join(&krate.name);
scx.ensure_dir(&dst)?;
let mut folder = SourceCollector {
dst,
scx,
};
Ok(folder.fold_crate(krate))
}
fn write_minify(fs:&DocFS, dst: PathBuf, contents: &str, enable_minification: bool
) -> Result<(), Error> {
if enable_minification {
@@ -1384,33 +1361,6 @@ fn write_minify_replacer<W: Write>(
}
}
/// Takes a path to a source file and cleans the path to it. This canonicalizes
/// things like ".." to components which preserve the "top down" hierarchy of a
/// static HTML tree. Each component in the cleaned path will be passed as an
/// argument to `f`. The very last component of the path (ie the file name) will
/// be passed to `f` if `keep_filename` is true, and ignored otherwise.
fn clean_srcpath<F>(src_root: &Path, p: &Path, keep_filename: bool, mut f: F)
where
F: FnMut(&OsStr),
{
// make it relative, if possible
let p = p.strip_prefix(src_root).unwrap_or(p);
let mut iter = p.components().peekable();
while let Some(c) = iter.next() {
if !keep_filename && iter.peek().is_none() {
break;
}
match c {
Component::ParentDir => f("up".as_ref()),
Component::Normal(c) => f(c),
_ => continue,
}
}
}
/// Attempts to find where an external crate is located, given that we're
/// rendering in to the specified source destination.
fn extern_location(e: &clean::ExternalCrate, extern_url: Option<&str>, dst: &Path)
@@ -1444,102 +1394,6 @@ fn extern_location(e: &clean::ExternalCrate, extern_url: Option<&str>, dst: &Pat
}).next().unwrap_or(Unknown) // Well, at least we tried.
}
impl<'a> DocFolder for SourceCollector<'a> {
fn fold_item(&mut self, item: clean::Item) -> Option<clean::Item> {
// If we're including source files, and we haven't seen this file yet,
// then we need to render it out to the filesystem.
if self.scx.include_sources
// skip all invalid or macro spans
&& item.source.filename.is_real()
// skip non-local items
&& item.def_id.is_local() {
// If it turns out that we couldn't read this file, then we probably
// can't read any of the files (generating html output from json or
// something like that), so just don't include sources for the
// entire crate. The other option is maintaining this mapping on a
// per-file basis, but that's probably not worth it...
self.scx
.include_sources = match self.emit_source(&item.source.filename) {
Ok(()) => true,
Err(e) => {
println!("warning: source code was requested to be rendered, \
but processing `{}` had an error: {}",
item.source.filename, e);
println!(" skipping rendering of source code");
false
}
};
}
self.fold_item_recur(item)
}
}
impl<'a> SourceCollector<'a> {
/// Renders the given filename into its corresponding HTML source file.
fn emit_source(&mut self, filename: &FileName) -> Result<(), Error> {
let p = match *filename {
FileName::Real(ref file) => file,
_ => return Ok(()),
};
if self.scx.local_sources.contains_key(&**p) {
// We've already emitted this source
return Ok(());
}
let contents = try_err!(fs::read_to_string(&p), &p);
// Remove the utf-8 BOM if any
let contents = if contents.starts_with("\u{feff}") {
&contents[3..]
} else {
&contents[..]
};
// Create the intermediate directories
let mut cur = self.dst.clone();
let mut root_path = String::from("../../");
let mut href = String::new();
clean_srcpath(&self.scx.src_root, &p, false, |component| {
cur.push(component);
root_path.push_str("../");
href.push_str(&component.to_string_lossy());
href.push('/');
});
self.scx.ensure_dir(&cur)?;
let mut fname = p.file_name()
.expect("source has no filename")
.to_os_string();
fname.push(".html");
cur.push(&fname);
href.push_str(&fname.to_string_lossy());
let mut v = Vec::new();
let title = format!("{} -- source", cur.file_name().expect("failed to get file name")
.to_string_lossy());
let desc = format!("Source to the Rust file `{}`.", filename);
let page = layout::Page {
title: &title,
css_class: "source",
root_path: &root_path,
static_root_path: self.scx.static_root_path.as_deref(),
description: &desc,
keywords: BASIC_KEYWORDS,
resource_suffix: &self.scx.resource_suffix,
extra_scripts: &[&format!("source-files{}", self.scx.resource_suffix)],
static_extra_scripts: &[&format!("source-script{}", self.scx.resource_suffix)],
};
try_err!(layout::render(&mut v, &self.scx.layout,
&page, &(""), &Source(contents),
self.scx.css_file_extension.is_some(),
&self.scx.themes,
self.scx.generate_search_filter), &cur);
self.scx.fs.write(&cur, &v)?;
self.scx.local_sources.insert(p.clone(), href);
Ok(())
}
}
impl DocFolder for Cache {
fn fold_item(&mut self, item: clean::Item) -> Option<clean::Item> {
if item.def_id.is_local() {
@@ -2399,7 +2253,7 @@ impl<'a> Item<'a> {
(_, _, Unknown) => return None,
};
clean_srcpath(&src_root, file, false, |component| {
sources::clean_path(&src_root, file, false, |component| {
path.push_str(&component.to_string_lossy());
path.push('/');
});
@@ -5048,27 +4902,6 @@ fn sidebar_foreign_type(fmt: &mut fmt::Formatter<'_>, it: &clean::Item) -> fmt::
Ok(())
}
impl<'a> fmt::Display for Source<'a> {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
let Source(s) = *self;
let lines = s.lines().count();
let mut cols = 0;
let mut tmp = lines;
while tmp > 0 {
cols += 1;
tmp /= 10;
}
write!(fmt, "<pre class=\"line-numbers\">")?;
for i in 1..=lines {
write!(fmt, "<span id=\"{0}\">{0:1$}</span>\n", i, cols)?;
}
write!(fmt, "</pre>")?;
write!(fmt, "{}",
highlight::render_with_highlighting(s, None, None, None))?;
Ok(())
}
}
fn item_macro(w: &mut fmt::Formatter<'_>, cx: &Context, it: &clean::Item,
t: &clean::Macro) -> fmt::Result {
wrap_into_docblock(w, |w| {
@@ -5125,7 +4958,7 @@ fn item_keyword(w: &mut fmt::Formatter<'_>, cx: &Context,
document(w, cx, it)
}
const BASIC_KEYWORDS: &'static str = "rust, rustlang, rust-lang";
crate const BASIC_KEYWORDS: &'static str = "rust, rustlang, rust-lang";
fn make_item_keywords(it: &clean::Item) -> String {
format!("{}, {}", BASIC_KEYWORDS, it.name.as_ref().unwrap())