From f14dbcbdf45885821c01899a78df5f9aa9af115d Mon Sep 17 00:00:00 2001 From: Vincent Prouillet Date: Mon, 15 Oct 2018 22:28:25 +0200 Subject: [PATCH] section.subsections is now an array of paths Close #446 Close #260 Close #478 Close #284 Close #480 --- CHANGELOG.md | 4 ++ components/library/src/content/page.rs | 19 ++++++--- components/library/src/content/section.rs | 15 +++++-- components/library/src/library.rs | 42 ++++++++++++++----- components/site/src/lib.rs | 4 +- components/site/tests/site.rs | 12 ++++-- components/templates/src/global_fns.rs | 2 +- .../documentation/templates/pages-sections.md | 8 +++- docs/templates/documentation.html | 3 +- test_site/templates/section.html | 3 +- 10 files changed, 82 insertions(+), 30 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4d81e34f..c54b53d1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,8 @@ - Gutenberg has changed name to REPLACE_ME! - The `pagers` variable of Paginator objects has been removed +- `section.subsections` is now an array of paths to be used with the `get_section` +Tera function ### Others - Update dependencies, fixing a few bugs with templates @@ -25,6 +27,8 @@ - RSS feed now takes all available articles by default instead of limiting to 10000 - `templates` directory is now optional - Add Reason and F# syntax highlighting +- Add `parent_section` to pages and section pointing to the relative path of the parent +section if there is one to be used with the `get_section` Tera function ## 0.4.2 (2018-09-03) diff --git a/components/library/src/content/page.rs b/components/library/src/content/page.rs index 085c2fd4..a462d5b5 100644 --- a/components/library/src/content/page.rs +++ b/components/library/src/content/page.rs @@ -4,7 +4,7 @@ use std::path::{Path, PathBuf}; use tera::{Tera, Context as TeraContext, Value, Map}; use slug::slugify; -use slotmap::{Key, DenseSlotMap}; +use slotmap::{Key}; use errors::{Result, ResultExt}; use config::Config; @@ -23,6 +23,7 @@ pub struct SerializingPage<'a> { content: &'a str, permalink: &'a str, slug: &'a str, + parent_section: Option, title: &'a Option, description: &'a Option, date: &'a Option, @@ -47,7 +48,7 @@ pub struct SerializingPage<'a> { impl<'a> SerializingPage<'a> { /// Grabs all the data from a page, including sibling pages - pub fn from_page(page: &'a Page, pages: &'a DenseSlotMap) -> Self { + pub fn from_page(page: &'a Page, library: &'a Library) -> Self { let mut year = None; let mut month = None; let mut day = None; @@ -56,12 +57,15 @@ impl<'a> SerializingPage<'a> { month = Some(d.1); day = Some(d.2); } + let pages = library.pages(); let lighter = page.lighter.map(|k| Box::new(SerializingPage::from_page_basic(pages.get(k).unwrap()))); let heavier = page.heavier.map(|k| Box::new(SerializingPage::from_page_basic(pages.get(k).unwrap()))); let earlier = page.earlier.map(|k| Box::new(SerializingPage::from_page_basic(pages.get(k).unwrap()))); let later = page.later.map(|k| Box::new(SerializingPage::from_page_basic(pages.get(k).unwrap()))); + let parent_section = page.parent_section.map(|k| library.get_section_by_key(k).file.relative.clone()); SerializingPage { + parent_section, content: &page.content, permalink: &page.permalink, slug: &page.slug, @@ -100,6 +104,7 @@ impl<'a> SerializingPage<'a> { } SerializingPage { + parent_section: None, content: &page.content, permalink: &page.permalink, slug: &page.slug, @@ -133,6 +138,8 @@ pub struct Page { pub file: FileInfo, /// The front matter meta-data pub meta: PageFrontMatter, + /// The parent section if there is one + pub parent_section: Option, /// The actual content of the page, in markdown pub raw_content: String, /// All the non-md files we found next to the .md file @@ -177,6 +184,7 @@ impl Page { Page { file: FileInfo::new_page(file_path), meta, + parent_section: None, raw_content: "".to_string(), assets: vec![], content: "".to_string(), @@ -320,7 +328,7 @@ impl Page { context.insert("config", config); context.insert("current_url", &self.permalink); context.insert("current_path", &self.path); - context.insert("page", &self.to_serialized(library.pages())); + context.insert("page", &self.to_serialized(library)); render_template(&tpl_name, tera, &context, &config.theme) .chain_err(|| format!("Failed to render page '{}'", self.file.path.display())) @@ -335,8 +343,8 @@ impl Page { .collect() } - pub fn to_serialized<'a>(&'a self, pages: &'a DenseSlotMap) -> SerializingPage<'a> { - SerializingPage::from_page(self, pages) + pub fn to_serialized<'a>(&'a self, library: &'a Library) -> SerializingPage<'a> { + SerializingPage::from_page(self, library) } pub fn to_serialized_basic(&self) -> SerializingPage { @@ -349,6 +357,7 @@ impl Default for Page { Page { file: FileInfo::default(), meta: PageFrontMatter::default(), + parent_section: None, raw_content: "".to_string(), assets: vec![], content: "".to_string(), diff --git a/components/library/src/content/section.rs b/components/library/src/content/section.rs index 79104228..e8335a4e 100644 --- a/components/library/src/content/section.rs +++ b/components/library/src/content/section.rs @@ -21,6 +21,7 @@ use library::Library; pub struct SerializingSection<'a> { content: &'a str, permalink: &'a str, + parent_section: Option, title: &'a Option, description: &'a Option, extra: &'a HashMap, @@ -31,7 +32,7 @@ pub struct SerializingSection<'a> { toc: &'a [Header], assets: Vec, pages: Vec>, - subsections: Vec>, + subsections: Vec<&'a str>, } impl<'a> SerializingSection<'a> { @@ -40,14 +41,17 @@ impl<'a> SerializingSection<'a> { let mut subsections = Vec::with_capacity(section.subsections.len()); for k in §ion.pages { - pages.push(library.get_page_by_key(*k).to_serialized(library.pages())); + pages.push(library.get_page_by_key(*k).to_serialized(library)); } for k in §ion.subsections { - subsections.push(library.get_section_by_key(*k).to_serialized(library)); + subsections.push(library.get_section_path_by_key(*k)); } + let parent_section = section.parent_section.map(|k| library.get_section_by_key(k).file.relative.clone()); + SerializingSection { + parent_section, content: §ion.content, permalink: §ion.permalink, title: §ion.meta.title, @@ -67,6 +71,7 @@ impl<'a> SerializingSection<'a> { /// Same as from_section but doesn't fetch pages and sections pub fn from_section_basic(section: &'a Section) -> Self { SerializingSection { + parent_section: None, content: §ion.content, permalink: §ion.permalink, title: §ion.meta.title, @@ -106,6 +111,8 @@ pub struct Section { pub pages: Vec, /// All pages that cannot be sorted in this section pub ignored_pages: Vec, + /// The relative path of the parent section if there is one + pub parent_section: Option, /// All direct subsections pub subsections: Vec, /// Toc made from the headers of the markdown file @@ -124,6 +131,7 @@ impl Section { Section { file: FileInfo::new_section(file_path), meta, + parent_section: None, path: "".to_string(), components: vec![], permalink: "".to_string(), @@ -262,6 +270,7 @@ impl Default for Section { Section { file: FileInfo::default(), meta: SectionFrontMatter::default(), + parent_section: None, path: "".to_string(), components: vec![], permalink: "".to_string(), diff --git a/components/library/src/library.rs b/components/library/src/library.rs index 8c45f1d8..c10dd642 100644 --- a/components/library/src/library.rs +++ b/components/library/src/library.rs @@ -81,7 +81,7 @@ impl Library { /// 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) { - let mut grandparent_paths: HashMap> = HashMap::new(); + let mut grandparent_paths: HashMap> = HashMap::new(); for section in self.sections.values_mut() { if let Some(ref grand_parent) = section.file.grand_parent { @@ -99,6 +99,7 @@ impl Library { let parent_section_path = page.file.parent.join("_index.md"); if let Some(section_key) = self.paths_to_sections.get(&parent_section_path) { self.sections.get_mut(*section_key).unwrap().pages.push(key); + page.parent_section = Some(*section_key); } } @@ -109,14 +110,22 @@ impl Library { for (key, section) in &self.sections { sections_weight.insert(key, section.meta.weight); } - for section in self.sections.values_mut() { - if let Some(paths) = grandparent_paths.get(§ion.file.parent) { - section.subsections = paths - .iter() - .map(|p| sections[p]) - .collect::>(); - section.subsections - .sort_by(|a, b| sections_weight[a].cmp(§ions_weight[b])); + + for (grandparent, children) in &grandparent_paths { + let mut subsections = vec![]; + let grandparent_path = grandparent.join("_index.md"); + + if let Some(ref mut section) = self.get_section_mut(&grandparent_path) { + subsections = children.iter().map(|p| sections[p]).collect(); + subsections.sort_by(|a, b| sections_weight[a].cmp(§ions_weight[b])); + section.subsections = subsections.clone(); + } + + // Only there for subsections so we must have a parent section + for key in &subsections { + if let Some(ref mut subsection) = self.sections.get_mut(*key) { + subsection.parent_section = Some(sections[&grandparent_path]); + } } } } @@ -219,6 +228,11 @@ impl Library { None } + /// Only used in tests + pub fn get_section_key(&self, path: &PathBuf) -> Option<&Key> { + self.paths_to_sections.get(path) + } + pub fn get_section(&self, path: &PathBuf) -> Option<&Section> { self.sections.get(self.paths_to_sections.get(path).cloned().unwrap_or_default()) } @@ -231,6 +245,14 @@ impl Library { self.sections.get(key).unwrap() } + pub fn get_section_mut_by_key(&mut self, key: Key) -> &mut Section { + self.sections.get_mut(key).unwrap() + } + + pub fn get_section_path_by_key(&self, key: Key) -> &str { + &self.get_section_by_key(key).file.relative + } + pub fn get_page(&self, path: &PathBuf) -> Option<&Page> { self.pages.get(self.paths_to_pages.get(path).cloned().unwrap_or_default()) } @@ -241,7 +263,6 @@ impl Library { pub fn remove_section(&mut self, path: &PathBuf) -> Option
{ if let Some(k) = self.paths_to_sections.remove(path) { - // TODO: delete section from parent subsection if there is one self.sections.remove(k) } else { None @@ -250,7 +271,6 @@ impl Library { pub fn remove_page(&mut self, path: &PathBuf) -> Option { if let Some(k) = self.paths_to_pages.remove(path) { - // TODO: delete page from all parent sections self.pages.remove(k) } else { None diff --git a/components/site/src/lib.rs b/components/site/src/lib.rs index b2cc3066..e5357c97 100644 --- a/components/site/src/lib.rs +++ b/components/site/src/lib.rs @@ -240,10 +240,8 @@ impl Site { } self.register_early_global_fns(); - self.render_markdown()?; self.populate_sections(); -// self.library.cache_all_pages(); -// self.library.cache_all_sections(); + self.render_markdown()?; self.populate_taxonomies()?; self.register_tera_global_fns(); diff --git a/components/site/tests/site.rs b/components/site/tests/site.rs index e240d205..71c5942f 100644 --- a/components/site/tests/site.rs +++ b/components/site/tests/site.rs @@ -22,10 +22,6 @@ fn can_parse_site() { assert_eq!(site.library.pages().len(), 15); let posts_path = path.join("content").join("posts"); - // Make sure we remove all the pwd + content from the sections - let basic = site.library.get_page(&posts_path.join("simple.md")).unwrap(); - assert_eq!(basic.file.components, vec!["posts".to_string()]); - // Make sure the page with a url doesn't have any sections let url_post = site.library.get_page(&posts_path.join("fixed-url.md")).unwrap(); assert_eq!(url_post.path, "a-fixed-url/"); @@ -41,10 +37,17 @@ fn can_parse_site() { let index_section = site.library.get_section(&path.join("content").join("_index.md")).unwrap(); assert_eq!(index_section.subsections.len(), 3); assert_eq!(index_section.pages.len(), 1); + assert!(index_section.parent_section.is_none()); let posts_section = site.library.get_section(&posts_path.join("_index.md")).unwrap(); assert_eq!(posts_section.subsections.len(), 1); assert_eq!(posts_section.pages.len(), 7); + assert_eq!(posts_section.parent_section, Some(*site.library.get_section_key(&index_section.file.path).unwrap())); + + // Make sure we remove all the pwd + content from the sections + let basic = site.library.get_page(&posts_path.join("simple.md")).unwrap(); + assert_eq!(basic.file.components, vec!["posts".to_string()]); + assert_eq!(basic.parent_section, Some(*site.library.get_section_key(&posts_section.file.path).unwrap())); let tutorials_section = site.library.get_section(&posts_path.join("tutorials").join("_index.md")).unwrap(); assert_eq!(tutorials_section.subsections.len(), 2); @@ -57,6 +60,7 @@ fn can_parse_site() { let devops_section = site.library.get_section(&posts_path.join("tutorials").join("devops").join("_index.md")).unwrap(); assert_eq!(devops_section.subsections.len(), 0); assert_eq!(devops_section.pages.len(), 2); + assert_eq!(devops_section.parent_section, Some(*site.library.get_section_key(&tutorials_section.file.path).unwrap())); let prog_section = site.library.get_section(&posts_path.join("tutorials").join("programming").join("_index.md")).unwrap(); assert_eq!(prog_section.subsections.len(), 0); diff --git a/components/templates/src/global_fns.rs b/components/templates/src/global_fns.rs index 3fa58b51..5b80c683 100644 --- a/components/templates/src/global_fns.rs +++ b/components/templates/src/global_fns.rs @@ -56,7 +56,7 @@ pub fn make_get_page(library: &Library) -> GlobalFn { for page in library.pages_values() { pages.insert( page.file.relative.clone(), - to_value(library.get_page(&page.file.path).unwrap().to_serialized(library.pages())).unwrap(), + to_value(library.get_page(&page.file.path).unwrap().to_serialized(library)).unwrap(), ); } diff --git a/docs/content/documentation/templates/pages-sections.md b/docs/content/documentation/templates/pages-sections.md index 3c04e389..99f11e12 100644 --- a/docs/content/documentation/templates/pages-sections.md +++ b/docs/content/documentation/templates/pages-sections.md @@ -45,6 +45,8 @@ month: Number?; day: Number?; // Paths of colocated assets, relative to the content directory assets: Array; +// The relative path of the parent section if existing, for use with the `get_section` Tera function +parent_section: String?; ``` ## Section variables @@ -70,7 +72,9 @@ extra: HashMap; // Pages directly in this section, sorted if asked pages: Array; // Direct subsections to this section, sorted by subsections weight -subsections: Array
; +// This only contains the path to use in the `get_section` Tera function to get +// the actual section object if you need it +subsections: Array; // Unicode word count word_count: Number; // Based on https://help.medium.com/hc/en-us/articles/214991667-Read-time @@ -79,6 +83,8 @@ reading_time: Number; toc: Array
; // Paths of colocated assets, relative to the content directory assets: Array; +// The relative path of the parent section if existing, for use with the `get_section` Tera function +parent_section: String?; ``` ## Table of contents diff --git a/docs/templates/documentation.html b/docs/templates/documentation.html index 86c25c73..cfeb2b71 100644 --- a/docs/templates/documentation.html +++ b/docs/templates/documentation.html @@ -8,7 +8,8 @@