Move insert_anchor to section and allow left/right
This commit is contained in:
parent
2a11b9d116
commit
dbe4a1d517
|
@ -6,6 +6,7 @@
|
|||
- Change the single item template context for categories/tags
|
||||
- Add a `get_url` global Tera function
|
||||
- Add a config option to control how many articles to show in RSS feed
|
||||
- Move `insert_anchor_links` from config to being a section option
|
||||
|
||||
## 0.0.5 (2017-05-15)
|
||||
|
||||
|
|
8
Cargo.lock
generated
8
Cargo.lock
generated
|
@ -56,7 +56,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
dependencies = [
|
||||
"backtrace-sys 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"cfg-if 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"cpp_demangle 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"cpp_demangle 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"dbghelp-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.23 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
@ -187,10 +187,12 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "cpp_demangle"
|
||||
version = "0.2.1"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"clap 2.24.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"fixedbitset 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1100,7 +1102,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
"checksum clap 2.24.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6b8f69e518f967224e628896b54e41ff6acfb4dcfefc5076325c36525dac900f"
|
||||
"checksum cmake 0.1.23 (registry+https://github.com/rust-lang/crates.io-index)" = "92278eb79412c8f75cfc89e707a1bb3a6490b68f7f2e78d15c774f30fe701122"
|
||||
"checksum conduit-mime-types 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "95ca30253581af809925ef68c2641cc140d6183f43e12e0af4992d53768bd7b8"
|
||||
"checksum cpp_demangle 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ae1040e8145a72a251c1ccdd1d3d4b4ad175acc363da8a9c21a21cbb5b1f9056"
|
||||
"checksum cpp_demangle 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2692e985e8b489736612f206c8b2d071767c05ac9343a654dd5ebd791a9035d5"
|
||||
"checksum dbghelp-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "97590ba53bcb8ac28279161ca943a924d1fd4a8fb3fa63302591647c4fc5b850"
|
||||
"checksum dtoa 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "80c8b71fd71146990a9742fc06dcbbde19161a267e0ad4e572c35162f4578c90"
|
||||
"checksum error 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "a6e606f14042bb87cc02ef6a14db6c90ab92ed6f62d87e69377bc759fd7987cc"
|
||||
|
|
|
@ -7,7 +7,7 @@ use gutenberg::errors::Result;
|
|||
/// Finds the section that contains the page given if there is one
|
||||
pub fn find_parent_section<'a>(site: &'a Site, page: &Page) -> Option<&'a Section> {
|
||||
for section in site.sections.values() {
|
||||
if section.is_child_page(page) {
|
||||
if section.is_child_page(&page.file.path) {
|
||||
return Some(section)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,14 +4,15 @@ use std::path::{Path, PathBuf};
|
|||
use std::result::Result as StdResult;
|
||||
|
||||
|
||||
use tera::{Tera, Context};
|
||||
use tera::{Tera, Context as TeraContext};
|
||||
use serde::ser::{SerializeStruct, self};
|
||||
use slug::slugify;
|
||||
|
||||
use errors::{Result, ResultExt};
|
||||
use config::Config;
|
||||
use front_matter::{PageFrontMatter, split_page_content};
|
||||
use front_matter::{PageFrontMatter, InsertAnchor, split_page_content};
|
||||
use rendering::markdown::markdown_to_html;
|
||||
use rendering::context::Context;
|
||||
use fs::{read_file};
|
||||
use content::utils::{find_related_assets, get_reading_analytics};
|
||||
use content::file_info::FileInfo;
|
||||
|
@ -112,13 +113,13 @@ 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<String, String>, tera: &Tera, config: &Config) -> Result<()> {
|
||||
self.content = markdown_to_html(&self.raw_content, permalinks, tera, config)?;
|
||||
|
||||
pub fn render_markdown(&mut self, permalinks: &HashMap<String, String>, tera: &Tera, config: &Config, anchor_insert: InsertAnchor) -> Result<()> {
|
||||
let context = Context::new(tera, config, permalinks, anchor_insert);
|
||||
self.content = markdown_to_html(&self.raw_content, &context)?;
|
||||
if self.raw_content.contains("<!-- more -->") {
|
||||
self.summary = Some({
|
||||
let summary = self.raw_content.splitn(2, "<!-- more -->").collect::<Vec<&str>>()[0];
|
||||
markdown_to_html(summary, permalinks, tera, config)?
|
||||
markdown_to_html(summary, &context)?
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -132,7 +133,7 @@ impl Page {
|
|||
None => "page.html".to_string()
|
||||
};
|
||||
|
||||
let mut context = Context::new();
|
||||
let mut context = TeraContext::new();
|
||||
context.add("config", config);
|
||||
context.add("page", self);
|
||||
context.add("current_url", &self.permalink);
|
||||
|
@ -195,6 +196,7 @@ mod tests {
|
|||
|
||||
use config::Config;
|
||||
use super::Page;
|
||||
use front_matter::InsertAnchor;
|
||||
|
||||
|
||||
#[test]
|
||||
|
@ -209,7 +211,7 @@ Hello world"#;
|
|||
let res = Page::parse(Path::new("post.md"), content, &Config::default());
|
||||
assert!(res.is_ok());
|
||||
let mut page = res.unwrap();
|
||||
page.render_markdown(&HashMap::default(), &Tera::default(), &Config::default()).unwrap();
|
||||
page.render_markdown(&HashMap::default(), &Tera::default(), &Config::default(), InsertAnchor::None).unwrap();
|
||||
|
||||
assert_eq!(page.meta.title.unwrap(), "Hello".to_string());
|
||||
assert_eq!(page.meta.slug.unwrap(), "hello-world".to_string());
|
||||
|
@ -228,8 +230,7 @@ Hello world"#;
|
|||
conf.base_url = "http://hello.com/".to_string();
|
||||
let res = Page::parse(Path::new("content/posts/intro/start.md"), content, &conf);
|
||||
assert!(res.is_ok());
|
||||
let mut page = res.unwrap();
|
||||
page.render_markdown(&HashMap::default(), &Tera::default(), &Config::default()).unwrap();
|
||||
let page = res.unwrap();
|
||||
assert_eq!(page.path, "posts/intro/hello-world");
|
||||
assert_eq!(page.permalink, "http://hello.com/posts/intro/hello-world");
|
||||
}
|
||||
|
@ -244,8 +245,7 @@ Hello world"#;
|
|||
let config = Config::default();
|
||||
let res = Page::parse(Path::new("start.md"), content, &config);
|
||||
assert!(res.is_ok());
|
||||
let mut page = res.unwrap();
|
||||
page.render_markdown(&HashMap::default(), &Tera::default(), &config).unwrap();
|
||||
let page = res.unwrap();
|
||||
assert_eq!(page.path, "hello-world");
|
||||
assert_eq!(page.permalink, config.make_permalink("hello-world"));
|
||||
}
|
||||
|
@ -268,8 +268,7 @@ Hello world"#;
|
|||
let config = Config::default();
|
||||
let res = Page::parse(Path::new(" file with space.md"), "+++\n+++", &config);
|
||||
assert!(res.is_ok());
|
||||
let mut page = res.unwrap();
|
||||
page.render_markdown(&HashMap::default(), &Tera::default(), &config).unwrap();
|
||||
let page = res.unwrap();
|
||||
assert_eq!(page.slug, "file-with-space");
|
||||
assert_eq!(page.permalink, config.make_permalink(&page.slug));
|
||||
}
|
||||
|
@ -285,7 +284,7 @@ Hello world
|
|||
let res = Page::parse(Path::new("hello.md"), &content, &config);
|
||||
assert!(res.is_ok());
|
||||
let mut page = res.unwrap();
|
||||
page.render_markdown(&HashMap::default(), &Tera::default(), &config).unwrap();
|
||||
page.render_markdown(&HashMap::default(), &Tera::default(), &config, InsertAnchor::None).unwrap();
|
||||
assert_eq!(page.summary, Some("<p>Hello world</p>\n".to_string()));
|
||||
}
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@ use std::collections::HashMap;
|
|||
use std::path::{Path, PathBuf};
|
||||
use std::result::Result as StdResult;
|
||||
|
||||
use tera::{Tera, Context};
|
||||
use tera::{Tera, Context as TeraContext};
|
||||
use serde::ser::{SerializeStruct, self};
|
||||
|
||||
use config::Config;
|
||||
|
@ -10,6 +10,7 @@ use front_matter::{SectionFrontMatter, split_section_content};
|
|||
use errors::{Result, ResultExt};
|
||||
use fs::{read_file};
|
||||
use rendering::markdown::markdown_to_html;
|
||||
use rendering::context::Context;
|
||||
use content::Page;
|
||||
use content::file_info::FileInfo;
|
||||
|
||||
|
@ -85,7 +86,8 @@ 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<String, String>, tera: &Tera, config: &Config) -> Result<()> {
|
||||
self.content = markdown_to_html(&self.raw_content, permalinks, tera, config)?;
|
||||
let context = Context::new(tera, config, permalinks, self.meta.insert_anchor.unwrap());
|
||||
self.content = markdown_to_html(&self.raw_content, &context)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -93,7 +95,7 @@ impl Section {
|
|||
pub fn render_html(&self, sections: HashMap<String, Section>, tera: &Tera, config: &Config) -> Result<String> {
|
||||
let tpl_name = self.get_template_name();
|
||||
|
||||
let mut context = Context::new();
|
||||
let mut context = TeraContext::new();
|
||||
context.add("config", config);
|
||||
context.add("section", self);
|
||||
context.add("current_url", &self.permalink);
|
||||
|
@ -120,8 +122,8 @@ impl Section {
|
|||
}
|
||||
|
||||
/// Whether the page given belongs to that section
|
||||
pub fn is_child_page(&self, page: &Page) -> bool {
|
||||
self.all_pages_path().contains(&page.file.path)
|
||||
pub fn is_child_page(&self, path: &PathBuf) -> bool {
|
||||
self.all_pages_path().contains(path)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ mod page;
|
|||
mod section;
|
||||
|
||||
pub use self::page::PageFrontMatter;
|
||||
pub use self::section::{SectionFrontMatter};
|
||||
pub use self::section::{SectionFrontMatter, InsertAnchor};
|
||||
|
||||
lazy_static! {
|
||||
static ref PAGE_RE: Regex = Regex::new(r"^[[:space:]]*\+\+\+\r?\n((?s).*?(?-s))\+\+\+\r?\n?((?s).*(?-s))$").unwrap();
|
||||
|
|
|
@ -9,6 +9,14 @@ use content::SortBy;
|
|||
static DEFAULT_PAGINATE_PATH: &'static str = "page";
|
||||
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum InsertAnchor {
|
||||
Left,
|
||||
Right,
|
||||
None,
|
||||
}
|
||||
|
||||
/// The front matter of every section
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
pub struct SectionFrontMatter {
|
||||
|
@ -28,6 +36,10 @@ pub struct SectionFrontMatter {
|
|||
/// Path to be used by pagination: the page number will be appended after it. Defaults to `page`.
|
||||
#[serde(skip_serializing)]
|
||||
pub paginate_path: Option<String>,
|
||||
/// Whether to insert a link for each header like in Github READMEs. Defaults to false
|
||||
/// The default template can be overridden by creating a `anchor-link.html` template and CSS will need to be
|
||||
/// written if you turn that on.
|
||||
pub insert_anchor: Option<InsertAnchor>,
|
||||
/// Whether to render that section or not. Defaults to `true`.
|
||||
/// Useful when the section is only there to organize things but is not meant
|
||||
/// to be used directly, like a posts section in a personal site
|
||||
|
@ -56,6 +68,10 @@ impl SectionFrontMatter {
|
|||
f.sort_by = Some(SortBy::None);
|
||||
}
|
||||
|
||||
if f.insert_anchor.is_none() {
|
||||
f.insert_anchor = Some(InsertAnchor::None);
|
||||
}
|
||||
|
||||
Ok(f)
|
||||
}
|
||||
|
||||
|
@ -87,6 +103,7 @@ impl Default for SectionFrontMatter {
|
|||
paginate_by: None,
|
||||
paginate_path: Some(DEFAULT_PAGINATE_PATH.to_string()),
|
||||
render: Some(true),
|
||||
insert_anchor: Some(InsertAnchor::None),
|
||||
extra: None,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,6 +31,6 @@ mod templates;
|
|||
|
||||
pub use site::{Site};
|
||||
pub use config::{Config, get_config};
|
||||
pub use front_matter::{PageFrontMatter, SectionFrontMatter, split_page_content, split_section_content};
|
||||
pub use front_matter::{PageFrontMatter, SectionFrontMatter, InsertAnchor, split_page_content, split_section_content};
|
||||
pub use content::{Page, Section, SortBy, sort_pages, populate_previous_and_next_pages};
|
||||
pub use fs::{create_file};
|
||||
|
|
33
src/rendering/context.rs
Normal file
33
src/rendering/context.rs
Normal file
|
@ -0,0 +1,33 @@
|
|||
use std::collections::HashMap;
|
||||
|
||||
use tera::Tera;
|
||||
|
||||
use config::Config;
|
||||
use front_matter::InsertAnchor;
|
||||
|
||||
|
||||
/// All the information from the gutenberg site that is needed to render HTML from markdown
|
||||
#[derive(Debug)]
|
||||
pub struct Context<'a> {
|
||||
pub tera: &'a Tera,
|
||||
pub highlight_code: bool,
|
||||
pub highlight_theme: String,
|
||||
pub permalinks: &'a HashMap<String, String>,
|
||||
pub insert_anchor: InsertAnchor,
|
||||
}
|
||||
|
||||
impl<'a> Context<'a> {
|
||||
pub fn new(tera: &'a Tera, config: &'a Config, permalinks: &'a HashMap<String, String>, insert_anchor: InsertAnchor) -> Context<'a> {
|
||||
Context {
|
||||
tera,
|
||||
permalinks,
|
||||
insert_anchor,
|
||||
highlight_code: config.highlight_code.unwrap(),
|
||||
highlight_theme: config.highlight_theme.clone().unwrap(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn should_insert_anchor(&self) -> bool {
|
||||
self.insert_anchor != InsertAnchor::None
|
||||
}
|
||||
}
|
|
@ -1,5 +1,4 @@
|
|||
use std::borrow::Cow::Owned;
|
||||
use std::collections::HashMap;
|
||||
|
||||
use pulldown_cmark as cmark;
|
||||
use self::cmark::{Parser, Event, Tag, Options, OPTION_ENABLE_TABLES, OPTION_ENABLE_FOOTNOTES};
|
||||
|
@ -9,11 +8,12 @@ use syntect::dumps::from_binary;
|
|||
use syntect::easy::HighlightLines;
|
||||
use syntect::parsing::SyntaxSet;
|
||||
use syntect::html::{start_coloured_html_snippet, styles_to_coloured_html, IncludeBackground};
|
||||
use tera::{Tera, Context};
|
||||
use tera::{Context as TeraContext};
|
||||
|
||||
use config::Config;
|
||||
use errors::{Result};
|
||||
use site::resolve_internal_link;
|
||||
use front_matter::InsertAnchor;
|
||||
use rendering::context::Context;
|
||||
use rendering::highlighting::THEME_SET;
|
||||
use rendering::short_code::{ShortCode, parse_shortcode, render_simple_shortcode};
|
||||
|
||||
|
@ -36,18 +36,18 @@ lazy_static!{
|
|||
};
|
||||
}
|
||||
|
||||
pub fn markdown_to_html(content: &str, permalinks: &HashMap<String, String>, tera: &Tera, config: &Config) -> Result<String> {
|
||||
|
||||
pub fn markdown_to_html(content: &str, context: &Context) -> Result<String> {
|
||||
// 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 config.highlight_code.unwrap() {
|
||||
let should_highlight = if context.highlight_code {
|
||||
content.contains("```")
|
||||
} else {
|
||||
false
|
||||
};
|
||||
let highlight_theme = config.highlight_theme.clone().unwrap();
|
||||
// Set while parsing
|
||||
let mut error = None;
|
||||
let mut highlighter: Option<HighlightLines> = None;
|
||||
|
@ -105,7 +105,7 @@ pub fn markdown_to_html(content: &str, permalinks: &HashMap<String, String>, ter
|
|||
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(tera, &name, &args) {
|
||||
match render_simple_shortcode(context.tera, &name, &args) {
|
||||
Ok(s) => return Event::Html(Owned(format!("</p>{}", s))),
|
||||
Err(e) => {
|
||||
error = Some(e);
|
||||
|
@ -131,7 +131,7 @@ pub fn markdown_to_html(content: &str, permalinks: &HashMap<String, String>, ter
|
|||
if let Some(ref mut shortcode) = shortcode_block {
|
||||
if text.trim() == "{% end %}" {
|
||||
added_shortcode = true;
|
||||
match shortcode.render(tera) {
|
||||
match shortcode.render(context.tera) {
|
||||
Ok(s) => return Event::Html(Owned(format!("</p>{}", s))),
|
||||
Err(e) => {
|
||||
error = Some(e);
|
||||
|
@ -151,15 +151,20 @@ pub fn markdown_to_html(content: &str, permalinks: &HashMap<String, String>, ter
|
|||
}
|
||||
let id = find_anchor(&anchors, slugify(&text), 0);
|
||||
anchors.push(id.clone());
|
||||
let anchor_link = if config.insert_anchor_links.unwrap() {
|
||||
let mut context = Context::new();
|
||||
context.add("id", &id);
|
||||
tera.render("anchor-link.html", &context).unwrap()
|
||||
let anchor_link = if context.should_insert_anchor() {
|
||||
let mut c = TeraContext::new();
|
||||
c.add("id", &id);
|
||||
context.tera.render("anchor-link.html", &c).unwrap()
|
||||
} else {
|
||||
String::new()
|
||||
};
|
||||
header_already_inserted = true;
|
||||
return Event::Html(Owned(format!(r#"id="{}">{}{}"#, id, anchor_link, text)));
|
||||
let event = match context.insert_anchor {
|
||||
InsertAnchor::Left => Event::Html(Owned(format!(r#"id="{}">{}{}"#, id, anchor_link, text))),
|
||||
InsertAnchor::Right => Event::Html(Owned(format!(r#"id="{}">{}{}"#, id, text, anchor_link))),
|
||||
InsertAnchor::None => Event::Html(Owned(format!(r#"id="{}">{}"#, id, text)))
|
||||
};
|
||||
return event;
|
||||
}
|
||||
|
||||
// Business as usual
|
||||
|
@ -170,7 +175,7 @@ pub fn markdown_to_html(content: &str, permalinks: &HashMap<String, String>, ter
|
|||
if !should_highlight {
|
||||
return Event::Html(Owned("<pre><code>".to_owned()));
|
||||
}
|
||||
let theme = &THEME_SET.themes[&highlight_theme];
|
||||
let theme = &THEME_SET.themes[&context.highlight_theme];
|
||||
let syntax = info
|
||||
.split(' ')
|
||||
.next()
|
||||
|
@ -195,7 +200,7 @@ pub fn markdown_to_html(content: &str, permalinks: &HashMap<String, String>, ter
|
|||
return Event::Html(Owned("".to_owned()));
|
||||
}
|
||||
if link.starts_with("./") {
|
||||
match resolve_internal_link(link, permalinks) {
|
||||
match resolve_internal_link(link, context.permalinks) {
|
||||
Ok(url) => {
|
||||
return Event::Start(Tag::Link(Owned(url), title.clone()));
|
||||
},
|
||||
|
@ -268,46 +273,33 @@ pub fn markdown_to_html(content: &str, permalinks: &HashMap<String, String>, ter
|
|||
mod tests {
|
||||
use std::collections::HashMap;
|
||||
|
||||
use templates::GUTENBERG_TERA;
|
||||
use tera::Tera;
|
||||
|
||||
use config::Config;
|
||||
use super::{markdown_to_html, parse_shortcode};
|
||||
use front_matter::InsertAnchor;
|
||||
use templates::GUTENBERG_TERA;
|
||||
use rendering::context::Context;
|
||||
|
||||
#[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");
|
||||
}
|
||||
use super::markdown_to_html;
|
||||
|
||||
#[test]
|
||||
fn can_do_markdown_to_html_simple() {
|
||||
let res = markdown_to_html("hello", &HashMap::new(), &Tera::default(), &Config::default()).unwrap();
|
||||
let tera_ctx = Tera::default();
|
||||
let permalinks_ctx = HashMap::new();
|
||||
let config_ctx = Config::default();
|
||||
let context = Context::new(&tera_ctx, &config_ctx, &permalinks_ctx, InsertAnchor::None);
|
||||
let res = markdown_to_html("hello", &context).unwrap();
|
||||
assert_eq!(res, "<p>hello</p>\n");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn doesnt_highlight_code_block_with_highlighting_off() {
|
||||
let mut config = Config::default();
|
||||
config.highlight_code = Some(false);
|
||||
let res = markdown_to_html("```\n$ gutenberg server\n```", &HashMap::new(), &Tera::default(), &config).unwrap();
|
||||
let tera_ctx = Tera::default();
|
||||
let permalinks_ctx = HashMap::new();
|
||||
let config_ctx = Config::default();
|
||||
let mut context = Context::new(&tera_ctx, &config_ctx, &permalinks_ctx, InsertAnchor::None);
|
||||
context.highlight_code = false;
|
||||
let res = markdown_to_html("```\n$ gutenberg server\n```", &context).unwrap();
|
||||
assert_eq!(
|
||||
res,
|
||||
"<pre><code>$ gutenberg server\n</code></pre>\n"
|
||||
|
@ -316,7 +308,11 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn can_highlight_code_block_no_lang() {
|
||||
let res = markdown_to_html("```\n$ gutenberg server\n$ ping\n```", &HashMap::new(), &Tera::default(), &Config::default()).unwrap();
|
||||
let tera_ctx = Tera::default();
|
||||
let permalinks_ctx = HashMap::new();
|
||||
let config_ctx = Config::default();
|
||||
let context = Context::new(&tera_ctx, &config_ctx, &permalinks_ctx, InsertAnchor::None);
|
||||
let res = markdown_to_html("```\n$ gutenberg server\n$ ping\n```", &context).unwrap();
|
||||
assert_eq!(
|
||||
res,
|
||||
"<pre style=\"background-color:#2b303b\">\n<span style=\"background-color:#2b303b;color:#c0c5ce;\">$ gutenberg server\n</span><span style=\"background-color:#2b303b;color:#c0c5ce;\">$ ping\n</span></pre>"
|
||||
|
@ -325,7 +321,11 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn can_highlight_code_block_with_lang() {
|
||||
let res = markdown_to_html("```python\nlist.append(1)\n```", &HashMap::new(), &Tera::default(), &Config::default()).unwrap();
|
||||
let tera_ctx = Tera::default();
|
||||
let permalinks_ctx = HashMap::new();
|
||||
let config_ctx = Config::default();
|
||||
let context = Context::new(&tera_ctx, &config_ctx, &permalinks_ctx, InsertAnchor::None);
|
||||
let res = markdown_to_html("```python\nlist.append(1)\n```", &context).unwrap();
|
||||
assert_eq!(
|
||||
res,
|
||||
"<pre style=\"background-color:#2b303b\">\n<span style=\"background-color:#2b303b;color:#c0c5ce;\">list</span><span style=\"background-color:#2b303b;color:#c0c5ce;\">.</span><span style=\"background-color:#2b303b;color:#bf616a;\">append</span><span style=\"background-color:#2b303b;color:#c0c5ce;\">(</span><span style=\"background-color:#2b303b;color:#d08770;\">1</span><span style=\"background-color:#2b303b;color:#c0c5ce;\">)</span><span style=\"background-color:#2b303b;color:#c0c5ce;\">\n</span></pre>"
|
||||
|
@ -334,7 +334,11 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn can_higlight_code_block_with_unknown_lang() {
|
||||
let res = markdown_to_html("```yolo\nlist.append(1)\n```", &HashMap::new(), &Tera::default(), &Config::default()).unwrap();
|
||||
let tera_ctx = Tera::default();
|
||||
let permalinks_ctx = HashMap::new();
|
||||
let config_ctx = Config::default();
|
||||
let context = Context::new(&tera_ctx, &config_ctx, &permalinks_ctx, InsertAnchor::None);
|
||||
let res = markdown_to_html("```yolo\nlist.append(1)\n```", &context).unwrap();
|
||||
// defaults to plain text
|
||||
assert_eq!(
|
||||
res,
|
||||
|
@ -344,17 +348,23 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn can_render_shortcode() {
|
||||
let permalinks_ctx = HashMap::new();
|
||||
let config_ctx = Config::default();
|
||||
let context = Context::new(&GUTENBERG_TERA, &config_ctx, &permalinks_ctx, InsertAnchor::None);
|
||||
let res = markdown_to_html(r#"
|
||||
Hello
|
||||
|
||||
{{ youtube(id="ub36ffWAqgQ") }}
|
||||
"#, &HashMap::new(), &GUTENBERG_TERA, &Config::default()).unwrap();
|
||||
"#, &context).unwrap();
|
||||
assert!(res.contains("<p>Hello</p>\n<div >"));
|
||||
assert!(res.contains(r#"<iframe src="https://www.youtube.com/embed/ub36ffWAqgQ""#));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_render_several_shortcode_in_row() {
|
||||
let permalinks_ctx = HashMap::new();
|
||||
let config_ctx = Config::default();
|
||||
let context = Context::new(&GUTENBERG_TERA, &config_ctx, &permalinks_ctx, InsertAnchor::None);
|
||||
let res = markdown_to_html(r#"
|
||||
Hello
|
||||
|
||||
|
@ -366,7 +376,7 @@ Hello
|
|||
|
||||
{{ gist(url="https://gist.github.com/Keats/32d26f699dcc13ebd41b") }}
|
||||
|
||||
"#, &HashMap::new(), &GUTENBERG_TERA, &Config::default()).unwrap();
|
||||
"#, &context).unwrap();
|
||||
assert!(res.contains("<p>Hello</p>\n<div >"));
|
||||
assert!(res.contains(r#"<iframe src="https://www.youtube.com/embed/ub36ffWAqgQ""#));
|
||||
assert!(res.contains(r#"<iframe src="https://www.youtube.com/embed/ub36ffWAqgQ?autoplay=1""#));
|
||||
|
@ -375,7 +385,10 @@ Hello
|
|||
|
||||
#[test]
|
||||
fn doesnt_render_shortcode_in_code_block() {
|
||||
let res = markdown_to_html(r#"```{{ youtube(id="w7Ft2ymGmfc") }}```"#, &HashMap::new(), &GUTENBERG_TERA, &Config::default()).unwrap();
|
||||
let permalinks_ctx = HashMap::new();
|
||||
let config_ctx = Config::default();
|
||||
let context = Context::new(&GUTENBERG_TERA, &config_ctx, &permalinks_ctx, InsertAnchor::None);
|
||||
let res = markdown_to_html(r#"```{{ youtube(id="w7Ft2ymGmfc") }}```"#, &context).unwrap();
|
||||
assert_eq!(res, "<p><code>{{ youtube(id="w7Ft2ymGmfc") }}</code></p>\n");
|
||||
}
|
||||
|
||||
|
@ -384,18 +397,26 @@ Hello
|
|||
let mut tera = Tera::default();
|
||||
tera.extend(&GUTENBERG_TERA).unwrap();
|
||||
tera.add_raw_template("shortcodes/quote.html", "<blockquote>{{ body }} - {{ author}}</blockquote>").unwrap();
|
||||
let permalinks_ctx = HashMap::new();
|
||||
let config_ctx = Config::default();
|
||||
let context = Context::new(&tera, &config_ctx, &permalinks_ctx, InsertAnchor::None);
|
||||
|
||||
let res = markdown_to_html(r#"
|
||||
Hello
|
||||
{% quote(author="Keats") %}
|
||||
A quote
|
||||
{% end %}
|
||||
"#, &HashMap::new(), &tera, &Config::default()).unwrap();
|
||||
"#, &context).unwrap();
|
||||
assert_eq!(res, "<p>Hello\n</p><blockquote>A quote - Keats</blockquote>");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn errors_rendering_unknown_shortcode() {
|
||||
let res = markdown_to_html("{{ hello(flash=true) }}", &HashMap::new(), &Tera::default(), &Config::default());
|
||||
let tera_ctx = Tera::default();
|
||||
let permalinks_ctx = HashMap::new();
|
||||
let config_ctx = Config::default();
|
||||
let context = Context::new(&tera_ctx, &config_ctx, &permalinks_ctx, InsertAnchor::None);
|
||||
let res = markdown_to_html("{{ hello(flash=true) }}", &context);
|
||||
assert!(res.is_err());
|
||||
}
|
||||
|
||||
|
@ -403,11 +424,12 @@ A quote
|
|||
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 config_ctx = Config::default();
|
||||
let context = Context::new(&tera_ctx, &config_ctx, &permalinks, InsertAnchor::None);
|
||||
let res = markdown_to_html(
|
||||
r#"[rel link](./pages/about.md), [abs link](https://vincent.is/about)"#,
|
||||
&permalinks,
|
||||
&GUTENBERG_TERA,
|
||||
&Config::default()
|
||||
&context
|
||||
).unwrap();
|
||||
|
||||
assert!(
|
||||
|
@ -419,12 +441,10 @@ A quote
|
|||
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 res = markdown_to_html(
|
||||
r#"[rel link](./pages/about.md#cv)"#,
|
||||
&permalinks,
|
||||
&GUTENBERG_TERA,
|
||||
&Config::default()
|
||||
).unwrap();
|
||||
let tera_ctx = Tera::default();
|
||||
let config_ctx = Config::default();
|
||||
let context = Context::new(&tera_ctx, &config_ctx, &permalinks, InsertAnchor::None);
|
||||
let res = markdown_to_html(r#"[rel link](./pages/about.md#cv)"#, &context).unwrap();
|
||||
|
||||
assert!(
|
||||
res.contains(r#"<p><a href="https://vincent.is/about#cv">rel link</a></p>"#)
|
||||
|
@ -433,39 +453,65 @@ A quote
|
|||
|
||||
#[test]
|
||||
fn errors_relative_link_inexistant() {
|
||||
let res = markdown_to_html("[rel link](./pages/about.md)", &HashMap::new(), &Tera::default(), &Config::default());
|
||||
let tera_ctx = Tera::default();
|
||||
let permalinks_ctx = HashMap::new();
|
||||
let config_ctx = Config::default();
|
||||
let context = Context::new(&tera_ctx, &config_ctx, &permalinks_ctx, InsertAnchor::None);
|
||||
let res = markdown_to_html("[rel link](./pages/about.md)", &context);
|
||||
assert!(res.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_add_id_to_headers() {
|
||||
let res = markdown_to_html(r#"# Hello"#, &HashMap::new(), &GUTENBERG_TERA, &Config::default()).unwrap();
|
||||
let tera_ctx = Tera::default();
|
||||
let permalinks_ctx = HashMap::new();
|
||||
let config_ctx = Config::default();
|
||||
let context = Context::new(&tera_ctx, &config_ctx, &permalinks_ctx, InsertAnchor::None);
|
||||
let res = markdown_to_html(r#"# Hello"#, &context).unwrap();
|
||||
assert_eq!(res, "<h1 id=\"hello\">Hello</h1>\n");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_add_id_to_headers_same_slug() {
|
||||
let res = markdown_to_html("# Hello\n# Hello", &HashMap::new(), &GUTENBERG_TERA, &Config::default()).unwrap();
|
||||
let tera_ctx = Tera::default();
|
||||
let permalinks_ctx = HashMap::new();
|
||||
let config_ctx = Config::default();
|
||||
let context = Context::new(&tera_ctx, &config_ctx, &permalinks_ctx, InsertAnchor::None);
|
||||
let res = markdown_to_html("# Hello\n# Hello", &context).unwrap();
|
||||
assert_eq!(res, "<h1 id=\"hello\">Hello</h1>\n<h1 id=\"hello-1\">Hello</h1>\n");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_insert_anchor() {
|
||||
let mut config = Config::default();
|
||||
config.insert_anchor_links = Some(true);
|
||||
let res = markdown_to_html("# Hello", &HashMap::new(), &GUTENBERG_TERA, &config).unwrap();
|
||||
fn can_insert_anchor_left() {
|
||||
let permalinks_ctx = HashMap::new();
|
||||
let config_ctx = Config::default();
|
||||
let context = Context::new(&GUTENBERG_TERA, &config_ctx, &permalinks_ctx, InsertAnchor::Left);
|
||||
let res = markdown_to_html("# Hello", &context).unwrap();
|
||||
assert_eq!(
|
||||
res,
|
||||
"<h1 id=\"hello\"><a class=\"anchor\" href=\"#hello\" aria-label=\"Anchor link for: hello\">🔗</a>\nHello</h1>\n"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_insert_anchor_right() {
|
||||
let permalinks_ctx = HashMap::new();
|
||||
let config_ctx = Config::default();
|
||||
let context = Context::new(&GUTENBERG_TERA, &config_ctx, &permalinks_ctx, InsertAnchor::Right);
|
||||
let res = markdown_to_html("# Hello", &context).unwrap();
|
||||
assert_eq!(
|
||||
res,
|
||||
"<h1 id=\"hello\">Hello<a class=\"anchor\" href=\"#hello\" aria-label=\"Anchor link for: hello\">🔗</a>\n</h1>\n"
|
||||
);
|
||||
}
|
||||
|
||||
// See https://github.com/Keats/gutenberg/issues/42
|
||||
#[test]
|
||||
fn can_insert_anchor_with_exclamation_mark() {
|
||||
let mut config = Config::default();
|
||||
config.insert_anchor_links = Some(true);
|
||||
let res = markdown_to_html("# Hello!", &HashMap::new(), &GUTENBERG_TERA, &config).unwrap();
|
||||
let permalinks_ctx = HashMap::new();
|
||||
let config_ctx = Config::default();
|
||||
let context = Context::new(&GUTENBERG_TERA, &config_ctx, &permalinks_ctx, InsertAnchor::Left);
|
||||
let res = markdown_to_html("# Hello!", &context).unwrap();
|
||||
assert_eq!(
|
||||
res,
|
||||
"<h1 id=\"hello\"><a class=\"anchor\" href=\"#hello\" aria-label=\"Anchor link for: hello\">🔗</a>\nHello!</h1>\n"
|
||||
|
@ -475,9 +521,10 @@ A quote
|
|||
// See https://github.com/Keats/gutenberg/issues/53
|
||||
#[test]
|
||||
fn can_insert_anchor_with_link() {
|
||||
let mut config = Config::default();
|
||||
config.insert_anchor_links = Some(true);
|
||||
let res = markdown_to_html("## [](#xresources)Xresources", &HashMap::new(), &GUTENBERG_TERA, &config).unwrap();
|
||||
let permalinks_ctx = HashMap::new();
|
||||
let config_ctx = Config::default();
|
||||
let context = Context::new(&GUTENBERG_TERA, &config_ctx, &permalinks_ctx, InsertAnchor::Left);
|
||||
let res = markdown_to_html("## [](#xresources)Xresources", &context).unwrap();
|
||||
assert_eq!(
|
||||
res,
|
||||
"<h2 id=\"xresources\"><a class=\"anchor\" href=\"#xresources\" aria-label=\"Anchor link for: xresources\">🔗</a>\nXresources</h2>\n"
|
||||
|
@ -486,9 +533,10 @@ A quote
|
|||
|
||||
#[test]
|
||||
fn can_insert_anchor_with_other_special_chars() {
|
||||
let mut config = Config::default();
|
||||
config.insert_anchor_links = Some(true);
|
||||
let res = markdown_to_html("# Hello*_()", &HashMap::new(), &GUTENBERG_TERA, &config).unwrap();
|
||||
let permalinks_ctx = HashMap::new();
|
||||
let config_ctx = Config::default();
|
||||
let context = Context::new(&GUTENBERG_TERA, &config_ctx, &permalinks_ctx, InsertAnchor::Left);
|
||||
let res = markdown_to_html("# Hello*_()", &context).unwrap();
|
||||
assert_eq!(
|
||||
res,
|
||||
"<h1 id=\"hello\"><a class=\"anchor\" href=\"#hello\" aria-label=\"Anchor link for: hello\">🔗</a>\nHello*_()</h1>\n"
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
pub mod highlighting;
|
||||
pub mod markdown;
|
||||
pub mod short_code;
|
||||
pub mod context;
|
||||
|
|
|
@ -69,3 +69,33 @@ pub fn render_simple_shortcode(tera: &Tera, name: &str, args: &HashMap<String, S
|
|||
|
||||
tera.render(&tpl_name, &context).chain_err(|| format!("Failed to render {} shortcode", name))
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{parse_shortcode};
|
||||
|
||||
#[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");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
24
src/site.rs
24
src/site.rs
|
@ -11,7 +11,7 @@ use config::{Config, get_config};
|
|||
use fs::{create_file, create_directory, ensure_directory_exists};
|
||||
use content::{Page, Section, Paginator, SortBy, Taxonomy, populate_previous_and_next_pages, sort_pages};
|
||||
use templates::{GUTENBERG_TERA, global_fns, render_redirect_template};
|
||||
|
||||
use front_matter::InsertAnchor;
|
||||
|
||||
|
||||
#[derive(Debug)]
|
||||
|
@ -112,9 +112,16 @@ impl Site {
|
|||
self.sections.insert(index_path, index_section);
|
||||
}
|
||||
|
||||
// Silly thing needed to make the borrow checker happy
|
||||
let mut pages_insert_anchors = HashMap::new();
|
||||
for page in self.pages.values() {
|
||||
pages_insert_anchors.insert(page.file.path.clone(), self.find_parent_section_insert_anchor(&page.file.parent.clone()));
|
||||
}
|
||||
|
||||
// TODO: make that parallel
|
||||
for page in self.pages.values_mut() {
|
||||
page.render_markdown(&self.permalinks, &self.tera, &self.config)?;
|
||||
let insert_anchor = pages_insert_anchors[&page.file.path];
|
||||
page.render_markdown(&self.permalinks, &self.tera, &self.config, insert_anchor)?;
|
||||
}
|
||||
// TODO: make that parallel
|
||||
for section in self.sections.values_mut() {
|
||||
|
@ -145,8 +152,9 @@ impl Site {
|
|||
let prev = self.pages.insert(page.file.path.clone(), page);
|
||||
|
||||
if render {
|
||||
let insert_anchor = self.find_parent_section_insert_anchor(&self.pages[path].file.parent);
|
||||
let mut page = self.pages.get_mut(path).unwrap();
|
||||
page.render_markdown(&self.permalinks, &self.tera, &self.config)?;
|
||||
page.render_markdown(&self.permalinks, &self.tera, &self.config, insert_anchor)?;
|
||||
}
|
||||
|
||||
Ok(prev)
|
||||
|
@ -169,6 +177,15 @@ impl Site {
|
|||
Ok(prev)
|
||||
}
|
||||
|
||||
/// Finds the insert_anchor for the parent section of the directory at `path`.
|
||||
/// Defaults to `AnchorInsert::None` if no parent section found
|
||||
pub fn find_parent_section_insert_anchor(&self, parent_path: &PathBuf) -> InsertAnchor {
|
||||
match self.sections.get(&parent_path.join("_index.md")) {
|
||||
Some(ref s) => s.meta.insert_anchor.unwrap(),
|
||||
None => InsertAnchor::None
|
||||
}
|
||||
}
|
||||
|
||||
/// Find out the direct subsections of each subsection if there are some
|
||||
/// as well as the pages for each section
|
||||
pub fn populate_sections(&mut self) {
|
||||
|
@ -299,6 +316,7 @@ impl Site {
|
|||
create_directory(¤t_path)?;
|
||||
}
|
||||
}
|
||||
println!("Rendering page");
|
||||
|
||||
// Make sure the folder exists
|
||||
create_directory(¤t_path)?;
|
||||
|
|
|
@ -2,4 +2,5 @@
|
|||
title = "Posts"
|
||||
paginate_by = 2
|
||||
template = "section_paginated.html"
|
||||
insert_anchor = "left"
|
||||
+++
|
||||
|
|
|
@ -8,3 +8,5 @@ date = "2017-01-01"
|
|||
A simple page with a slug defined
|
||||
|
||||
# Title
|
||||
|
||||
Hey
|
||||
|
|
|
@ -277,7 +277,6 @@ fn can_build_site_and_insert_anchor_links() {
|
|||
let mut path = env::current_dir().unwrap().to_path_buf();
|
||||
path.push("test_site");
|
||||
let mut site = Site::new(&path, "config.toml").unwrap();
|
||||
site.config.insert_anchor_links = Some(true);
|
||||
site.load().unwrap();
|
||||
let tmp_dir = TempDir::new("example").expect("create temp dir");
|
||||
let public = &tmp_dir.path().join("public");
|
||||
|
|
Loading…
Reference in a new issue