]> git.proxmox.com Git - rustc.git/blame - src/librustdoc/html/sources.rs
New upstream version 1.48.0~beta.8+dfsg1
[rustc.git] / src / librustdoc / html / sources.rs
CommitLineData
e1599b0c
XL
1use crate::clean;
2use crate::docfs::PathError;
3dfed10e 3use crate::error::Error;
e1599b0c 4use crate::fold::DocFolder;
dfeec247
XL
5use crate::html::format::Buffer;
6use crate::html::highlight;
e1599b0c 7use crate::html::layout;
3dfed10e 8use crate::html::render::{SharedContext, BASIC_KEYWORDS};
ba9703b0 9use rustc_hir::def_id::LOCAL_CRATE;
dfeec247 10use rustc_span::source_map::FileName;
e1599b0c
XL
11use std::ffi::OsStr;
12use std::fs;
13use std::path::{Component, Path, PathBuf};
e1599b0c 14
dfeec247
XL
15crate fn render(
16 dst: &Path,
17 scx: &mut SharedContext,
18 krate: clean::Crate,
19) -> Result<clean::Crate, Error> {
e1599b0c
XL
20 info!("emitting source files");
21 let dst = dst.join("src").join(&krate.name);
22 scx.ensure_dir(&dst)?;
dfeec247 23 let mut folder = SourceCollector { dst, scx };
e1599b0c
XL
24 Ok(folder.fold_crate(krate))
25}
26
27/// Helper struct to render all source code to HTML pages
28struct SourceCollector<'a> {
29 scx: &'a mut SharedContext,
30
31 /// Root destination to place all HTML output into
32 dst: PathBuf,
33}
34
35impl<'a> DocFolder for SourceCollector<'a> {
36 fn fold_item(&mut self, item: clean::Item) -> Option<clean::Item> {
37 // If we're including source files, and we haven't seen this file yet,
38 // then we need to render it out to the filesystem.
39 if self.scx.include_sources
ba9703b0 40 // skip all synthetic "files"
e1599b0c 41 && item.source.filename.is_real()
ba9703b0
XL
42 // skip non-local files
43 && item.source.cnum == LOCAL_CRATE
dfeec247 44 {
e1599b0c
XL
45 // If it turns out that we couldn't read this file, then we probably
46 // can't read any of the files (generating html output from json or
47 // something like that), so just don't include sources for the
48 // entire crate. The other option is maintaining this mapping on a
49 // per-file basis, but that's probably not worth it...
dfeec247 50 self.scx.include_sources = match self.emit_source(&item.source.filename) {
e1599b0c
XL
51 Ok(()) => true,
52 Err(e) => {
dfeec247
XL
53 println!(
54 "warning: source code was requested to be rendered, \
1b1a35ee 55 but processing `{}` had an error: {}",
dfeec247
XL
56 item.source.filename, e
57 );
e1599b0c
XL
58 println!(" skipping rendering of source code");
59 false
60 }
61 };
62 }
63 self.fold_item_recur(item)
64 }
65}
66
67impl<'a> SourceCollector<'a> {
68 /// Renders the given filename into its corresponding HTML source file.
69 fn emit_source(&mut self, filename: &FileName) -> Result<(), Error> {
70 let p = match *filename {
ba9703b0 71 FileName::Real(ref file) => file.local_path().to_path_buf(),
e1599b0c
XL
72 _ => return Ok(()),
73 };
ba9703b0 74 if self.scx.local_sources.contains_key(&*p) {
e1599b0c
XL
75 // We've already emitted this source
76 return Ok(());
77 }
78
3dfed10e 79 let mut contents = match fs::read_to_string(&p) {
e1599b0c
XL
80 Ok(contents) => contents,
81 Err(e) => {
82 return Err(Error::new(e, &p));
83 }
84 };
85
86 // Remove the utf-8 BOM if any
3dfed10e
XL
87 if contents.starts_with("\u{feff}") {
88 contents.drain(..3);
89 }
e1599b0c
XL
90
91 // Create the intermediate directories
92 let mut cur = self.dst.clone();
93 let mut root_path = String::from("../../");
94 let mut href = String::new();
95 clean_path(&self.scx.src_root, &p, false, |component| {
96 cur.push(component);
97 root_path.push_str("../");
98 href.push_str(&component.to_string_lossy());
99 href.push('/');
100 });
101 self.scx.ensure_dir(&cur)?;
dfeec247 102 let mut fname = p.file_name().expect("source has no filename").to_os_string();
e1599b0c
XL
103 fname.push(".html");
104 cur.push(&fname);
105 href.push_str(&fname.to_string_lossy());
106
dfeec247
XL
107 let title = format!(
108 "{} -- source",
109 cur.file_name().expect("failed to get file name").to_string_lossy()
110 );
e1599b0c
XL
111 let desc = format!("Source to the Rust file `{}`.", filename);
112 let page = layout::Page {
113 title: &title,
114 css_class: "source",
115 root_path: &root_path,
116 static_root_path: self.scx.static_root_path.as_deref(),
117 description: &desc,
118 keywords: BASIC_KEYWORDS,
119 resource_suffix: &self.scx.resource_suffix,
120 extra_scripts: &[&format!("source-files{}", self.scx.resource_suffix)],
121 static_extra_scripts: &[&format!("source-script{}", self.scx.resource_suffix)],
122 };
dfeec247
XL
123 let v = layout::render(
124 &self.scx.layout,
125 &page,
126 "",
3dfed10e
XL
127 |buf: &mut _| print_src(buf, contents),
128 &self.scx.style_files,
dfeec247 129 );
e1599b0c 130 self.scx.fs.write(&cur, v.as_bytes())?;
f035d41b 131 self.scx.local_sources.insert(p, href);
e1599b0c
XL
132 Ok(())
133 }
134}
135
136/// Takes a path to a source file and cleans the path to it. This canonicalizes
137/// things like ".." to components which preserve the "top down" hierarchy of a
138/// static HTML tree. Each component in the cleaned path will be passed as an
139/// argument to `f`. The very last component of the path (ie the file name) will
140/// be passed to `f` if `keep_filename` is true, and ignored otherwise.
141pub fn clean_path<F>(src_root: &Path, p: &Path, keep_filename: bool, mut f: F)
142where
143 F: FnMut(&OsStr),
144{
145 // make it relative, if possible
146 let p = p.strip_prefix(src_root).unwrap_or(p);
147
148 let mut iter = p.components().peekable();
149
150 while let Some(c) = iter.next() {
151 if !keep_filename && iter.peek().is_none() {
152 break;
153 }
154
155 match c {
156 Component::ParentDir => f("up".as_ref()),
157 Component::Normal(c) => f(c),
158 _ => continue,
159 }
160 }
161}
162
163/// Wrapper struct to render the source code of a file. This will do things like
164/// adding line numbers to the left-hand side.
3dfed10e 165fn print_src(buf: &mut Buffer, s: String) {
e1599b0c
XL
166 let lines = s.lines().count();
167 let mut cols = 0;
168 let mut tmp = lines;
169 while tmp > 0 {
170 cols += 1;
171 tmp /= 10;
172 }
173 write!(buf, "<pre class=\"line-numbers\">");
174 for i in 1..=lines {
175 write!(buf, "<span id=\"{0}\">{0:1$}</span>\n", i, cols);
176 }
177 write!(buf, "</pre>");
dfeec247 178 write!(buf, "{}", highlight::render_with_highlighting(s, None, None, None));
e1599b0c 179}