]>
Commit | Line | Data |
---|---|---|
ba9703b0 | 1 | use std::fs::{create_dir_all, File}; |
c34b1796 | 2 | use std::io::prelude::*; |
a1dfa0c6 | 3 | use std::path::PathBuf; |
1a4d82fc | 4 | |
60c5eb7d | 5 | use rustc_feature::UnstableFeatures; |
dfeec247 XL |
6 | use rustc_span::edition::Edition; |
7 | use rustc_span::source_map::DUMMY_SP; | |
1a4d82fc | 8 | |
9fa01778 | 9 | use crate::config::{Options, RenderOptions}; |
dfeec247 | 10 | use crate::externalfiles::{load_string, LoadStringError}; |
9fa01778 XL |
11 | use crate::html::escape::Escape; |
12 | use crate::html::markdown; | |
dfeec247 XL |
13 | use crate::html::markdown::{find_testable_code, ErrorCodes, IdMap, Markdown, MarkdownWithToc}; |
14 | use crate::test::{Collector, TestOptions}; | |
1a4d82fc | 15 | |
cc61c64b | 16 | /// Separate any lines at the start of the file that begin with `# ` or `%`. |
416331ca | 17 | fn extract_leading_metadata(s: &str) -> (Vec<&str>, &str) { |
1a4d82fc | 18 | let mut metadata = Vec::new(); |
c1a9b12d | 19 | let mut count = 0; |
cc61c64b | 20 | |
1a4d82fc | 21 | for line in s.lines() { |
74b04a01 | 22 | if line.starts_with("# ") || line.starts_with('%') { |
cc61c64b | 23 | // trim the whitespace after the symbol |
0731742a | 24 | metadata.push(line[1..].trim_start()); |
c1a9b12d | 25 | count += line.len() + 1; |
1a4d82fc | 26 | } else { |
c1a9b12d | 27 | return (metadata, &s[count..]); |
1a4d82fc JJ |
28 | } |
29 | } | |
cc61c64b XL |
30 | |
31 | // if we're here, then all lines were metadata `# ` or `%` lines. | |
1a4d82fc JJ |
32 | (metadata, "") |
33 | } | |
34 | ||
0731742a XL |
35 | /// Render `input` (e.g., "foo.md") into an HTML file in `output` |
36 | /// (e.g., output = "bar" => "bar/foo.html"). | |
48663c56 XL |
37 | pub fn render( |
38 | input: PathBuf, | |
39 | options: RenderOptions, | |
dfeec247 XL |
40 | diag: &rustc_errors::Handler, |
41 | edition: Edition, | |
48663c56 | 42 | ) -> i32 { |
ba9703b0 XL |
43 | if let Err(e) = create_dir_all(&options.output) { |
44 | diag.struct_err(&format!("{}: {}", options.output.display(), e)).emit(); | |
45 | return 4; | |
46 | } | |
47 | ||
a1dfa0c6 | 48 | let mut output = options.output; |
e1599b0c | 49 | output.push(input.file_name().unwrap()); |
1a4d82fc JJ |
50 | output.set_extension("html"); |
51 | ||
52 | let mut css = String::new(); | |
a1dfa0c6 | 53 | for name in &options.markdown_css { |
1a4d82fc | 54 | let s = format!("<link rel=\"stylesheet\" type=\"text/css\" href=\"{}\">\n", name); |
85aaf69f | 55 | css.push_str(&s) |
1a4d82fc JJ |
56 | } |
57 | ||
a1dfa0c6 | 58 | let input_str = match load_string(&input, diag) { |
c30ab7b3 SL |
59 | Ok(s) => s, |
60 | Err(LoadStringError::ReadFail) => return 1, | |
61 | Err(LoadStringError::BadUtf8) => return 2, | |
62 | }; | |
dfeec247 XL |
63 | let playground_url = options.markdown_playground_url.or(options.playground_url); |
64 | let playground = playground_url.map(|url| markdown::Playground { crate_name: None, url }); | |
1a4d82fc | 65 | |
c34b1796 | 66 | let mut out = match File::create(&output) { |
1a4d82fc | 67 | Err(e) => { |
94b46f34 | 68 | diag.struct_err(&format!("{}: {}", output.display(), e)).emit(); |
1a4d82fc JJ |
69 | return 4; |
70 | } | |
a1dfa0c6 | 71 | Ok(f) => f, |
1a4d82fc JJ |
72 | }; |
73 | ||
85aaf69f | 74 | let (metadata, text) = extract_leading_metadata(&input_str); |
9346a6ac | 75 | if metadata.is_empty() { |
94b46f34 | 76 | diag.struct_err("invalid markdown file: no initial lines starting with `# ` or `%`").emit(); |
1a4d82fc JJ |
77 | return 5; |
78 | } | |
85aaf69f | 79 | let title = metadata[0]; |
1a4d82fc | 80 | |
b7449926 XL |
81 | let mut ids = IdMap::new(); |
82 | let error_codes = ErrorCodes::from(UnstableFeatures::from_environment().is_nightly_build()); | |
a1dfa0c6 | 83 | let text = if !options.markdown_no_toc { |
416331ca | 84 | MarkdownWithToc(text, &mut ids, error_codes, edition, &playground).to_string() |
1a4d82fc | 85 | } else { |
416331ca | 86 | Markdown(text, &[], &mut ids, error_codes, edition, &playground).to_string() |
1a4d82fc JJ |
87 | }; |
88 | ||
89 | let err = write!( | |
90 | &mut out, | |
91 | r#"<!DOCTYPE html> | |
92 | <html lang="en"> | |
93 | <head> | |
94 | <meta charset="utf-8"> | |
85aaf69f | 95 | <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
1a4d82fc JJ |
96 | <meta name="generator" content="rustdoc"> |
97 | <title>{title}</title> | |
98 | ||
99 | {css} | |
100 | {in_header} | |
101 | </head> | |
102 | <body class="rustdoc"> | |
103 | <!--[if lte IE 8]> | |
104 | <div class="warning"> | |
105 | This old browser is unsupported and will most likely display funky | |
106 | things. | |
107 | </div> | |
108 | <![endif]--> | |
109 | ||
110 | {before_content} | |
111 | <h1 class="title">{title}</h1> | |
112 | {text} | |
1a4d82fc JJ |
113 | {after_content} |
114 | </body> | |
115 | </html>"#, | |
116 | title = Escape(title), | |
117 | css = css, | |
a1dfa0c6 XL |
118 | in_header = options.external_html.in_header, |
119 | before_content = options.external_html.before_content, | |
0531ce1d | 120 | text = text, |
a1dfa0c6 | 121 | after_content = options.external_html.after_content, |
ff7c6d11 | 122 | ); |
1a4d82fc JJ |
123 | |
124 | match err { | |
125 | Err(e) => { | |
94b46f34 | 126 | diag.struct_err(&format!("cannot write to `{}`: {}", output.display(), e)).emit(); |
1a4d82fc JJ |
127 | 6 |
128 | } | |
ff7c6d11 | 129 | Ok(_) => 0, |
1a4d82fc JJ |
130 | } |
131 | } | |
132 | ||
9fa01778 | 133 | /// Runs any tests/code examples in the markdown file `input`. |
dfeec247 | 134 | pub fn test(mut options: Options, diag: &rustc_errors::Handler) -> i32 { |
a1dfa0c6 | 135 | let input_str = match load_string(&options.input, diag) { |
c30ab7b3 SL |
136 | Ok(s) => s, |
137 | Err(LoadStringError::ReadFail) => return 1, | |
138 | Err(LoadStringError::BadUtf8) => return 2, | |
139 | }; | |
1a4d82fc | 140 | |
9346a6ac AL |
141 | let mut opts = TestOptions::default(); |
142 | opts.no_crate_inject = true; | |
a1dfa0c6 | 143 | opts.display_warnings = options.display_warnings; |
dfeec247 XL |
144 | let mut collector = Collector::new( |
145 | options.input.display().to_string(), | |
146 | options.clone(), | |
147 | true, | |
148 | opts, | |
149 | None, | |
150 | Some(options.input), | |
151 | options.enable_per_target_ignores, | |
152 | ); | |
b7449926 XL |
153 | collector.set_position(DUMMY_SP); |
154 | let codes = ErrorCodes::from(UnstableFeatures::from_environment().is_nightly_build()); | |
48663c56 | 155 | |
e1599b0c | 156 | find_testable_code(&input_str, &mut collector, codes, options.enable_per_target_ignores); |
48663c56 | 157 | |
a1dfa0c6 | 158 | options.test_args.insert(0, "rustdoctest".to_string()); |
dfeec247 XL |
159 | testing::test_main( |
160 | &options.test_args, | |
161 | collector.tests, | |
162 | Some(testing::Options::new().display_output(options.display_warnings)), | |
163 | ); | |
1a4d82fc JJ |
164 | 0 |
165 | } |