2 use crate::docfs
::PathError
;
3 use crate::error
::Error
;
4 use crate::fold
::DocFolder
;
5 use crate::html
::format
::Buffer
;
6 use crate::html
::highlight
;
7 use crate::html
::layout
;
8 use crate::html
::render
::{SharedContext, BASIC_KEYWORDS}
;
9 use rustc_hir
::def_id
::LOCAL_CRATE
;
10 use rustc_span
::source_map
::FileName
;
13 use std
::path
::{Component, Path, PathBuf}
;
17 scx
: &mut SharedContext
,
19 ) -> Result
<clean
::Crate
, Error
> {
20 info
!("emitting source files");
21 let dst
= dst
.join("src").join(&krate
.name
);
22 scx
.ensure_dir(&dst
)?
;
23 let mut folder
= SourceCollector { dst, scx }
;
24 Ok(folder
.fold_crate(krate
))
27 /// Helper struct to render all source code to HTML pages
28 struct SourceCollector
<'a
> {
29 scx
: &'a
mut SharedContext
,
31 /// Root destination to place all HTML output into
35 impl<'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
40 // skip all synthetic "files"
41 && item
.source
.filename
.is_real()
42 // skip non-local files
43 && item
.source
.cnum
== LOCAL_CRATE
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...
50 self.scx
.include_sources
= match self.emit_source(&item
.source
.filename
) {
54 "warning: source code was requested to be rendered, \
55 but processing `{}` had an error: {}",
56 item
.source
.filename
, e
58 println
!(" skipping rendering of source code");
63 self.fold_item_recur(item
)
67 impl<'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
{
71 FileName
::Real(ref file
) => file
.local_path().to_path_buf(),
74 if self.scx
.local_sources
.contains_key(&*p
) {
75 // We've already emitted this source
79 let mut contents
= match fs
::read_to_string(&p
) {
80 Ok(contents
) => contents
,
82 return Err(Error
::new(e
, &p
));
86 // Remove the utf-8 BOM if any
87 if contents
.starts_with("\u{feff}") {
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
| {
97 root_path
.push_str("../");
98 href
.push_str(&component
.to_string_lossy());
101 self.scx
.ensure_dir(&cur
)?
;
102 let mut fname
= p
.file_name().expect("source has no filename").to_os_string();
105 href
.push_str(&fname
.to_string_lossy());
109 cur
.file_name().expect("failed to get file name").to_string_lossy()
111 let desc
= format
!("Source to the Rust file `{}`.", filename
);
112 let page
= layout
::Page
{
115 root_path
: &root_path
,
116 static_root_path
: self.scx
.static_root_path
.as_deref(),
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
)],
123 let v
= layout
::render(
127 |buf
: &mut _
| print_src(buf
, contents
),
128 &self.scx
.style_files
,
130 self.scx
.fs
.write(&cur
, v
.as_bytes())?
;
131 self.scx
.local_sources
.insert(p
, href
);
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.
141 pub fn clean_path
<F
>(src_root
: &Path
, p
: &Path
, keep_filename
: bool
, mut f
: F
)
145 // make it relative, if possible
146 let p
= p
.strip_prefix(src_root
).unwrap_or(p
);
148 let mut iter
= p
.components().peekable();
150 while let Some(c
) = iter
.next() {
151 if !keep_filename
&& iter
.peek().is_none() {
156 Component
::ParentDir
=> f("up".as_ref()),
157 Component
::Normal(c
) => f(c
),
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.
165 fn print_src(buf
: &mut Buffer
, s
: String
) {
166 let lines
= s
.lines().count();
173 write
!(buf
, "<pre class=\"line-numbers\">");
175 write
!(buf
, "<span id=\"{0}\">{0:1$}</span>\n", i
, cols
);
177 write
!(buf
, "</pre>");
178 write
!(buf
, "{}", highlight
::render_with_highlighting(s
, None
, None
, None
));