From 5bc97946ca35a789b690668bb6b27ca41bfeb5b2 Mon Sep 17 00:00:00 2001 From: Joseph Ryan Date: Wed, 17 Jun 2020 12:41:45 -0500 Subject: [PATCH] Refactor html backend to use generic interface --- src/librustdoc/formats/mod.rs | 107 ++++++ src/librustdoc/html/render.rs | 604 +++++++++++++++++----------------- src/librustdoc/lib.rs | 5 +- 3 files changed, 406 insertions(+), 310 deletions(-) create mode 100644 src/librustdoc/formats/mod.rs diff --git a/src/librustdoc/formats/mod.rs b/src/librustdoc/formats/mod.rs new file mode 100644 index 000000000000..55827f473858 --- /dev/null +++ b/src/librustdoc/formats/mod.rs @@ -0,0 +1,107 @@ +use std::cell::RefCell; +use std::rc::Rc; + +use rustc_span::edition::Edition; + +use crate::clean; +use crate::config::{RenderInfo, RenderOptions}; +use crate::error::Error; + +pub trait FormatRenderer: Clone { + type Output: FormatRenderer; + + fn init( + krate: clean::Crate, + options: RenderOptions, + renderinfo: RenderInfo, + diag: &rustc_errors::Handler, + edition: Edition, + parent: Rc>, + ) -> Result<(Self::Output, clean::Crate), Error>; + + /// Renders a single non-module item. This means no recursive sub-item rendering is required. + fn item(&mut self, item: clean::Item) -> Result<(), Error>; + + /// Renders a module. Doesn't need to handle recursing into children, the driver does that + /// automatically. + fn mod_item_in( + &mut self, + item: &clean::Item, + item_name: &str, + module: &clean::Module, + ) -> Result<(), Error>; + + /// Runs after recursively rendering all sub-items of a module. + fn mod_item_out(&mut self) -> Result<(), Error>; + + /// Post processing hook for cleanup and dumping output to files. + fn after_krate(&mut self, krate: &clean::Crate) -> Result<(), Error>; + + /// Called after everything else to write out errors. + fn after_run(&mut self, diag: &rustc_errors::Handler) -> Result<(), Error>; +} + +#[derive(Clone)] +pub struct Renderer; + +impl Renderer { + pub fn new() -> Renderer { + Renderer + } + + /// Main method for rendering a crate. + pub fn run( + self, + krate: clean::Crate, + options: RenderOptions, + renderinfo: RenderInfo, + diag: &rustc_errors::Handler, + edition: Edition, + ) -> Result<(), Error> { + let rself = Rc::new(RefCell::new(self)); + let (mut renderer, mut krate) = + T::init(krate, options, renderinfo, diag, edition, rself.clone())?; + let mut item = match krate.module.take() { + Some(i) => i, + None => return Ok(()), + }; + + item.name = Some(krate.name.clone()); + + // Render the crate documentation + let mut work = vec![(renderer.clone(), item)]; + + while let Some((mut cx, item)) = work.pop() { + if item.is_mod() { + // modules are special because they add a namespace. We also need to + // recurse into the items of the module as well. + let name = item.name.as_ref().unwrap().to_string(); + if name.is_empty() { + panic!("Unexpected module with empty name"); + } + + let module = match item.inner { + clean::StrippedItem(box clean::ModuleItem(ref m)) + | clean::ModuleItem(ref m) => m, + _ => unreachable!(), + }; + cx.mod_item_in(&item, &name, module)?; + let module = match item.inner { + clean::StrippedItem(box clean::ModuleItem(m)) | clean::ModuleItem(m) => m, + _ => unreachable!(), + }; + for it in module.items { + info!("Adding {:?} to worklist", it.name); + work.push((cx.clone(), it)); + } + + cx.mod_item_out()?; + } else if item.name.is_some() { + cx.item(item)?; + } + } + + renderer.after_krate(&krate)?; + renderer.after_run(diag) + } +} diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs index 18ef97118e09..fd32ba66d91d 100644 --- a/src/librustdoc/html/render.rs +++ b/src/librustdoc/html/render.rs @@ -63,6 +63,7 @@ use crate::config::RenderOptions; use crate::docfs::{DocFS, ErrorStorage, PathError}; use crate::doctree; use crate::error::Error; +use crate::formats::{FormatRenderer, Renderer}; use crate::html::escape::Escape; use crate::html::format::fmt_impl_for_trait_page; use crate::html::format::Function; @@ -98,7 +99,7 @@ crate fn ensure_trailing_slash(v: &str) -> impl fmt::Display + '_ { /// easily cloned because it is cloned per work-job (about once per item in the /// rustdoc tree). #[derive(Clone)] -struct Context { +crate struct Context { /// Current hierarchy of components leading down to what's currently being /// rendered pub current: Vec, @@ -113,6 +114,9 @@ struct Context { id_map: Rc>, pub shared: Arc, pub cache: Arc, + pub parent: Rc>, + all: Rc>, + pub errors: Arc, } crate struct SharedContext { @@ -390,148 +394,307 @@ pub fn initial_ids() -> Vec { .collect() } -/// Generates the documentation for `crate` into the directory `dst` -pub fn run( - mut krate: clean::Crate, - options: RenderOptions, - renderinfo: RenderInfo, - diag: &rustc_errors::Handler, - edition: Edition, -) -> Result<(), Error> { - // need to save a copy of the options for rendering the index page - let md_opts = options.clone(); - let RenderOptions { - output, - external_html, - id_map, - playground_url, - sort_modules_alphabetically, - themes: style_files, - extension_css, - extern_html_root_urls, - resource_suffix, - static_root_path, - generate_search_filter, - document_private, - .. - } = options; +impl FormatRenderer for Context { + type Output = Self; - let src_root = match krate.src { - FileName::Real(ref p) => match p.local_path().parent() { - Some(p) => p.to_path_buf(), - None => PathBuf::new(), - }, - _ => PathBuf::new(), - }; - let mut errors = Arc::new(ErrorStorage::new()); - // If user passed in `--playground-url` arg, we fill in crate name here - let mut playground = None; - if let Some(url) = playground_url { - playground = Some(markdown::Playground { crate_name: Some(krate.name.clone()), url }); - } - let mut layout = layout::Layout { - logo: String::new(), - favicon: String::new(), - external_html, - krate: krate.name.clone(), - css_file_extension: extension_css, - generate_search_filter, - }; - let mut issue_tracker_base_url = None; - let mut include_sources = true; + /// Generates the documentation for `crate` into the directory `dst` + fn init( + mut krate: clean::Crate, + options: RenderOptions, + renderinfo: RenderInfo, + _diag: &rustc_errors::Handler, + edition: Edition, + parent: Rc>, + ) -> Result<(Context, clean::Crate), Error> { + // need to save a copy of the options for rendering the index page + let md_opts = options.clone(); + let RenderOptions { + output, + external_html, + id_map, + playground_url, + sort_modules_alphabetically, + themes: style_files, + extension_css, + extern_html_root_urls, + resource_suffix, + static_root_path, + generate_search_filter, + document_private, + .. + } = options; - // Crawl the crate attributes looking for attributes which control how we're - // going to emit HTML - if let Some(attrs) = krate.module.as_ref().map(|m| &m.attrs) { - for attr in attrs.lists(sym::doc) { - match (attr.name_or_empty(), attr.value_str()) { - (sym::html_favicon_url, Some(s)) => { - layout.favicon = s.to_string(); + let src_root = match krate.src { + FileName::Real(ref p) => match p.local_path().parent() { + Some(p) => p.to_path_buf(), + None => PathBuf::new(), + }, + _ => PathBuf::new(), + }; + let errors = Arc::new(ErrorStorage::new()); + // If user passed in `--playground-url` arg, we fill in crate name here + let mut playground = None; + if let Some(url) = playground_url { + playground = Some(markdown::Playground { crate_name: Some(krate.name.clone()), url }); + } + let mut layout = layout::Layout { + logo: String::new(), + favicon: String::new(), + external_html, + krate: krate.name.clone(), + css_file_extension: extension_css, + generate_search_filter, + }; + let mut issue_tracker_base_url = None; + let mut include_sources = true; + + // Crawl the crate attributes looking for attributes which control how we're + // going to emit HTML + if let Some(attrs) = krate.module.as_ref().map(|m| &m.attrs) { + for attr in attrs.lists(sym::doc) { + match (attr.name_or_empty(), attr.value_str()) { + (sym::html_favicon_url, Some(s)) => { + layout.favicon = s.to_string(); + } + (sym::html_logo_url, Some(s)) => { + layout.logo = s.to_string(); + } + (sym::html_playground_url, Some(s)) => { + playground = Some(markdown::Playground { + crate_name: Some(krate.name.clone()), + url: s.to_string(), + }); + } + (sym::issue_tracker_base_url, Some(s)) => { + issue_tracker_base_url = Some(s.to_string()); + } + (sym::html_no_source, None) if attr.is_word() => { + include_sources = false; + } + _ => {} } - (sym::html_logo_url, Some(s)) => { - layout.logo = s.to_string(); - } - (sym::html_playground_url, Some(s)) => { - playground = Some(markdown::Playground { - crate_name: Some(krate.name.clone()), - url: s.to_string(), - }); - } - (sym::issue_tracker_base_url, Some(s)) => { - issue_tracker_base_url = Some(s.to_string()); - } - (sym::html_no_source, None) if attr.is_word() => { - include_sources = false; - } - _ => {} } } + let mut scx = SharedContext { + collapsed: krate.collapsed, + src_root, + include_sources, + local_sources: Default::default(), + issue_tracker_base_url, + layout, + created_dirs: Default::default(), + sort_modules_alphabetically, + style_files, + resource_suffix, + static_root_path, + fs: DocFS::new(&errors), + edition, + codes: ErrorCodes::from(UnstableFeatures::from_environment().is_nightly_build()), + playground, + }; + + // Add the default themes to the `Vec` of stylepaths + // + // Note that these must be added before `sources::render` is called + // so that the resulting source pages are styled + // + // `light.css` is not disabled because it is the stylesheet that stays loaded + // by the browser as the theme stylesheet. The theme system (hackily) works by + // changing the href to this stylesheet. All other themes are disabled to + // prevent rule conflicts + scx.style_files.push(StylePath { path: PathBuf::from("light.css"), disabled: false }); + scx.style_files.push(StylePath { path: PathBuf::from("dark.css"), disabled: true }); + scx.style_files.push(StylePath { path: PathBuf::from("ayu.css"), disabled: true }); + + let dst = output; + scx.ensure_dir(&dst)?; + krate = sources::render(&dst, &mut scx, krate)?; + let (new_crate, index, cache) = + Cache::from_krate(renderinfo, document_private, &extern_html_root_urls, &dst, krate); + krate = new_crate; + let cache = Arc::new(cache); + let mut cx = Context { + current: Vec::new(), + dst, + render_redirect_pages: false, + id_map: Rc::new(RefCell::new(id_map)), + shared: Arc::new(scx), + cache: cache.clone(), + parent, + all: Rc::new(RefCell::new(AllTypes::new())), + errors, + }; + + // Freeze the cache now that the index has been built. Put an Arc into TLS + // for future parallelization opportunities + CACHE_KEY.with(|v| *v.borrow_mut() = cache.clone()); + CURRENT_DEPTH.with(|s| s.set(0)); + + // Write shared runs within a flock; disable thread dispatching of IO temporarily. + Arc::get_mut(&mut cx.shared).unwrap().fs.set_sync_only(true); + write_shared(&cx, &krate, index, &md_opts)?; + Arc::get_mut(&mut cx.shared).unwrap().fs.set_sync_only(false); + Ok((cx, krate)) } - let mut scx = SharedContext { - collapsed: krate.collapsed, - src_root, - include_sources, - local_sources: Default::default(), - issue_tracker_base_url, - layout, - created_dirs: Default::default(), - sort_modules_alphabetically, - style_files, - resource_suffix, - static_root_path, - fs: DocFS::new(&errors), - edition, - codes: ErrorCodes::from(UnstableFeatures::from_environment().is_nightly_build()), - playground, - }; - // Add the default themes to the `Vec` of stylepaths - // - // Note that these must be added before `sources::render` is called - // so that the resulting source pages are styled - // - // `light.css` is not disabled because it is the stylesheet that stays loaded - // by the browser as the theme stylesheet. The theme system (hackily) works by - // changing the href to this stylesheet. All other themes are disabled to - // prevent rule conflicts - scx.style_files.push(StylePath { path: PathBuf::from("light.css"), disabled: false }); - scx.style_files.push(StylePath { path: PathBuf::from("dark.css"), disabled: true }); - scx.style_files.push(StylePath { path: PathBuf::from("ayu.css"), disabled: true }); + fn after_run(&mut self, diag: &rustc_errors::Handler) -> Result<(), Error> { + let nb_errors = + Arc::get_mut(&mut self.errors).map_or_else(|| 0, |errors| errors.write_errors(diag)); + if nb_errors > 0 { + Err(Error::new(io::Error::new(io::ErrorKind::Other, "I/O error"), "")) + } else { + Ok(()) + } + } - let dst = output; - scx.ensure_dir(&dst)?; - krate = sources::render(&dst, &mut scx, krate)?; - let (new_crate, index, cache) = - Cache::from_krate(renderinfo, document_private, &extern_html_root_urls, &dst, krate); - krate = new_crate; - let cache = Arc::new(cache); - let mut cx = Context { - current: Vec::new(), - dst, - render_redirect_pages: false, - id_map: Rc::new(RefCell::new(id_map)), - shared: Arc::new(scx), - cache: cache.clone(), - }; + fn after_krate(&mut self, krate: &clean::Crate) -> Result<(), Error> { + let final_file = self.dst.join(&krate.name).join("all.html"); + let settings_file = self.dst.join("settings.html"); + let crate_name = krate.name.clone(); - // Freeze the cache now that the index has been built. Put an Arc into TLS - // for future parallelization opportunities - CACHE_KEY.with(|v| *v.borrow_mut() = cache.clone()); - CURRENT_DEPTH.with(|s| s.set(0)); + let mut root_path = self.dst.to_str().expect("invalid path").to_owned(); + if !root_path.ends_with('/') { + root_path.push('/'); + } + let mut page = layout::Page { + title: "List of all items in this crate", + css_class: "mod", + root_path: "../", + static_root_path: self.shared.static_root_path.as_deref(), + description: "List of all items in this crate", + keywords: BASIC_KEYWORDS, + resource_suffix: &self.shared.resource_suffix, + extra_scripts: &[], + static_extra_scripts: &[], + }; + let sidebar = if let Some(ref version) = self.cache.crate_version { + format!( + "

Crate {}

\ +
\ +

Version {}

\ +
\ +

Back to index

", + crate_name, + Escape(version), + ) + } else { + String::new() + }; + let all = self.all.replace(AllTypes::new()); + let v = layout::render( + &self.shared.layout, + &page, + sidebar, + |buf: &mut Buffer| all.print(buf), + &self.shared.style_files, + ); + self.shared.fs.write(&final_file, v.as_bytes())?; - // Write shared runs within a flock; disable thread dispatching of IO temporarily. - Arc::get_mut(&mut cx.shared).unwrap().fs.set_sync_only(true); - write_shared(&cx, &krate, index, &md_opts)?; - Arc::get_mut(&mut cx.shared).unwrap().fs.set_sync_only(false); + // Generating settings page. + page.title = "Rustdoc settings"; + page.description = "Settings of Rustdoc"; + page.root_path = "./"; - // And finally render the whole crate's documentation - let ret = cx.krate(krate); - let nb_errors = Arc::get_mut(&mut errors).map_or_else(|| 0, |errors| errors.write_errors(diag)); - if ret.is_err() { - ret - } else if nb_errors > 0 { - Err(Error::new(io::Error::new(io::ErrorKind::Other, "I/O error"), "")) - } else { + let mut style_files = self.shared.style_files.clone(); + let sidebar = "

Settings

"; + style_files.push(StylePath { path: PathBuf::from("settings.css"), disabled: false }); + let v = layout::render( + &self.shared.layout, + &page, + sidebar, + settings( + self.shared.static_root_path.as_deref().unwrap_or("./"), + &self.shared.resource_suffix, + ), + &style_files, + ); + self.shared.fs.write(&settings_file, v.as_bytes())?; + Ok(()) + } + + fn mod_item_in( + &mut self, + item: &clean::Item, + item_name: &str, + module: &clean::Module, + ) -> Result<(), Error> { + // Stripped modules survive the rustdoc passes (i.e., `strip-private`) + // if they contain impls for public types. These modules can also + // contain items such as publicly re-exported structures. + // + // External crates will provide links to these structures, so + // these modules are recursed into, but not rendered normally + // (a flag on the context). + if !self.render_redirect_pages { + self.render_redirect_pages = item.is_stripped(); + } + let scx = &self.shared; + self.dst.push(item_name); + self.current.push(item_name.to_owned()); + + info!("Recursing into {}", self.dst.display()); + + let buf = self.render_item(item, false); + // buf will be empty if the module is stripped and there is no redirect for it + if !buf.is_empty() { + self.shared.ensure_dir(&self.dst)?; + let joint_dst = self.dst.join("index.html"); + scx.fs.write(&joint_dst, buf.as_bytes())?; + } + + // Render sidebar-items.js used throughout this module. + if !self.render_redirect_pages { + let items = self.build_sidebar_items(module); + let js_dst = self.dst.join("sidebar-items.js"); + let v = format!("initSidebarItems({});", serde_json::to_string(&items).unwrap()); + scx.fs.write(&js_dst, &v)?; + } + Ok(()) + } + + fn mod_item_out(&mut self) -> Result<(), Error> { + info!("Recursed; leaving {}", self.dst.display()); + + // Go back to where we were at + self.dst.pop(); + self.current.pop(); + Ok(()) + } + + fn item(&mut self, item: clean::Item) -> Result<(), Error> { + // Stripped modules survive the rustdoc passes (i.e., `strip-private`) + // if they contain impls for public types. These modules can also + // contain items such as publicly re-exported structures. + // + // External crates will provide links to these structures, so + // these modules are recursed into, but not rendered normally + // (a flag on the context). + if !self.render_redirect_pages { + self.render_redirect_pages = item.is_stripped(); + } + + let buf = self.render_item(&item, true); + // buf will be empty if the item is stripped and there is no redirect for it + if !buf.is_empty() { + let name = item.name.as_ref().unwrap(); + let item_type = item.type_(); + let file_name = &item_path(item_type, name); + self.shared.ensure_dir(&self.dst)?; + let joint_dst = self.dst.join(file_name); + self.shared.fs.write(&joint_dst, buf.as_bytes())?; + + if !self.render_redirect_pages { + self.all.borrow_mut().append(full_path(self, &item), &item_type); + } + // If the item is a macro, redirect from the old macro URL (with !) + // to the new one (without). + if item_type == ItemType::Macro { + let redir_name = format!("{}.{}!.html", item_type, name); + let redir_dst = self.dst.join(redir_name); + let v = layout::redirect(file_name); + self.shared.fs.write(&redir_dst, v.as_bytes())?; + } + } Ok(()) } } @@ -1291,92 +1454,6 @@ impl Context { "../".repeat(self.current.len()) } - /// Main method for rendering a crate. - /// - /// This currently isn't parallelized, but it'd be pretty easy to add - /// parallelization to this function. - fn krate(self, mut krate: clean::Crate) -> Result<(), Error> { - let mut item = match krate.module.take() { - Some(i) => i, - None => return Ok(()), - }; - let final_file = self.dst.join(&krate.name).join("all.html"); - let settings_file = self.dst.join("settings.html"); - - let crate_name = krate.name.clone(); - item.name = Some(krate.name); - - let mut all = AllTypes::new(); - - { - // Render the crate documentation - let mut work = vec![(self.clone(), item)]; - - while let Some((mut cx, item)) = work.pop() { - cx.item(item, &mut all, |cx, item| work.push((cx.clone(), item)))? - } - } - - let mut root_path = self.dst.to_str().expect("invalid path").to_owned(); - if !root_path.ends_with('/') { - root_path.push('/'); - } - let mut page = layout::Page { - title: "List of all items in this crate", - css_class: "mod", - root_path: "../", - static_root_path: self.shared.static_root_path.as_deref(), - description: "List of all items in this crate", - keywords: BASIC_KEYWORDS, - resource_suffix: &self.shared.resource_suffix, - extra_scripts: &[], - static_extra_scripts: &[], - }; - let sidebar = if let Some(ref version) = self.cache.crate_version { - format!( - "

Crate {}

\ -
\ -

Version {}

\ -
\ -

Back to index

", - crate_name, - Escape(version), - ) - } else { - String::new() - }; - let v = layout::render( - &self.shared.layout, - &page, - sidebar, - |buf: &mut Buffer| all.print(buf), - &self.shared.style_files, - ); - self.shared.fs.write(&final_file, v.as_bytes())?; - - // Generating settings page. - page.title = "Rustdoc settings"; - page.description = "Settings of Rustdoc"; - page.root_path = "./"; - - let mut style_files = self.shared.style_files.clone(); - let sidebar = "

Settings

"; - style_files.push(StylePath { path: PathBuf::from("settings.css"), disabled: false }); - let v = layout::render( - &self.shared.layout, - &page, - sidebar, - settings( - self.shared.static_root_path.as_deref().unwrap_or("./"), - &self.shared.resource_suffix, - ), - &style_files, - ); - self.shared.fs.write(&settings_file, v.as_bytes())?; - - Ok(()) - } - fn render_item(&self, it: &clean::Item, pushname: bool) -> String { // A little unfortunate that this is done like this, but it sure // does make formatting *a lot* nicer. @@ -1449,97 +1526,6 @@ impl Context { } } - /// Non-parallelized version of rendering an item. This will take the input - /// item, render its contents, and then invoke the specified closure with - /// all sub-items which need to be rendered. - /// - /// The rendering driver uses this closure to queue up more work. - fn item(&mut self, item: clean::Item, all: &mut AllTypes, mut f: F) -> Result<(), Error> - where - F: FnMut(&mut Context, clean::Item), - { - // Stripped modules survive the rustdoc passes (i.e., `strip-private`) - // if they contain impls for public types. These modules can also - // contain items such as publicly re-exported structures. - // - // External crates will provide links to these structures, so - // these modules are recursed into, but not rendered normally - // (a flag on the context). - if !self.render_redirect_pages { - self.render_redirect_pages = item.is_stripped(); - } - - if item.is_mod() { - // modules are special because they add a namespace. We also need to - // recurse into the items of the module as well. - let name = item.name.as_ref().unwrap().to_string(); - let scx = &self.shared; - if name.is_empty() { - panic!("Unexpected empty destination: {:?}", self.current); - } - let prev = self.dst.clone(); - self.dst.push(&name); - self.current.push(name); - - info!("Recursing into {}", self.dst.display()); - - let buf = self.render_item(&item, false); - // buf will be empty if the module is stripped and there is no redirect for it - if !buf.is_empty() { - self.shared.ensure_dir(&self.dst)?; - let joint_dst = self.dst.join("index.html"); - scx.fs.write(&joint_dst, buf.as_bytes())?; - } - - let m = match item.inner { - clean::StrippedItem(box clean::ModuleItem(m)) | clean::ModuleItem(m) => m, - _ => unreachable!(), - }; - - // Render sidebar-items.js used throughout this module. - if !self.render_redirect_pages { - let items = self.build_sidebar_items(&m); - let js_dst = self.dst.join("sidebar-items.js"); - let v = format!("initSidebarItems({});", serde_json::to_string(&items).unwrap()); - scx.fs.write(&js_dst, &v)?; - } - - for item in m.items { - f(self, item); - } - - info!("Recursed; leaving {}", self.dst.display()); - - // Go back to where we were at - self.dst = prev; - self.current.pop().unwrap(); - } else if item.name.is_some() { - let buf = self.render_item(&item, true); - // buf will be empty if the item is stripped and there is no redirect for it - if !buf.is_empty() { - let name = item.name.as_ref().unwrap(); - let item_type = item.type_(); - let file_name = &item_path(item_type, name); - self.shared.ensure_dir(&self.dst)?; - let joint_dst = self.dst.join(file_name); - self.shared.fs.write(&joint_dst, buf.as_bytes())?; - - if !self.render_redirect_pages { - all.append(full_path(self, &item), &item_type); - } - // If the item is a macro, redirect from the old macro URL (with !) - // to the new one (without). - if item_type == ItemType::Macro { - let redir_name = format!("{}.{}!.html", item_type, name); - let redir_dst = self.dst.join(redir_name); - let v = layout::redirect(file_name); - self.shared.fs.write(&redir_dst, v.as_bytes())?; - } - } - } - Ok(()) - } - fn build_sidebar_items(&self, m: &clean::Module) -> BTreeMap> { // BTreeMap instead of HashMap to get a sorted output let mut map: BTreeMap<_, Vec<_>> = BTreeMap::new(); diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index ac31ab5980cc..715956ea1720 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -66,6 +66,7 @@ mod doctree; #[macro_use] mod error; mod fold; +mod formats; pub mod html { crate mod escape; crate mod format; @@ -512,7 +513,9 @@ fn main_options(options: config::Options) -> i32 { info!("going to format"); let (error_format, edition, debugging_options) = diag_opts; let diag = core::new_handler(error_format, None, &debugging_options); - match html::render::run(krate, renderopts, renderinfo, &diag, edition) { + match formats::Renderer::new() + .run::(krate, renderopts, renderinfo, &diag, edition) + { Ok(_) => rustc_driver::EXIT_SUCCESS, Err(e) => { diag.struct_err(&format!("couldn't generate documentation: {}", e.error))