2 use crate::docfs::PathError;
3 use crate::fold::DocFolder;
4 use crate::html::layout;
5 use crate::html::render::{Error, SharedContext, BASIC_KEYWORDS};
6 use crate::html::highlight;
7 use crate::html::format::Buffer;
10 use std::path::{Component, Path, PathBuf};
11 use syntax::source_map::FileName;
13 crate fn render(dst: &Path, scx: &mut SharedContext,
14 krate: clean::Crate) -> Result<clean::Crate, Error> {
15 info!("emitting source files");
16 let dst = dst.join("src").join(&krate.name);
17 scx.ensure_dir(&dst)?;
18 let mut folder = SourceCollector {
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...
49 .include_sources = match self.emit_source(&item.source.filename) {
52 println!("warning: source code was requested to be rendered, \
53 but processing `{}` had an error: {}",
54 item.source.filename, e);
55 println!(" skipping rendering of source code");
60 self.fold_item_recur(item)
64 impl<'a> SourceCollector<'a> {
65 /// Renders the given filename into its corresponding HTML source file.
66 fn emit_source(&mut self, filename: &FileName) -> Result<(), Error> {
67 let p = match *filename {
68 FileName::Real(ref file) => file,
71 if self.scx.local_sources.contains_key(&**p) {
72 // We've already emitted this source
76 let contents = match fs::read_to_string(&p) {
77 Ok(contents) => contents,
79 return Err(Error::new(e, &p));
83 // Remove the utf-8 BOM if any
84 let contents = if contents.starts_with("\u{feff}") {
90 // Create the intermediate directories
91 let mut cur = self.dst.clone();
92 let mut root_path = String::from("../../");
93 let mut href = String::new();
94 clean_path(&self.scx.src_root, &p, false, |component| {
96 root_path.push_str("../");
97 href.push_str(&component.to_string_lossy());
100 self.scx.ensure_dir(&cur)?;
101 let mut fname = p.file_name()
102 .expect("source has no filename")
106 href.push_str(&fname.to_string_lossy());
108 let title = format!("{} -- source", cur.file_name().expect("failed to get file name")
110 let desc = format!("Source to the Rust file `{}`.", filename);
111 let page = layout::Page {
114 root_path: &root_path,
115 static_root_path: self.scx.static_root_path.as_deref(),
117 keywords: BASIC_KEYWORDS,
118 resource_suffix: &self.scx.resource_suffix,
119 extra_scripts: &[&format!("source-files{}", self.scx.resource_suffix)],
120 static_extra_scripts: &[&format!("source-script{}", self.scx.resource_suffix)],
122 let v = layout::render(&self.scx.layout,
123 &page, "", |buf: &mut _| print_src(buf, &contents),
125 self.scx.fs.write(&cur, v.as_bytes())?;
126 self.scx.local_sources.insert(p.clone(), href);
131 /// Takes a path to a source file and cleans the path to it. This canonicalizes
132 /// things like ".." to components which preserve the "top down" hierarchy of a
133 /// static HTML tree. Each component in the cleaned path will be passed as an
134 /// argument to `f`. The very last component of the path (ie the file name) will
135 /// be passed to `f` if `keep_filename` is true, and ignored otherwise.
136 pub fn clean_path<F>(src_root: &Path, p: &Path, keep_filename: bool, mut f: F)
140 // make it relative, if possible
141 let p = p.strip_prefix(src_root).unwrap_or(p);
143 let mut iter = p.components().peekable();
145 while let Some(c) = iter.next() {
146 if !keep_filename && iter.peek().is_none() {
151 Component::ParentDir => f("up".as_ref()),
152 Component::Normal(c) => f(c),
158 /// Wrapper struct to render the source code of a file. This will do things like
159 /// adding line numbers to the left-hand side.
160 fn print_src(buf: &mut Buffer, s: &str) {
161 let lines = s.lines().count();
168 write!(buf, "<pre class=\"line-numbers\">");
170 write!(buf, "<span id=\"{0}\">{0:1$}</span>\n", i, cols);
172 write!(buf, "</pre>");
174 highlight::render_with_highlighting(s, None, None, None));