diff options
| author | Baoshan <pangbw@gmail.com> | 2019-09-01 17:52:09 -0700 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2019-09-01 17:52:09 -0700 |
| commit | d5ef9df032ec32c48d6c59050735352c48ff16f8 (patch) | |
| tree | 31a87971a84ffc967645099773910d6498d0cb0b /src/librustdoc/html/render.rs | |
| parent | 7726b54c059db0759e9725a58e222118eacec6d9 (diff) | |
| parent | dfd43f0fdd4e6969c7d82c0670d70bf305fbccf8 (diff) | |
| download | rust-d5ef9df032ec32c48d6c59050735352c48ff16f8.tar.gz rust-d5ef9df032ec32c48d6c59050735352c48ff16f8.zip | |
Merge pull request #13 from rust-lang/master
sync with rust-lang/rust
Diffstat (limited to 'src/librustdoc/html/render.rs')
| -rw-r--r-- | src/librustdoc/html/render.rs | 421 |
1 files changed, 112 insertions, 309 deletions
diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs index ea97cea9428..d5c47a15948 100644 --- a/src/librustdoc/html/render.rs +++ b/src/librustdoc/html/render.rs @@ -28,7 +28,7 @@ pub use self::ExternalLocation::*; use std::borrow::Cow; -use std::cell::RefCell; +use std::cell::{Cell, RefCell}; use std::cmp::Ordering; use std::collections::{BTreeMap, VecDeque}; use std::default::Default; @@ -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); @@ -281,11 +282,6 @@ impl Impl { /// rendering threads. #[derive(Default)] pub struct Cache { - /// Mapping of typaram ids to the name of the type parameter. This is used - /// when pretty-printing a type (so pretty-printing doesn't have to - /// painfully maintain a context like this) - pub param_names: FxHashMap<DefId, String>, - /// Maps a type ID to all known implementations for that type. This is only /// recognized for intra-crate `ResolvedPath` types, and is used to print /// out extra documentation on the page of an enum/struct. @@ -381,7 +377,6 @@ pub struct Cache { pub struct RenderInfo { pub inlined: FxHashSet<DefId>, pub external_paths: crate::core::ExternalPaths, - pub external_param_names: FxHashMap<DefId, String>, pub exact_paths: FxHashMap<DefId, Vec<String>>, pub access_levels: AccessLevels<DefId>, pub deref_trait_did: Option<DefId>, @@ -389,18 +384,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 @@ -496,7 +479,7 @@ impl ToJson for IndexItemFunctionType { } thread_local!(static CACHE_KEY: RefCell<Arc<Cache>> = Default::default()); -thread_local!(pub static CURRENT_LOCATION_KEY: RefCell<Vec<String>> = RefCell::new(Vec::new())); +thread_local!(pub static CURRENT_DEPTH: Cell<usize> = Cell::new(0)); pub fn initial_ids() -> Vec<String> { [ @@ -612,7 +595,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, @@ -628,7 +611,6 @@ pub fn run(mut krate: clean::Crate, let RenderInfo { inlined: _, external_paths, - external_param_names, exact_paths, access_levels, deref_trait_did, @@ -662,7 +644,6 @@ pub fn run(mut krate: clean::Crate, deref_mut_trait_did, owned_box_did, masked_crates: mem::take(&mut krate.masked_crates), - param_names: external_param_names, aliases: Default::default(), }; @@ -714,7 +695,7 @@ pub fn run(mut krate: clean::Crate, // for future parallelization opportunities let cache = Arc::new(cache); CACHE_KEY.with(|v| *v.borrow_mut() = cache.clone()); - CURRENT_LOCATION_KEY.with(|s| s.borrow_mut().clear()); + 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); @@ -751,7 +732,7 @@ fn build_index(krate: &clean::Crate, cache: &mut Cache) -> String { ty: item.type_(), name: item.name.clone().unwrap(), path: fqp[..fqp.len() - 1].join("::"), - desc: plain_summary_line_short(item.doc_value()), + desc: shorten(plain_summary_line(item.doc_value())), parent: Some(did), parent_idx: None, search_type: get_index_search_type(&item), @@ -789,7 +770,7 @@ fn build_index(krate: &clean::Crate, cache: &mut Cache) -> String { } let crate_doc = krate.module.as_ref().map(|module| { - plain_summary_line_short(module.doc_value()) + shorten(plain_summary_line(module.doc_value())) }).unwrap_or(String::new()); let mut crate_data = BTreeMap::new(); @@ -876,22 +857,22 @@ r#"var themes = document.getElementById("theme-choices"); var themePicker = document.getElementById("theme-picker"); function showThemeButtonState() {{ - themes.style.display = "none"; - themePicker.style.borderBottomRightRadius = "3px"; - themePicker.style.borderBottomLeftRadius = "3px"; -}} - -function hideThemeButtonState() {{ themes.style.display = "block"; themePicker.style.borderBottomRightRadius = "0"; themePicker.style.borderBottomLeftRadius = "0"; }} +function hideThemeButtonState() {{ + themes.style.display = "none"; + themePicker.style.borderBottomRightRadius = "3px"; + themePicker.style.borderBottomLeftRadius = "3px"; +}} + function switchThemeButtonState() {{ if (themes.style.display === "block") {{ - showThemeButtonState(); - }} else {{ hideThemeButtonState(); + }} else {{ + showThemeButtonState(); }} }}; @@ -914,7 +895,7 @@ themePicker.onblur = handleThemeButtonsBlur; var but = document.createElement('button'); but.innerHTML = item; but.onclick = function(el) {{ - switchTheme(currentTheme, mainTheme, item); + switchTheme(currentTheme, mainTheme, item, true); }}; but.onblur = handleThemeButtonsBlur; themes.appendChild(but); @@ -1293,18 +1274,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 +1353,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 +1386,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() { @@ -1565,12 +1411,6 @@ impl DocFolder for Cache { } } - // Register any generics to their corresponding string. This is used - // when pretty-printing types. - if let Some(generics) = item.inner.generics() { - self.generics(generics); - } - // Propagate a trait method's documentation to all implementors of the // trait. if let clean::TraitItem(ref t) = item.inner { @@ -1642,7 +1482,7 @@ impl DocFolder for Cache { ty: item.type_(), name: s.to_string(), path: path.join("::"), - desc: plain_summary_line_short(item.doc_value()), + desc: shorten(plain_summary_line(item.doc_value())), parent, parent_idx: None, search_type: get_index_search_type(&item), @@ -1803,18 +1643,6 @@ impl DocFolder for Cache { } impl Cache { - fn generics(&mut self, generics: &clean::Generics) { - for param in &generics.params { - match param.kind { - clean::GenericParamDefKind::Lifetime => {} - clean::GenericParamDefKind::Type { did, .. } | - clean::GenericParamDefKind::Const { did, .. } => { - self.param_names.insert(did, param.name.clone()); - } - } - } - } - fn add_aliases(&mut self, item: &clean::Item) { if item.def_id.index == CRATE_DEF_INDEX { return @@ -1836,7 +1664,7 @@ impl Cache { ty: item.type_(), name: item_name.to_string(), path: path.clone(), - desc: plain_summary_line_short(item.doc_value()), + desc: shorten(plain_summary_line(item.doc_value())), parent: None, parent_idx: None, search_type: get_index_search_type(&item), @@ -2057,31 +1885,6 @@ impl Context { "../".repeat(self.current.len()) } - /// Recurse in the directory structure and change the "root path" to make - /// sure it always points to the top (relatively). - fn recurse<T, F>(&mut self, s: String, f: F) -> T where - F: FnOnce(&mut Context) -> T, - { - if s.is_empty() { - panic!("Unexpected empty destination: {:?}", self.current); - } - let prev = self.dst.clone(); - self.dst.push(&s); - self.current.push(s); - - info!("Recursing into {}", self.dst.display()); - - let ret = f(self); - - info!("Recursed; leaving {}", self.dst.display()); - - // Go back to where we were at - self.dst = prev; - self.current.pop().unwrap(); - - ret - } - /// Main method for rendering a crate. /// /// This currently isn't parallelized, but it'd be pretty easy to add @@ -2175,8 +1978,8 @@ impl Context { -> io::Result<()> { // A little unfortunate that this is done like this, but it sure // does make formatting *a lot* nicer. - CURRENT_LOCATION_KEY.with(|slot| { - *slot.borrow_mut() = self.current.clone(); + CURRENT_DEPTH.with(|slot| { + slot.set(self.current.len()); }); let mut title = if it.is_primitive() || it.is_keyword() { @@ -2262,42 +2065,50 @@ impl Context { // 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 mut item = Some(item); - let scx = self.shared.clone(); - self.recurse(name, |this| { - let item = item.take().unwrap(); - - let mut buf = Vec::new(); - this.render_item(&mut buf, &item, false).unwrap(); - // buf will be empty if the module is stripped and there is no redirect for it - if !buf.is_empty() { - this.shared.ensure_dir(&this.dst)?; - let joint_dst = this.dst.join("index.html"); - scx.fs.write(&joint_dst, buf)?; - } + 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); - let m = match item.inner { - clean::StrippedItem(box clean::ModuleItem(m)) | - clean::ModuleItem(m) => m, - _ => unreachable!() - }; + info!("Recursing into {}", self.dst.display()); - // Render sidebar-items.js used throughout this module. - if !this.render_redirect_pages { - let items = this.build_sidebar_items(&m); - let js_dst = this.dst.join("sidebar-items.js"); - let mut v = Vec::new(); - try_err!(write!(&mut v, "initSidebarItems({});", - as_json(&items)), &js_dst); - scx.fs.write(&js_dst, &v)?; - } + let mut buf = Vec::new(); + self.render_item(&mut buf, &item, false).unwrap(); + // 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)?; + } - for item in m.items { - f(this, item); - } + 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 mut v = Vec::new(); + try_err!(write!(&mut v, "initSidebarItems({});", + as_json(&items)), &js_dst); + scx.fs.write(&js_dst, &v)?; + } + + for item in m.items { + f(self, item); + } - Ok(()) - })?; + 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 mut buf = Vec::new(); self.render_item(&mut buf, &item, true).unwrap(); @@ -2399,7 +2210,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('/'); }); @@ -2549,29 +2360,39 @@ fn full_path(cx: &Context, item: &clean::Item) -> String { s } -fn shorter(s: Option<&str>) -> String { - match s { - Some(s) => s.lines() - .skip_while(|s| s.chars().all(|c| c.is_whitespace())) - .take_while(|line|{ - (*line).chars().any(|chr|{ - !chr.is_whitespace() - }) - }).collect::<Vec<_>>().join("\n"), - None => String::new() - } -} - #[inline] fn plain_summary_line(s: Option<&str>) -> String { - let line = shorter(s).replace("\n", " "); - markdown::plain_summary_line_full(&line[..], false) -} - -#[inline] -fn plain_summary_line_short(s: Option<&str>) -> String { - let line = shorter(s).replace("\n", " "); - markdown::plain_summary_line_full(&line[..], true) + let s = s.unwrap_or(""); + // This essentially gets the first paragraph of text in one line. + let mut line = s.lines() + .skip_while(|line| line.chars().all(|c| c.is_whitespace())) + .take_while(|line| line.chars().any(|c| !c.is_whitespace())) + .fold(String::new(), |mut acc, line| { + acc.push_str(line); + acc.push(' '); + acc + }); + // remove final whitespace + line.pop(); + markdown::plain_summary_line(&line[..]) +} + +fn shorten(s: String) -> String { + if s.chars().count() > 60 { + let mut len = 0; + let mut ret = s.split_whitespace() + .take_while(|p| { + // + 1 for the added character after the word. + len += p.chars().count() + 1; + len < 60 + }) + .collect::<Vec<_>>() + .join(" "); + ret.push('…'); + ret + } else { + s + } } fn document(w: &mut fmt::Formatter<'_>, cx: &Context, item: &clean::Item) -> fmt::Result { @@ -2584,13 +2405,14 @@ fn document(w: &mut fmt::Formatter<'_>, cx: &Context, item: &clean::Item) -> fmt } /// Render md_text as markdown. -fn render_markdown(w: &mut fmt::Formatter<'_>, - cx: &Context, - md_text: &str, - links: Vec<(String, String)>, - prefix: &str, - is_hidden: bool) - -> fmt::Result { +fn render_markdown( + w: &mut fmt::Formatter<'_>, + cx: &Context, + md_text: &str, + links: Vec<(String, String)>, + prefix: &str, + is_hidden: bool, +) -> fmt::Result { let mut ids = cx.id_map.borrow_mut(); write!(w, "<div class='docblock{}'>{}{}</div>", if is_hidden { " hidden" } else { "" }, @@ -2604,7 +2426,8 @@ fn document_short( cx: &Context, item: &clean::Item, link: AssocItemLink<'_>, - prefix: &str, is_hidden: bool + prefix: &str, + is_hidden: bool, ) -> fmt::Result { if let Some(s) = item.doc_value() { let markdown = if s.contains('\n') { @@ -2816,19 +2639,19 @@ fn item_module(w: &mut fmt::Formatter<'_>, cx: &Context, match myitem.inner { clean::ExternCrateItem(ref name, ref src) => { - use crate::html::format::HRef; + use crate::html::format::anchor; match *src { Some(ref src) => { write!(w, "<tr><td><code>{}extern crate {} as {};", VisSpace(&myitem.visibility), - HRef::new(myitem.def_id, src), + anchor(myitem.def_id, src), name)? } None => { write!(w, "<tr><td><code>{}extern crate {};", VisSpace(&myitem.visibility), - HRef::new(myitem.def_id, name))? + anchor(myitem.def_id, name))? } } write!(w, "</code></td></tr>")?; @@ -4263,9 +4086,10 @@ fn render_impl(w: &mut fmt::Formatter<'_>, cx: &Context, i: &Impl, link: AssocIt RenderMode::ForDeref { mut_: deref_mut_ } => should_render_item(&item, deref_mut_), }; - let (is_hidden, extra_class) = if trait_.is_none() || - item.doc_value().is_some() || - item.inner.is_associated() { + let (is_hidden, extra_class) = if (trait_.is_none() || + item.doc_value().is_some() || + item.inner.is_associated()) && + !is_default_item { (false, "") } else { (true, " hidden") @@ -5048,27 +4872,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 +4928,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()) |
