diff options
| author | QuietMisdreavus <grey@quietmisdreavus.net> | 2017-12-21 14:54:16 -0600 |
|---|---|---|
| committer | Manish Goregaokar <manishsmail@gmail.com> | 2018-01-22 15:21:27 +0530 |
| commit | 473fcfd49a1bdb3981e7354165f98af04f847df1 (patch) | |
| tree | 4ce2496dc4a574068dd7560d1ca3153b44d12480 | |
| parent | bc072ed0ca8e2e9f8c79fb04e85b47b5c0e8d6ae (diff) | |
| download | rust-473fcfd49a1bdb3981e7354165f98af04f847df1.tar.gz rust-473fcfd49a1bdb3981e7354165f98af04f847df1.zip | |
new function to pull the links from a chunk of markdown
| -rw-r--r-- | src/librustdoc/html/markdown.rs | 417 |
1 files changed, 250 insertions, 167 deletions
diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs index e66add20376..498c8911dc0 100644 --- a/src/librustdoc/html/markdown.rs +++ b/src/librustdoc/html/markdown.rs @@ -527,6 +527,7 @@ struct MyOpaque { *const hoedown_buffer, *const hoedown_renderer_data, libc::size_t), toc_builder: Option<TocBuilder>, + links: Option<Vec<String>>, } extern { @@ -555,201 +556,203 @@ impl hoedown_buffer { } } -pub fn render(w: &mut fmt::Formatter, - s: &str, - print_toc: bool, - html_flags: libc::c_uint) -> fmt::Result { - extern fn block(ob: *mut hoedown_buffer, orig_text: *const hoedown_buffer, - lang: *const hoedown_buffer, data: *const hoedown_renderer_data, - line: libc::size_t) { - unsafe { - if orig_text.is_null() { return } - - let opaque = (*data).opaque as *mut hoedown_html_renderer_state; - let my_opaque: &MyOpaque = &*((*opaque).opaque as *const MyOpaque); - let text = (*orig_text).as_bytes(); - let origtext = str::from_utf8(text).unwrap(); - let origtext = origtext.trim_left(); - debug!("docblock: ==============\n{:?}\n=======", text); - let mut compile_fail = false; - let mut ignore = false; - - let rendered = if lang.is_null() || origtext.is_empty() { - false +extern fn hoedown_block(ob: *mut hoedown_buffer, orig_text: *const hoedown_buffer, + lang: *const hoedown_buffer, data: *const hoedown_renderer_data, + line: libc::size_t) { + unsafe { + if orig_text.is_null() { return } + + let opaque = (*data).opaque as *mut hoedown_html_renderer_state; + let my_opaque: &MyOpaque = &*((*opaque).opaque as *const MyOpaque); + let text = (*orig_text).as_bytes(); + let origtext = str::from_utf8(text).unwrap(); + let origtext = origtext.trim_left(); + debug!("docblock: ==============\n{:?}\n=======", text); + let mut compile_fail = false; + let mut ignore = false; + + let rendered = if lang.is_null() || origtext.is_empty() { + false + } else { + let rlang = (*lang).as_bytes(); + let rlang = str::from_utf8(rlang).unwrap(); + let parse_result = LangString::parse(rlang); + compile_fail = parse_result.compile_fail; + ignore = parse_result.ignore; + if !parse_result.rust { + (my_opaque.dfltblk)(ob, orig_text, lang, + opaque as *const hoedown_renderer_data, + line); + true } else { - let rlang = (*lang).as_bytes(); - let rlang = str::from_utf8(rlang).unwrap(); - let parse_result = LangString::parse(rlang); - compile_fail = parse_result.compile_fail; - ignore = parse_result.ignore; - if !parse_result.rust { - (my_opaque.dfltblk)(ob, orig_text, lang, - opaque as *const hoedown_renderer_data, - line); - true + false + } + }; + + let lines = origtext.lines().filter_map(|l| map_line(l).for_html()); + let text = lines.collect::<Vec<&str>>().join("\n"); + if rendered { return } + PLAYGROUND.with(|play| { + // insert newline to clearly separate it from the + // previous block so we can shorten the html output + let mut s = String::from("\n"); + let playground_button = play.borrow().as_ref().and_then(|&(ref krate, ref url)| { + if url.is_empty() { + return None; + } + let test = origtext.lines() + .map(|l| map_line(l).for_code()) + .collect::<Vec<&str>>().join("\n"); + let krate = krate.as_ref().map(|s| &**s); + let (test, _) = test::make_test(&test, krate, false, + &Default::default()); + let channel = if test.contains("#![feature(") { + "&version=nightly" } else { - false + "" + }; + // These characters don't need to be escaped in a URI. + // FIXME: use a library function for percent encoding. + fn dont_escape(c: u8) -> bool { + (b'a' <= c && c <= b'z') || + (b'A' <= c && c <= b'Z') || + (b'0' <= c && c <= b'9') || + c == b'-' || c == b'_' || c == b'.' || + c == b'~' || c == b'!' || c == b'\'' || + c == b'(' || c == b')' || c == b'*' } - }; - - let lines = origtext.lines().filter_map(|l| map_line(l).for_html()); - let text = lines.collect::<Vec<&str>>().join("\n"); - if rendered { return } - PLAYGROUND.with(|play| { - // insert newline to clearly separate it from the - // previous block so we can shorten the html output - let mut s = String::from("\n"); - let playground_button = play.borrow().as_ref().and_then(|&(ref krate, ref url)| { - if url.is_empty() { - return None; - } - let test = origtext.lines() - .map(|l| map_line(l).for_code()) - .collect::<Vec<&str>>().join("\n"); - let krate = krate.as_ref().map(|s| &**s); - let (test, _) = test::make_test(&test, krate, false, - &Default::default()); - let channel = if test.contains("#![feature(") { - "&version=nightly" + let mut test_escaped = String::new(); + for b in test.bytes() { + if dont_escape(b) { + test_escaped.push(char::from(b)); } else { - "" - }; - // These characters don't need to be escaped in a URI. - // FIXME: use a library function for percent encoding. - fn dont_escape(c: u8) -> bool { - (b'a' <= c && c <= b'z') || - (b'A' <= c && c <= b'Z') || - (b'0' <= c && c <= b'9') || - c == b'-' || c == b'_' || c == b'.' || - c == b'~' || c == b'!' || c == b'\'' || - c == b'(' || c == b')' || c == b'*' - } - let mut test_escaped = String::new(); - for b in test.bytes() { - if dont_escape(b) { - test_escaped.push(char::from(b)); - } else { - write!(test_escaped, "%{:02X}", b).unwrap(); - } + write!(test_escaped, "%{:02X}", b).unwrap(); } - Some(format!( - r#"<a class="test-arrow" target="_blank" href="{}?code={}{}">Run</a>"#, - url, test_escaped, channel - )) - }); - let tooltip = if ignore { - Some(("This example is not tested", "ignore")) - } else if compile_fail { - Some(("This example deliberately fails to compile", "compile_fail")) - } else { - None - }; - s.push_str(&highlight::render_with_highlighting( - &text, - Some(&format!("rust-example-rendered{}", - if ignore { " ignore" } - else if compile_fail { " compile_fail" } - else { "" })), - None, - playground_button.as_ref().map(String::as_str), - tooltip)); - hoedown_buffer_put(ob, s.as_ptr(), s.len()); - }) - } + } + Some(format!( + r#"<a class="test-arrow" target="_blank" href="{}?code={}{}">Run</a>"#, + url, test_escaped, channel + )) + }); + let tooltip = if ignore { + Some(("This example is not tested", "ignore")) + } else if compile_fail { + Some(("This example deliberately fails to compile", "compile_fail")) + } else { + None + }; + s.push_str(&highlight::render_with_highlighting( + &text, + Some(&format!("rust-example-rendered{}", + if ignore { " ignore" } + else if compile_fail { " compile_fail" } + else { "" })), + None, + playground_button.as_ref().map(String::as_str), + tooltip)); + hoedown_buffer_put(ob, s.as_ptr(), s.len()); + }) } +} - extern fn header(ob: *mut hoedown_buffer, text: *const hoedown_buffer, - level: libc::c_int, data: *const hoedown_renderer_data, - _: libc::size_t) { - // hoedown does this, we may as well too - unsafe { hoedown_buffer_put(ob, "\n".as_ptr(), 1); } +extern fn hoedown_header(ob: *mut hoedown_buffer, text: *const hoedown_buffer, + level: libc::c_int, data: *const hoedown_renderer_data, + _: libc::size_t) { + // hoedown does this, we may as well too + unsafe { hoedown_buffer_put(ob, "\n".as_ptr(), 1); } - // Extract the text provided - let s = if text.is_null() { - "".to_owned() - } else { - let s = unsafe { (*text).as_bytes() }; - str::from_utf8(&s).unwrap().to_owned() - }; + // Extract the text provided + let s = if text.is_null() { + "".to_owned() + } else { + let s = unsafe { (*text).as_bytes() }; + str::from_utf8(&s).unwrap().to_owned() + }; - // Discard '<em>', '<code>' tags and some escaped characters, - // transform the contents of the header into a hyphenated string - // without non-alphanumeric characters other than '-' and '_'. - // - // This is a terrible hack working around how hoedown gives us rendered - // html for text rather than the raw text. - let mut id = s.clone(); - let repl_sub = vec!["<em>", "</em>", "<code>", "</code>", - "<strong>", "</strong>", - "<", ">", "&", "'", """]; - for sub in repl_sub { - id = id.replace(sub, ""); - } - let id = id.chars().filter_map(|c| { - if c.is_alphanumeric() || c == '-' || c == '_' { - if c.is_ascii() { - Some(c.to_ascii_lowercase()) - } else { - Some(c) - } - } else if c.is_whitespace() && c.is_ascii() { - Some('-') + // Discard '<em>', '<code>' tags and some escaped characters, + // transform the contents of the header into a hyphenated string + // without non-alphanumeric characters other than '-' and '_'. + // + // This is a terrible hack working around how hoedown gives us rendered + // html for text rather than the raw text. + let mut id = s.clone(); + let repl_sub = vec!["<em>", "</em>", "<code>", "</code>", + "<strong>", "</strong>", + "<", ">", "&", "'", """]; + for sub in repl_sub { + id = id.replace(sub, ""); + } + let id = id.chars().filter_map(|c| { + if c.is_alphanumeric() || c == '-' || c == '_' { + if c.is_ascii() { + Some(c.to_ascii_lowercase()) } else { - None + Some(c) } - }).collect::<String>(); + } else if c.is_whitespace() && c.is_ascii() { + Some('-') + } else { + None + } + }).collect::<String>(); - let opaque = unsafe { (*data).opaque as *mut hoedown_html_renderer_state }; - let opaque = unsafe { &mut *((*opaque).opaque as *mut MyOpaque) }; + let opaque = unsafe { (*data).opaque as *mut hoedown_html_renderer_state }; + let opaque = unsafe { &mut *((*opaque).opaque as *mut MyOpaque) }; - let id = derive_id(id); + let id = derive_id(id); - let sec = opaque.toc_builder.as_mut().map_or("".to_owned(), |builder| { - format!("{} ", builder.push(level as u32, s.clone(), id.clone())) - }); + let sec = opaque.toc_builder.as_mut().map_or("".to_owned(), |builder| { + format!("{} ", builder.push(level as u32, s.clone(), id.clone())) + }); - // Render the HTML - let text = format!("<h{lvl} id='{id}' class='section-header'>\ - <a href='#{id}'>{sec}{}</a></h{lvl}>", - s, lvl = level, id = id, sec = sec); + // Render the HTML + let text = format!("<h{lvl} id='{id}' class='section-header'>\ + <a href='#{id}'>{sec}{}</a></h{lvl}>", + s, lvl = level, id = id, sec = sec); - unsafe { hoedown_buffer_put(ob, text.as_ptr(), text.len()); } - } + unsafe { hoedown_buffer_put(ob, text.as_ptr(), text.len()); } +} - extern fn codespan( - ob: *mut hoedown_buffer, - text: *const hoedown_buffer, - _: *const hoedown_renderer_data, - _: libc::size_t - ) -> libc::c_int { - let content = if text.is_null() { - "".to_owned() - } else { - let bytes = unsafe { (*text).as_bytes() }; - let s = str::from_utf8(bytes).unwrap(); - collapse_whitespace(s) - }; +extern fn hoedown_codespan( + ob: *mut hoedown_buffer, + text: *const hoedown_buffer, + _: *const hoedown_renderer_data, + _: libc::size_t +) -> libc::c_int { + let content = if text.is_null() { + "".to_owned() + } else { + let bytes = unsafe { (*text).as_bytes() }; + let s = str::from_utf8(bytes).unwrap(); + collapse_whitespace(s) + }; - let content = format!("<code>{}</code>", Escape(&content)); - unsafe { - hoedown_buffer_put(ob, content.as_ptr(), content.len()); - } - // Return anything except 0, which would mean "also print the code span verbatim". - 1 + let content = format!("<code>{}</code>", Escape(&content)); + unsafe { + hoedown_buffer_put(ob, content.as_ptr(), content.len()); } + // Return anything except 0, which would mean "also print the code span verbatim". + 1 +} + +pub fn render(w: &mut fmt::Formatter, + s: &str, + print_toc: bool, + html_flags: libc::c_uint) -> fmt::Result { unsafe { let ob = hoedown_buffer_new(DEF_OUNIT); let renderer = hoedown_html_renderer_new(html_flags, 0); let mut opaque = MyOpaque { dfltblk: (*renderer).blockcode.unwrap(), - toc_builder: if print_toc {Some(TocBuilder::new())} else {None} + toc_builder: if print_toc {Some(TocBuilder::new())} else {None}, + links: None, }; (*((*renderer).opaque as *mut hoedown_html_renderer_state)).opaque = &mut opaque as *mut _ as *mut libc::c_void; - (*renderer).blockcode = Some(block); - (*renderer).header = Some(header); - (*renderer).codespan = Some(codespan); + (*renderer).blockcode = Some(hoedown_block); + (*renderer).header = Some(hoedown_header); + (*renderer).codespan = Some(hoedown_codespan); let document = hoedown_document_new(renderer, HOEDOWN_EXTENSIONS, 16); hoedown_document_render(document, ob, s.as_ptr(), @@ -1140,6 +1143,86 @@ pub fn plain_summary_line(md: &str) -> String { s } +pub fn markdown_links(md: &str, render_type: RenderType) -> Vec<String> { + if md.is_empty() { + return vec![]; + } + + match render_type { + RenderType::Hoedown => { + extern fn hoedown_link( + _ob: *mut hoedown_buffer, + _content: *const hoedown_buffer, + link: *const hoedown_buffer, + _title: *const hoedown_buffer, + data: *const hoedown_renderer_data, + _line: libc::size_t + ) -> libc::c_int { + if link.is_null() { + return 0; + } + + let opaque = unsafe { (*data).opaque as *mut hoedown_html_renderer_state }; + let opaque = unsafe { &mut *((*opaque).opaque as *mut MyOpaque) }; + + if let Some(ref mut links) = opaque.links { + let s = unsafe { (*link).as_bytes() }; + let s = str::from_utf8(&s).unwrap().to_owned(); + + links.push(s); + } + + //returning 0 here means "emit the span verbatim", but we're not using the output + //anyway so we don't really care + 0 + } + + unsafe { + let ob = hoedown_buffer_new(DEF_OUNIT); + let renderer = hoedown_html_renderer_new(0, 0); + let mut opaque = MyOpaque { + dfltblk: (*renderer).blockcode.unwrap(), + toc_builder: None, + links: Some(vec![]), + }; + (*((*renderer).opaque as *mut hoedown_html_renderer_state)).opaque + = &mut opaque as *mut _ as *mut libc::c_void; + (*renderer).blockcode = Some(hoedown_block); + (*renderer).header = Some(hoedown_header); + (*renderer).codespan = Some(hoedown_codespan); + (*renderer).link = Some(hoedown_link); + + let document = hoedown_document_new(renderer, HOEDOWN_EXTENSIONS, 16); + hoedown_document_render(document, ob, md.as_ptr(), + md.len() as libc::size_t); + hoedown_document_free(document); + + hoedown_html_renderer_free(renderer); + + opaque.links.unwrap() + } + } + RenderType::Pulldown => { + let mut opts = Options::empty(); + opts.insert(OPTION_ENABLE_TABLES); + opts.insert(OPTION_ENABLE_FOOTNOTES); + + let p = Parser::new_ext(md, opts); + + let iter = Footnotes::new(CodeBlocks::new(HeadingLinks::new(p, None))); + let mut links = vec![]; + + for ev in iter { + if let Event::Start(Tag::Link(dest, _)) = ev { + links.push(dest.into_owned()); + } + } + + links + } + } +} + #[cfg(test)] mod tests { use super::{LangString, Markdown, MarkdownHtml}; |
