* Bump pulldown_cmark to version 0.6.0 * Rename headers to headingsindex-subcmd
@@ -1323,7 +1323,7 @@ name = "mime" | |||||
version = "0.3.13" | version = "0.3.13" | ||||
source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
dependencies = [ | dependencies = [ | ||||
"unicase 2.4.0 (registry+https://github.com/rust-lang/crates.io-index)", | |||||
"unicase 2.5.1 (registry+https://github.com/rust-lang/crates.io-index)", | |||||
] | ] | ||||
[[package]] | [[package]] | ||||
@@ -1332,7 +1332,7 @@ version = "2.0.1" | |||||
source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
dependencies = [ | dependencies = [ | ||||
"mime 0.3.13 (registry+https://github.com/rust-lang/crates.io-index)", | "mime 0.3.13 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
"unicase 2.4.0 (registry+https://github.com/rust-lang/crates.io-index)", | |||||
"unicase 2.5.1 (registry+https://github.com/rust-lang/crates.io-index)", | |||||
] | ] | ||||
[[package]] | [[package]] | ||||
@@ -1800,13 +1800,13 @@ dependencies = [ | |||||
[[package]] | [[package]] | ||||
name = "pulldown-cmark" | name = "pulldown-cmark" | ||||
version = "0.5.3" | |||||
version = "0.6.0" | |||||
source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
dependencies = [ | dependencies = [ | ||||
"bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", | "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
"getopts 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)", | "getopts 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
"memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)", | "memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
"unicase 2.4.0 (registry+https://github.com/rust-lang/crates.io-index)", | |||||
"unicase 2.5.1 (registry+https://github.com/rust-lang/crates.io-index)", | |||||
] | ] | ||||
[[package]] | [[package]] | ||||
@@ -2053,7 +2053,7 @@ dependencies = [ | |||||
"link_checker 0.1.0", | "link_checker 0.1.0", | ||||
"pest 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)", | "pest 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
"pest_derive 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", | "pest_derive 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
"pulldown-cmark 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", | |||||
"pulldown-cmark 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", | |||||
"regex 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", | "regex 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
"serde 1.0.99 (registry+https://github.com/rust-lang/crates.io-index)", | "serde 1.0.99 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
"serde_derive 1.0.99 (registry+https://github.com/rust-lang/crates.io-index)", | "serde_derive 1.0.99 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
@@ -2521,7 +2521,7 @@ dependencies = [ | |||||
"imageproc 0.1.0", | "imageproc 0.1.0", | ||||
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", | "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
"library 0.1.0", | "library 0.1.0", | ||||
"pulldown-cmark 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", | |||||
"pulldown-cmark 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", | |||||
"reqwest 0.9.20 (registry+https://github.com/rust-lang/crates.io-index)", | "reqwest 0.9.20 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
"serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)", | "serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
"tera 1.0.0-beta.14 (registry+https://github.com/rust-lang/crates.io-index)", | "tera 1.0.0-beta.14 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
@@ -2894,7 +2894,7 @@ dependencies = [ | |||||
[[package]] | [[package]] | ||||
name = "unicase" | name = "unicase" | ||||
version = "2.4.0" | |||||
version = "2.5.1" | |||||
source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
dependencies = [ | dependencies = [ | ||||
"version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", | "version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
@@ -3367,7 +3367,7 @@ dependencies = [ | |||||
"checksum proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)" = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759" | "checksum proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)" = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759" | ||||
"checksum proc-macro2 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4c5c2380ae88876faae57698be9e9775e3544decad214599c3a6266cca6ac802" | "checksum proc-macro2 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4c5c2380ae88876faae57698be9e9775e3544decad214599c3a6266cca6ac802" | ||||
"checksum publicsuffix 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "9bf259a81de2b2eb9850ec990ec78e6a25319715584fd7652b9b26f96fcb1510" | "checksum publicsuffix 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "9bf259a81de2b2eb9850ec990ec78e6a25319715584fd7652b9b26f96fcb1510" | ||||
"checksum pulldown-cmark 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "77043da1282374688ee212dc44b3f37ff929431de9c9adc3053bd3cee5630357" | |||||
"checksum pulldown-cmark 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "85b0ad0d4c1702965ee6bb5b4ff5e71f83850b497d497e9444302987bf9e26a4" | |||||
"checksum quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9274b940887ce9addde99c4eee6b5c44cc494b182b97e73dc8ffdcb3397fd3f0" | "checksum quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9274b940887ce9addde99c4eee6b5c44cc494b182b97e73dc8ffdcb3397fd3f0" | ||||
"checksum quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)" = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1" | "checksum quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)" = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1" | ||||
"checksum quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe" | "checksum quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe" | ||||
@@ -3473,7 +3473,7 @@ dependencies = [ | |||||
"checksum unic-segment 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e4ed5d26be57f84f176157270c112ef57b86debac9cd21daaabbe56db0f88f23" | "checksum unic-segment 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e4ed5d26be57f84f176157270c112ef57b86debac9cd21daaabbe56db0f88f23" | ||||
"checksum unic-ucd-segment 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2079c122a62205b421f499da10f3ee0f7697f012f55b675e002483c73ea34700" | "checksum unic-ucd-segment 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2079c122a62205b421f499da10f3ee0f7697f012f55b675e002483c73ea34700" | ||||
"checksum unic-ucd-version 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "96bd2f2237fe450fcd0a1d2f5f4e91711124f7857ba2e964247776ebeeb7b0c4" | "checksum unic-ucd-version 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "96bd2f2237fe450fcd0a1d2f5f4e91711124f7857ba2e964247776ebeeb7b0c4" | ||||
"checksum unicase 2.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a84e5511b2a947f3ae965dcb29b13b7b1691b6e7332cf5dbc1744138d5acb7f6" | |||||
"checksum unicase 2.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2e2e6bd1e59e56598518beb94fd6db628ded570326f0a98c679a304bd9f00150" | |||||
"checksum unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5" | "checksum unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5" | ||||
"checksum unicode-normalization 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "141339a08b982d942be2ca06ff8b076563cbe223d1befd5450716790d44e2426" | "checksum unicode-normalization 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "141339a08b982d942be2ca06ff8b076563cbe223d1befd5450716790d44e2426" | ||||
"checksum unicode-segmentation 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1967f4cdfc355b37fd76d2a954fb2ed3871034eb4f26d60537d88795cfc332a9" | "checksum unicode-segmentation 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1967f4cdfc355b37fd76d2a954fb2ed3871034eb4f26d60537d88795cfc332a9" | ||||
@@ -8,9 +8,9 @@ pub use self::page::Page; | |||||
pub use self::section::Section; | pub use self::section::Section; | ||||
pub use self::ser::{SerializingPage, SerializingSection}; | pub use self::ser::{SerializingPage, SerializingSection}; | ||||
use rendering::Header; | |||||
use rendering::Heading; | |||||
pub fn has_anchor(headings: &[Header], anchor: &str) -> bool { | |||||
pub fn has_anchor(headings: &[Heading], anchor: &str) -> bool { | |||||
for heading in headings { | for heading in headings { | ||||
if heading.id == anchor { | if heading.id == anchor { | ||||
return true; | return true; | ||||
@@ -30,28 +30,28 @@ mod tests { | |||||
#[test] | #[test] | ||||
fn can_find_anchor_at_root() { | fn can_find_anchor_at_root() { | ||||
let input = vec![ | let input = vec![ | ||||
Header { | |||||
Heading { | |||||
level: 1, | level: 1, | ||||
id: "1".to_string(), | id: "1".to_string(), | ||||
permalink: String::new(), | permalink: String::new(), | ||||
title: String::new(), | title: String::new(), | ||||
children: vec![], | children: vec![], | ||||
}, | }, | ||||
Header { | |||||
Heading { | |||||
level: 2, | level: 2, | ||||
id: "1-1".to_string(), | id: "1-1".to_string(), | ||||
permalink: String::new(), | permalink: String::new(), | ||||
title: String::new(), | title: String::new(), | ||||
children: vec![], | children: vec![], | ||||
}, | }, | ||||
Header { | |||||
Heading { | |||||
level: 3, | level: 3, | ||||
id: "1-1-1".to_string(), | id: "1-1-1".to_string(), | ||||
permalink: String::new(), | permalink: String::new(), | ||||
title: String::new(), | title: String::new(), | ||||
children: vec![], | children: vec![], | ||||
}, | }, | ||||
Header { | |||||
Heading { | |||||
level: 2, | level: 2, | ||||
id: "1-2".to_string(), | id: "1-2".to_string(), | ||||
permalink: String::new(), | permalink: String::new(), | ||||
@@ -65,27 +65,27 @@ mod tests { | |||||
#[test] | #[test] | ||||
fn can_find_anchor_in_children() { | fn can_find_anchor_in_children() { | ||||
let input = vec![Header { | |||||
let input = vec![Heading { | |||||
level: 1, | level: 1, | ||||
id: "1".to_string(), | id: "1".to_string(), | ||||
permalink: String::new(), | permalink: String::new(), | ||||
title: String::new(), | title: String::new(), | ||||
children: vec![ | children: vec![ | ||||
Header { | |||||
Heading { | |||||
level: 2, | level: 2, | ||||
id: "1-1".to_string(), | id: "1-1".to_string(), | ||||
permalink: String::new(), | permalink: String::new(), | ||||
title: String::new(), | title: String::new(), | ||||
children: vec![], | children: vec![], | ||||
}, | }, | ||||
Header { | |||||
Heading { | |||||
level: 3, | level: 3, | ||||
id: "1-1-1".to_string(), | id: "1-1-1".to_string(), | ||||
permalink: String::new(), | permalink: String::new(), | ||||
title: String::new(), | title: String::new(), | ||||
children: vec![], | children: vec![], | ||||
}, | }, | ||||
Header { | |||||
Heading { | |||||
level: 2, | level: 2, | ||||
id: "1-2".to_string(), | id: "1-2".to_string(), | ||||
permalink: String::new(), | permalink: String::new(), | ||||
@@ -11,7 +11,7 @@ use config::Config; | |||||
use errors::{Error, Result}; | use errors::{Error, Result}; | ||||
use front_matter::{split_page_content, InsertAnchor, PageFrontMatter}; | use front_matter::{split_page_content, InsertAnchor, PageFrontMatter}; | ||||
use library::Library; | use library::Library; | ||||
use rendering::{render_content, Header, RenderContext}; | |||||
use rendering::{render_content, Heading, RenderContext}; | |||||
use utils::fs::{find_related_assets, read_file}; | use utils::fs::{find_related_assets, read_file}; | ||||
use utils::site::get_reading_analytics; | use utils::site::get_reading_analytics; | ||||
use utils::templates::render_template; | use utils::templates::render_template; | ||||
@@ -65,8 +65,8 @@ pub struct Page { | |||||
pub lighter: Option<Key>, | pub lighter: Option<Key>, | ||||
/// The heavier page, for pages sorted by weight | /// The heavier page, for pages sorted by weight | ||||
pub heavier: Option<Key>, | pub heavier: Option<Key>, | ||||
/// Toc made from the headers of the markdown file | |||||
pub toc: Vec<Header>, | |||||
/// Toc made from the headings of the markdown file | |||||
pub toc: Vec<Heading>, | |||||
/// How many words in the raw content | /// How many words in the raw content | ||||
pub word_count: Option<usize>, | pub word_count: Option<usize>, | ||||
/// How long would it take to read the raw content. | /// How long would it take to read the raw content. | ||||
@@ -7,7 +7,7 @@ use tera::{Context as TeraContext, Tera}; | |||||
use config::Config; | use config::Config; | ||||
use errors::{Error, Result}; | use errors::{Error, Result}; | ||||
use front_matter::{split_section_content, SectionFrontMatter}; | use front_matter::{split_section_content, SectionFrontMatter}; | ||||
use rendering::{render_content, Header, RenderContext}; | |||||
use rendering::{render_content, Heading, RenderContext}; | |||||
use utils::fs::{find_related_assets, read_file}; | use utils::fs::{find_related_assets, read_file}; | ||||
use utils::site::get_reading_analytics; | use utils::site::get_reading_analytics; | ||||
use utils::templates::render_template; | use utils::templates::render_template; | ||||
@@ -45,8 +45,8 @@ pub struct Section { | |||||
pub ancestors: Vec<Key>, | pub ancestors: Vec<Key>, | ||||
/// All direct subsections | /// All direct subsections | ||||
pub subsections: Vec<Key>, | pub subsections: Vec<Key>, | ||||
/// Toc made from the headers of the markdown file | |||||
pub toc: Vec<Header>, | |||||
/// Toc made from the headings of the markdown file | |||||
pub toc: Vec<Heading>, | |||||
/// How many words in the raw content | /// How many words in the raw content | ||||
pub word_count: Option<usize>, | pub word_count: Option<usize>, | ||||
/// How long would it take to read the raw content. | /// How long would it take to read the raw content. | ||||
@@ -6,7 +6,7 @@ authors = ["Vincent Prouillet <prouillet.vincent@gmail.com>"] | |||||
[dependencies] | [dependencies] | ||||
tera = { version = "1.0.0-beta.10", features = ["preserve_order"] } | tera = { version = "1.0.0-beta.10", features = ["preserve_order"] } | ||||
syntect = "=3.2.0" | syntect = "=3.2.0" | ||||
pulldown-cmark = "0.5" | |||||
pulldown-cmark = "0.6" | |||||
slug = "0.1" | slug = "0.1" | ||||
serde = "1" | serde = "1" | ||||
serde_derive = "1" | serde_derive = "1" | ||||
@@ -32,7 +32,7 @@ use errors::Result; | |||||
pub use context::RenderContext; | pub use context::RenderContext; | ||||
use markdown::markdown_to_html; | use markdown::markdown_to_html; | ||||
pub use shortcode::render_shortcodes; | pub use shortcode::render_shortcodes; | ||||
pub use table_of_contents::Header; | |||||
pub use table_of_contents::Heading; | |||||
pub fn render_content(content: &str, context: &RenderContext) -> Result<markdown::Rendered> { | pub fn render_content(content: &str, context: &RenderContext) -> Result<markdown::Rendered> { | ||||
// Don't do shortcodes if there is nothing like a shortcode in the content | // Don't do shortcodes if there is nothing like a shortcode in the content | ||||
@@ -9,7 +9,7 @@ use config::highlighting::{get_highlighter, SYNTAX_SET, THEME_SET}; | |||||
use context::RenderContext; | use context::RenderContext; | ||||
use errors::{Error, Result}; | use errors::{Error, Result}; | ||||
use front_matter::InsertAnchor; | use front_matter::InsertAnchor; | ||||
use table_of_contents::{make_table_of_contents, Header}; | |||||
use table_of_contents::{make_table_of_contents, Heading}; | |||||
use utils::site::resolve_internal_link; | use utils::site::resolve_internal_link; | ||||
use utils::vec::InsertMany; | use utils::vec::InsertMany; | ||||
@@ -23,23 +23,23 @@ const ANCHOR_LINK_TEMPLATE: &str = "anchor-link.html"; | |||||
pub struct Rendered { | pub struct Rendered { | ||||
pub body: String, | pub body: String, | ||||
pub summary_len: Option<usize>, | pub summary_len: Option<usize>, | ||||
pub toc: Vec<Header>, | |||||
pub toc: Vec<Heading>, | |||||
pub internal_links_with_anchors: Vec<(String, String)>, | pub internal_links_with_anchors: Vec<(String, String)>, | ||||
pub external_links: Vec<String>, | pub external_links: Vec<String>, | ||||
} | } | ||||
// tracks a header in a slice of pulldown-cmark events | |||||
// tracks a heading in a slice of pulldown-cmark events | |||||
#[derive(Debug)] | #[derive(Debug)] | ||||
struct HeaderRef { | |||||
struct HeadingRef { | |||||
start_idx: usize, | start_idx: usize, | ||||
end_idx: usize, | end_idx: usize, | ||||
level: i32, | |||||
level: u32, | |||||
id: Option<String>, | id: Option<String>, | ||||
} | } | ||||
impl HeaderRef { | |||||
fn new(start: usize, level: i32) -> HeaderRef { | |||||
HeaderRef { start_idx: start, end_idx: 0, level, id: None } | |||||
impl HeadingRef { | |||||
fn new(start: usize, level: u32) -> HeadingRef { | |||||
HeadingRef { start_idx: start, end_idx: 0, level, id: None } | |||||
} | } | ||||
} | } | ||||
@@ -125,23 +125,23 @@ fn get_text(parser_slice: &[Event]) -> String { | |||||
title | title | ||||
} | } | ||||
fn get_header_refs(events: &[Event]) -> Vec<HeaderRef> { | |||||
let mut header_refs = vec![]; | |||||
fn get_heading_refs(events: &[Event]) -> Vec<HeadingRef> { | |||||
let mut heading_refs = vec![]; | |||||
for (i, event) in events.iter().enumerate() { | for (i, event) in events.iter().enumerate() { | ||||
match event { | match event { | ||||
Event::Start(Tag::Header(level)) => { | |||||
header_refs.push(HeaderRef::new(i, *level)); | |||||
Event::Start(Tag::Heading(level)) => { | |||||
heading_refs.push(HeadingRef::new(i, *level)); | |||||
} | } | ||||
Event::End(Tag::Header(_)) => { | |||||
let msg = "Header end before start?"; | |||||
header_refs.last_mut().expect(msg).end_idx = i; | |||||
Event::End(Tag::Heading(_)) => { | |||||
let msg = "Heading end before start?"; | |||||
heading_refs.last_mut().expect(msg).end_idx = i; | |||||
} | } | ||||
_ => (), | _ => (), | ||||
} | } | ||||
} | } | ||||
header_refs | |||||
heading_refs | |||||
} | } | ||||
pub fn markdown_to_html(content: &str, context: &RenderContext) -> Result<Rendered> { | pub fn markdown_to_html(content: &str, context: &RenderContext) -> Result<Rendered> { | ||||
@@ -154,7 +154,7 @@ pub fn markdown_to_html(content: &str, context: &RenderContext) -> Result<Render | |||||
let mut highlighter: Option<(HighlightLines, bool)> = None; | let mut highlighter: Option<(HighlightLines, bool)> = None; | ||||
let mut inserted_anchors: Vec<String> = vec![]; | let mut inserted_anchors: Vec<String> = vec![]; | ||||
let mut headers: Vec<Header> = vec![]; | |||||
let mut headings: Vec<Heading> = vec![]; | |||||
let mut internal_links_with_anchors = Vec::new(); | let mut internal_links_with_anchors = Vec::new(); | ||||
let mut external_links = Vec::new(); | let mut external_links = Vec::new(); | ||||
@@ -247,14 +247,14 @@ pub fn markdown_to_html(content: &str, context: &RenderContext) -> Result<Render | |||||
}) | }) | ||||
.collect::<Vec<_>>(); // We need to collect the events to make a second pass | .collect::<Vec<_>>(); // We need to collect the events to make a second pass | ||||
let mut header_refs = get_header_refs(&events); | |||||
let mut heading_refs = get_heading_refs(&events); | |||||
let mut anchors_to_insert = vec![]; | let mut anchors_to_insert = vec![]; | ||||
// First header pass: look for a manually-specified IDs, e.g. `# Heading text {#hash}` | |||||
// First heading pass: look for a manually-specified IDs, e.g. `# Heading text {#hash}` | |||||
// (This is a separate first pass so that auto IDs can avoid collisions with manual IDs.) | // (This is a separate first pass so that auto IDs can avoid collisions with manual IDs.) | ||||
for header_ref in header_refs.iter_mut() { | |||||
let end_idx = header_ref.end_idx; | |||||
for heading_ref in heading_refs.iter_mut() { | |||||
let end_idx = heading_ref.end_idx; | |||||
if let Event::Text(ref mut text) = events[end_idx - 1] { | if let Event::Text(ref mut text) = events[end_idx - 1] { | ||||
if text.as_bytes().last() == Some(&b'}') { | if text.as_bytes().last() == Some(&b'}') { | ||||
if let Some(mut i) = text.find("{#") { | if let Some(mut i) = text.find("{#") { | ||||
@@ -263,24 +263,24 @@ pub fn markdown_to_html(content: &str, context: &RenderContext) -> Result<Render | |||||
while i > 0 && text.as_bytes()[i - 1] == b' ' { | while i > 0 && text.as_bytes()[i - 1] == b' ' { | ||||
i -= 1; | i -= 1; | ||||
} | } | ||||
header_ref.id = Some(id); | |||||
heading_ref.id = Some(id); | |||||
*text = text[..i].to_owned().into(); | *text = text[..i].to_owned().into(); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
} | } | ||||
// Second header pass: auto-generate remaining IDs, and emit HTML | |||||
for header_ref in header_refs { | |||||
let start_idx = header_ref.start_idx; | |||||
let end_idx = header_ref.end_idx; | |||||
// Second heading pass: auto-generate remaining IDs, and emit HTML | |||||
for heading_ref in heading_refs { | |||||
let start_idx = heading_ref.start_idx; | |||||
let end_idx = heading_ref.end_idx; | |||||
let title = get_text(&events[start_idx + 1..end_idx]); | let title = get_text(&events[start_idx + 1..end_idx]); | ||||
let id = | let id = | ||||
header_ref.id.unwrap_or_else(|| find_anchor(&inserted_anchors, slugify(&title), 0)); | |||||
heading_ref.id.unwrap_or_else(|| find_anchor(&inserted_anchors, slugify(&title), 0)); | |||||
inserted_anchors.push(id.clone()); | inserted_anchors.push(id.clone()); | ||||
// insert `id` to the tag | // insert `id` to the tag | ||||
let html = format!("<h{lvl} id=\"{id}\">", lvl = header_ref.level, id = id); | |||||
let html = format!("<h{lvl} id=\"{id}\">", lvl = heading_ref.level, id = id); | |||||
events[start_idx] = Event::Html(html.into()); | events[start_idx] = Event::Html(html.into()); | ||||
// generate anchors and places to insert them | // generate anchors and places to insert them | ||||
@@ -303,10 +303,10 @@ pub fn markdown_to_html(content: &str, context: &RenderContext) -> Result<Render | |||||
anchors_to_insert.push((anchor_idx, Event::Html(anchor_link.into()))); | anchors_to_insert.push((anchor_idx, Event::Html(anchor_link.into()))); | ||||
} | } | ||||
// record header to make table of contents | |||||
// record heading to make table of contents | |||||
let permalink = format!("{}#{}", context.current_page_permalink, id); | let permalink = format!("{}#{}", context.current_page_permalink, id); | ||||
let h = Header { level: header_ref.level, id, permalink, title, children: Vec::new() }; | |||||
headers.push(h); | |||||
let h = Heading { level: heading_ref.level, id, permalink, title, children: Vec::new() }; | |||||
headings.push(h); | |||||
} | } | ||||
if context.insert_anchor != InsertAnchor::None { | if context.insert_anchor != InsertAnchor::None { | ||||
@@ -322,7 +322,7 @@ pub fn markdown_to_html(content: &str, context: &RenderContext) -> Result<Render | |||||
Ok(Rendered { | Ok(Rendered { | ||||
summary_len: if has_summary { html.find(CONTINUE_READING) } else { None }, | summary_len: if has_summary { html.find(CONTINUE_READING) } else { None }, | ||||
body: html, | body: html, | ||||
toc: make_table_of_contents(headers), | |||||
toc: make_table_of_contents(headings), | |||||
internal_links_with_anchors, | internal_links_with_anchors, | ||||
external_links, | external_links, | ||||
}) | }) | ||||
@@ -1,17 +1,17 @@ | |||||
/// Populated while receiving events from the markdown parser | /// Populated while receiving events from the markdown parser | ||||
#[derive(Debug, PartialEq, Clone, Serialize)] | #[derive(Debug, PartialEq, Clone, Serialize)] | ||||
pub struct Header { | |||||
pub struct Heading { | |||||
#[serde(skip_serializing)] | #[serde(skip_serializing)] | ||||
pub level: i32, | |||||
pub level: u32, | |||||
pub id: String, | pub id: String, | ||||
pub permalink: String, | pub permalink: String, | ||||
pub title: String, | pub title: String, | ||||
pub children: Vec<Header>, | |||||
pub children: Vec<Heading>, | |||||
} | } | ||||
impl Header { | |||||
pub fn new(level: i32) -> Header { | |||||
Header { | |||||
impl Heading { | |||||
pub fn new(level: u32) -> Heading { | |||||
Heading { | |||||
level, | level, | ||||
id: String::new(), | id: String::new(), | ||||
permalink: String::new(), | permalink: String::new(), | ||||
@@ -21,49 +21,48 @@ impl Header { | |||||
} | } | ||||
} | } | ||||
impl Default for Header { | |||||
impl Default for Heading { | |||||
fn default() -> Self { | fn default() -> Self { | ||||
Header::new(0) | |||||
Heading::new(0) | |||||
} | } | ||||
} | } | ||||
// Takes a potential (mutable) parent and a header to try and insert into | |||||
// Takes a potential (mutable) parent and a heading to try and insert into | |||||
// Returns true when it performed the insertion, false otherwise | // Returns true when it performed the insertion, false otherwise | ||||
fn insert_into_parent(potential_parent: Option<&mut Header>, header: &Header) -> bool { | |||||
fn insert_into_parent(potential_parent: Option<&mut Heading>, heading: &Heading) -> bool { | |||||
match potential_parent { | match potential_parent { | ||||
None => { | None => { | ||||
// No potential parent to insert into so it needs to be insert higher | // No potential parent to insert into so it needs to be insert higher | ||||
false | false | ||||
} | } | ||||
Some(parent) => { | Some(parent) => { | ||||
let diff = header.level - parent.level; | |||||
if diff <= 0 { | |||||
if heading.level <= parent.level { | |||||
// Heading is same level or higher so we don't insert here | // Heading is same level or higher so we don't insert here | ||||
return false; | return false; | ||||
} | } | ||||
if diff == 1 { | |||||
if heading.level + 1 == parent.level { | |||||
// We have a direct child of the parent | // We have a direct child of the parent | ||||
parent.children.push(header.clone()); | |||||
parent.children.push(heading.clone()); | |||||
return true; | return true; | ||||
} | } | ||||
// We need to go deeper | // We need to go deeper | ||||
if !insert_into_parent(parent.children.iter_mut().last(), header) { | |||||
if !insert_into_parent(parent.children.iter_mut().last(), heading) { | |||||
// No, we need to insert it here | // No, we need to insert it here | ||||
parent.children.push(header.clone()); | |||||
parent.children.push(heading.clone()); | |||||
} | } | ||||
true | true | ||||
} | } | ||||
} | } | ||||
} | } | ||||
/// Converts the flat temp headers into a nested set of headers | |||||
/// Converts the flat temp headings into a nested set of headings | |||||
/// representing the hierarchy | /// representing the hierarchy | ||||
pub fn make_table_of_contents(headers: Vec<Header>) -> Vec<Header> { | |||||
pub fn make_table_of_contents(headings: Vec<Heading>) -> Vec<Heading> { | |||||
let mut toc = vec![]; | let mut toc = vec![]; | ||||
for header in headers { | |||||
// First header or we try to insert the current header in a previous one | |||||
if toc.is_empty() || !insert_into_parent(toc.iter_mut().last(), &header) { | |||||
toc.push(header); | |||||
for heading in headings { | |||||
// First heading or we try to insert the current heading in a previous one | |||||
if toc.is_empty() || !insert_into_parent(toc.iter_mut().last(), &heading) { | |||||
toc.push(heading); | |||||
} | } | ||||
} | } | ||||
@@ -76,7 +75,7 @@ mod tests { | |||||
#[test] | #[test] | ||||
fn can_make_basic_toc() { | fn can_make_basic_toc() { | ||||
let input = vec![Header::new(1), Header::new(1), Header::new(1)]; | |||||
let input = vec![Heading::new(1), Heading::new(1), Heading::new(1)]; | |||||
let toc = make_table_of_contents(input); | let toc = make_table_of_contents(input); | ||||
assert_eq!(toc.len(), 3); | assert_eq!(toc.len(), 3); | ||||
} | } | ||||
@@ -84,15 +83,15 @@ mod tests { | |||||
#[test] | #[test] | ||||
fn can_make_more_complex_toc() { | fn can_make_more_complex_toc() { | ||||
let input = vec![ | let input = vec![ | ||||
Header::new(1), | |||||
Header::new(2), | |||||
Header::new(2), | |||||
Header::new(3), | |||||
Header::new(2), | |||||
Header::new(1), | |||||
Header::new(2), | |||||
Header::new(3), | |||||
Header::new(3), | |||||
Heading::new(1), | |||||
Heading::new(2), | |||||
Heading::new(2), | |||||
Heading::new(3), | |||||
Heading::new(2), | |||||
Heading::new(1), | |||||
Heading::new(2), | |||||
Heading::new(3), | |||||
Heading::new(3), | |||||
]; | ]; | ||||
let toc = make_table_of_contents(input); | let toc = make_table_of_contents(input); | ||||
assert_eq!(toc.len(), 2); | assert_eq!(toc.len(), 2); | ||||
@@ -105,12 +104,12 @@ mod tests { | |||||
#[test] | #[test] | ||||
fn can_make_deep_toc() { | fn can_make_deep_toc() { | ||||
let input = vec![ | let input = vec![ | ||||
Header::new(1), | |||||
Header::new(2), | |||||
Header::new(3), | |||||
Header::new(4), | |||||
Header::new(5), | |||||
Header::new(4), | |||||
Heading::new(1), | |||||
Heading::new(2), | |||||
Heading::new(3), | |||||
Heading::new(4), | |||||
Heading::new(5), | |||||
Heading::new(4), | |||||
]; | ]; | ||||
let toc = make_table_of_contents(input); | let toc = make_table_of_contents(input); | ||||
assert_eq!(toc.len(), 1); | assert_eq!(toc.len(), 1); | ||||
@@ -123,16 +122,16 @@ mod tests { | |||||
#[test] | #[test] | ||||
fn can_make_deep_messy_toc() { | fn can_make_deep_messy_toc() { | ||||
let input = vec![ | let input = vec![ | ||||
Header::new(2), // toc[0] | |||||
Header::new(3), | |||||
Header::new(4), | |||||
Header::new(5), | |||||
Header::new(4), | |||||
Header::new(2), // toc[1] | |||||
Header::new(1), // toc[2] | |||||
Header::new(2), | |||||
Header::new(3), | |||||
Header::new(4), | |||||
Heading::new(2), // toc[0] | |||||
Heading::new(3), | |||||
Heading::new(4), | |||||
Heading::new(5), | |||||
Heading::new(4), | |||||
Heading::new(2), // toc[1] | |||||
Heading::new(1), // toc[2] | |||||
Heading::new(2), | |||||
Heading::new(3), | |||||
Heading::new(4), | |||||
]; | ]; | ||||
let toc = make_table_of_contents(input); | let toc = make_table_of_contents(input); | ||||
assert_eq!(toc.len(), 3); | assert_eq!(toc.len(), 3); | ||||
@@ -148,13 +147,13 @@ mod tests { | |||||
#[test] | #[test] | ||||
fn can_make_messy_toc() { | fn can_make_messy_toc() { | ||||
let input = vec![ | let input = vec![ | ||||
Header::new(3), | |||||
Header::new(2), | |||||
Header::new(2), | |||||
Header::new(3), | |||||
Header::new(2), | |||||
Header::new(1), | |||||
Header::new(4), | |||||
Heading::new(3), | |||||
Heading::new(2), | |||||
Heading::new(2), | |||||
Heading::new(3), | |||||
Heading::new(2), | |||||
Heading::new(1), | |||||
Heading::new(4), | |||||
]; | ]; | ||||
let toc = make_table_of_contents(input); | let toc = make_table_of_contents(input); | ||||
println!("{:#?}", toc); | println!("{:#?}", toc); | ||||
@@ -332,7 +332,7 @@ fn errors_relative_link_inexistant() { | |||||
} | } | ||||
#[test] | #[test] | ||||
fn can_add_id_to_headers() { | |||||
fn can_add_id_to_headings() { | |||||
let tera_ctx = Tera::default(); | let tera_ctx = Tera::default(); | ||||
let permalinks_ctx = HashMap::new(); | let permalinks_ctx = HashMap::new(); | ||||
let config = Config::default(); | let config = Config::default(); | ||||
@@ -342,7 +342,7 @@ fn can_add_id_to_headers() { | |||||
} | } | ||||
#[test] | #[test] | ||||
fn can_add_id_to_headers_same_slug() { | |||||
fn can_add_id_to_headings_same_slug() { | |||||
let tera_ctx = Tera::default(); | let tera_ctx = Tera::default(); | ||||
let permalinks_ctx = HashMap::new(); | let permalinks_ctx = HashMap::new(); | ||||
let config = Config::default(); | let config = Config::default(); | ||||
@@ -352,7 +352,7 @@ fn can_add_id_to_headers_same_slug() { | |||||
} | } | ||||
#[test] | #[test] | ||||
fn can_handle_manual_ids_on_headers() { | |||||
fn can_handle_manual_ids_on_headings() { | |||||
let tera_ctx = Tera::default(); | let tera_ctx = Tera::default(); | ||||
let permalinks_ctx = HashMap::new(); | let permalinks_ctx = HashMap::new(); | ||||
let config = Config::default(); | let config = Config::default(); | ||||
@@ -361,7 +361,7 @@ fn can_handle_manual_ids_on_headers() { | |||||
// manual IDs; that duplicates are in fact permitted among manual IDs; that any non-plain-text | // manual IDs; that duplicates are in fact permitted among manual IDs; that any non-plain-text | ||||
// in the middle of `{#…}` will disrupt it from being acknowledged as a manual ID (that last | // in the middle of `{#…}` will disrupt it from being acknowledged as a manual ID (that last | ||||
// one could reasonably be considered a bug rather than a feature, but test it either way); one | // one could reasonably be considered a bug rather than a feature, but test it either way); one | ||||
// workaround for the improbable case where you actually want `{#…}` at the end of a header. | |||||
// workaround for the improbable case where you actually want `{#…}` at the end of a heading. | |||||
let res = render_content( | let res = render_content( | ||||
"\ | "\ | ||||
# Hello\n\ | # Hello\n\ | ||||
@@ -389,7 +389,7 @@ fn can_handle_manual_ids_on_headers() { | |||||
} | } | ||||
#[test] | #[test] | ||||
fn blank_headers() { | |||||
fn blank_headings() { | |||||
let tera_ctx = Tera::default(); | let tera_ctx = Tera::default(); | ||||
let permalinks_ctx = HashMap::new(); | let permalinks_ctx = HashMap::new(); | ||||
let config = Config::default(); | let config = Config::default(); | ||||
@@ -426,7 +426,7 @@ fn can_insert_anchor_right() { | |||||
} | } | ||||
#[test] | #[test] | ||||
fn can_insert_anchor_for_multi_header() { | |||||
fn can_insert_anchor_for_multi_heading() { | |||||
let permalinks_ctx = HashMap::new(); | let permalinks_ctx = HashMap::new(); | ||||
let config = Config::default(); | let config = Config::default(); | ||||
let context = RenderContext::new(&ZOLA_TERA, &config, "", &permalinks_ctx, InsertAnchor::Right); | let context = RenderContext::new(&ZOLA_TERA, &config, "", &permalinks_ctx, InsertAnchor::Right); | ||||
@@ -490,11 +490,11 @@ fn can_make_toc() { | |||||
let res = render_content( | let res = render_content( | ||||
r#" | r#" | ||||
# Header 1 | |||||
# Heading 1 | |||||
## Header 2 | |||||
## Heading 2 | |||||
## Another Header 2 | |||||
## Another Heading 2 | |||||
### Last one | ### Last one | ||||
"#, | "#, | ||||
@@ -522,9 +522,9 @@ fn can_ignore_tags_in_toc() { | |||||
let res = render_content( | let res = render_content( | ||||
r#" | r#" | ||||
## header with `code` | |||||
## heading with `code` | |||||
## [anchor](https://duckduckgo.com/) in header | |||||
## [anchor](https://duckduckgo.com/) in heading | |||||
## **bold** and *italics* | ## **bold** and *italics* | ||||
"#, | "#, | ||||
@@ -534,11 +534,11 @@ fn can_ignore_tags_in_toc() { | |||||
let toc = res.toc; | let toc = res.toc; | ||||
assert_eq!(toc[0].id, "header-with-code"); | |||||
assert_eq!(toc[0].title, "header with code"); | |||||
assert_eq!(toc[0].id, "heading-with-code"); | |||||
assert_eq!(toc[0].title, "heading with code"); | |||||
assert_eq!(toc[1].id, "anchor-in-header"); | |||||
assert_eq!(toc[1].title, "anchor in header"); | |||||
assert_eq!(toc[1].id, "anchor-in-heading"); | |||||
assert_eq!(toc[1].title, "anchor in heading"); | |||||
assert_eq!(toc[2].id, "bold-and-italics"); | assert_eq!(toc[2].id, "bold-and-italics"); | ||||
assert_eq!(toc[2].title, "bold and italics"); | assert_eq!(toc[2].title, "bold and italics"); | ||||
@@ -564,7 +564,7 @@ fn can_understand_backtick_in_paragraphs() { | |||||
// https://github.com/Keats/gutenberg/issues/297 | // https://github.com/Keats/gutenberg/issues/297 | ||||
#[test] | #[test] | ||||
fn can_understand_links_in_header() { | |||||
fn can_understand_links_in_heading() { | |||||
let permalinks_ctx = HashMap::new(); | let permalinks_ctx = HashMap::new(); | ||||
let config = Config::default(); | let config = Config::default(); | ||||
let context = RenderContext::new(&ZOLA_TERA, &config, "", &permalinks_ctx, InsertAnchor::None); | let context = RenderContext::new(&ZOLA_TERA, &config, "", &permalinks_ctx, InsertAnchor::None); | ||||
@@ -573,7 +573,7 @@ fn can_understand_links_in_header() { | |||||
} | } | ||||
#[test] | #[test] | ||||
fn can_understand_link_with_title_in_header() { | |||||
fn can_understand_link_with_title_in_heading() { | |||||
let permalinks_ctx = HashMap::new(); | let permalinks_ctx = HashMap::new(); | ||||
let config = Config::default(); | let config = Config::default(); | ||||
let context = RenderContext::new(&ZOLA_TERA, &config, "", &permalinks_ctx, InsertAnchor::None); | let context = RenderContext::new(&ZOLA_TERA, &config, "", &permalinks_ctx, InsertAnchor::None); | ||||
@@ -586,7 +586,7 @@ fn can_understand_link_with_title_in_header() { | |||||
} | } | ||||
#[test] | #[test] | ||||
fn can_understand_emphasis_in_header() { | |||||
fn can_understand_emphasis_in_heading() { | |||||
let permalinks_ctx = HashMap::new(); | let permalinks_ctx = HashMap::new(); | ||||
let config = Config::default(); | let config = Config::default(); | ||||
let context = RenderContext::new(&ZOLA_TERA, &config, "", &permalinks_ctx, InsertAnchor::None); | let context = RenderContext::new(&ZOLA_TERA, &config, "", &permalinks_ctx, InsertAnchor::None); | ||||
@@ -595,7 +595,7 @@ fn can_understand_emphasis_in_header() { | |||||
} | } | ||||
#[test] | #[test] | ||||
fn can_understand_strong_in_header() { | |||||
fn can_understand_strong_in_heading() { | |||||
let permalinks_ctx = HashMap::new(); | let permalinks_ctx = HashMap::new(); | ||||
let config = Config::default(); | let config = Config::default(); | ||||
let context = RenderContext::new(&ZOLA_TERA, &config, "", &permalinks_ctx, InsertAnchor::None); | let context = RenderContext::new(&ZOLA_TERA, &config, "", &permalinks_ctx, InsertAnchor::None); | ||||
@@ -604,7 +604,7 @@ fn can_understand_strong_in_header() { | |||||
} | } | ||||
#[test] | #[test] | ||||
fn can_understand_code_in_header() { | |||||
fn can_understand_code_in_heading() { | |||||
let permalinks_ctx = HashMap::new(); | let permalinks_ctx = HashMap::new(); | ||||
let config = Config::default(); | let config = Config::default(); | ||||
let context = RenderContext::new(&ZOLA_TERA, &config, "", &permalinks_ctx, InsertAnchor::None); | let context = RenderContext::new(&ZOLA_TERA, &config, "", &permalinks_ctx, InsertAnchor::None); | ||||
@@ -614,7 +614,7 @@ fn can_understand_code_in_header() { | |||||
// See https://github.com/getzola/zola/issues/569 | // See https://github.com/getzola/zola/issues/569 | ||||
#[test] | #[test] | ||||
fn can_understand_footnote_in_header() { | |||||
fn can_understand_footnote_in_heading() { | |||||
let permalinks_ctx = HashMap::new(); | let permalinks_ctx = HashMap::new(); | ||||
let config = Config::default(); | let config = Config::default(); | ||||
let context = RenderContext::new(&ZOLA_TERA, &config, "", &permalinks_ctx, InsertAnchor::None); | let context = RenderContext::new(&ZOLA_TERA, &config, "", &permalinks_ctx, InsertAnchor::None); | ||||
@@ -627,7 +627,7 @@ fn can_understand_footnote_in_header() { | |||||
} | } | ||||
#[test] | #[test] | ||||
fn can_make_valid_relative_link_in_header() { | |||||
fn can_make_valid_relative_link_in_heading() { | |||||
let mut permalinks = HashMap::new(); | let mut permalinks = HashMap::new(); | ||||
permalinks.insert("pages/about.md".to_string(), "https://vincent.is/about/".to_string()); | permalinks.insert("pages/about.md".to_string(), "https://vincent.is/about/".to_string()); | ||||
let tera_ctx = Tera::default(); | let tera_ctx = Tera::default(); | ||||
@@ -7,7 +7,7 @@ authors = ["Vincent Prouillet <prouillet.vincent@gmail.com>"] | |||||
tera = "1.0.0-beta.10" | tera = "1.0.0-beta.10" | ||||
base64 = "0.10" | base64 = "0.10" | ||||
lazy_static = "1" | lazy_static = "1" | ||||
pulldown-cmark = "0.5" | |||||
pulldown-cmark = "0.6" | |||||
toml = "0.5" | toml = "0.5" | ||||
csv = "1" | csv = "1" | ||||
image = "0.22" | image = "0.22" | ||||