Add a rendering module

This commit is contained in:
Vincent Prouillet 2017-05-17 21:53:26 +09:00
parent b2a63e2ada
commit 26159609d2
8 changed files with 90 additions and 74 deletions

View file

@ -6,7 +6,7 @@ use std::collections::HashMap;
use toml::{Value as Toml, self}; use toml::{Value as Toml, self};
use errors::{Result, ResultExt}; use errors::{Result, ResultExt};
use markdown::SETUP; use rendering::highlighting::THEME_SET;
#[derive(Debug, PartialEq, Serialize, Deserialize)] #[derive(Debug, PartialEq, Serialize, Deserialize)]
@ -65,7 +65,7 @@ impl Config {
match config.highlight_theme { match config.highlight_theme {
Some(ref t) => { Some(ref t) => {
if !SETUP.theme_set.themes.contains_key(t) { if !THEME_SET.themes.contains_key(t) {
bail!("Theme {} not available", t) bail!("Theme {} not available", t)
} }
}, },

View file

@ -11,7 +11,7 @@ use slug::slugify;
use errors::{Result, ResultExt}; use errors::{Result, ResultExt};
use config::Config; use config::Config;
use front_matter::{PageFrontMatter, split_page_content}; use front_matter::{PageFrontMatter, split_page_content};
use markdown::markdown_to_html; use rendering::markdown::markdown_to_html;
use fs::{read_file}; use fs::{read_file};
use content::utils::{find_related_assets, get_reading_analytics}; use content::utils::{find_related_assets, get_reading_analytics};
use content::file_info::FileInfo; use content::file_info::FileInfo;

View file

@ -9,7 +9,7 @@ use config::Config;
use front_matter::{SectionFrontMatter, split_section_content}; use front_matter::{SectionFrontMatter, split_section_content};
use errors::{Result, ResultExt}; use errors::{Result, ResultExt};
use fs::{read_file}; use fs::{read_file};
use markdown::markdown_to_html; use rendering::markdown::markdown_to_html;
use content::Page; use content::Page;
use content::file_info::FileInfo; use content::file_info::FileInfo;

View file

@ -25,7 +25,7 @@ pub mod errors;
mod front_matter; mod front_matter;
mod content; mod content;
mod site; mod site;
mod markdown; mod rendering;
// Filters, Global Fns and default instance of Tera // Filters, Global Fns and default instance of Tera
mod templates; mod templates;
@ -34,4 +34,3 @@ pub use config::{Config, get_config};
pub use front_matter::{PageFrontMatter, SectionFrontMatter, split_page_content, split_section_content}; pub use front_matter::{PageFrontMatter, SectionFrontMatter, split_page_content, split_section_content};
pub use content::{Page, Section, SortBy, sort_pages, populate_previous_and_next_pages}; pub use content::{Page, Section, SortBy, sort_pages, populate_previous_and_next_pages};
pub use fs::{create_file}; pub use fs::{create_file};
pub use markdown::markdown_to_html;

View file

@ -0,0 +1,6 @@
use syntect::dumps::from_binary;
use syntect::highlighting::ThemeSet;
lazy_static!{
pub static ref THEME_SET: ThemeSet = from_binary(include_bytes!("../../sublime_themes/all.themedump"));
}

View file

@ -8,19 +8,18 @@ use slug::slugify;
use syntect::dumps::from_binary; use syntect::dumps::from_binary;
use syntect::easy::HighlightLines; use syntect::easy::HighlightLines;
use syntect::parsing::SyntaxSet; use syntect::parsing::SyntaxSet;
use syntect::highlighting::ThemeSet;
use syntect::html::{start_coloured_html_snippet, styles_to_coloured_html, IncludeBackground}; use syntect::html::{start_coloured_html_snippet, styles_to_coloured_html, IncludeBackground};
use tera::{Tera, Context}; use tera::{Tera, Context};
use config::Config; use config::Config;
use errors::{Result, ResultExt}; use errors::{Result};
use site::resolve_internal_link; use site::resolve_internal_link;
use rendering::highlighting::THEME_SET;
use rendering::short_code::{ShortCode, parse_shortcode, render_simple_shortcode};
// We need to put those in a struct to impl Send and sync // We need to put those in a struct to impl Send and sync
pub struct Setup { pub struct Setup {
pub syntax_set: SyntaxSet, pub syntax_set: SyntaxSet,
pub theme_set: ThemeSet,
} }
unsafe impl Send for Setup {} unsafe impl Send for Setup {}
@ -30,75 +29,13 @@ lazy_static!{
static ref SHORTCODE_RE: Regex = Regex::new(r#"\{(?:%|\{)\s+([[:alnum:]]+?)\(([[:alnum:]]+?="?.+?"?)\)\s+(?:%|\})\}"#).unwrap(); static ref SHORTCODE_RE: Regex = Regex::new(r#"\{(?:%|\{)\s+([[:alnum:]]+?)\(([[:alnum:]]+?="?.+?"?)\)\s+(?:%|\})\}"#).unwrap();
pub static ref SETUP: Setup = Setup { pub static ref SETUP: Setup = Setup {
syntax_set: { syntax_set: {
let mut ps: SyntaxSet = from_binary(include_bytes!("../sublime_syntaxes/newlines.packdump")); let mut ps: SyntaxSet = from_binary(include_bytes!("../../sublime_syntaxes/newlines.packdump"));
ps.link_syntaxes(); ps.link_syntaxes();
ps ps
}, },
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<String, String>,
body: String,
}
impl ShortCode {
pub fn new(name: &str, args: HashMap<String, String>) -> ShortCode {
ShortCode {
name: name.to_string(),
args: args,
body: String::new(),
}
}
pub fn append(&mut self, text: &str) {
self.body.push_str(text)
}
pub fn render(&self, tera: &Tera) -> Result<String> {
let mut context = Context::new();
for (key, value) in &self.args {
context.add(key, value);
}
context.add("body", &self.body);
let tpl_name = format!("shortcodes/{}.html", self.name);
tera.render(&tpl_name, &context)
.chain_err(|| format!("Failed to render {} shortcode", self.name))
}
}
/// Parse a shortcode without a body
fn parse_shortcode(input: &str) -> (String, HashMap<String, String>) {
let mut args = HashMap::new();
let caps = SHORTCODE_RE.captures(input).unwrap();
// caps[0] is the full match
let name = &caps[1];
let arg_list = &caps[2];
for arg in arg_list.split(',') {
let bits = arg.split('=').collect::<Vec<_>>();
args.insert(bits[0].trim().to_string(), bits[1].replace("\"", ""));
}
(name.to_string(), args)
}
/// Renders a shortcode or return an error
fn render_simple_shortcode(tera: &Tera, name: &str, args: &HashMap<String, String>) -> Result<String> {
let mut context = Context::new();
for (key, value) in args.iter() {
context.add(key, value);
}
let tpl_name = format!("shortcodes/{}.html", name);
tera.render(&tpl_name, &context).chain_err(|| format!("Failed to render {} shortcode", name))
}
pub fn markdown_to_html(content: &str, permalinks: &HashMap<String, String>, tera: &Tera, config: &Config) -> Result<String> { pub fn markdown_to_html(content: &str, permalinks: &HashMap<String, String>, tera: &Tera, config: &Config) -> Result<String> {
// We try to be smart about highlighting code as it can be time-consuming // We try to be smart about highlighting code as it can be time-consuming
// If the global config disables it, then we do nothing. However, // If the global config disables it, then we do nothing. However,
@ -233,7 +170,7 @@ pub fn markdown_to_html(content: &str, permalinks: &HashMap<String, String>, ter
if !should_highlight { if !should_highlight {
return Event::Html(Owned("<pre><code>".to_owned())); return Event::Html(Owned("<pre><code>".to_owned()));
} }
let theme = &SETUP.theme_set.themes[&highlight_theme]; let theme = &THEME_SET.themes[&highlight_theme];
let syntax = info let syntax = info
.split(' ') .split(' ')
.next() .next()

3
src/rendering/mod.rs Normal file
View file

@ -0,0 +1,3 @@
pub mod highlighting;
pub mod markdown;
pub mod short_code;

View file

@ -0,0 +1,71 @@
use std::collections::HashMap;
use regex::Regex;
use tera::{Tera, Context};
use errors::{Result, ResultExt};
lazy_static!{
static ref SHORTCODE_RE: Regex = Regex::new(r#"\{(?:%|\{)\s+([[:alnum:]]+?)\(([[:alnum:]]+?="?.+?"?)\)\s+(?:%|\})\}"#).unwrap();
}
/// 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)]
pub struct ShortCode {
name: String,
args: HashMap<String, String>,
body: String,
}
impl ShortCode {
pub fn new(name: &str, args: HashMap<String, String>) -> ShortCode {
ShortCode {
name: name.to_string(),
args: args,
body: String::new(),
}
}
pub fn append(&mut self, text: &str) {
self.body.push_str(text)
}
pub fn render(&self, tera: &Tera) -> Result<String> {
let mut context = Context::new();
for (key, value) in &self.args {
context.add(key, value);
}
context.add("body", &self.body);
let tpl_name = format!("shortcodes/{}.html", self.name);
tera.render(&tpl_name, &context)
.chain_err(|| format!("Failed to render {} shortcode", self.name))
}
}
/// Parse a shortcode without a body
pub fn parse_shortcode(input: &str) -> (String, HashMap<String, String>) {
let mut args = HashMap::new();
let caps = SHORTCODE_RE.captures(input).unwrap();
// caps[0] is the full match
let name = &caps[1];
let arg_list = &caps[2];
for arg in arg_list.split(',') {
let bits = arg.split('=').collect::<Vec<_>>();
args.insert(bits[0].trim().to_string(), bits[1].replace("\"", ""));
}
(name.to_string(), args)
}
/// Renders a shortcode or return an error
pub fn render_simple_shortcode(tera: &Tera, name: &str, args: &HashMap<String, String>) -> Result<String> {
let mut context = Context::new();
for (key, value) in args.iter() {
context.add(key, value);
}
let tpl_name = format!("shortcodes/{}.html", name);
tera.render(&tpl_name, &context).chain_err(|| format!("Failed to render {} shortcode", name))
}