2014-04-04 06:03:01 +00:00
|
|
|
// Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT
|
2013-09-19 05:18:38 +00:00
|
|
|
// file at the top-level directory of this distribution and at
|
|
|
|
// http://rust-lang.org/COPYRIGHT.
|
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
|
|
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
|
|
|
// option. This file may not be copied, modified, or distributed
|
|
|
|
// except according to those terms.
|
|
|
|
|
2013-10-03 17:24:40 +00:00
|
|
|
//! Markdown formatting for rustdoc
|
|
|
|
//!
|
2014-05-03 00:56:35 +00:00
|
|
|
//! This module implements markdown formatting through the hoedown C-library
|
2013-10-03 17:24:40 +00:00
|
|
|
//! (bundled into the rust runtime). This module self-contains the C bindings
|
|
|
|
//! and necessary legwork to render markdown, and exposes all of the
|
|
|
|
//! functionality through a unit-struct, `Markdown`, which has an implementation
|
2014-12-20 08:09:35 +00:00
|
|
|
//! of `fmt::String`. Example usage:
|
2013-10-03 17:24:40 +00:00
|
|
|
//!
|
2014-01-25 05:00:31 +00:00
|
|
|
//! ```rust,ignore
|
|
|
|
//! use rustdoc::html::markdown::Markdown;
|
|
|
|
//!
|
2013-10-03 17:24:40 +00:00
|
|
|
//! let s = "My *markdown* _text_";
|
|
|
|
//! let html = format!("{}", Markdown(s));
|
|
|
|
//! // ... something using html
|
|
|
|
//! ```
|
|
|
|
|
2014-06-06 13:51:42 +00:00
|
|
|
#![allow(dead_code)]
|
2014-03-22 01:05:05 +00:00
|
|
|
#![allow(non_camel_case_types)]
|
2014-02-10 14:36:31 +00:00
|
|
|
|
2014-02-26 17:58:41 +00:00
|
|
|
use libc;
|
2014-11-21 20:00:05 +00:00
|
|
|
use std::ascii::AsciiExt;
|
2015-01-12 09:37:01 +00:00
|
|
|
use std::cell::RefCell;
|
2014-12-22 17:04:23 +00:00
|
|
|
use std::collections::HashMap;
|
2015-04-07 01:39:39 +00:00
|
|
|
use std::default::Default;
|
|
|
|
use std::ffi::CString;
|
2013-09-19 05:18:38 +00:00
|
|
|
use std::fmt;
|
2014-03-08 23:11:52 +00:00
|
|
|
use std::slice;
|
2014-04-29 03:36:08 +00:00
|
|
|
use std::str;
|
2013-09-19 05:18:38 +00:00
|
|
|
|
2014-03-07 14:13:17 +00:00
|
|
|
use html::toc::TocBuilder;
|
2014-02-20 09:14:51 +00:00
|
|
|
use html::highlight;
|
2014-06-06 16:12:18 +00:00
|
|
|
use html::escape::Escape;
|
|
|
|
use test;
|
2014-02-20 09:14:51 +00:00
|
|
|
|
2014-12-20 08:09:35 +00:00
|
|
|
/// A unit struct which has the `fmt::String` trait implemented. When
|
2013-10-03 17:24:40 +00:00
|
|
|
/// formatted, this struct will emit the HTML corresponding to the rendered
|
|
|
|
/// version of the contained markdown string.
|
2014-04-01 02:01:01 +00:00
|
|
|
pub struct Markdown<'a>(pub &'a str);
|
2014-03-07 14:13:17 +00:00
|
|
|
/// A unit struct like `Markdown`, that renders the markdown with a
|
|
|
|
/// table of contents.
|
2014-04-01 02:01:01 +00:00
|
|
|
pub struct MarkdownWithToc<'a>(pub &'a str);
|
2013-09-19 05:18:38 +00:00
|
|
|
|
2014-10-07 00:41:15 +00:00
|
|
|
const DEF_OUNIT: libc::size_t = 64;
|
|
|
|
const HOEDOWN_EXT_NO_INTRA_EMPHASIS: libc::c_uint = 1 << 10;
|
|
|
|
const HOEDOWN_EXT_TABLES: libc::c_uint = 1 << 0;
|
|
|
|
const HOEDOWN_EXT_FENCED_CODE: libc::c_uint = 1 << 1;
|
|
|
|
const HOEDOWN_EXT_AUTOLINK: libc::c_uint = 1 << 3;
|
|
|
|
const HOEDOWN_EXT_STRIKETHROUGH: libc::c_uint = 1 << 4;
|
|
|
|
const HOEDOWN_EXT_SUPERSCRIPT: libc::c_uint = 1 << 8;
|
|
|
|
const HOEDOWN_EXT_FOOTNOTES: libc::c_uint = 1 << 2;
|
2014-05-03 02:56:19 +00:00
|
|
|
|
2014-10-07 00:41:15 +00:00
|
|
|
const HOEDOWN_EXTENSIONS: libc::c_uint =
|
2014-05-03 02:56:19 +00:00
|
|
|
HOEDOWN_EXT_NO_INTRA_EMPHASIS | HOEDOWN_EXT_TABLES |
|
|
|
|
HOEDOWN_EXT_FENCED_CODE | HOEDOWN_EXT_AUTOLINK |
|
|
|
|
HOEDOWN_EXT_STRIKETHROUGH | HOEDOWN_EXT_SUPERSCRIPT |
|
|
|
|
HOEDOWN_EXT_FOOTNOTES;
|
2014-05-03 00:56:35 +00:00
|
|
|
|
|
|
|
type hoedown_document = libc::c_void; // this is opaque to us
|
|
|
|
|
2014-11-26 10:52:16 +00:00
|
|
|
type blockcodefn = extern "C" fn(*mut hoedown_buffer, *const hoedown_buffer,
|
|
|
|
*const hoedown_buffer, *mut libc::c_void);
|
|
|
|
|
|
|
|
type headerfn = extern "C" fn(*mut hoedown_buffer, *const hoedown_buffer,
|
|
|
|
libc::c_int, *mut libc::c_void);
|
|
|
|
|
2015-04-06 17:06:39 +00:00
|
|
|
type codespanfn = extern "C" fn(*mut hoedown_buffer, *const hoedown_buffer,
|
|
|
|
*mut libc::c_void);
|
|
|
|
|
2014-12-25 07:01:24 +00:00
|
|
|
type linkfn = extern "C" fn (*mut hoedown_buffer, *const hoedown_buffer,
|
|
|
|
*const hoedown_buffer, *const hoedown_buffer,
|
|
|
|
*mut libc::c_void) -> libc::c_int;
|
|
|
|
|
|
|
|
type normaltextfn = extern "C" fn(*mut hoedown_buffer, *const hoedown_buffer,
|
|
|
|
*mut libc::c_void);
|
|
|
|
|
2014-05-28 01:37:49 +00:00
|
|
|
#[repr(C)]
|
2014-05-03 00:56:35 +00:00
|
|
|
struct hoedown_renderer {
|
2014-12-25 05:39:29 +00:00
|
|
|
opaque: *mut libc::c_void,
|
|
|
|
|
2014-11-26 10:52:16 +00:00
|
|
|
blockcode: Option<blockcodefn>,
|
2014-06-25 19:47:34 +00:00
|
|
|
blockquote: Option<extern "C" fn(*mut hoedown_buffer, *const hoedown_buffer,
|
2014-05-03 00:56:35 +00:00
|
|
|
*mut libc::c_void)>,
|
2014-06-25 19:47:34 +00:00
|
|
|
blockhtml: Option<extern "C" fn(*mut hoedown_buffer, *const hoedown_buffer,
|
2014-05-03 00:56:35 +00:00
|
|
|
*mut libc::c_void)>,
|
2014-11-26 10:52:16 +00:00
|
|
|
header: Option<headerfn>,
|
2014-12-25 07:01:24 +00:00
|
|
|
other_block_level_callbacks: [libc::size_t; 9],
|
2014-12-25 05:39:29 +00:00
|
|
|
|
|
|
|
/* span level callbacks - NULL or return 0 prints the span verbatim */
|
2015-04-06 17:06:39 +00:00
|
|
|
autolink: libc::size_t, // unused
|
|
|
|
codespan: Option<codespanfn>,
|
|
|
|
other_span_level_callbacks_1: [libc::size_t; 7],
|
2014-12-25 07:01:24 +00:00
|
|
|
link: Option<linkfn>,
|
|
|
|
other_span_level_callbacks_2: [libc::size_t; 5],
|
2014-12-25 05:39:29 +00:00
|
|
|
// hoedown will add `math` callback here, but we use an old version of it.
|
|
|
|
|
|
|
|
/* low level callbacks - NULL copies input directly into the output */
|
|
|
|
entity: Option<extern "C" fn(*mut hoedown_buffer, *const hoedown_buffer,
|
|
|
|
*mut libc::c_void)>,
|
2014-12-25 07:01:24 +00:00
|
|
|
normal_text: Option<normaltextfn>,
|
2014-12-25 05:39:29 +00:00
|
|
|
|
|
|
|
/* header and footer */
|
|
|
|
doc_header: Option<extern "C" fn(*mut hoedown_buffer, *mut libc::c_void)>,
|
|
|
|
doc_footer: Option<extern "C" fn(*mut hoedown_buffer, *mut libc::c_void)>,
|
2014-05-03 00:56:35 +00:00
|
|
|
}
|
|
|
|
|
2014-05-28 01:37:49 +00:00
|
|
|
#[repr(C)]
|
2014-05-03 00:56:35 +00:00
|
|
|
struct hoedown_html_renderer_state {
|
|
|
|
opaque: *mut libc::c_void,
|
|
|
|
toc_data: html_toc_data,
|
|
|
|
flags: libc::c_uint,
|
2014-06-25 19:47:34 +00:00
|
|
|
link_attributes: Option<extern "C" fn(*mut hoedown_buffer,
|
|
|
|
*const hoedown_buffer,
|
2014-05-03 00:56:35 +00:00
|
|
|
*mut libc::c_void)>,
|
2013-12-22 19:23:04 +00:00
|
|
|
}
|
2013-09-23 23:55:48 +00:00
|
|
|
|
2014-05-28 01:37:49 +00:00
|
|
|
#[repr(C)]
|
2013-09-23 23:55:48 +00:00
|
|
|
struct html_toc_data {
|
|
|
|
header_count: libc::c_int,
|
|
|
|
current_level: libc::c_int,
|
|
|
|
level_offset: libc::c_int,
|
2014-05-03 00:56:35 +00:00
|
|
|
nesting_level: libc::c_int,
|
2013-09-23 23:55:48 +00:00
|
|
|
}
|
|
|
|
|
2014-05-03 00:56:35 +00:00
|
|
|
struct MyOpaque {
|
2014-06-25 19:47:34 +00:00
|
|
|
dfltblk: extern "C" fn(*mut hoedown_buffer, *const hoedown_buffer,
|
|
|
|
*const hoedown_buffer, *mut libc::c_void),
|
2014-03-07 14:13:17 +00:00
|
|
|
toc_builder: Option<TocBuilder>,
|
2013-12-22 19:23:04 +00:00
|
|
|
}
|
|
|
|
|
2014-05-28 01:37:49 +00:00
|
|
|
#[repr(C)]
|
2014-05-03 00:56:35 +00:00
|
|
|
struct hoedown_buffer {
|
2014-06-25 19:47:34 +00:00
|
|
|
data: *const u8,
|
2013-09-23 23:55:48 +00:00
|
|
|
size: libc::size_t,
|
|
|
|
asize: libc::size_t,
|
|
|
|
unit: libc::size_t,
|
|
|
|
}
|
|
|
|
|
2014-05-03 00:56:35 +00:00
|
|
|
// hoedown FFI
|
|
|
|
#[link(name = "hoedown", kind = "static")]
|
2013-09-23 23:55:48 +00:00
|
|
|
extern {
|
2014-05-03 00:56:35 +00:00
|
|
|
fn hoedown_html_renderer_new(render_flags: libc::c_uint,
|
|
|
|
nesting_level: libc::c_int)
|
|
|
|
-> *mut hoedown_renderer;
|
|
|
|
fn hoedown_html_renderer_free(renderer: *mut hoedown_renderer);
|
|
|
|
|
|
|
|
fn hoedown_document_new(rndr: *mut hoedown_renderer,
|
|
|
|
extensions: libc::c_uint,
|
|
|
|
max_nesting: libc::size_t) -> *mut hoedown_document;
|
|
|
|
fn hoedown_document_render(doc: *mut hoedown_document,
|
|
|
|
ob: *mut hoedown_buffer,
|
2014-06-25 19:47:34 +00:00
|
|
|
document: *const u8,
|
2014-05-03 00:56:35 +00:00
|
|
|
doc_size: libc::size_t);
|
|
|
|
fn hoedown_document_free(md: *mut hoedown_document);
|
|
|
|
|
|
|
|
fn hoedown_buffer_new(unit: libc::size_t) -> *mut hoedown_buffer;
|
2014-12-25 05:39:29 +00:00
|
|
|
fn hoedown_buffer_put(b: *mut hoedown_buffer, c: *const libc::c_char,
|
|
|
|
n: libc::size_t);
|
2014-06-25 19:47:34 +00:00
|
|
|
fn hoedown_buffer_puts(b: *mut hoedown_buffer, c: *const libc::c_char);
|
2014-05-03 00:56:35 +00:00
|
|
|
fn hoedown_buffer_free(b: *mut hoedown_buffer);
|
2013-09-23 23:55:48 +00:00
|
|
|
|
|
|
|
}
|
|
|
|
|
2015-02-03 23:00:38 +00:00
|
|
|
// hoedown_buffer helpers
|
|
|
|
impl hoedown_buffer {
|
|
|
|
fn as_bytes(&self) -> &[u8] {
|
|
|
|
unsafe { slice::from_raw_parts(self.data, self.size as usize) }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-12-30 05:31:24 +00:00
|
|
|
/// Returns Some(code) if `s` is a line that should be stripped from
|
|
|
|
/// documentation but used in example code. `code` is the portion of
|
|
|
|
/// `s` that should be used in tests. (None for lines that should be
|
|
|
|
/// left as-is.)
|
|
|
|
fn stripped_filtered_line<'a>(s: &'a str) -> Option<&'a str> {
|
|
|
|
let trimmed = s.trim();
|
2015-04-07 22:30:05 +00:00
|
|
|
if trimmed == "#" {
|
|
|
|
Some("")
|
|
|
|
} else if trimmed.starts_with("# ") {
|
2015-01-18 00:15:52 +00:00
|
|
|
Some(&trimmed[2..])
|
2013-12-30 05:31:24 +00:00
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-04-06 17:06:39 +00:00
|
|
|
/// Returns a new string with all consecutive whitespace collapsed into
|
|
|
|
/// single spaces.
|
|
|
|
///
|
2015-04-06 18:56:39 +00:00
|
|
|
/// Any leading or trailing whitespace will be trimmed.
|
2015-04-06 17:06:39 +00:00
|
|
|
fn collapse_whitespace(s: &str) -> String {
|
2015-04-06 18:56:39 +00:00
|
|
|
s.split(|c: char| c.is_whitespace()).filter(|s| {
|
|
|
|
!s.is_empty()
|
|
|
|
}).collect::<Vec<_>>().connect(" ")
|
2015-04-06 17:06:39 +00:00
|
|
|
}
|
|
|
|
|
2015-03-26 00:06:52 +00:00
|
|
|
thread_local!(static USED_HEADER_MAP: RefCell<HashMap<String, usize>> = {
|
2014-11-14 22:20:57 +00:00
|
|
|
RefCell::new(HashMap::new())
|
2014-11-14 17:18:10 +00:00
|
|
|
});
|
2014-11-14 22:20:57 +00:00
|
|
|
|
|
|
|
thread_local!(pub static PLAYGROUND_KRATE: RefCell<Option<Option<String>>> = {
|
|
|
|
RefCell::new(None)
|
2014-11-14 17:18:10 +00:00
|
|
|
});
|
2014-03-04 19:24:20 +00:00
|
|
|
|
2014-05-10 21:05:06 +00:00
|
|
|
pub fn render(w: &mut fmt::Formatter, s: &str, print_toc: bool) -> fmt::Result {
|
2014-09-07 23:42:58 +00:00
|
|
|
extern fn block(ob: *mut hoedown_buffer, orig_text: *const hoedown_buffer,
|
2014-06-25 19:47:34 +00:00
|
|
|
lang: *const hoedown_buffer, opaque: *mut libc::c_void) {
|
2013-12-22 19:23:04 +00:00
|
|
|
unsafe {
|
2014-09-07 23:42:58 +00:00
|
|
|
if orig_text.is_null() { return }
|
2014-05-24 02:32:18 +00:00
|
|
|
|
2014-05-03 00:56:35 +00:00
|
|
|
let opaque = opaque as *mut hoedown_html_renderer_state;
|
2014-06-25 19:47:34 +00:00
|
|
|
let my_opaque: &MyOpaque = &*((*opaque).opaque as *const MyOpaque);
|
2015-02-03 23:00:38 +00:00
|
|
|
let text = (*orig_text).as_bytes();
|
2014-11-20 18:11:15 +00:00
|
|
|
let origtext = str::from_utf8(text).unwrap();
|
2014-12-20 08:09:35 +00:00
|
|
|
debug!("docblock: ==============\n{:?}\n=======", text);
|
2014-11-20 18:11:15 +00:00
|
|
|
let rendered = if lang.is_null() {
|
|
|
|
false
|
|
|
|
} else {
|
2015-02-03 23:00:38 +00:00
|
|
|
let rlang = (*lang).as_bytes();
|
2014-11-20 18:11:15 +00:00
|
|
|
let rlang = str::from_utf8(rlang).unwrap();
|
2014-12-10 11:17:14 +00:00
|
|
|
if !LangString::parse(rlang).rust {
|
2014-11-20 18:11:15 +00:00
|
|
|
(my_opaque.dfltblk)(ob, orig_text, lang,
|
|
|
|
opaque as *mut libc::c_void);
|
|
|
|
true
|
2014-02-20 09:14:51 +00:00
|
|
|
} else {
|
2014-11-20 18:11:15 +00:00
|
|
|
false
|
2014-02-20 09:14:51 +00:00
|
|
|
}
|
2014-11-20 18:11:15 +00:00
|
|
|
};
|
|
|
|
|
2014-11-06 17:32:37 +00:00
|
|
|
let lines = origtext.lines().filter(|l| {
|
2014-11-20 18:11:15 +00:00
|
|
|
stripped_filtered_line(*l).is_none()
|
|
|
|
});
|
|
|
|
let text = lines.collect::<Vec<&str>>().connect("\n");
|
2014-11-14 22:20:57 +00:00
|
|
|
if rendered { return }
|
|
|
|
PLAYGROUND_KRATE.with(|krate| {
|
2014-11-20 18:11:15 +00:00
|
|
|
let mut s = String::new();
|
2015-01-12 09:37:01 +00:00
|
|
|
krate.borrow().as_ref().map(|krate| {
|
2014-11-20 18:11:15 +00:00
|
|
|
let test = origtext.lines().map(|l| {
|
|
|
|
stripped_filtered_line(l).unwrap_or(l)
|
|
|
|
}).collect::<Vec<&str>>().connect("\n");
|
2015-02-02 02:53:25 +00:00
|
|
|
let krate = krate.as_ref().map(|s| &**s);
|
2015-04-07 01:39:39 +00:00
|
|
|
let test = test::maketest(&test, krate, false,
|
|
|
|
&Default::default());
|
2015-02-02 02:53:25 +00:00
|
|
|
s.push_str(&format!("<span class='rusttest'>{}</span>", Escape(&test)));
|
2014-11-20 18:11:15 +00:00
|
|
|
});
|
2015-02-02 02:53:25 +00:00
|
|
|
s.push_str(&highlight::highlight(&text,
|
|
|
|
None,
|
|
|
|
Some("rust-example-rendered")));
|
2015-02-18 22:39:37 +00:00
|
|
|
let output = CString::new(s).unwrap();
|
2014-11-20 18:11:15 +00:00
|
|
|
hoedown_buffer_puts(ob, output.as_ptr());
|
2014-11-14 22:20:57 +00:00
|
|
|
})
|
2013-12-22 19:23:04 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-06-25 19:47:34 +00:00
|
|
|
extern fn header(ob: *mut hoedown_buffer, text: *const hoedown_buffer,
|
2014-05-03 00:56:35 +00:00
|
|
|
level: libc::c_int, opaque: *mut libc::c_void) {
|
|
|
|
// hoedown does this, we may as well too
|
2014-11-25 21:28:35 +00:00
|
|
|
unsafe { hoedown_buffer_puts(ob, "\n\0".as_ptr() as *const _); }
|
2014-03-04 19:24:20 +00:00
|
|
|
|
|
|
|
// Extract the text provided
|
|
|
|
let s = if text.is_null() {
|
2014-05-25 10:10:11 +00:00
|
|
|
"".to_string()
|
2014-03-04 19:24:20 +00:00
|
|
|
} else {
|
2015-02-03 23:00:38 +00:00
|
|
|
let s = unsafe { (*text).as_bytes() };
|
2014-11-25 21:28:35 +00:00
|
|
|
str::from_utf8(s).unwrap().to_string()
|
2014-03-04 19:24:20 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
// Transform the contents of the header into a hyphenated string
|
2015-04-18 17:49:51 +00:00
|
|
|
let id = s.split_whitespace().map(|s| s.to_ascii_lowercase())
|
2014-11-21 20:00:05 +00:00
|
|
|
.collect::<Vec<String>>().connect("-");
|
2014-03-04 19:24:20 +00:00
|
|
|
|
2014-05-03 00:56:35 +00:00
|
|
|
// This is a terrible hack working around how hoedown gives us rendered
|
|
|
|
// html for text rather than the raw text.
|
|
|
|
|
|
|
|
let opaque = opaque as *mut hoedown_html_renderer_state;
|
|
|
|
let opaque = unsafe { &mut *((*opaque).opaque as *mut MyOpaque) };
|
2014-03-07 14:13:17 +00:00
|
|
|
|
2014-03-04 19:24:20 +00:00
|
|
|
// Make sure our hyphenated ID is unique for this page
|
2014-11-14 22:20:57 +00:00
|
|
|
let id = USED_HEADER_MAP.with(|map| {
|
|
|
|
let id = id.replace("<code>", "").replace("</code>", "").to_string();
|
|
|
|
let id = match map.borrow_mut().get_mut(&id) {
|
|
|
|
None => id,
|
|
|
|
Some(a) => { *a += 1; format!("{}-{}", id, *a - 1) }
|
|
|
|
};
|
|
|
|
map.borrow_mut().insert(id.clone(), 1);
|
|
|
|
id
|
|
|
|
});
|
2014-03-04 19:24:20 +00:00
|
|
|
|
2014-03-07 14:13:17 +00:00
|
|
|
let sec = match opaque.toc_builder {
|
|
|
|
Some(ref mut builder) => {
|
2014-06-26 06:15:14 +00:00
|
|
|
builder.push(level as u32, s.clone(), id.clone())
|
2014-03-07 14:13:17 +00:00
|
|
|
}
|
|
|
|
None => {""}
|
|
|
|
};
|
|
|
|
|
2014-03-04 19:24:20 +00:00
|
|
|
// Render the HTML
|
2014-06-14 18:03:34 +00:00
|
|
|
let text = format!(r##"<h{lvl} id="{id}" class='section-header'><a
|
|
|
|
href="#{id}">{sec}{}</a></h{lvl}>"##,
|
2014-03-07 14:13:17 +00:00
|
|
|
s, lvl = level, id = id,
|
2015-03-24 23:53:34 +00:00
|
|
|
sec = if sec.is_empty() {
|
2014-05-28 16:24:28 +00:00
|
|
|
sec.to_string()
|
|
|
|
} else {
|
|
|
|
format!("{} ", sec)
|
|
|
|
});
|
2014-03-07 14:13:17 +00:00
|
|
|
|
2015-02-18 22:39:37 +00:00
|
|
|
let text = CString::new(text).unwrap();
|
2014-11-25 21:28:35 +00:00
|
|
|
unsafe { hoedown_buffer_puts(ob, text.as_ptr()) }
|
2014-03-04 19:24:20 +00:00
|
|
|
}
|
|
|
|
|
2014-11-14 22:20:57 +00:00
|
|
|
reset_headers();
|
2014-10-05 13:00:50 +00:00
|
|
|
|
2015-04-06 17:06:39 +00:00
|
|
|
extern fn codespan(ob: *mut hoedown_buffer, text: *const hoedown_buffer, _: *mut libc::c_void) {
|
|
|
|
let content = if text.is_null() {
|
|
|
|
"".to_string()
|
|
|
|
} else {
|
|
|
|
let bytes = unsafe { (*text).as_bytes() };
|
|
|
|
let s = str::from_utf8(bytes).unwrap();
|
|
|
|
collapse_whitespace(s)
|
|
|
|
};
|
|
|
|
|
|
|
|
let content = format!("<code>{}</code>", Escape(&content));
|
|
|
|
let element = CString::new(content).unwrap();
|
|
|
|
unsafe { hoedown_buffer_puts(ob, element.as_ptr()); }
|
|
|
|
}
|
|
|
|
|
2013-09-23 23:55:48 +00:00
|
|
|
unsafe {
|
2014-05-03 00:56:35 +00:00
|
|
|
let ob = hoedown_buffer_new(DEF_OUNIT);
|
|
|
|
let renderer = hoedown_html_renderer_new(0, 0);
|
|
|
|
let mut opaque = MyOpaque {
|
|
|
|
dfltblk: (*renderer).blockcode.unwrap(),
|
2014-03-07 14:13:17 +00:00
|
|
|
toc_builder: if print_toc {Some(TocBuilder::new())} else {None}
|
2013-12-22 19:23:04 +00:00
|
|
|
};
|
2014-12-25 05:39:29 +00:00
|
|
|
(*((*renderer).opaque as *mut hoedown_html_renderer_state)).opaque
|
|
|
|
= &mut opaque as *mut _ as *mut libc::c_void;
|
Add trivial cast lints.
This permits all coercions to be performed in casts, but adds lints to warn in those cases.
Part of this patch moves cast checking to a later stage of type checking. We acquire obligations to check casts as part of type checking where we previously checked them. Once we have type checked a function or module, then we check any cast obligations which have been acquired. That means we have more type information available to check casts (this was crucial to making coercions work properly in place of some casts), but it means that casts cannot feed input into type inference.
[breaking change]
* Adds two new lints for trivial casts and trivial numeric casts, these are warn by default, but can cause errors if you build with warnings as errors. Previously, trivial numeric casts and casts to trait objects were allowed.
* The unused casts lint has gone.
* Interactions between casting and type inference have changed in subtle ways. Two ways this might manifest are:
- You may need to 'direct' casts more with extra type information, for example, in some cases where `foo as _ as T` succeeded, you may now need to specify the type for `_`
- Casts do not influence inference of integer types. E.g., the following used to type check:
```
let x = 42;
let y = &x as *const u32;
```
Because the cast would inform inference that `x` must have type `u32`. This no longer applies and the compiler will fallback to `i32` for `x` and thus there will be a type error in the cast. The solution is to add more type information:
```
let x: u32 = 42;
let y = &x as *const u32;
```
2015-03-20 04:15:27 +00:00
|
|
|
(*renderer).blockcode = Some(block);
|
|
|
|
(*renderer).header = Some(header);
|
2015-04-06 17:06:39 +00:00
|
|
|
(*renderer).codespan = Some(codespan);
|
2013-09-23 23:55:48 +00:00
|
|
|
|
2014-05-03 02:56:19 +00:00
|
|
|
let document = hoedown_document_new(renderer, HOEDOWN_EXTENSIONS, 16);
|
2014-05-03 00:56:35 +00:00
|
|
|
hoedown_document_render(document, ob, s.as_ptr(),
|
|
|
|
s.len() as libc::size_t);
|
|
|
|
hoedown_document_free(document);
|
2013-12-17 15:37:30 +00:00
|
|
|
|
2014-05-03 00:56:35 +00:00
|
|
|
hoedown_html_renderer_free(renderer);
|
2013-09-23 23:55:48 +00:00
|
|
|
|
2014-03-07 14:13:17 +00:00
|
|
|
let mut ret = match opaque.toc_builder {
|
|
|
|
Some(b) => write!(w, "<nav id=\"TOC\">{}</nav>", b.into_toc()),
|
|
|
|
None => Ok(())
|
|
|
|
};
|
2013-09-23 23:55:48 +00:00
|
|
|
|
2014-03-07 14:13:17 +00:00
|
|
|
if ret.is_ok() {
|
2015-02-03 23:00:38 +00:00
|
|
|
let buf = (*ob).as_bytes();
|
2014-12-12 18:59:41 +00:00
|
|
|
ret = w.write_str(str::from_utf8(buf).unwrap());
|
2014-03-07 14:13:17 +00:00
|
|
|
}
|
2014-05-03 00:56:35 +00:00
|
|
|
hoedown_buffer_free(ob);
|
2014-01-30 19:30:21 +00:00
|
|
|
ret
|
2013-09-23 23:55:48 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-12-22 19:23:04 +00:00
|
|
|
pub fn find_testable_code(doc: &str, tests: &mut ::test::Collector) {
|
2014-06-25 19:47:34 +00:00
|
|
|
extern fn block(_ob: *mut hoedown_buffer,
|
|
|
|
text: *const hoedown_buffer,
|
|
|
|
lang: *const hoedown_buffer,
|
|
|
|
opaque: *mut libc::c_void) {
|
2013-12-22 19:23:04 +00:00
|
|
|
unsafe {
|
2014-02-15 07:30:10 +00:00
|
|
|
if text.is_null() { return }
|
2014-06-19 12:38:01 +00:00
|
|
|
let block_info = if lang.is_null() {
|
|
|
|
LangString::all_false()
|
2014-02-15 07:30:10 +00:00
|
|
|
} else {
|
2015-02-03 23:00:38 +00:00
|
|
|
let lang = (*lang).as_bytes();
|
2014-11-20 18:11:15 +00:00
|
|
|
let s = str::from_utf8(lang).unwrap();
|
|
|
|
LangString::parse(s)
|
2014-02-15 07:30:10 +00:00
|
|
|
};
|
2014-12-10 11:17:14 +00:00
|
|
|
if !block_info.rust { return }
|
2015-02-03 23:00:38 +00:00
|
|
|
let text = (*text).as_bytes();
|
2014-11-20 18:11:15 +00:00
|
|
|
let opaque = opaque as *mut hoedown_html_renderer_state;
|
|
|
|
let tests = &mut *((*opaque).opaque as *mut ::test::Collector);
|
|
|
|
let text = str::from_utf8(text).unwrap();
|
2014-11-06 17:32:37 +00:00
|
|
|
let lines = text.lines().map(|l| {
|
2014-11-20 18:11:15 +00:00
|
|
|
stripped_filtered_line(l).unwrap_or(l)
|
|
|
|
});
|
|
|
|
let text = lines.collect::<Vec<&str>>().connect("\n");
|
|
|
|
tests.add_test(text.to_string(),
|
2015-03-26 20:30:33 +00:00
|
|
|
block_info.should_panic, block_info.no_run,
|
2014-11-20 18:11:15 +00:00
|
|
|
block_info.ignore, block_info.test_harness);
|
2013-12-22 19:23:04 +00:00
|
|
|
}
|
|
|
|
}
|
2014-05-03 00:56:35 +00:00
|
|
|
|
2014-06-25 19:47:34 +00:00
|
|
|
extern fn header(_ob: *mut hoedown_buffer,
|
|
|
|
text: *const hoedown_buffer,
|
2014-05-03 00:56:35 +00:00
|
|
|
level: libc::c_int, opaque: *mut libc::c_void) {
|
2014-03-07 03:31:41 +00:00
|
|
|
unsafe {
|
2014-05-03 00:56:35 +00:00
|
|
|
let opaque = opaque as *mut hoedown_html_renderer_state;
|
|
|
|
let tests = &mut *((*opaque).opaque as *mut ::test::Collector);
|
2014-03-07 03:31:41 +00:00
|
|
|
if text.is_null() {
|
|
|
|
tests.register_header("", level as u32);
|
|
|
|
} else {
|
2015-02-03 23:00:38 +00:00
|
|
|
let text = (*text).as_bytes();
|
2014-11-20 18:11:15 +00:00
|
|
|
let text = str::from_utf8(text).unwrap();
|
|
|
|
tests.register_header(text, level as u32);
|
2014-03-07 03:31:41 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2013-12-22 19:23:04 +00:00
|
|
|
|
|
|
|
unsafe {
|
2014-05-03 00:56:35 +00:00
|
|
|
let ob = hoedown_buffer_new(DEF_OUNIT);
|
|
|
|
let renderer = hoedown_html_renderer_new(0, 0);
|
Add trivial cast lints.
This permits all coercions to be performed in casts, but adds lints to warn in those cases.
Part of this patch moves cast checking to a later stage of type checking. We acquire obligations to check casts as part of type checking where we previously checked them. Once we have type checked a function or module, then we check any cast obligations which have been acquired. That means we have more type information available to check casts (this was crucial to making coercions work properly in place of some casts), but it means that casts cannot feed input into type inference.
[breaking change]
* Adds two new lints for trivial casts and trivial numeric casts, these are warn by default, but can cause errors if you build with warnings as errors. Previously, trivial numeric casts and casts to trait objects were allowed.
* The unused casts lint has gone.
* Interactions between casting and type inference have changed in subtle ways. Two ways this might manifest are:
- You may need to 'direct' casts more with extra type information, for example, in some cases where `foo as _ as T` succeeded, you may now need to specify the type for `_`
- Casts do not influence inference of integer types. E.g., the following used to type check:
```
let x = 42;
let y = &x as *const u32;
```
Because the cast would inform inference that `x` must have type `u32`. This no longer applies and the compiler will fallback to `i32` for `x` and thus there will be a type error in the cast. The solution is to add more type information:
```
let x: u32 = 42;
let y = &x as *const u32;
```
2015-03-20 04:15:27 +00:00
|
|
|
(*renderer).blockcode = Some(block);
|
|
|
|
(*renderer).header = Some(header);
|
2014-12-25 05:39:29 +00:00
|
|
|
(*((*renderer).opaque as *mut hoedown_html_renderer_state)).opaque
|
|
|
|
= tests as *mut _ as *mut libc::c_void;
|
2014-05-03 00:56:35 +00:00
|
|
|
|
2014-05-03 02:56:19 +00:00
|
|
|
let document = hoedown_document_new(renderer, HOEDOWN_EXTENSIONS, 16);
|
2014-05-03 00:56:35 +00:00
|
|
|
hoedown_document_render(document, ob, doc.as_ptr(),
|
|
|
|
doc.len() as libc::size_t);
|
|
|
|
hoedown_document_free(document);
|
|
|
|
|
|
|
|
hoedown_html_renderer_free(renderer);
|
|
|
|
hoedown_buffer_free(ob);
|
2013-12-22 19:23:04 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-01-28 13:34:18 +00:00
|
|
|
#[derive(Eq, PartialEq, Clone, Debug)]
|
2014-06-19 12:38:01 +00:00
|
|
|
struct LangString {
|
2015-03-26 20:30:33 +00:00
|
|
|
should_panic: bool,
|
2014-06-19 12:38:01 +00:00
|
|
|
no_run: bool,
|
|
|
|
ignore: bool,
|
2014-12-10 11:17:14 +00:00
|
|
|
rust: bool,
|
2014-06-19 13:11:18 +00:00
|
|
|
test_harness: bool,
|
2014-06-19 12:38:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl LangString {
|
|
|
|
fn all_false() -> LangString {
|
|
|
|
LangString {
|
2015-03-26 20:30:33 +00:00
|
|
|
should_panic: false,
|
2014-06-19 12:38:01 +00:00
|
|
|
no_run: false,
|
|
|
|
ignore: false,
|
2014-12-24 02:47:32 +00:00
|
|
|
rust: true, // NB This used to be `notrust = false`
|
2014-06-19 13:11:18 +00:00
|
|
|
test_harness: false,
|
2014-05-31 22:33:32 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-06-19 12:38:01 +00:00
|
|
|
fn parse(string: &str) -> LangString {
|
|
|
|
let mut seen_rust_tags = false;
|
|
|
|
let mut seen_other_tags = false;
|
|
|
|
let mut data = LangString::all_false();
|
|
|
|
|
2015-02-01 17:44:15 +00:00
|
|
|
let tokens = string.split(|c: char|
|
2014-06-19 12:38:01 +00:00
|
|
|
!(c == '_' || c == '-' || c.is_alphanumeric())
|
|
|
|
);
|
|
|
|
|
|
|
|
for token in tokens {
|
|
|
|
match token {
|
|
|
|
"" => {},
|
2015-03-26 20:30:33 +00:00
|
|
|
"should_panic" => { data.should_panic = true; seen_rust_tags = true; },
|
2014-06-19 12:38:01 +00:00
|
|
|
"no_run" => { data.no_run = true; seen_rust_tags = true; },
|
|
|
|
"ignore" => { data.ignore = true; seen_rust_tags = true; },
|
2014-12-10 11:17:14 +00:00
|
|
|
"rust" => { data.rust = true; seen_rust_tags = true; },
|
2014-06-19 13:11:18 +00:00
|
|
|
"test_harness" => { data.test_harness = true; seen_rust_tags = true; }
|
2014-06-19 12:38:01 +00:00
|
|
|
_ => { seen_other_tags = true }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-12-24 02:47:32 +00:00
|
|
|
data.rust &= !seen_other_tags || seen_rust_tags;
|
2014-05-31 22:33:32 +00:00
|
|
|
|
2014-06-19 12:38:01 +00:00
|
|
|
data
|
|
|
|
}
|
2014-05-31 22:33:32 +00:00
|
|
|
}
|
|
|
|
|
2014-03-04 19:24:20 +00:00
|
|
|
/// By default this markdown renderer generates anchors for each header in the
|
2014-06-09 04:00:52 +00:00
|
|
|
/// rendered document. The anchor name is the contents of the header separated
|
2014-03-04 19:24:20 +00:00
|
|
|
/// by hyphens, and a task-local map is used to disambiguate among duplicate
|
|
|
|
/// headers (numbers are appended).
|
|
|
|
///
|
|
|
|
/// This method will reset the local table for these headers. This is typically
|
|
|
|
/// used at the beginning of rendering an entire HTML page to reset from the
|
|
|
|
/// previous state (if any).
|
|
|
|
pub fn reset_headers() {
|
2014-11-14 22:20:57 +00:00
|
|
|
USED_HEADER_MAP.with(|s| s.borrow_mut().clear());
|
2014-03-04 19:24:20 +00:00
|
|
|
}
|
|
|
|
|
2015-01-20 23:45:07 +00:00
|
|
|
impl<'a> fmt::Display for Markdown<'a> {
|
2014-02-05 12:55:13 +00:00
|
|
|
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
|
|
|
let Markdown(md) = *self;
|
2013-09-23 23:55:48 +00:00
|
|
|
// This is actually common enough to special-case
|
2015-03-24 23:53:34 +00:00
|
|
|
if md.is_empty() { return Ok(()) }
|
2015-02-02 02:53:25 +00:00
|
|
|
render(fmt, md, false)
|
2014-03-07 14:13:17 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-01-20 23:45:07 +00:00
|
|
|
impl<'a> fmt::Display for MarkdownWithToc<'a> {
|
2014-03-07 14:13:17 +00:00
|
|
|
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
|
|
|
let MarkdownWithToc(md) = *self;
|
2015-02-02 02:53:25 +00:00
|
|
|
render(fmt, md, true)
|
2013-09-19 05:18:38 +00:00
|
|
|
}
|
|
|
|
}
|
2014-05-31 22:33:32 +00:00
|
|
|
|
2014-12-25 05:39:29 +00:00
|
|
|
pub fn plain_summary_line(md: &str) -> String {
|
2015-01-16 12:56:26 +00:00
|
|
|
extern fn link(_ob: *mut hoedown_buffer,
|
2014-12-25 05:39:29 +00:00
|
|
|
_link: *const hoedown_buffer,
|
|
|
|
_title: *const hoedown_buffer,
|
|
|
|
content: *const hoedown_buffer,
|
|
|
|
opaque: *mut libc::c_void) -> libc::c_int
|
|
|
|
{
|
|
|
|
unsafe {
|
|
|
|
if !content.is_null() && (*content).size > 0 {
|
|
|
|
let ob = opaque as *mut hoedown_buffer;
|
|
|
|
hoedown_buffer_put(ob, (*content).data as *const libc::c_char,
|
|
|
|
(*content).size);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
1
|
|
|
|
}
|
|
|
|
|
2015-01-16 12:56:26 +00:00
|
|
|
extern fn normal_text(_ob: *mut hoedown_buffer,
|
2014-12-25 05:39:29 +00:00
|
|
|
text: *const hoedown_buffer,
|
|
|
|
opaque: *mut libc::c_void)
|
|
|
|
{
|
|
|
|
unsafe {
|
|
|
|
let ob = opaque as *mut hoedown_buffer;
|
|
|
|
hoedown_buffer_put(ob, (*text).data as *const libc::c_char,
|
|
|
|
(*text).size);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
unsafe {
|
|
|
|
let ob = hoedown_buffer_new(DEF_OUNIT);
|
|
|
|
let mut plain_renderer: hoedown_renderer = ::std::mem::zeroed();
|
Add trivial cast lints.
This permits all coercions to be performed in casts, but adds lints to warn in those cases.
Part of this patch moves cast checking to a later stage of type checking. We acquire obligations to check casts as part of type checking where we previously checked them. Once we have type checked a function or module, then we check any cast obligations which have been acquired. That means we have more type information available to check casts (this was crucial to making coercions work properly in place of some casts), but it means that casts cannot feed input into type inference.
[breaking change]
* Adds two new lints for trivial casts and trivial numeric casts, these are warn by default, but can cause errors if you build with warnings as errors. Previously, trivial numeric casts and casts to trait objects were allowed.
* The unused casts lint has gone.
* Interactions between casting and type inference have changed in subtle ways. Two ways this might manifest are:
- You may need to 'direct' casts more with extra type information, for example, in some cases where `foo as _ as T` succeeded, you may now need to specify the type for `_`
- Casts do not influence inference of integer types. E.g., the following used to type check:
```
let x = 42;
let y = &x as *const u32;
```
Because the cast would inform inference that `x` must have type `u32`. This no longer applies and the compiler will fallback to `i32` for `x` and thus there will be a type error in the cast. The solution is to add more type information:
```
let x: u32 = 42;
let y = &x as *const u32;
```
2015-03-20 04:15:27 +00:00
|
|
|
let renderer: *mut hoedown_renderer = &mut plain_renderer;
|
2014-12-25 05:39:29 +00:00
|
|
|
(*renderer).opaque = ob as *mut libc::c_void;
|
Add trivial cast lints.
This permits all coercions to be performed in casts, but adds lints to warn in those cases.
Part of this patch moves cast checking to a later stage of type checking. We acquire obligations to check casts as part of type checking where we previously checked them. Once we have type checked a function or module, then we check any cast obligations which have been acquired. That means we have more type information available to check casts (this was crucial to making coercions work properly in place of some casts), but it means that casts cannot feed input into type inference.
[breaking change]
* Adds two new lints for trivial casts and trivial numeric casts, these are warn by default, but can cause errors if you build with warnings as errors. Previously, trivial numeric casts and casts to trait objects were allowed.
* The unused casts lint has gone.
* Interactions between casting and type inference have changed in subtle ways. Two ways this might manifest are:
- You may need to 'direct' casts more with extra type information, for example, in some cases where `foo as _ as T` succeeded, you may now need to specify the type for `_`
- Casts do not influence inference of integer types. E.g., the following used to type check:
```
let x = 42;
let y = &x as *const u32;
```
Because the cast would inform inference that `x` must have type `u32`. This no longer applies and the compiler will fallback to `i32` for `x` and thus there will be a type error in the cast. The solution is to add more type information:
```
let x: u32 = 42;
let y = &x as *const u32;
```
2015-03-20 04:15:27 +00:00
|
|
|
(*renderer).link = Some(link);
|
|
|
|
(*renderer).normal_text = Some(normal_text);
|
2014-12-25 05:39:29 +00:00
|
|
|
|
|
|
|
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);
|
2015-02-03 23:00:38 +00:00
|
|
|
let plain_slice = (*ob).as_bytes();
|
2014-12-25 07:01:24 +00:00
|
|
|
let plain = match str::from_utf8(plain_slice) {
|
|
|
|
Ok(s) => s.to_string(),
|
|
|
|
Err(_) => "".to_string(),
|
|
|
|
};
|
2014-12-25 05:39:29 +00:00
|
|
|
hoedown_buffer_free(ob);
|
|
|
|
plain
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-05-31 22:33:32 +00:00
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
2014-10-05 13:00:50 +00:00
|
|
|
use super::{LangString, Markdown};
|
2015-04-06 17:06:39 +00:00
|
|
|
use super::{collapse_whitespace, plain_summary_line};
|
2014-05-31 22:33:32 +00:00
|
|
|
|
|
|
|
#[test]
|
2014-06-19 12:38:01 +00:00
|
|
|
fn test_lang_string_parse() {
|
2014-06-19 13:11:18 +00:00
|
|
|
fn t(s: &str,
|
2015-03-26 20:30:33 +00:00
|
|
|
should_panic: bool, no_run: bool, ignore: bool, rust: bool, test_harness: bool) {
|
2014-06-19 12:38:01 +00:00
|
|
|
assert_eq!(LangString::parse(s), LangString {
|
2015-03-26 20:30:33 +00:00
|
|
|
should_panic: should_panic,
|
2014-06-19 12:38:01 +00:00
|
|
|
no_run: no_run,
|
|
|
|
ignore: ignore,
|
2014-12-10 11:17:14 +00:00
|
|
|
rust: rust,
|
2014-06-19 13:11:18 +00:00
|
|
|
test_harness: test_harness,
|
2014-06-19 12:38:01 +00:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2015-03-26 20:30:33 +00:00
|
|
|
// marker | should_panic| no_run | ignore | rust | test_harness
|
2014-12-24 02:47:32 +00:00
|
|
|
t("", false, false, false, true, false);
|
|
|
|
t("rust", false, false, false, true, false);
|
|
|
|
t("sh", false, false, false, false, false);
|
|
|
|
t("ignore", false, false, true, true, false);
|
2015-03-26 20:30:33 +00:00
|
|
|
t("should_panic", true, false, false, true, false);
|
2014-12-24 02:47:32 +00:00
|
|
|
t("no_run", false, true, false, true, false);
|
|
|
|
t("test_harness", false, false, false, true, true);
|
|
|
|
t("{.no_run .example}", false, true, false, true, false);
|
2015-03-26 20:30:33 +00:00
|
|
|
t("{.sh .should_panic}", true, false, false, true, false);
|
2014-12-24 02:47:32 +00:00
|
|
|
t("{.example .rust}", false, false, false, true, false);
|
|
|
|
t("{.test_harness .rust}", false, false, false, true, true);
|
2014-05-31 22:33:32 +00:00
|
|
|
}
|
2014-10-05 13:00:50 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn issue_17736() {
|
|
|
|
let markdown = "# title";
|
2015-02-02 02:53:25 +00:00
|
|
|
format!("{}", Markdown(markdown));
|
2014-10-05 13:00:50 +00:00
|
|
|
}
|
2014-12-25 05:39:29 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_plain_summary_line() {
|
|
|
|
fn t(input: &str, expect: &str) {
|
|
|
|
let output = plain_summary_line(input);
|
|
|
|
assert_eq!(output, expect);
|
|
|
|
}
|
|
|
|
|
|
|
|
t("hello [Rust](http://rust-lang.org) :)", "hello Rust :)");
|
|
|
|
t("code `let x = i32;` ...", "code `let x = i32;` ...");
|
|
|
|
t("type `Type<'static>` ...", "type `Type<'static>` ...");
|
|
|
|
t("# top header", "top header");
|
|
|
|
t("## header", "header");
|
|
|
|
}
|
2015-04-06 17:06:39 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_collapse_whitespace() {
|
|
|
|
fn t(input: &str, expected: &str) {
|
|
|
|
let actual = collapse_whitespace(input);
|
|
|
|
assert_eq!(actual, expected);
|
|
|
|
}
|
|
|
|
|
|
|
|
t("foo", "foo");
|
2015-04-06 18:56:39 +00:00
|
|
|
t("foo bar baz", "foo bar baz");
|
|
|
|
t(" foo bar", "foo bar");
|
|
|
|
t("\tfoo bar\nbaz", "foo bar baz");
|
|
|
|
t("foo bar \n baz\t\tqux\n", "foo bar baz qux");
|
2015-04-06 17:06:39 +00:00
|
|
|
}
|
2014-05-30 02:03:06 +00:00
|
|
|
}
|