From 9398ab789c401db60b81b62491b8cc478ec52156 Mon Sep 17 00:00:00 2001 From: Vincent Prouillet Date: Mon, 28 Jan 2019 00:34:18 +0100 Subject: [PATCH] Clone-less toc making --- components/rendering/src/markdown.rs | 10 +- components/rendering/src/table_of_contents.rs | 150 ++++++------------ 2 files changed, 54 insertions(+), 106 deletions(-) diff --git a/components/rendering/src/markdown.rs b/components/rendering/src/markdown.rs index 7e7611ff..3c6f20b2 100644 --- a/components/rendering/src/markdown.rs +++ b/components/rendering/src/markdown.rs @@ -12,7 +12,7 @@ use context::RenderContext; use errors::{Error, Result}; use front_matter::InsertAnchor; use link_checker::check_url; -use table_of_contents::{Header, make_table_of_contents, TempHeader}; +use table_of_contents::{Header, make_table_of_contents}; use utils::site::resolve_internal_link; use utils::vec::InsertMany; @@ -140,7 +140,7 @@ pub fn markdown_to_html(content: &str, context: &RenderContext) -> Result = None; let mut inserted_anchors: Vec = vec![]; - let mut headers: Vec = vec![]; + let mut headers: Vec
= vec![]; let mut opts = Options::empty(); let mut has_summary = false; @@ -253,8 +253,8 @@ pub fn markdown_to_html(content: &str, context: &RenderContext) -> Result Result, } impl Header { - pub fn from_temp_header(tmp: &TempHeader, children: Vec
) -> Header { + pub fn new(level: i32) -> Header { Header { - level: tmp.level, - id: tmp.id.clone(), - title: tmp.title.clone(), - permalink: tmp.permalink.clone(), - children, - } - } -} - -/// Populated while receiving events from the markdown parser -#[derive(Debug, PartialEq, Clone)] -pub struct TempHeader { - pub level: i32, - pub id: String, - pub permalink: String, - pub title: String, -} - -impl TempHeader { - pub fn new(level: i32) -> TempHeader { - TempHeader { level, id: String::new(), permalink: String::new(), title: String::new(), + children: Vec::new(), } } } -impl Default for TempHeader { +impl Default for Header { fn default() -> Self { - TempHeader::new(0) + Header::new(0) } } -/// Recursively finds children of a header -fn find_children( - parent_level: i32, - start_at: usize, - temp_headers: &[TempHeader], -) -> (usize, Vec
) { - let mut headers = vec![]; - - let mut start_at = start_at; - // If we have children, we will need to skip some headers since they are already inserted - let mut to_skip = 0; - - for h in &temp_headers[start_at..] { - // stop when we encounter a title at the same level or higher - // than the parent one. Here a lower integer is considered higher as we are talking about - // HTML headers: h1, h2, h3, h4, h5 and h6 - if h.level <= parent_level { - return (start_at, headers); - } - - // Do we need to skip some headers? - if to_skip > 0 { - to_skip -= 1; - continue; - } - - let (end, children) = find_children(h.level, start_at + 1, temp_headers); - headers.push(Header::from_temp_header(h, children)); - - // we didn't find any children - if end == start_at { - start_at += 1; - to_skip = 0; - } else { - // calculates how many we need to skip. Since the find_children start_at starts at 1, - // we need to remove 1 to ensure correctness - to_skip = end - start_at - 1; - start_at = end; - } - - // we don't want to index out of bounds - if start_at + 1 > temp_headers.len() { - return (start_at, headers); - } - } - - (start_at, headers) -} - /// Converts the flat temp headers into a nested set of headers /// representing the hierarchy -pub fn make_table_of_contents(temp_headers: &[TempHeader]) -> Vec
{ +pub fn make_table_of_contents(headers: Vec
) -> Vec
{ let mut toc = vec![]; - let mut start_idx = 0; - for (i, h) in temp_headers.iter().enumerate() { - if i < start_idx { + 'parent: for header in headers { + if toc.is_empty() { + toc.push(header); continue; } - let (end_idx, children) = find_children(h.level, start_idx + 1, temp_headers); - start_idx = end_idx; - toc.push(Header::from_temp_header(h, children)); + + // See if we have to insert as a child of a previous header + for h in toc.iter_mut().rev() { + // Look in its children first + for child in h.children.iter_mut().rev() { + if header.level > child.level { + child.children.push(header); + continue 'parent; + } + } + if header.level > h.level { + h.children.push(header); + continue 'parent; + } + } + + // Nop, just insert it + toc.push(header) } toc @@ -118,25 +65,25 @@ mod tests { #[test] fn can_make_basic_toc() { - let input = vec![TempHeader::new(1), TempHeader::new(1), TempHeader::new(1)]; - let toc = make_table_of_contents(&input); + let input = vec![Header::new(1), Header::new(1), Header::new(1)]; + let toc = make_table_of_contents(input); assert_eq!(toc.len(), 3); } #[test] fn can_make_more_complex_toc() { let input = vec![ - TempHeader::new(1), - TempHeader::new(2), - TempHeader::new(2), - TempHeader::new(3), - TempHeader::new(2), - TempHeader::new(1), - TempHeader::new(2), - TempHeader::new(3), - TempHeader::new(3), + 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), ]; - let toc = make_table_of_contents(&input); + let toc = make_table_of_contents(input); assert_eq!(toc.len(), 2); assert_eq!(toc[0].children.len(), 3); assert_eq!(toc[1].children.len(), 1); @@ -147,15 +94,16 @@ mod tests { #[test] fn can_make_messy_toc() { let input = vec![ - TempHeader::new(3), - TempHeader::new(2), - TempHeader::new(2), - TempHeader::new(3), - TempHeader::new(2), - TempHeader::new(1), - TempHeader::new(4), + Header::new(3), + Header::new(2), + Header::new(2), + Header::new(3), + Header::new(2), + Header::new(1), + Header::new(4), ]; - let toc = make_table_of_contents(&input); + let toc = make_table_of_contents(input); + println!("{:#?}", toc); assert_eq!(toc.len(), 5); assert_eq!(toc[2].children.len(), 1); assert_eq!(toc[4].children.len(), 1);