]> git.lizzy.rs Git - rust.git/blob - src/librustdoc/markdown.rs
std: Add a new top-level thread_local module
[rust.git] / src / librustdoc / markdown.rs
1 // Copyright 2014 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 use std::io;
12 use std::string::String;
13
14 use core;
15 use getopts;
16 use testing;
17
18 use externalfiles::ExternalHtml;
19
20 use html::escape::Escape;
21 use html::markdown;
22 use html::markdown::{Markdown, MarkdownWithToc, find_testable_code, reset_headers};
23 use test::Collector;
24
25 /// Separate any lines at the start of the file that begin with `%`.
26 fn extract_leading_metadata<'a>(s: &'a str) -> (Vec<&'a str>, &'a str) {
27     let mut metadata = Vec::new();
28     for line in s.lines() {
29         if line.starts_with("%") {
30             // remove %<whitespace>
31             metadata.push(line.slice_from(1).trim_left())
32         } else {
33             let line_start_byte = s.subslice_offset(line);
34             return (metadata, s.slice_from(line_start_byte));
35         }
36     }
37     // if we're here, then all lines were metadata % lines.
38     (metadata, "")
39 }
40
41 /// Render `input` (e.g. "foo.md") into an HTML file in `output`
42 /// (e.g. output = "bar" => "bar/foo.html").
43 pub fn render(input: &str, mut output: Path, matches: &getopts::Matches,
44               external_html: &ExternalHtml, include_toc: bool) -> int {
45     let input_p = Path::new(input);
46     output.push(input_p.filestem().unwrap());
47     output.set_extension("html");
48
49     let mut css = String::new();
50     for name in matches.opt_strs("markdown-css").iter() {
51         let s = format!("<link rel=\"stylesheet\" type=\"text/css\" href=\"{}\">\n", name);
52         css.push_str(s.as_slice())
53     }
54
55     let input_str = load_or_return!(input, 1, 2);
56     let playground = matches.opt_str("markdown-playground-url");
57     if playground.is_some() {
58         markdown::PLAYGROUND_KRATE.with(|s| { *s.borrow_mut() = None; });
59     }
60     let playground = playground.unwrap_or("".to_string());
61
62     let mut out = match io::File::create(&output) {
63         Err(e) => {
64             let _ = writeln!(&mut io::stderr(),
65                              "error opening `{}` for writing: {}",
66                              output.display(), e);
67             return 4;
68         }
69         Ok(f) => f
70     };
71
72     let (metadata, text) = extract_leading_metadata(input_str.as_slice());
73     if metadata.len() == 0 {
74         let _ = writeln!(&mut io::stderr(),
75                          "invalid markdown file: expecting initial line with `% ...TITLE...`");
76         return 5;
77     }
78     let title = metadata[0].as_slice();
79
80     reset_headers();
81
82     let rendered = if include_toc {
83         format!("{}", MarkdownWithToc(text))
84     } else {
85         format!("{}", Markdown(text))
86     };
87
88     let err = write!(
89         &mut out,
90         r#"<!DOCTYPE html>
91 <html lang="en">
92 <head>
93     <meta charset="utf-8">
94     <meta name="generator" content="rustdoc">
95     <title>{title}</title>
96
97     {css}
98     {in_header}
99 </head>
100 <body class="rustdoc">
101     <!--[if lte IE 8]>
102     <div class="warning">
103         This old browser is unsupported and will most likely display funky
104         things.
105     </div>
106     <![endif]-->
107
108     {before_content}
109     <h1 class="title">{title}</h1>
110     {text}
111     <script type="text/javascript">
112         window.playgroundUrl = "{playground}";
113     </script>
114     {after_content}
115 </body>
116 </html>"#,
117         title = Escape(title),
118         css = css,
119         in_header = external_html.in_header,
120         before_content = external_html.before_content,
121         text = rendered,
122         after_content = external_html.after_content,
123         playground = playground,
124         );
125
126     match err {
127         Err(e) => {
128             let _ = writeln!(&mut io::stderr(),
129                              "error writing to `{}`: {}",
130                              output.display(), e);
131             6
132         }
133         Ok(_) => 0
134     }
135 }
136
137 /// Run any tests/code examples in the markdown file `input`.
138 pub fn test(input: &str, libs: Vec<Path>, externs: core::Externs,
139             mut test_args: Vec<String>) -> int {
140     let input_str = load_or_return!(input, 1, 2);
141
142     let mut collector = Collector::new(input.to_string(), libs, externs, true);
143     find_testable_code(input_str.as_slice(), &mut collector);
144     test_args.insert(0, "rustdoctest".to_string());
145     testing::test_main(test_args.as_slice(), collector.tests);
146     0
147 }