1717//! let s = "My *markdown* _text_";
1818//! let mut id_map = IdMap::new();
1919//! let html = format!("{}", Markdown(s, &[], RefCell::new(&mut id_map),
20- //! ErrorCodes::Yes, Edition::Edition2015));
20+ //! ErrorCodes::Yes, Edition::Edition2015, None ));
2121//! // ... something using html
2222//! ```
2323
@@ -59,16 +59,24 @@ pub struct Markdown<'a>(
5959 pub ErrorCodes ,
6060 /// Default edition to use when parsing doctests (to add a `fn main`).
6161 pub Edition ,
62+ pub & ' a Option < Playground > ,
6263) ;
6364/// A tuple struct like `Markdown` that renders the markdown with a table of contents.
6465pub struct MarkdownWithToc < ' a > (
6566 pub & ' a str ,
6667 pub RefCell < & ' a mut IdMap > ,
6768 pub ErrorCodes ,
6869 pub Edition ,
70+ pub & ' a Option < Playground > ,
6971) ;
7072/// A tuple struct like `Markdown` that renders the markdown escaping HTML tags.
71- pub struct MarkdownHtml < ' a > ( pub & ' a str , pub RefCell < & ' a mut IdMap > , pub ErrorCodes , pub Edition ) ;
73+ pub struct MarkdownHtml < ' a > (
74+ pub & ' a str ,
75+ pub RefCell < & ' a mut IdMap > ,
76+ pub ErrorCodes ,
77+ pub Edition ,
78+ pub & ' a Option < Playground > ,
79+ ) ;
7280/// A tuple struct like `Markdown` that renders only the first paragraph.
7381pub struct MarkdownSummaryLine < ' a > ( pub & ' a str , pub & ' a [ ( String , String ) ] ) ;
7482
@@ -155,30 +163,39 @@ fn slugify(c: char) -> Option<char> {
155163 }
156164}
157165
158- // Information about the playground if a URL has been specified, containing an
159- // optional crate name and the URL.
160- thread_local ! ( pub static PLAYGROUND : RefCell < Option <( Option < String >, String ) >> = {
161- RefCell :: new ( None )
162- } ) ;
166+ # [ derive ( Clone , Debug ) ]
167+ pub struct Playground {
168+ pub crate_name : Option < String > ,
169+ pub url : String ,
170+ }
163171
164172/// Adds syntax highlighting and playground Run buttons to Rust code blocks.
165- struct CodeBlocks < ' a , I : Iterator < Item = Event < ' a > > > {
173+ struct CodeBlocks < ' p , ' a , I : Iterator < Item = Event < ' a > > > {
166174 inner : I ,
167175 check_error_codes : ErrorCodes ,
168176 edition : Edition ,
177+ // Information about the playground if a URL has been specified, containing an
178+ // optional crate name and the URL.
179+ playground : & ' p Option < Playground > ,
169180}
170181
171- impl < ' a , I : Iterator < Item = Event < ' a > > > CodeBlocks < ' a , I > {
172- fn new ( iter : I , error_codes : ErrorCodes , edition : Edition ) -> Self {
182+ impl < ' p , ' a , I : Iterator < Item = Event < ' a > > > CodeBlocks < ' p , ' a , I > {
183+ fn new (
184+ iter : I ,
185+ error_codes : ErrorCodes ,
186+ edition : Edition ,
187+ playground : & ' p Option < Playground > ,
188+ ) -> Self {
173189 CodeBlocks {
174190 inner : iter,
175191 check_error_codes : error_codes,
176192 edition,
193+ playground,
177194 }
178195 }
179196}
180197
181- impl < ' a , I : Iterator < Item = Event < ' a > > > Iterator for CodeBlocks < ' a , I > {
198+ impl < ' a , I : Iterator < Item = Event < ' a > > > Iterator for CodeBlocks < ' _ , ' a , I > {
182199 type Item = Event < ' a > ;
183200
184201 fn next ( & mut self ) -> Option < Self :: Item > {
@@ -213,86 +230,86 @@ impl<'a, I: Iterator<Item = Event<'a>>> Iterator for CodeBlocks<'a, I> {
213230 }
214231 let lines = origtext. lines ( ) . filter_map ( |l| map_line ( l) . for_html ( ) ) ;
215232 let text = lines. collect :: < Vec < Cow < ' _ , str > > > ( ) . join ( "\n " ) ;
216- PLAYGROUND . with ( |play| {
217- // insert newline to clearly separate it from the
218- // previous block so we can shorten the html output
219- let mut s = String :: from ( "\n " ) ;
220- let playground_button = play. borrow ( ) . as_ref ( ) . and_then ( |& ( ref krate, ref url) | {
221- if url. is_empty ( ) {
222- return None ;
223- }
224- let test = origtext. lines ( )
225- . map ( |l| map_line ( l) . for_code ( ) )
226- . collect :: < Vec < Cow < ' _ , str > > > ( ) . join ( "\n " ) ;
227- let krate = krate. as_ref ( ) . map ( |s| & * * s) ;
228- let ( test, _) = test:: make_test ( & test, krate, false ,
229- & Default :: default ( ) , edition) ;
230- let channel = if test. contains ( "#![feature(" ) {
231- "&version=nightly"
232- } else {
233- ""
234- } ;
235-
236- let edition_string = format ! ( "&edition={}" , edition) ;
237-
238- // These characters don't need to be escaped in a URI.
239- // FIXME: use a library function for percent encoding.
240- fn dont_escape ( c : u8 ) -> bool {
241- ( b'a' <= c && c <= b'z' ) ||
242- ( b'A' <= c && c <= b'Z' ) ||
243- ( b'0' <= c && c <= b'9' ) ||
244- c == b'-' || c == b'_' || c == b'.' ||
245- c == b'~' || c == b'!' || c == b'\'' ||
246- c == b'(' || c == b')' || c == b'*'
247- }
248- let mut test_escaped = String :: new ( ) ;
249- for b in test. bytes ( ) {
250- if dont_escape ( b) {
251- test_escaped. push ( char:: from ( b) ) ;
252- } else {
253- write ! ( test_escaped, "%{:02X}" , b) . unwrap ( ) ;
254- }
255- }
256- Some ( format ! (
257- r#"<a class="test-arrow" target="_blank" href="{}?code={}{}{}">Run</a>"# ,
258- url, test_escaped, channel, edition_string
259- ) )
260- } ) ;
261-
262- let tooltip = if ignore {
263- Some ( ( "This example is not tested" . to_owned ( ) , "ignore" ) )
264- } else if compile_fail {
265- Some ( ( "This example deliberately fails to compile" . to_owned ( ) , "compile_fail" ) )
266- } else if explicit_edition {
267- Some ( ( format ! ( "This code runs with edition {}" , edition) , "edition" ) )
233+ // insert newline to clearly separate it from the
234+ // previous block so we can shorten the html output
235+ let mut s = String :: from ( "\n " ) ;
236+ let playground_button = self . playground . as_ref ( ) . and_then ( |playground| {
237+ let krate = & playground. crate_name ;
238+ let url = & playground. url ;
239+ if url. is_empty ( ) {
240+ return None ;
241+ }
242+ let test = origtext. lines ( )
243+ . map ( |l| map_line ( l) . for_code ( ) )
244+ . collect :: < Vec < Cow < ' _ , str > > > ( ) . join ( "\n " ) ;
245+ let krate = krate. as_ref ( ) . map ( |s| & * * s) ;
246+ let ( test, _) = test:: make_test ( & test, krate, false ,
247+ & Default :: default ( ) , edition) ;
248+ let channel = if test. contains ( "#![feature(" ) {
249+ "&version=nightly"
268250 } else {
269- None
251+ ""
270252 } ;
271253
272- if let Some ( ( s1, s2) ) = tooltip {
273- s. push_str ( & highlight:: render_with_highlighting (
274- & text,
275- Some ( & format ! ( "rust-example-rendered{}" ,
276- if ignore { " ignore" }
277- else if compile_fail { " compile_fail" }
278- else if explicit_edition { " edition " }
279- else { "" } ) ) ,
280- playground_button. as_ref ( ) . map ( String :: as_str) ,
281- Some ( ( s1. as_str ( ) , s2) ) ) ) ;
282- Some ( Event :: Html ( s. into ( ) ) )
283- } else {
284- s. push_str ( & highlight:: render_with_highlighting (
285- & text,
286- Some ( & format ! ( "rust-example-rendered{}" ,
287- if ignore { " ignore" }
288- else if compile_fail { " compile_fail" }
289- else if explicit_edition { " edition " }
290- else { "" } ) ) ,
291- playground_button. as_ref ( ) . map ( String :: as_str) ,
292- None ) ) ;
293- Some ( Event :: Html ( s. into ( ) ) )
254+ let edition_string = format ! ( "&edition={}" , edition) ;
255+
256+ // These characters don't need to be escaped in a URI.
257+ // FIXME: use a library function for percent encoding.
258+ fn dont_escape ( c : u8 ) -> bool {
259+ ( b'a' <= c && c <= b'z' ) ||
260+ ( b'A' <= c && c <= b'Z' ) ||
261+ ( b'0' <= c && c <= b'9' ) ||
262+ c == b'-' || c == b'_' || c == b'.' ||
263+ c == b'~' || c == b'!' || c == b'\'' ||
264+ c == b'(' || c == b')' || c == b'*'
294265 }
295- } )
266+ let mut test_escaped = String :: new ( ) ;
267+ for b in test. bytes ( ) {
268+ if dont_escape ( b) {
269+ test_escaped. push ( char:: from ( b) ) ;
270+ } else {
271+ write ! ( test_escaped, "%{:02X}" , b) . unwrap ( ) ;
272+ }
273+ }
274+ Some ( format ! (
275+ r#"<a class="test-arrow" target="_blank" href="{}?code={}{}{}">Run</a>"# ,
276+ url, test_escaped, channel, edition_string
277+ ) )
278+ } ) ;
279+
280+ let tooltip = if ignore {
281+ Some ( ( "This example is not tested" . to_owned ( ) , "ignore" ) )
282+ } else if compile_fail {
283+ Some ( ( "This example deliberately fails to compile" . to_owned ( ) , "compile_fail" ) )
284+ } else if explicit_edition {
285+ Some ( ( format ! ( "This code runs with edition {}" , edition) , "edition" ) )
286+ } else {
287+ None
288+ } ;
289+
290+ if let Some ( ( s1, s2) ) = tooltip {
291+ s. push_str ( & highlight:: render_with_highlighting (
292+ & text,
293+ Some ( & format ! ( "rust-example-rendered{}" ,
294+ if ignore { " ignore" }
295+ else if compile_fail { " compile_fail" }
296+ else if explicit_edition { " edition " }
297+ else { "" } ) ) ,
298+ playground_button. as_ref ( ) . map ( String :: as_str) ,
299+ Some ( ( s1. as_str ( ) , s2) ) ) ) ;
300+ Some ( Event :: Html ( s. into ( ) ) )
301+ } else {
302+ s. push_str ( & highlight:: render_with_highlighting (
303+ & text,
304+ Some ( & format ! ( "rust-example-rendered{}" ,
305+ if ignore { " ignore" }
306+ else if compile_fail { " compile_fail" }
307+ else if explicit_edition { " edition " }
308+ else { "" } ) ) ,
309+ playground_button. as_ref ( ) . map ( String :: as_str) ,
310+ None ) ) ;
311+ Some ( Event :: Html ( s. into ( ) ) )
312+ }
296313 }
297314}
298315
@@ -676,7 +693,7 @@ impl LangString {
676693
677694impl < ' a > fmt:: Display for Markdown < ' a > {
678695 fn fmt ( & self , fmt : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
679- let Markdown ( md, links, ref ids, codes, edition) = * self ;
696+ let Markdown ( md, links, ref ids, codes, edition, playground ) = * self ;
680697 let mut ids = ids. borrow_mut ( ) ;
681698
682699 // This is actually common enough to special-case
@@ -695,7 +712,7 @@ impl<'a> fmt::Display for Markdown<'a> {
695712
696713 let p = HeadingLinks :: new ( p, None , & mut ids) ;
697714 let p = LinkReplacer :: new ( p, links) ;
698- let p = CodeBlocks :: new ( p, codes, edition) ;
715+ let p = CodeBlocks :: new ( p, codes, edition, playground ) ;
699716 let p = Footnotes :: new ( p) ;
700717 html:: push_html ( & mut s, p) ;
701718
@@ -705,7 +722,7 @@ impl<'a> fmt::Display for Markdown<'a> {
705722
706723impl < ' a > fmt:: Display for MarkdownWithToc < ' a > {
707724 fn fmt ( & self , fmt : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
708- let MarkdownWithToc ( md, ref ids, codes, edition) = * self ;
725+ let MarkdownWithToc ( md, ref ids, codes, edition, playground ) = * self ;
709726 let mut ids = ids. borrow_mut ( ) ;
710727
711728 let p = Parser :: new_ext ( md, opts ( ) ) ;
@@ -716,7 +733,7 @@ impl<'a> fmt::Display for MarkdownWithToc<'a> {
716733
717734 {
718735 let p = HeadingLinks :: new ( p, Some ( & mut toc) , & mut ids) ;
719- let p = CodeBlocks :: new ( p, codes, edition) ;
736+ let p = CodeBlocks :: new ( p, codes, edition, playground ) ;
720737 let p = Footnotes :: new ( p) ;
721738 html:: push_html ( & mut s, p) ;
722739 }
@@ -729,7 +746,7 @@ impl<'a> fmt::Display for MarkdownWithToc<'a> {
729746
730747impl < ' a > fmt:: Display for MarkdownHtml < ' a > {
731748 fn fmt ( & self , fmt : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
732- let MarkdownHtml ( md, ref ids, codes, edition) = * self ;
749+ let MarkdownHtml ( md, ref ids, codes, edition, playground ) = * self ;
733750 let mut ids = ids. borrow_mut ( ) ;
734751
735752 // This is actually common enough to special-case
@@ -745,7 +762,7 @@ impl<'a> fmt::Display for MarkdownHtml<'a> {
745762 let mut s = String :: with_capacity ( md. len ( ) * 3 / 2 ) ;
746763
747764 let p = HeadingLinks :: new ( p, None , & mut ids) ;
748- let p = CodeBlocks :: new ( p, codes, edition) ;
765+ let p = CodeBlocks :: new ( p, codes, edition, playground ) ;
749766 let p = Footnotes :: new ( p) ;
750767 html:: push_html ( & mut s, p) ;
751768
0 commit comments