use std::borrow::Cow::Owned;
use std::collections::HashMap;
use pulldown_cmark as cmark;
use self::cmark::{Parser, Event, Tag};
use regex::Regex;
use syntect::dumps::from_binary;
use syntect::easy::HighlightLines;
use syntect::parsing::SyntaxSet;
use syntect::highlighting::ThemeSet;
use syntect::html::{start_coloured_html_snippet, styles_to_coloured_html, IncludeBackground};
use tera::{Tera, Context};
use config::Config;
use errors::{Result, ResultExt};
// We need to put those in a struct to impl Send and sync
pub struct Setup {
syntax_set: SyntaxSet,
pub theme_set: ThemeSet,
}
unsafe impl Send for Setup {}
unsafe impl Sync for Setup {}
lazy_static!{
static ref SHORTCODE_RE: Regex = Regex::new(r#"\{(?:%|\{)\s+([[:alnum:]]+?)\(([[:alnum:]]+?="?.+?"?)\)\s+(?:%|\})\}"#).unwrap();
pub static ref SETUP: Setup = Setup {
syntax_set: SyntaxSet::load_defaults_newlines(),
theme_set: from_binary(include_bytes!("../sublime_themes/all.themedump"))
};
}
/// A ShortCode that has a body
/// Called by having some content like {% ... %} body {% end %}
/// We need the struct to hold the data while we're processing the markdown
#[derive(Debug)]
struct ShortCode {
name: String,
args: HashMap
".to_owned()));
}
let theme = &SETUP.theme_set.themes[&highlight_theme];
let syntax = info
.split(' ')
.next()
.and_then(|lang| SETUP.syntax_set.find_syntax_by_token(lang))
.unwrap_or_else(|| SETUP.syntax_set.find_syntax_plain_text());
highlighter = Some(HighlightLines::new(syntax, theme));
let snippet = start_coloured_html_snippet(theme);
Event::Html(Owned(snippet))
},
Event::End(Tag::CodeBlock(_)) => {
in_code_block = false;
if !should_highlight{
return Event::Html(Owned("
\n".to_owned()))
}
// reset highlight and close the code block
highlighter = None;
Event::Html(Owned("".to_owned()))
},
Event::Start(Tag::Code) => {
in_code_block = true;
event
},
Event::End(Tag::Code) => {
in_code_block = false;
event
},
Event::End(Tag::Paragraph) => {
if added_shortcode {
added_shortcode = false;
return Event::Html(Owned("".to_owned()));
}
event
},
Event::SoftBreak => {
if shortcode_block.is_some() {
return Event::Html(Owned("".to_owned()));
}
event
},
_ => {
// println!("event = {:?}", event);
event
},
});
cmark::html::push_html(&mut html, parser);
}
match error {
Some(e) => Err(e),
None => Ok(html),
}
}
#[cfg(test)]
mod tests {
use std::collections::HashMap;
use tera::Tera;
use config::Config;
use super::{markdown_to_html, parse_shortcode};
fn create_test_tera() -> Tera {
let mut tera = Tera::default();
tera.add_raw_template("shortcodes/youtube.html", "Youtube video: {{id}}").unwrap();
tera.add_raw_template("shortcodes/quote.html", "Quote: {{body}} - {{author}}").unwrap();
tera
}
#[test]
fn test_parse_simple_shortcode_one_arg() {
let (name, args) = parse_shortcode(r#"{{ youtube(id="w7Ft2ymGmfc") }}"#);
assert_eq!(name, "youtube");
assert_eq!(args["id"], "w7Ft2ymGmfc");
}
#[test]
fn test_parse_simple_shortcode_several_arg() {
let (name, args) = parse_shortcode(r#"{{ youtube(id="w7Ft2ymGmfc", autoplay=true) }}"#);
assert_eq!(name, "youtube");
assert_eq!(args["id"], "w7Ft2ymGmfc");
assert_eq!(args["autoplay"], "true");
}
#[test]
fn test_parse_block_shortcode_several_arg() {
let (name, args) = parse_shortcode(r#"{% youtube(id="w7Ft2ymGmfc", autoplay=true) %}"#);
assert_eq!(name, "youtube");
assert_eq!(args["id"], "w7Ft2ymGmfc");
assert_eq!(args["autoplay"], "true");
}
#[test]
fn test_markdown_to_html_simple() {
let res = markdown_to_html("# hello", &HashMap::new(), &Tera::default(), &Config::default()).unwrap();
assert_eq!(res, "$ gutenberg server\n
\n"
);
}
#[test]
fn test_markdown_to_html_code_block_no_lang() {
let res = markdown_to_html("```\n$ gutenberg server\n$ ping\n```", &HashMap::new(), &Tera::default(), &Config::default()).unwrap();
assert_eq!(
res,
"\n$ gutenberg server\n$ ping\n" ); } #[test] fn test_markdown_to_html_code_block_with_lang() { let res = markdown_to_html("```python\nlist.append(1)\n```", &HashMap::new(), &Tera::default(), &Config::default()).unwrap(); assert_eq!( res, "
\nlist.append(1)\n" ); } #[test] fn test_markdown_to_html_code_block_with_unknown_lang() { let res = markdown_to_html("```yolo\nlist.append(1)\n```", &HashMap::new(), &Tera::default(), &Config::default()).unwrap(); // defaults to plain text assert_eq!( res, "
\nlist.append(1)\n
"
);
}
#[test]
fn test_markdown_to_html_simple_shortcode() {
let res = markdown_to_html(r#"
Hello
{{ youtube(id="w7Ft2ymGmfc") }}
"#, &HashMap::new(), &create_test_tera(), &Config::default()).unwrap();
assert_eq!(res, "Hello\n
Youtube video: w7Ft2ymGmfc"); } #[test] fn test_markdown_to_html_shortcode_in_code_block() { let res = markdown_to_html(r#"```{{ youtube(id="w7Ft2ymGmfc") }}```"#, &HashMap::new(), &create_test_tera(), &Config::default()).unwrap(); assert_eq!(res, "{{ youtube(id="w7Ft2ymGmfc") }}
Hello\n
Quote: A quote - Keats"); } }