2 use crate::docfs::PathError;
3 use crate::fold::DocFolder;
4 use crate::html::format::Buffer;
5 use crate::html::highlight;
6 use crate::html::layout;
7 use crate::html::render::{Error, SharedContext, BASIC_KEYWORDS};
8 use rustc_span::source_map::FileName;
11 use std::path::{Component, Path, PathBuf};
15 scx: &mut SharedContext,
17 ) -> Result<clean::Crate, Error> {
18 info!("emitting source files");
19 let dst = dst.join("src").join(&krate.name);
20 scx.ensure_dir(&dst)?;
21 let mut folder = SourceCollector { dst, scx };
22 Ok(folder.fold_crate(krate))
25 /// Helper struct to render all source code to HTML pages
26 struct SourceCollector<'a> {
27 scx: &'a mut SharedContext,
29 /// Root destination to place all HTML output into
33 impl<'a> DocFolder for SourceCollector<'a> {
34 fn fold_item(&mut self, item: clean::Item) -> Option<clean::Item> {
35 // If we're including source files, and we haven't seen this file yet,
36 // then we need to render it out to the filesystem.
37 if self.scx.include_sources
38 // skip all invalid or macro spans
39 && item.source.filename.is_real()
40 // skip non-local items
41 && item.def_id.is_local()
43 // If it turns out that we couldn't read this file, then we probably
44 // can't read any of the files (generating html output from json or
45 // something like that), so just don't include sources for the
46 // entire crate. The other option is maintaining this mapping on a
47 // per-file basis, but that's probably not worth it...
48 self.scx.include_sources = match self.emit_source(&item.source.filename) {
52 "warning: source code was requested to be rendered, \
53 but processing `{}` had an error: {}",
54 item.source.filename, e
56 println!(" skipping rendering of source code");
61 self.fold_item_recur(item)
65 impl<'a> SourceCollector<'a> {
66 /// Renders the given filename into its corresponding HTML source file.
67 fn emit_source(&mut self, filename: &FileName) -> Result<(), Error> {
68 let p = match *filename {
69 FileName::Real(ref file) => file,
72 if self.scx.local_sources.contains_key(&**p) {
73 // We've already emitted this source
77 let contents = match fs::read_to_string(&p) {
78 Ok(contents) => contents,
80 return Err(Error::new(e, &p));
84 // Remove the utf-8 BOM if any
86 if contents.starts_with("\u{feff}") { &contents[3..] } else { &contents[..] };
88 // Create the intermediate directories
89 let mut cur = self.dst.clone();
90 let mut root_path = String::from("../../");
91 let mut href = String::new();
92 clean_path(&self.scx.src_root, &p, false, |component| {
94 root_path.push_str("../");
95 href.push_str(&component.to_string_lossy());
98 self.scx.ensure_dir(&cur)?;
99 let mut fname = p.file_name().expect("source has no filename").to_os_string();
102 href.push_str(&fname.to_string_lossy());
106 cur.file_name().expect("failed to get file name").to_string_lossy()
108 let desc = format!("Source to the Rust file `{}`.", filename);
109 let page = layout::Page {
112 root_path: &root_path,
113 static_root_path: self.scx.static_root_path.as_deref(),
115 keywords: BASIC_KEYWORDS,
116 resource_suffix: &self.scx.resource_suffix,
117 extra_scripts: &[&format!("source-files{}", self.scx.resource_suffix)],
118 static_extra_scripts: &[&format!("source-script{}", self.scx.resource_suffix)],
120 let v = layout::render(
124 |buf: &mut _| print_src(buf, &contents),
127 self.scx.fs.write(&cur, v.as_bytes())?;
128 self.scx.local_sources.insert(p.clone(), href);
133 /// Takes a path to a source file and cleans the path to it. This canonicalizes
134 /// things like ".." to components which preserve the "top down" hierarchy of a
135 /// static HTML tree. Each component in the cleaned path will be passed as an
136 /// argument to `f`. The very last component of the path (ie the file name) will
137 /// be passed to `f` if `keep_filename` is true, and ignored otherwise.
138 pub fn clean_path<F>(src_root: &Path, p: &Path, keep_filename: bool, mut f: F)
142 // make it relative, if possible
143 let p = p.strip_prefix(src_root).unwrap_or(p);
145 let mut iter = p.components().peekable();
147 while let Some(c) = iter.next() {
148 if !keep_filename && iter.peek().is_none() {
153 Component::ParentDir => f("up".as_ref()),
154 Component::Normal(c) => f(c),
160 /// Wrapper struct to render the source code of a file. This will do things like
161 /// adding line numbers to the left-hand side.
162 fn print_src(buf: &mut Buffer, s: &str) {
163 let lines = s.lines().count();
170 write!(buf, "<pre class=\"line-numbers\">");
172 write!(buf, "<span id=\"{0}\">{0:1$}</span>\n", i, cols);
174 write!(buf, "</pre>");
175 write!(buf, "{}", highlight::render_with_highlighting(s, None, None, None));