]> git.proxmox.com Git - rustc.git/blame - src/vendor/mdbook/src/utils/mod.rs
New upstream version 1.25.0+dfsg1
[rustc.git] / src / vendor / mdbook / src / utils / mod.rs
CommitLineData
2c00a5a8
XL
1#![allow(missing_docs)] // FIXME: Document this
2
ea8adc8c 3pub mod fs;
2c00a5a8
XL
4mod string;
5use errors::Error;
ea8adc8c 6
2c00a5a8
XL
7use pulldown_cmark::{html, Event, Options, Parser, Tag, OPTION_ENABLE_FOOTNOTES,
8 OPTION_ENABLE_TABLES};
ea8adc8c
XL
9use std::borrow::Cow;
10
2c00a5a8 11pub use self::string::{RangeArgument, take_lines};
ea8adc8c 12
2c00a5a8 13/// Wrapper around the pulldown-cmark parser for rendering markdown to HTML.
ea8adc8c
XL
14pub fn render_markdown(text: &str, curly_quotes: bool) -> String {
15 let mut s = String::with_capacity(text.len() * 3 / 2);
16
17 let mut opts = Options::empty();
18 opts.insert(OPTION_ENABLE_TABLES);
19 opts.insert(OPTION_ENABLE_FOOTNOTES);
20
21 let p = Parser::new_ext(text, opts);
22 let mut converter = EventQuoteConverter::new(curly_quotes);
2c00a5a8
XL
23 let events = p.map(clean_codeblock_headers)
24 .map(|event| converter.convert(event));
ea8adc8c
XL
25
26 html::push_html(&mut s, events);
27 s
28}
29
30struct EventQuoteConverter {
31 enabled: bool,
32 convert_text: bool,
33}
34
35impl EventQuoteConverter {
36 fn new(enabled: bool) -> Self {
2c00a5a8
XL
37 EventQuoteConverter {
38 enabled: enabled,
39 convert_text: true,
40 }
ea8adc8c
XL
41 }
42
43 fn convert<'a>(&mut self, event: Event<'a>) -> Event<'a> {
44 if !self.enabled {
45 return event;
46 }
47
48 match event {
2c00a5a8 49 Event::Start(Tag::CodeBlock(_)) | Event::Start(Tag::Code) => {
ea8adc8c
XL
50 self.convert_text = false;
51 event
2c00a5a8
XL
52 }
53 Event::End(Tag::CodeBlock(_)) | Event::End(Tag::Code) => {
ea8adc8c
XL
54 self.convert_text = true;
55 event
2c00a5a8
XL
56 }
57 Event::Text(ref text) if self.convert_text => {
58 Event::Text(Cow::from(convert_quotes_to_curly(text)))
59 }
ea8adc8c
XL
60 _ => event,
61 }
62 }
63}
64
65fn clean_codeblock_headers(event: Event) -> Event {
66 match event {
67 Event::Start(Tag::CodeBlock(ref info)) => {
2c00a5a8 68 let info: String = info.chars().filter(|ch| !ch.is_whitespace()).collect();
ea8adc8c
XL
69
70 Event::Start(Tag::CodeBlock(Cow::from(info)))
2c00a5a8 71 }
ea8adc8c
XL
72 _ => event,
73 }
74}
75
76
77fn convert_quotes_to_curly(original_text: &str) -> String {
78 // We'll consider the start to be "whitespace".
79 let mut preceded_by_whitespace = true;
80
2c00a5a8
XL
81 original_text.chars()
82 .map(|original_char| {
83 let converted_char = match original_char {
84 '\'' => {
85 if preceded_by_whitespace {
86 '‘'
87 } else {
88 '’'
89 }
90 }
91 '"' => {
92 if preceded_by_whitespace {
93 '“'
94 } else {
95 '”'
96 }
97 }
98 _ => original_char,
99 };
100
101 preceded_by_whitespace = original_char.is_whitespace();
102
103 converted_char
104 })
105 .collect()
106}
ea8adc8c 107
2c00a5a8
XL
108/// Prints a "backtrace" of some `Error`.
109pub fn log_backtrace(e: &Error) {
110 error!("Error: {}", e);
ea8adc8c 111
2c00a5a8
XL
112 for cause in e.iter().skip(1) {
113 error!("\tCaused By: {}", cause);
114 }
ea8adc8c
XL
115}
116
117#[cfg(test)]
118mod tests {
119 mod render_markdown {
120 use super::super::render_markdown;
121
122 #[test]
123 fn it_can_keep_quotes_straight() {
124 assert_eq!(render_markdown("'one'", false), "<p>'one'</p>\n");
125 }
126
127 #[test]
128 fn it_can_make_quotes_curly_except_when_they_are_in_code() {
129 let input = r#"
130'one'
131```
132'two'
133```
134`'three'` 'four'"#;
135 let expected = r#"<p>‘one’</p>
136<pre><code>'two'
137</code></pre>
138<p><code>'three'</code> ‘four’</p>
139"#;
140 assert_eq!(render_markdown(input, true), expected);
141 }
142
143 #[test]
144 fn whitespace_outside_of_codeblock_header_is_preserved() {
145 let input = r#"
146some text with spaces
147```rust
148fn main() {
149// code inside is unchanged
150}
151```
152more text with spaces
153"#;
154
155 let expected = r#"<p>some text with spaces</p>
156<pre><code class="language-rust">fn main() {
157// code inside is unchanged
158}
159</code></pre>
160<p>more text with spaces</p>
161"#;
162 assert_eq!(render_markdown(input, false), expected);
163 assert_eq!(render_markdown(input, true), expected);
164 }
165
166 #[test]
167 fn rust_code_block_properties_are_passed_as_space_delimited_class() {
168 let input = r#"
169```rust,no_run,should_panic,property_3
170```
171"#;
172
2c00a5a8
XL
173 let expected =
174 r#"<pre><code class="language-rust,no_run,should_panic,property_3"></code></pre>
ea8adc8c
XL
175"#;
176 assert_eq!(render_markdown(input, false), expected);
177 assert_eq!(render_markdown(input, true), expected);
178 }
179
180 #[test]
181 fn rust_code_block_properties_with_whitespace_are_passed_as_space_delimited_class() {
182 let input = r#"
183```rust, no_run,,,should_panic , ,property_3
184```
185"#;
186
2c00a5a8
XL
187 let expected =
188 r#"<pre><code class="language-rust,no_run,,,should_panic,,property_3"></code></pre>
ea8adc8c
XL
189"#;
190 assert_eq!(render_markdown(input, false), expected);
191 assert_eq!(render_markdown(input, true), expected);
192 }
193
194 #[test]
195 fn rust_code_block_without_properties_has_proper_html_class() {
196 let input = r#"
2c00a5a8 197```rust
ea8adc8c
XL
198```
199"#;
200
201 let expected = r#"<pre><code class="language-rust"></code></pre>
202"#;
203 assert_eq!(render_markdown(input, false), expected);
204 assert_eq!(render_markdown(input, true), expected);
205
206 let input = r#"
207```rust
208```
209"#;
210 assert_eq!(render_markdown(input, false), expected);
211 assert_eq!(render_markdown(input, true), expected);
ea8adc8c
XL
212 }
213 }
214
215 mod convert_quotes_to_curly {
216 use super::super::convert_quotes_to_curly;
217
218 #[test]
219 fn it_converts_single_quotes() {
2c00a5a8
XL
220 assert_eq!(convert_quotes_to_curly("'one', 'two'"),
221 "‘one’, ‘two’");
ea8adc8c
XL
222 }
223
224 #[test]
225 fn it_converts_double_quotes() {
2c00a5a8
XL
226 assert_eq!(convert_quotes_to_curly(r#""one", "two""#),
227 "“one”, “two”");
ea8adc8c
XL
228 }
229
230 #[test]
231 fn it_treats_tab_as_whitespace() {
232 assert_eq!(convert_quotes_to_curly("\t'one'"), "\t‘one’");
233 }
234 }
235}