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_hir
::def_id
::LOCAL_CRATE
;
9 use rustc_span
::source_map
::FileName
;
12 use std
::path
::{Component, Path, PathBuf}
;
16 scx
: &mut SharedContext
,
18 ) -> Result
<clean
::Crate
, Error
> {
19 info
!("emitting source files");
20 let dst
= dst
.join("src").join(&krate
.name
);
21 scx
.ensure_dir(&dst
)?
;
22 let mut folder
= SourceCollector { dst, scx }
;
23 Ok(folder
.fold_crate(krate
))
26 /// Helper struct to render all source code to HTML pages
27 struct SourceCollector
<'a
> {
28 scx
: &'a
mut SharedContext
,
30 /// Root destination to place all HTML output into
34 impl<'a
> DocFolder
for SourceCollector
<'a
> {
35 fn fold_item(&mut self, item
: clean
::Item
) -> Option
<clean
::Item
> {
36 // If we're including source files, and we haven't seen this file yet,
37 // then we need to render it out to the filesystem.
38 if self.scx
.include_sources
39 // skip all synthetic "files"
40 && item
.source
.filename
.is_real()
41 // skip non-local files
42 && item
.source
.cnum
== LOCAL_CRATE
44 // If it turns out that we couldn't read this file, then we probably
45 // can't read any of the files (generating html output from json or
46 // something like that), so just don't include sources for the
47 // entire crate. The other option is maintaining this mapping on a
48 // per-file basis, but that's probably not worth it...
49 self.scx
.include_sources
= match self.emit_source(&item
.source
.filename
) {
53 "warning: source code was requested to be rendered, \
54 but processing `{}` had an error: {}",
55 item
.source
.filename
, e
57 println
!(" skipping rendering of source code");
62 self.fold_item_recur(item
)
66 impl<'a
> SourceCollector
<'a
> {
67 /// Renders the given filename into its corresponding HTML source file.
68 fn emit_source(&mut self, filename
: &FileName
) -> Result
<(), Error
> {
69 let p
= match *filename
{
70 FileName
::Real(ref file
) => file
.local_path().to_path_buf(),
73 if self.scx
.local_sources
.contains_key(&*p
) {
74 // We've already emitted this source
78 let contents
= match fs
::read_to_string(&p
) {
79 Ok(contents
) => contents
,
81 return Err(Error
::new(e
, &p
));
85 // Remove the utf-8 BOM if any
87 if contents
.starts_with("\u{feff}") { &contents[3..] }
else { &contents[..] }
;
89 // Create the intermediate directories
90 let mut cur
= self.dst
.clone();
91 let mut root_path
= String
::from("../../");
92 let mut href
= String
::new();
93 clean_path(&self.scx
.src_root
, &p
, false, |component
| {
95 root_path
.push_str("../");
96 href
.push_str(&component
.to_string_lossy());
99 self.scx
.ensure_dir(&cur
)?
;
100 let mut fname
= p
.file_name().expect("source has no filename").to_os_string();
103 href
.push_str(&fname
.to_string_lossy());
107 cur
.file_name().expect("failed to get file name").to_string_lossy()
109 let desc
= format
!("Source to the Rust file `{}`.", filename
);
110 let page
= layout
::Page
{
113 root_path
: &root_path
,
114 static_root_path
: self.scx
.static_root_path
.as_deref(),
116 keywords
: BASIC_KEYWORDS
,
117 resource_suffix
: &self.scx
.resource_suffix
,
118 extra_scripts
: &[&format
!("source-files{}", self.scx
.resource_suffix
)],
119 static_extra_scripts
: &[&format
!("source-script{}", self.scx
.resource_suffix
)],
121 let v
= layout
::render(
125 |buf
: &mut _
| print_src(buf
, &contents
),
128 self.scx
.fs
.write(&cur
, v
.as_bytes())?
;
129 self.scx
.local_sources
.insert(p
.clone(), href
);
134 /// Takes a path to a source file and cleans the path to it. This canonicalizes
135 /// things like ".." to components which preserve the "top down" hierarchy of a
136 /// static HTML tree. Each component in the cleaned path will be passed as an
137 /// argument to `f`. The very last component of the path (ie the file name) will
138 /// be passed to `f` if `keep_filename` is true, and ignored otherwise.
139 pub fn clean_path
<F
>(src_root
: &Path
, p
: &Path
, keep_filename
: bool
, mut f
: F
)
143 // make it relative, if possible
144 let p
= p
.strip_prefix(src_root
).unwrap_or(p
);
146 let mut iter
= p
.components().peekable();
148 while let Some(c
) = iter
.next() {
149 if !keep_filename
&& iter
.peek().is_none() {
154 Component
::ParentDir
=> f("up".as_ref()),
155 Component
::Normal(c
) => f(c
),
161 /// Wrapper struct to render the source code of a file. This will do things like
162 /// adding line numbers to the left-hand side.
163 fn print_src(buf
: &mut Buffer
, s
: &str) {
164 let lines
= s
.lines().count();
171 write
!(buf
, "<pre class=\"line-numbers\">");
173 write
!(buf
, "<span id=\"{0}\">{0:1$}</span>\n", i
, cols
);
175 write
!(buf
, "</pre>");
176 write
!(buf
, "{}", highlight
::render_with_highlighting(s
, None
, None
, None
));