diff --git a/Cargo.lock b/Cargo.lock
index 9023ba27..513d8662 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1113,12 +1113,13 @@ dependencies = [
name = "rendering"
version = "0.1.0"
dependencies = [
+ "config 0.1.0",
"errors 0.1.0",
"front_matter 0.1.0",
"highlighting 0.1.0",
- "lazy_static 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "pest 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "pest_derive 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)",
"pulldown-cmark 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "regex 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.66 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_derive 1.0.66 (registry+https://github.com/rust-lang/crates.io-index)",
"slug 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
diff --git a/README.md b/README.md
index 95bb0f52..23454eb7 100644
--- a/README.md
+++ b/README.md
@@ -52,7 +52,7 @@ If you want a feature added or modified, please open an issue to discuss it befo
Syntax highlighting depends on submodules so ensure you load them first:
```bash
-$ git submodule update --init
+$ git submodule update --init
```
Gutenberg only works with syntaxes in the `.sublime-syntax` format. If your syntax
@@ -75,7 +75,7 @@ You can check for any updates to the current packages by running:
$ git submodule update --remote --merge
```
-And finally from the root of the components/rendering crate run the following command:
+And finally from the root of the components/highlighting crate run the following command:
```bash
$ cargo run --example generate_sublime synpack ../../sublime_syntaxes ../../sublime_syntaxes/newlines.packdump ../../sublime_syntaxes/nonewlines.packdump
diff --git a/components/content/src/page.rs b/components/content/src/page.rs
index b3cd820b..f977c464 100644
--- a/components/content/src/page.rs
+++ b/components/content/src/page.rs
@@ -14,7 +14,7 @@ use utils::fs::{read_file, find_related_assets};
use utils::site::get_reading_analytics;
use utils::templates::render_template;
use front_matter::{PageFrontMatter, InsertAnchor, split_page_content};
-use rendering::{Context, Header, markdown_to_html};
+use rendering::{RenderContext, Header, render_content};
use file_info::FileInfo;
@@ -162,21 +162,23 @@ impl Page {
/// We need access to all pages url to render links relative to content
/// so that can't happen at the same time as parsing
pub fn render_markdown(&mut self, permalinks: &HashMap, tera: &Tera, config: &Config, anchor_insert: InsertAnchor) -> Result<()> {
- let context = Context::new(
+ let context = RenderContext::new(
tera,
- config.highlight_code,
- config.highlight_theme.clone(),
+ config,
&self.permalink,
permalinks,
anchor_insert
);
- let res = markdown_to_html(&self.raw_content.replacen("", " ", 1), &context)?;
+ let res = render_content(
+ &self.raw_content.replacen("", " ", 1),
+ &context
+ )?;
self.content = res.0;
self.toc = res.1;
if self.raw_content.contains("") {
self.summary = Some({
let summary = self.raw_content.splitn(2, "").collect::>()[0];
- markdown_to_html(summary, &context)?.0
+ render_content(summary, &context)?.0
})
}
diff --git a/components/content/src/section.rs b/components/content/src/section.rs
index b75581c6..c864318e 100644
--- a/components/content/src/section.rs
+++ b/components/content/src/section.rs
@@ -11,7 +11,7 @@ use errors::{Result, ResultExt};
use utils::fs::read_file;
use utils::templates::render_template;
use utils::site::get_reading_analytics;
-use rendering::{Context, Header, markdown_to_html};
+use rendering::{RenderContext, Header, render_content};
use page::Page;
use file_info::FileInfo;
@@ -98,15 +98,14 @@ impl Section {
/// We need access to all pages url to render links relative to content
/// so that can't happen at the same time as parsing
pub fn render_markdown(&mut self, permalinks: &HashMap, tera: &Tera, config: &Config) -> Result<()> {
- let context = Context::new(
+ let context = RenderContext::new(
tera,
- config.highlight_code,
- config.highlight_theme.clone(),
+ config,
&self.permalink,
permalinks,
self.meta.insert_anchor_links,
);
- let res = markdown_to_html(&self.raw_content, &context)?;
+ let res = render_content(&self.raw_content, &context)?;
self.content = res.0;
self.toc = res.1;
Ok(())
diff --git a/components/front_matter/src/page.rs b/components/front_matter/src/page.rs
index 86cde2a9..0b33a3a2 100644
--- a/components/front_matter/src/page.rs
+++ b/components/front_matter/src/page.rs
@@ -21,7 +21,7 @@ fn from_toml_datetime<'de, D>(deserializer: D) -> StdResult, D::E
fn convert_toml_date(table: Map) -> Value {
let mut new = Map::new();
- for (k, v) in table.into_iter() {
+ for (k, v) in table {
if k == "$__toml_private_datetime" {
return v;
}
diff --git a/components/rendering/examples/generate_sublime.rs b/components/highlighting/examples/generate_sublime.rs
similarity index 100%
rename from components/rendering/examples/generate_sublime.rs
rename to components/highlighting/examples/generate_sublime.rs
diff --git a/components/highlighting/src/lib.rs b/components/highlighting/src/lib.rs
index f0a80b83..10834dbe 100644
--- a/components/highlighting/src/lib.rs
+++ b/components/highlighting/src/lib.rs
@@ -4,7 +4,8 @@ extern crate syntect;
use syntect::dumps::from_binary;
use syntect::parsing::SyntaxSet;
-use syntect::highlighting::ThemeSet;
+use syntect::highlighting::{ThemeSet, Theme};
+use syntect::easy::HighlightLines;
thread_local!{
pub static SYNTAX_SET: SyntaxSet = {
@@ -17,3 +18,15 @@ thread_local!{
lazy_static!{
pub static ref THEME_SET: ThemeSet = from_binary(include_bytes!("../../../sublime_themes/all.themedump"));
}
+
+
+pub fn get_highlighter<'a>(theme: &'a Theme, info: &str) -> HighlightLines<'a> {
+ SYNTAX_SET.with(|ss| {
+ let syntax = info
+ .split(' ')
+ .next()
+ .and_then(|lang| ss.find_syntax_by_token(lang))
+ .unwrap_or_else(|| ss.find_syntax_plain_text());
+ HighlightLines::new(syntax, theme)
+ })
+}
diff --git a/components/rendering/Cargo.toml b/components/rendering/Cargo.toml
index 92ff5c08..029c5056 100644
--- a/components/rendering/Cargo.toml
+++ b/components/rendering/Cargo.toml
@@ -5,8 +5,6 @@ authors = ["Vincent Prouillet "]
[dependencies]
tera = "0.11"
-regex = "1"
-lazy_static = "1"
syntect = "2"
pulldown-cmark = "0"
slug = "0.1"
diff --git a/components/rendering/benches/all.rs b/components/rendering/benches/all.rs
index e7a968b0..70244521 100644
--- a/components/rendering/benches/all.rs
+++ b/components/rendering/benches/all.rs
@@ -9,7 +9,7 @@ extern crate front_matter;
use std::collections::HashMap;
use tera::Tera;
-use rendering::{Context, markdown_to_html, render_shortcodes};
+use rendering::{RenderContext, render_content, render_shortcodes};
use front_matter::InsertAnchor;
use config::Config;
@@ -86,19 +86,36 @@ if __name__ == "__main__":
"#;
#[bench]
-fn bench_markdown_to_html_with_highlighting(b: &mut test::Bencher) {
- let tera_ctx = Tera::default();
+fn bench_render_content_with_highlighting(b: &mut test::Bencher) {
+ let mut tera = Tera::default();
+ tera.add_raw_template("shortcodes/youtube.html", "{{id}}").unwrap();
let permalinks_ctx = HashMap::new();
- let context = Context::new(&tera_ctx, true, "base16-ocean-dark".to_string(), "", &permalinks_ctx, InsertAnchor::None);
- b.iter(|| markdown_to_html(CONTENT, &context));
+ let config = Config::default();
+ let context = RenderContext::new(&tera, &config, "", &permalinks_ctx, InsertAnchor::None);
+ b.iter(|| render_content(CONTENT, &context).unwrap());
}
#[bench]
-fn bench_markdown_to_html_without_highlighting(b: &mut test::Bencher) {
- let tera_ctx = Tera::default();
+fn bench_render_content_without_highlighting(b: &mut test::Bencher) {
+ let mut tera = Tera::default();
+ tera.add_raw_template("shortcodes/youtube.html", "{{id}}").unwrap();
let permalinks_ctx = HashMap::new();
- let context = Context::new(&tera_ctx, false, "base16-ocean-dark".to_string(), "", &permalinks_ctx, InsertAnchor::None);
- b.iter(|| markdown_to_html(CONTENT, &context));
+ let mut config = Config::default();
+ config.highlight_code = false;
+ let context = RenderContext::new(&tera, &config, "", &permalinks_ctx, InsertAnchor::None);
+ b.iter(|| render_content(CONTENT, &context).unwrap());
+}
+
+#[bench]
+fn bench_render_content_no_shortcode(b: &mut test::Bencher) {
+ let tera = Tera::default();
+ let content2 = CONTENT.replace(r#"{{ youtube(id="my_youtube_id") }}"#, "");
+ let mut config = Config::default();
+ config.highlight_code = false;
+ let permalinks_ctx = HashMap::new();
+ let context = RenderContext::new(&tera, &config, "", &permalinks_ctx, InsertAnchor::None);
+
+ b.iter(|| render_content(&content2, &context).unwrap());
}
#[bench]
@@ -109,11 +126,3 @@ fn bench_render_shortcodes_one_present(b: &mut test::Bencher) {
b.iter(|| render_shortcodes(CONTENT, &tera, &Config::default()));
}
-#[bench]
-fn bench_render_shortcodes_none(b: &mut test::Bencher) {
- let mut tera = Tera::default();
- tera.add_raw_template("shortcodes/youtube.html", "{{id}}").unwrap();
- let content2 = CONTENT.replace(r#"{{ youtube(id="my_youtube_id") }}"#, "");
-
- b.iter(|| render_shortcodes(&content2, &tera, &Config::default()));
-}
diff --git a/components/rendering/src/context.rs b/components/rendering/src/context.rs
index aa866be6..d8a2513f 100644
--- a/components/rendering/src/context.rs
+++ b/components/rendering/src/context.rs
@@ -1,41 +1,35 @@
use std::collections::HashMap;
use tera::Tera;
-
use front_matter::InsertAnchor;
+use config::Config;
/// All the information from the gutenberg site that is needed to render HTML from markdown
#[derive(Debug)]
-pub struct Context<'a> {
+pub struct RenderContext<'a> {
pub tera: &'a Tera,
- pub highlight_code: bool,
- pub highlight_theme: String,
- pub current_page_permalink: String,
+ pub config: &'a Config,
+ pub current_page_permalink: &'a str,
pub permalinks: &'a HashMap,
pub insert_anchor: InsertAnchor,
}
-impl<'a> Context<'a> {
+impl<'a> RenderContext<'a> {
pub fn new(
tera: &'a Tera,
- highlight_code: bool,
- highlight_theme: String,
- current_page_permalink: &str,
+ config: &'a Config,
+ current_page_permalink: &'a str,
permalinks: &'a HashMap,
insert_anchor: InsertAnchor,
- ) -> Context<'a> {
- Context {
+ ) -> RenderContext<'a> {
+ RenderContext {
tera,
- current_page_permalink: current_page_permalink.to_string(),
+ current_page_permalink,
permalinks,
insert_anchor,
- highlight_code,
- highlight_theme,
+ config,
}
}
- pub fn should_insert_anchor(&self) -> bool {
- self.insert_anchor != InsertAnchor::None
- }
}
diff --git a/components/rendering/src/lib.rs b/components/rendering/src/lib.rs
index 185c006a..00715af2 100644
--- a/components/rendering/src/lib.rs
+++ b/components/rendering/src/lib.rs
@@ -1,6 +1,3 @@
-#[macro_use]
-extern crate lazy_static;
-extern crate regex;
extern crate tera;
extern crate syntect;
extern crate pulldown_cmark;
@@ -12,7 +9,7 @@ extern crate pest;
#[macro_use]
extern crate pest_derive;
-
+#[macro_use]
extern crate errors;
extern crate front_matter;
extern crate highlighting;
@@ -26,9 +23,20 @@ mod context;
mod markdown;
mod table_of_contents;
mod shortcode;
-mod short_code;
-pub use context::Context;
-pub use markdown::markdown_to_html;
+use errors::Result;
+
+use markdown::markdown_to_html;
pub use table_of_contents::Header;
pub use shortcode::render_shortcodes;
+pub use context::RenderContext;
+
+pub fn render_content(content: &str, context: &RenderContext) -> Result<(String, Vec)> {
+ // Don't do anything if there is nothing like a shortcode in the content
+ if content.contains("{{") || content.contains("{%") {
+ let rendered = render_shortcodes(content, context.tera, context.config)?;
+ return markdown_to_html(&rendered, context);
+ }
+
+ markdown_to_html(&content, context)
+}
diff --git a/components/rendering/src/markdown.rs b/components/rendering/src/markdown.rs
index b993dd70..a6ac28c9 100644
--- a/components/rendering/src/markdown.rs
+++ b/components/rendering/src/markdown.rs
@@ -8,37 +8,36 @@ use syntect::html::{start_coloured_html_snippet, styles_to_coloured_html, Includ
use errors::Result;
use utils::site::resolve_internal_link;
-use context::Context;
-use highlighting::{SYNTAX_SET, THEME_SET};
-use short_code::{SHORTCODE_RE, ShortCode, parse_shortcode, render_simple_shortcode};
+use highlighting::{get_highlighter, THEME_SET};
+
use table_of_contents::{TempHeader, Header, make_table_of_contents};
+use context::RenderContext;
+
+// We might have cases where the slug is already present in our list of anchor
+// for example an article could have several titles named Example
+// We add a counter after the slug if the slug is already present, which
+// means we will have example, example-1, example-2 etc
+fn find_anchor(anchors: &[String], name: String, level: u8) -> String {
+ if level == 0 && !anchors.contains(&name) {
+ return name.to_string();
+ }
+
+ let new_anchor = format!("{}-{}", name, level + 1);
+ if !anchors.contains(&new_anchor) {
+ return new_anchor;
+ }
+
+ find_anchor(anchors, name, level + 1)
+}
-pub fn markdown_to_html(content: &str, context: &Context) -> Result<(String, Vec)> {
- // 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 we see a code block in the content, we assume that this page needs
- // to be highlighted. It could potentially have false positive if the content
- // has ``` in it but that seems kind of unlikely
- let should_highlight = if context.highlight_code {
- content.contains("```")
- } else {
- false
- };
+pub fn markdown_to_html(content: &str, context: &RenderContext) -> Result<(String, Vec)> {
+ // the rendered html
+ let mut html = String::new();
// Set while parsing
let mut error = None;
+
let mut highlighter: Option = None;
- // the markdown parser will send several Text event if a markdown character
- // is present in it, for example `hello_test` will be split in 2: hello and _test.
- // Since we can use those chars in shortcode arguments, we need to collect
- // the full shortcode somehow first
- let mut current_shortcode = String::new();
- let mut shortcode_block = None;
- // shortcodes live outside of paragraph so we need to ensure we don't close
- // a paragraph that has already been closed
- let mut added_shortcode = false;
- // Don't transform things that look like shortcodes in code blocks
- let mut in_code_block = false;
// If we get text in header, we need to insert the id and a anchor
let mut in_header = false;
// pulldown_cmark can send several text events for a title if there are markdown
@@ -46,254 +45,128 @@ pub fn markdown_to_html(content: &str, context: &Context) -> Result<(String, Vec
let mut header_created = false;
let mut anchors: Vec = vec![];
- // the rendered html
- let mut html = String::new();
-
- // We might have cases where the slug is already present in our list of anchor
- // for example an article could have several titles named Example
- // We add a counter after the slug if the slug is already present, which
- // means we will have example, example-1, example-2 etc
- fn find_anchor(anchors: &[String], name: String, level: u8) -> String {
- if level == 0 && !anchors.contains(&name) {
- return name.to_string();
- }
-
- let new_anchor = format!("{}-{}", name, level + 1);
- if !anchors.contains(&new_anchor) {
- return new_anchor;
- }
-
- find_anchor(anchors, name, level + 1)
- }
-
let mut headers = vec![];
// Defaults to a 0 level so not a real header
// It should be an Option ideally but not worth the hassle to update
let mut temp_header = TempHeader::default();
- let mut clear_shortcode_block = false;
let mut opts = Options::empty();
opts.insert(OPTION_ENABLE_TABLES);
opts.insert(OPTION_ENABLE_FOOTNOTES);
{
-
let parser = Parser::new_ext(content, opts).map(|event| {
- if clear_shortcode_block {
- clear_shortcode_block = false;
- shortcode_block = None;
- }
-
match event {
- Event::Text(mut text) => {
- // Header first
- if in_header {
- if header_created {
- temp_header.push(&text);
+ Event::Text(text) => {
+ // Header first
+ if in_header {
+ if header_created {
+ temp_header.push(&text);
+ return Event::Html(Owned(String::new()));
+ }
+ let id = find_anchor(&anchors, slugify(&text), 0);
+ anchors.push(id.clone());
+ // update the header and add it to the list
+ temp_header.id = id.clone();
+ // += as we might have some or other things already there
+ temp_header.title += &text;
+ temp_header.permalink = format!("{}#{}", context.current_page_permalink, id);
+ header_created = true;
return Event::Html(Owned(String::new()));
}
- let id = find_anchor(&anchors, slugify(&text), 0);
- anchors.push(id.clone());
- // update the header and add it to the list
- temp_header.id = id.clone();
- // += as we might have some or other things already there
- temp_header.title += &text;
- temp_header.permalink = format!("{}#{}", context.current_page_permalink, id);
- header_created = true;
- return Event::Html(Owned(String::new()));
- }
- // if we are in the middle of a code block
- if let Some(ref mut highlighter) = highlighter {
- let highlighted = &highlighter.highlight(&text);
- let html = styles_to_coloured_html(highlighted, IncludeBackground::Yes);
- return Event::Html(Owned(html));
- }
-
- if in_code_block {
- return Event::Text(text);
- }
-
- // Are we in the middle of a shortcode that somehow got cut off
- // by the markdown parser?
- if current_shortcode.is_empty() {
- if text.starts_with("{{") && !text.ends_with("}}") {
- current_shortcode += &text;
- } else if text.starts_with("{%") && !text.ends_with("%}") {
- current_shortcode += &text;
+ // if we are in the middle of a code block
+ if let Some(ref mut highlighter) = highlighter {
+ let highlighted = &highlighter.highlight(&text);
+ let html = styles_to_coloured_html(highlighted, IncludeBackground::Yes);
+ return Event::Html(Owned(html));
}
- } else {
- current_shortcode += &text;
- }
- if current_shortcode.ends_with("}}") || current_shortcode.ends_with("%}") {
- text = Owned(current_shortcode.clone());
- current_shortcode = String::new();
- }
-
- // Shortcode without body
- if shortcode_block.is_none() && text.starts_with("{{") && text.ends_with("}}") && SHORTCODE_RE.is_match(&text) {
- let (name, args) = parse_shortcode(&text);
-
- added_shortcode = true;
- match render_simple_shortcode(context.tera, &name, &args) {
- // Make before and after cleaning up of extra /
tags more parallel.
- // Or, in other words:
- // TERRIBLE HORRIBLE NO GOOD VERY BAD HACK
- Ok(s) => return Event::Html(Owned(format!("
{}", s))),
- Err(e) => {
- error = Some(e);
- return Event::Html(Owned(String::new()));
- }
+ // Business as usual
+ Event::Text(text)
+ },
+ Event::Start(Tag::CodeBlock(ref info)) => {
+ if !context.config.highlight_code {
+ return Event::Html(Owned("
".to_owned()));
}
- }
-
- // Shortcode with a body
- if shortcode_block.is_none() && text.starts_with("{%") && text.ends_with("%}") {
- if SHORTCODE_RE.is_match(&text) {
- let (name, args) = parse_shortcode(&text);
- shortcode_block = Some(ShortCode::new(&name, args));
+ let theme = &THEME_SET.themes[&context.config.highlight_theme];
+ highlighter = Some(get_highlighter(&theme, info));
+ let snippet = start_coloured_html_snippet(theme);
+ Event::Html(Owned(snippet))
+ },
+ Event::End(Tag::CodeBlock(_)) => {
+ if !context.config.highlight_code {
+ return Event::Html(Owned("
\n".to_owned()))
}
- // Don't return anything
- return Event::Text(Owned(String::new()));
- }
-
- // If we have some text while in a shortcode, it's either the body
- // or the end tag
- if shortcode_block.is_some() {
- if let Some(ref mut shortcode) = shortcode_block {
- if text.trim() == "{% end %}" {
- added_shortcode = true;
- clear_shortcode_block = true;
- match shortcode.render(context.tera) {
- Ok(s) => return Event::Html(Owned(format!("{}", s))),
- Err(e) => {
- error = Some(e);
- return Event::Html(Owned(String::new()));
- }
+ // reset highlight and close the code block
+ highlighter = None;
+ Event::Html(Owned("".to_owned()))
+ },
+ // Need to handle relative links
+ Event::Start(Tag::Link(ref link, ref title)) => {
+ if in_header {
+ return Event::Html(Owned("".to_owned()));
+ }
+ if link.starts_with("./") {
+ match resolve_internal_link(link, context.permalinks) {
+ Ok(url) => {
+ return Event::Start(Tag::Link(Owned(url), title.clone()));
+ },
+ Err(_) => {
+ error = Some(format!("Relative link {} not found.", link).into());
+ return Event::Html(Owned("".to_string()));
}
- } else {
- shortcode.append(&text);
- return Event::Html(Owned(String::new()));
- }
+ };
}
- }
- // Business as usual
- Event::Text(text)
- },
- Event::Start(Tag::CodeBlock(ref info)) => {
- in_code_block = true;
- if !should_highlight {
- return Event::Html(Owned("".to_owned()));
+ Event::Start(Tag::Link(link.clone(), title.clone()))
+ },
+ Event::End(Tag::Link(_, _)) => {
+ if in_header {
+ return Event::Html(Owned("".to_owned()));
+ }
+ event
}
- let theme = &THEME_SET.themes[&context.highlight_theme];
- highlighter = SYNTAX_SET.with(|ss| {
- let syntax = info
- .split(' ')
- .next()
- .and_then(|lang| ss.find_syntax_by_token(lang))
- .unwrap_or_else(|| ss.find_syntax_plain_text());
- 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()))
- },
- // Need to handle relative links
- Event::Start(Tag::Link(ref link, ref title)) => {
- if in_header {
- return Event::Html(Owned("".to_owned()));
- }
- if link.starts_with("./") {
- match resolve_internal_link(link, context.permalinks) {
- Ok(url) => {
- return Event::Start(Tag::Link(Owned(url), title.clone()));
- },
- Err(_) => {
- error = Some(format!("Relative link {} not found.", link).into());
- return Event::Html(Owned("".to_string()));
- }
- };
- }
-
- Event::Start(Tag::Link(link.clone(), title.clone()))
- },
- Event::End(Tag::Link(_, _)) => {
- if in_header {
- return Event::Html(Owned("".to_owned()));
- }
- event
+ Event::Start(Tag::Code) => {
+ if in_header {
+ temp_header.push("");
+ return Event::Html(Owned(String::new()));
+ }
+ event
+ },
+ Event::End(Tag::Code) => {
+ if in_header {
+ temp_header.push("
");
+ return Event::Html(Owned(String::new()));
+ }
+ event
+ },
+ Event::Start(Tag::Header(num)) => {
+ in_header = true;
+ temp_header = TempHeader::new(num);
+ Event::Html(Owned(String::new()))
+ },
+ Event::End(Tag::Header(_)) => {
+ // End of a header, reset all the things and return the stringified version of the header
+ in_header = false;
+ header_created = false;
+ let val = temp_header.to_string(context.tera, context.insert_anchor);
+ headers.push(temp_header.clone());
+ temp_header = TempHeader::default();
+ Event::Html(Owned(val))
+ },
+ _ => event,
}
- // need to know when we are in a code block to disable shortcodes in them
- Event::Start(Tag::Code) => {
- in_code_block = true;
- if in_header {
- temp_header.push("");
- return Event::Html(Owned(String::new()));
- }
- event
- },
- Event::End(Tag::Code) => {
- in_code_block = false;
- if in_header {
- temp_header.push("
");
- return Event::Html(Owned(String::new()));
- }
- event
- },
- Event::Start(Tag::Header(num)) => {
- in_header = true;
- temp_header = TempHeader::new(num);
- Event::Html(Owned(String::new()))
- },
- Event::End(Tag::Header(_)) => {
- // End of a header, reset all the things and return the stringified version of the header
- in_header = false;
- header_created = false;
- let val = temp_header.to_string(context);
- headers.push(temp_header.clone());
- temp_header = TempHeader::default();
- Event::Html(Owned(val))
- },
- // If we added shortcodes, don't close a paragraph since there's none
- Event::End(Tag::Paragraph) => {
- if added_shortcode {
- added_shortcode = false;
- return Event::Html(Owned("".to_owned()));
- }
- event
- },
- // Ignore softbreaks inside shortcodes
- 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);
}
- if !current_shortcode.is_empty() {
- return Err(format!("A shortcode was not closed properly:\n{:?}", current_shortcode).into());
- }
-
match error {
Some(e) => Err(e),
- None => Ok((html.replace("
", "").replace("", ""), make_table_of_contents(&headers))),
+ None => Ok((
+ html.replace("
", "").replace("", ""),
+ make_table_of_contents(&headers)
+ )),
}
}
diff --git a/components/rendering/src/short_code.rs b/components/rendering/src/short_code.rs
deleted file mode 100644
index f1cd3fbb..00000000
--- a/components/rendering/src/short_code.rs
+++ /dev/null
@@ -1,190 +0,0 @@
-use std::collections::HashMap;
-
-use regex::Regex;
-use tera::{Tera, Context, Value, to_value};
-
-use errors::{Result, ResultExt};
-
-lazy_static!{
- // Does this look like a shortcode?
- pub static ref SHORTCODE_RE: Regex = Regex::new(
- r#"\{(?:%|\{)\s+(\w+?)\((\w+?="?(?:.|\n)+?"?)?\)\s+(?:%|\})\}"#
- ).unwrap();
-
- // Parse the shortcode args with capture groups named after their type
- pub static ref SHORTCODE_ARGS_RE: Regex = Regex::new(
- r#"(?P\w+)=\s*((?P".*?")|(?P[-+]?[0-9]+\.[0-9]+)|(?P[-+]?[0-9]+)|(?Ptrue|false))"#
- ).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,
- body: String,
-}
-
-impl ShortCode {
- pub fn new(name: &str, args: HashMap) -> ShortCode {
- ShortCode {
- name: name.to_string(),
- args,
- body: String::new(),
- }
- }
-
- pub fn append(&mut self, text: &str) {
- self.body.push_str(text)
- }
-
- pub fn render(&self, tera: &Tera) -> Result {
- 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) {
- let mut args = HashMap::new();
- let caps = SHORTCODE_RE.captures(input).unwrap();
- // caps[0] is the full match
- let name = &caps[1];
-
- if let Some(arg_list) = caps.get(2) {
- for arg_cap in SHORTCODE_ARGS_RE.captures_iter(arg_list.as_str()) {
- let arg_name = arg_cap["name"].trim().to_string();
-
- if let Some(arg_val) = arg_cap.name("str") {
- args.insert(arg_name, to_value(arg_val.as_str().replace("\"", "")).unwrap());
- continue;
- }
-
- if let Some(arg_val) = arg_cap.name("int") {
- args.insert(arg_name, to_value(arg_val.as_str().parse::().unwrap()).unwrap());
- continue;
- }
-
- if let Some(arg_val) = arg_cap.name("float") {
- args.insert(arg_name, to_value(arg_val.as_str().parse::().unwrap()).unwrap());
- continue;
- }
-
- if let Some(arg_val) = arg_cap.name("bool") {
- args.insert(arg_name, to_value(arg_val.as_str() == "true").unwrap());
- continue;
- }
- }
- }
-
- (name.to_string(), args)
-}
-
-/// Renders a shortcode or return an error
-pub fn render_simple_shortcode(tera: &Tera, name: &str, args: &HashMap) -> Result {
- 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))
-}
-
-
-#[cfg(test)]
-mod tests {
- use super::{parse_shortcode, SHORTCODE_RE};
-
- #[test]
- fn can_match_all_kinds_of_shortcode() {
- let inputs = vec![
- "{{ basic() }}",
- "{{ basic(ho=1) }}",
- "{{ basic(ho=\"hey\") }}",
- "{{ basic(ho=\"hey_underscore\") }}",
- "{{ basic(ho=\"hey-dash\") }}",
- "{% basic(ho=\"hey-dash\") %}",
- "{% basic(ho=\"hey_underscore\") %}",
- "{% basic() %}",
- "{% quo_te(author=\"Bob\") %}",
- "{{ quo_te(author=\"Bob\") }}",
- // https://github.com/Keats/gutenberg/issues/229
- r#"{{ youtube(id="dQw4w9WgXcQ",
-
- autoplay=true) }}"#,
- ];
-
- for i in inputs {
- println!("{}", i);
- assert!(SHORTCODE_RE.is_match(i));
- }
- }
-
- // https://github.com/Keats/gutenberg/issues/228
- #[test]
- fn doesnt_panic_on_invalid_shortcode() {
- let (name, args) = parse_shortcode(r#"{{ youtube(id="dQw4w9WgXcQ", autoplay) }}"#);
- assert_eq!(name, "youtube");
- assert_eq!(args["id"], "dQw4w9WgXcQ");
- assert!(args.get("autoplay").is_none());
- }
-
- #[test]
- fn can_parse_simple_shortcode_no_arg() {
- let (name, args) = parse_shortcode(r#"{{ basic() }}"#);
- assert_eq!(name, "basic");
- assert!(args.is_empty());
- }
-
- #[test]
- fn can_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 can_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 can_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 can_parse_shortcode_number() {
- let (name, args) = parse_shortcode(r#"{% test(int=42, float=42.0, autoplay=false) %}"#);
- assert_eq!(name, "test");
- assert_eq!(args["int"], 42);
- assert_eq!(args["float"], 42.0);
- assert_eq!(args["autoplay"], false);
- }
-
- // https://github.com/Keats/gutenberg/issues/249
- #[test]
- fn can_parse_shortcode_with_comma_in_it() {
- let (name, args) = parse_shortcode(
- r#"{% quote(author="C++ Standard Core Language Defect Reports and Accepted Issues, Revision 82, delete and user-written deallocation function", href="http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#348") %}"#
- );
- assert_eq!(name, "quote");
- assert_eq!(args["author"], "C++ Standard Core Language Defect Reports and Accepted Issues, Revision 82, delete and user-written deallocation function");
- assert_eq!(args["href"], "http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#348");
- }
-}
diff --git a/components/rendering/src/shortcode.rs b/components/rendering/src/shortcode.rs
index 009a0cfe..4f0d0f1a 100644
--- a/components/rendering/src/shortcode.rs
+++ b/components/rendering/src/shortcode.rs
@@ -90,7 +90,8 @@ fn render_shortcode(name: String, args: Map, tera: &Tera, config:
context.insert(key, value);
}
if let Some(ref b) = body {
- context.insert("body", b);
+ // Trimming right to avoid most shortcodes with bodies ending up with a HTML new line
+ context.insert("body", b.trim_right());
}
context.insert("config", config);
let tpl_name = format!("shortcodes/{}.html", name);
@@ -99,16 +100,25 @@ fn render_shortcode(name: String, args: Map, tera: &Tera, config:
}
pub fn render_shortcodes(content: &str, tera: &Tera, config: &Config) -> Result {
- // Don't do anything if there is nothing like a shortcode in the content
- if !content.contains("{{") && !content.contains("{%") {
- return Ok(content.to_string());
- }
-
let mut res = String::with_capacity(content.len());
let mut pairs = match ContentParser::parse(Rule::page, content) {
Ok(p) => p,
- Err(_) => panic!("TODO"), // TODO: error handling
+ Err(e) => {
+ let fancy_e = e.renamed_rules(|rule| {
+ match *rule {
+ Rule::int => "an integer".to_string(),
+ Rule::float => "a float".to_string(),
+ Rule::string => "a string".to_string(),
+ Rule::literal => "a literal (int, float, string, bool)".to_string(),
+ Rule::array => "an array".to_string(),
+ Rule::kwarg => "a keyword argument".to_string(),
+ Rule::ident => "an identifier".to_string(),
+ _ => format!("TODO error: {:?}", rule).to_string(),
+ }
+ });
+ bail!("{}", fancy_e);
+ },
};
// We have at least a `page` pair
@@ -333,4 +343,10 @@ Hello World
let res = render_shortcodes("Body\n {% youtube() %}Hey!{% end %}", &tera, &Config::default()).unwrap();
assert_eq!(res, "Body\n Hey!");
}
+
+ #[test]
+ fn errors_on_unterminated_shortcode() {
+ let res = render_shortcodes("{{ youtube(", &Tera::default(), &Config::default());
+ assert!(res.is_err());
+ }
}
diff --git a/components/rendering/src/table_of_contents.rs b/components/rendering/src/table_of_contents.rs
index 70f75954..688c64d9 100644
--- a/components/rendering/src/table_of_contents.rs
+++ b/components/rendering/src/table_of_contents.rs
@@ -1,8 +1,6 @@
-use tera::{Context as TeraContext};
+use tera::{Tera, Context as TeraContext};
use front_matter::InsertAnchor;
-use context::Context;
-
#[derive(Debug, PartialEq, Clone, Serialize)]
pub struct Header {
@@ -50,16 +48,16 @@ impl TempHeader {
}
/// Transform all the information we have about this header into the HTML string for it
- pub fn to_string(&self, context: &Context) -> String {
- let anchor_link = if context.should_insert_anchor() {
+ pub fn to_string(&self, tera: &Tera, insert_anchor: InsertAnchor) -> String {
+ let anchor_link = if insert_anchor != InsertAnchor::None {
let mut c = TeraContext::new();
c.add("id", &self.id);
- context.tera.render("anchor-link.html", &c).unwrap()
+ tera.render("anchor-link.html", &c).unwrap()
} else {
String::new()
};
- match context.insert_anchor {
+ match insert_anchor {
InsertAnchor::None => format!("{t} \n", lvl=self.level, t=self.title, id=self.id),
InsertAnchor::Left => format!("{a}{t} \n", lvl=self.level, a=anchor_link, t=self.title, id=self.id),
InsertAnchor::Right => format!("{t}{a} \n", lvl=self.level, a=anchor_link, t=self.title, id=self.id),
diff --git a/components/rendering/tests/markdown.rs b/components/rendering/tests/markdown.rs
index 8dbdcc73..2e547f33 100644
--- a/components/rendering/tests/markdown.rs
+++ b/components/rendering/tests/markdown.rs
@@ -2,22 +2,25 @@ extern crate tera;
extern crate front_matter;
extern crate templates;
extern crate rendering;
+extern crate config;
use std::collections::HashMap;
use tera::Tera;
+use config::Config;
use front_matter::InsertAnchor;
use templates::GUTENBERG_TERA;
-use rendering::{Context, markdown_to_html};
+use rendering::{RenderContext, render_content};
#[test]
-fn can_do_markdown_to_html_simple() {
+fn can_do_render_content_simple() {
let tera_ctx = Tera::default();
let permalinks_ctx = HashMap::new();
- let context = Context::new(&tera_ctx, true, "base16-ocean-dark".to_string(), "", &permalinks_ctx, InsertAnchor::None);
- let res = markdown_to_html("hello", &context).unwrap();
+ let config = Config::default();
+ let context = RenderContext::new(&tera_ctx, &config, "", &permalinks_ctx, InsertAnchor::None);
+ let res = render_content("hello", &context).unwrap();
assert_eq!(res.0, "hello
\n");
}
@@ -25,9 +28,10 @@ fn can_do_markdown_to_html_simple() {
fn doesnt_highlight_code_block_with_highlighting_off() {
let tera_ctx = Tera::default();
let permalinks_ctx = HashMap::new();
- let mut context = Context::new(&tera_ctx, true, "base16-ocean-dark".to_string(), "", &permalinks_ctx, InsertAnchor::None);
- context.highlight_code = false;
- let res = markdown_to_html("```\n$ gutenberg server\n```", &context).unwrap();
+ let mut config = Config::default();
+ config.highlight_code = false;
+ let context = RenderContext::new(&tera_ctx, &config, "", &permalinks_ctx, InsertAnchor::None);
+ let res = render_content("```\n$ gutenberg server\n```", &context).unwrap();
assert_eq!(
res.0,
"$ gutenberg server\n
\n"
@@ -38,8 +42,9 @@ fn doesnt_highlight_code_block_with_highlighting_off() {
fn can_highlight_code_block_no_lang() {
let tera_ctx = Tera::default();
let permalinks_ctx = HashMap::new();
- let context = Context::new(&tera_ctx, true, "base16-ocean-dark".to_string(), "", &permalinks_ctx, InsertAnchor::None);
- let res = markdown_to_html("```\n$ gutenberg server\n$ ping\n```", &context).unwrap();
+ let config = Config::default();
+ let context = RenderContext::new(&tera_ctx, &config, "", &permalinks_ctx, InsertAnchor::None);
+ let res = render_content("```\n$ gutenberg server\n$ ping\n```", &context).unwrap();
assert_eq!(
res.0,
"\n$ gutenberg server\n $ ping\n "
@@ -50,8 +55,9 @@ fn can_highlight_code_block_no_lang() {
fn can_highlight_code_block_with_lang() {
let tera_ctx = Tera::default();
let permalinks_ctx = HashMap::new();
- let context = Context::new(&tera_ctx, true, "base16-ocean-dark".to_string(), "", &permalinks_ctx, InsertAnchor::None);
- let res = markdown_to_html("```python\nlist.append(1)\n```", &context).unwrap();
+ let config = Config::default();
+ let context = RenderContext::new(&tera_ctx, &config, "", &permalinks_ctx, InsertAnchor::None);
+ let res = render_content("```python\nlist.append(1)\n```", &context).unwrap();
assert_eq!(
res.0,
"\nlist. append ( 1 )\n "
@@ -62,8 +68,9 @@ fn can_highlight_code_block_with_lang() {
fn can_higlight_code_block_with_unknown_lang() {
let tera_ctx = Tera::default();
let permalinks_ctx = HashMap::new();
- let context = Context::new(&tera_ctx, true, "base16-ocean-dark".to_string(), "", &permalinks_ctx, InsertAnchor::None);
- let res = markdown_to_html("```yolo\nlist.append(1)\n```", &context).unwrap();
+ let config = Config::default();
+ let context = RenderContext::new(&tera_ctx, &config, "", &permalinks_ctx, InsertAnchor::None);
+ let res = render_content("```yolo\nlist.append(1)\n```", &context).unwrap();
// defaults to plain text
assert_eq!(
res.0,
@@ -74,8 +81,9 @@ fn can_higlight_code_block_with_unknown_lang() {
#[test]
fn can_render_shortcode() {
let permalinks_ctx = HashMap::new();
- let context = Context::new(&GUTENBERG_TERA, true, "base16-ocean-dark".to_string(), "", &permalinks_ctx, InsertAnchor::None);
- let res = markdown_to_html(r#"
+ let config = Config::default();
+ let context = RenderContext::new(&GUTENBERG_TERA, &config, "", &permalinks_ctx, InsertAnchor::None);
+ let res = render_content(r#"
Hello
{{ youtube(id="ub36ffWAqgQ") }}
@@ -87,7 +95,8 @@ Hello
#[test]
fn can_render_shortcode_with_markdown_char_in_args_name() {
let permalinks_ctx = HashMap::new();
- let context = Context::new(&GUTENBERG_TERA, true, "base16-ocean-dark".to_string(), "", &permalinks_ctx, InsertAnchor::None);
+ let config = Config::default();
+ let context = RenderContext::new(&GUTENBERG_TERA, &config, "", &permalinks_ctx, InsertAnchor::None);
let input = vec![
"name",
"na_me",
@@ -95,7 +104,7 @@ fn can_render_shortcode_with_markdown_char_in_args_name() {
"n1",
];
for i in input {
- let res = markdown_to_html(&format!("{{{{ youtube(id=\"hey\", {}=1) }}}}", i), &context).unwrap();
+ let res = render_content(&format!("{{{{ youtube(id=\"hey\", {}=1) }}}}", i), &context).unwrap();
assert!(res.0.contains(r#"VIDEOhey - Bob "));
}
@@ -157,9 +168,10 @@ Here is another paragraph.
";
tera.add_raw_template(&format!("shortcodes/{}.html", "figure"), shortcode).unwrap();
- let context = Context::new(&tera, true, "base16-ocean-dark".to_string(), "", &permalinks_ctx, InsertAnchor::None);
+ let config = Config::default();
+ let context = RenderContext::new(&tera, &config, "", &permalinks_ctx, InsertAnchor::None);
- let res = markdown_to_html(markdown_string, &context).unwrap();
+ let res = render_content(markdown_string, &context).unwrap();
println!("{:?}", res);
assert_eq!(res.0, expected);
}
@@ -189,9 +201,10 @@ Here is another paragraph.
";
tera.add_raw_template(&format!("shortcodes/{}.html", "figure"), shortcode).unwrap();
- let context = Context::new(&tera, true, "base16-ocean-dark".to_string(), "", &permalinks_ctx, InsertAnchor::None);
+ let config = Config::default();
+ let context = RenderContext::new(&tera, &config, "", &permalinks_ctx, InsertAnchor::None);
- let res = markdown_to_html(markdown_string, &context).unwrap();
+ let res = render_content(markdown_string, &context).unwrap();
println!("{:?}", res);
assert_eq!(res.0, expected);
}
@@ -199,8 +212,9 @@ Here is another paragraph.
#[test]
fn can_render_several_shortcode_in_row() {
let permalinks_ctx = HashMap::new();
- let context = Context::new(&GUTENBERG_TERA, true, "base16-ocean-dark".to_string(), "", &permalinks_ctx, InsertAnchor::None);
- let res = markdown_to_html(r#"
+ let config = Config::default();
+ let context = RenderContext::new(&GUTENBERG_TERA, &config, "", &permalinks_ctx, InsertAnchor::None);
+ let res = render_content(r#"
Hello
{{ youtube(id="ub36ffWAqgQ") }}
@@ -222,18 +236,12 @@ Hello
}
#[test]
-fn errors_if_unterminated_shortcode() {
+fn doesnt_render_ignored_shortcodes() {
let permalinks_ctx = HashMap::new();
- let context = Context::new(&GUTENBERG_TERA, true, "base16-ocean-dark".to_string(), "", &permalinks_ctx, InsertAnchor::None);
- let res = markdown_to_html(r#"{{ youtube(id="w7Ft2ym_a"#, &context);
- assert!(res.is_err());
-}
-
-#[test]
-fn doesnt_render_shortcode_in_code_block() {
- let permalinks_ctx = HashMap::new();
- let context = Context::new(&GUTENBERG_TERA, true, "base16-ocean-dark".to_string(), "", &permalinks_ctx, InsertAnchor::None);
- let res = markdown_to_html(r#"```{{ youtube(id="w7Ft2ymGmfc") }}```"#, &context).unwrap();
+ let mut config = Config::default();
+ config.highlight_code = false;
+ let context = RenderContext::new(&GUTENBERG_TERA, &config, "", &permalinks_ctx, InsertAnchor::None);
+ let res = render_content(r#"```{{/* youtube(id="w7Ft2ymGmfc") */}}```"#, &context).unwrap();
assert_eq!(res.0, "{{ youtube(id="w7Ft2ymGmfc") }}
\n");
}
@@ -243,23 +251,25 @@ fn can_render_shortcode_with_body() {
tera.extend(&GUTENBERG_TERA).unwrap();
tera.add_raw_template("shortcodes/quote.html", "{{ body }} - {{ author }} ").unwrap();
let permalinks_ctx = HashMap::new();
- let context = Context::new(&tera, true, "base16-ocean-dark".to_string(), "", &permalinks_ctx, InsertAnchor::None);
+ let config = Config::default();
+ let context = RenderContext::new(&tera, &config, "", &permalinks_ctx, InsertAnchor::None);
- let res = markdown_to_html(r#"
+ let res = render_content(r#"
Hello
{% quote(author="Keats") %}
A quote
{% end %}
"#, &context).unwrap();
- assert_eq!(res.0, "Hello\n
A quote - Keats ");
+ assert_eq!(res.0, "Hello
\nA quote - Keats \n");
}
#[test]
fn errors_rendering_unknown_shortcode() {
let tera_ctx = Tera::default();
let permalinks_ctx = HashMap::new();
- let context = Context::new(&tera_ctx, true, "base16-ocean-dark".to_string(), "", &permalinks_ctx, InsertAnchor::None);
- let res = markdown_to_html("{{ hello(flash=true) }}", &context);
+ let config = Config::default();
+ let context = RenderContext::new(&tera_ctx, &config, "", &permalinks_ctx, InsertAnchor::None);
+ let res = render_content("{{ hello(flash=true) }}", &context);
assert!(res.is_err());
}
@@ -268,8 +278,9 @@ fn can_make_valid_relative_link() {
let mut permalinks = HashMap::new();
permalinks.insert("pages/about.md".to_string(), "https://vincent.is/about".to_string());
let tera_ctx = Tera::default();
- let context = Context::new(&tera_ctx, true, "base16-ocean-dark".to_string(), "", &permalinks, InsertAnchor::None);
- let res = markdown_to_html(
+ let config = Config::default();
+ let context = RenderContext::new(&tera_ctx, &config, "", &permalinks, InsertAnchor::None);
+ let res = render_content(
r#"[rel link](./pages/about.md), [abs link](https://vincent.is/about)"#,
&context
).unwrap();
@@ -284,8 +295,9 @@ fn can_make_relative_links_with_anchors() {
let mut permalinks = HashMap::new();
permalinks.insert("pages/about.md".to_string(), "https://vincent.is/about".to_string());
let tera_ctx = Tera::default();
- let context = Context::new(&tera_ctx, true, "base16-ocean-dark".to_string(), "", &permalinks, InsertAnchor::None);
- let res = markdown_to_html(r#"[rel link](./pages/about.md#cv)"#, &context).unwrap();
+ let config = Config::default();
+ let context = RenderContext::new(&tera_ctx, &config, "", &permalinks, InsertAnchor::None);
+ let res = render_content(r#"[rel link](./pages/about.md#cv)"#, &context).unwrap();
assert!(
res.0.contains(r#"rel link
"#)
@@ -296,8 +308,9 @@ fn can_make_relative_links_with_anchors() {
fn errors_relative_link_inexistant() {
let tera_ctx = Tera::default();
let permalinks_ctx = HashMap::new();
- let context = Context::new(&tera_ctx, true, "base16-ocean-dark".to_string(), "", &permalinks_ctx, InsertAnchor::None);
- let res = markdown_to_html("[rel link](./pages/about.md)", &context);
+ let config = Config::default();
+ let context = RenderContext::new(&tera_ctx, &config, "", &permalinks_ctx, InsertAnchor::None);
+ let res = render_content("[rel link](./pages/about.md)", &context);
assert!(res.is_err());
}
@@ -305,8 +318,9 @@ fn errors_relative_link_inexistant() {
fn can_add_id_to_headers() {
let tera_ctx = Tera::default();
let permalinks_ctx = HashMap::new();
- let context = Context::new(&tera_ctx, true, "base16-ocean-dark".to_string(), "", &permalinks_ctx, InsertAnchor::None);
- let res = markdown_to_html(r#"# Hello"#, &context).unwrap();
+ let config = Config::default();
+ let context = RenderContext::new(&tera_ctx, &config, "", &permalinks_ctx, InsertAnchor::None);
+ let res = render_content(r#"# Hello"#, &context).unwrap();
assert_eq!(res.0, "Hello \n");
}
@@ -314,16 +328,18 @@ fn can_add_id_to_headers() {
fn can_add_id_to_headers_same_slug() {
let tera_ctx = Tera::default();
let permalinks_ctx = HashMap::new();
- let context = Context::new(&tera_ctx, true, "base16-ocean-dark".to_string(), "", &permalinks_ctx, InsertAnchor::None);
- let res = markdown_to_html("# Hello\n# Hello", &context).unwrap();
+ let config = Config::default();
+ let context = RenderContext::new(&tera_ctx, &config, "", &permalinks_ctx, InsertAnchor::None);
+ let res = render_content("# Hello\n# Hello", &context).unwrap();
assert_eq!(res.0, "Hello \nHello \n");
}
#[test]
fn can_insert_anchor_left() {
let permalinks_ctx = HashMap::new();
- let context = Context::new(&GUTENBERG_TERA, true, "base16-ocean-dark".to_string(), "", &permalinks_ctx, InsertAnchor::Left);
- let res = markdown_to_html("# Hello", &context).unwrap();
+ let config = Config::default();
+ let context = RenderContext::new(&GUTENBERG_TERA, &config, "", &permalinks_ctx, InsertAnchor::Left);
+ let res = render_content("# Hello", &context).unwrap();
assert_eq!(
res.0,
"\n"
@@ -333,8 +349,9 @@ fn can_insert_anchor_left() {
#[test]
fn can_insert_anchor_right() {
let permalinks_ctx = HashMap::new();
- let context = Context::new(&GUTENBERG_TERA, true, "base16-ocean-dark".to_string(), "", &permalinks_ctx, InsertAnchor::Right);
- let res = markdown_to_html("# Hello", &context).unwrap();
+ let config = Config::default();
+ let context = RenderContext::new(&GUTENBERG_TERA, &config, "", &permalinks_ctx, InsertAnchor::Right);
+ let res = render_content("# Hello", &context).unwrap();
assert_eq!(
res.0,
"\n"
@@ -345,8 +362,9 @@ fn can_insert_anchor_right() {
#[test]
fn can_insert_anchor_with_exclamation_mark() {
let permalinks_ctx = HashMap::new();
- let context = Context::new(&GUTENBERG_TERA, true, "base16-ocean-dark".to_string(), "", &permalinks_ctx, InsertAnchor::Left);
- let res = markdown_to_html("# Hello!", &context).unwrap();
+ let config = Config::default();
+ let context = RenderContext::new(&GUTENBERG_TERA, &config, "", &permalinks_ctx, InsertAnchor::Left);
+ let res = render_content("# Hello!", &context).unwrap();
assert_eq!(
res.0,
"\n"
@@ -357,8 +375,9 @@ fn can_insert_anchor_with_exclamation_mark() {
#[test]
fn can_insert_anchor_with_link() {
let permalinks_ctx = HashMap::new();
- let context = Context::new(&GUTENBERG_TERA, true, "base16-ocean-dark".to_string(), "", &permalinks_ctx, InsertAnchor::Left);
- let res = markdown_to_html("## [](#xresources)Xresources", &context).unwrap();
+ let config = Config::default();
+ let context = RenderContext::new(&GUTENBERG_TERA, &config, "", &permalinks_ctx, InsertAnchor::Left);
+ let res = render_content("## [](#xresources)Xresources", &context).unwrap();
assert_eq!(
res.0,
"🔗 \nXresources \n"
@@ -368,8 +387,9 @@ fn can_insert_anchor_with_link() {
#[test]
fn can_insert_anchor_with_other_special_chars() {
let permalinks_ctx = HashMap::new();
- let context = Context::new(&GUTENBERG_TERA, true, "base16-ocean-dark".to_string(), "", &permalinks_ctx, InsertAnchor::Left);
- let res = markdown_to_html("# Hello*_()", &context).unwrap();
+ let config = Config::default();
+ let context = RenderContext::new(&GUTENBERG_TERA, &config, "", &permalinks_ctx, InsertAnchor::Left);
+ let res = render_content("# Hello*_()", &context).unwrap();
assert_eq!(
res.0,
"🔗 \nHello*_() \n"
@@ -379,16 +399,16 @@ fn can_insert_anchor_with_other_special_chars() {
#[test]
fn can_make_toc() {
let permalinks_ctx = HashMap::new();
- let context = Context::new(
+ let config = Config::default();
+ let context = RenderContext::new(
&GUTENBERG_TERA,
- true,
- "base16-ocean-dark".to_string(),
+ &config,
"https://mysite.com/something",
&permalinks_ctx,
InsertAnchor::Left
);
- let res = markdown_to_html(r#"
+ let res = render_content(r#"
# Header 1
## Header 2
@@ -408,8 +428,9 @@ fn can_make_toc() {
#[test]
fn can_understand_backtick_in_titles() {
let permalinks_ctx = HashMap::new();
- let context = Context::new(&GUTENBERG_TERA, true, "base16-ocean-dark".to_string(), "", &permalinks_ctx, InsertAnchor::None);
- let res = markdown_to_html("# `Hello`", &context).unwrap();
+ let config = Config::default();
+ let context = RenderContext::new(&GUTENBERG_TERA, &config, "", &permalinks_ctx, InsertAnchor::None);
+ let res = render_content("# `Hello`", &context).unwrap();
assert_eq!(
res.0,
"Hello
\n"
@@ -419,8 +440,9 @@ fn can_understand_backtick_in_titles() {
#[test]
fn can_understand_backtick_in_paragraphs() {
let permalinks_ctx = HashMap::new();
- let context = Context::new(&GUTENBERG_TERA, true, "base16-ocean-dark".to_string(), "", &permalinks_ctx, InsertAnchor::None);
- let res = markdown_to_html("Hello `world`", &context).unwrap();
+ let config = Config::default();
+ let context = RenderContext::new(&GUTENBERG_TERA, &config, "", &permalinks_ctx, InsertAnchor::None);
+ let res = render_content("Hello `world`", &context).unwrap();
assert_eq!(
res.0,
"Hello world
\n"
diff --git a/sublime_syntaxes/newlines.packdump b/sublime_syntaxes/newlines.packdump
index 22456f8b..a48fc3b0 100644
Binary files a/sublime_syntaxes/newlines.packdump and b/sublime_syntaxes/newlines.packdump differ
diff --git a/sublime_syntaxes/nonewlines.packdump b/sublime_syntaxes/nonewlines.packdump
index 333a81d7..56624135 100644
Binary files a/sublime_syntaxes/nonewlines.packdump and b/sublime_syntaxes/nonewlines.packdump differ