Remove `order` and add `heavier`/`later`index-subcmd
@@ -11,7 +11,7 @@ use std::collections::HashMap; | |||||
use config::Config; | use config::Config; | ||||
use tera::Tera; | use tera::Tera; | ||||
use front_matter::{SortBy, InsertAnchor}; | use front_matter::{SortBy, InsertAnchor}; | ||||
use content::{Page, sort_pages, populate_previous_and_next_pages}; | |||||
use content::{Page, sort_pages, populate_siblings}; | |||||
fn create_pages(number: usize, sort_by: SortBy) -> Vec<Page> { | fn create_pages(number: usize, sort_by: SortBy) -> Vec<Page> { | ||||
@@ -128,17 +128,17 @@ fn bench_sorting_order(b: &mut test::Bencher) { | |||||
} | } | ||||
#[bench] | #[bench] | ||||
fn bench_populate_previous_and_next_pages(b: &mut test::Bencher) { | |||||
fn bench_populate_siblings(b: &mut test::Bencher) { | |||||
let pages = create_pages(250, SortBy::Order); | let pages = create_pages(250, SortBy::Order); | ||||
let (sorted_pages, _) = sort_pages(pages, SortBy::Order); | let (sorted_pages, _) = sort_pages(pages, SortBy::Order); | ||||
b.iter(|| populate_previous_and_next_pages(&sorted_pages.clone())); | |||||
b.iter(|| populate_siblings(&sorted_pages.clone())); | |||||
} | } | ||||
#[bench] | #[bench] | ||||
fn bench_page_render_html(b: &mut test::Bencher) { | fn bench_page_render_html(b: &mut test::Bencher) { | ||||
let pages = create_pages(10, SortBy::Order); | let pages = create_pages(10, SortBy::Order); | ||||
let (mut sorted_pages, _) = sort_pages(pages, SortBy::Order); | let (mut sorted_pages, _) = sort_pages(pages, SortBy::Order); | ||||
sorted_pages = populate_previous_and_next_pages(&sorted_pages); | |||||
sorted_pages = populate_siblings(&sorted_pages); | |||||
let config = Config::default(); | let config = Config::default(); | ||||
let mut tera = Tera::default(); | let mut tera = Tera::default(); | ||||
@@ -26,4 +26,4 @@ mod sorting; | |||||
pub use file_info::FileInfo; | pub use file_info::FileInfo; | ||||
pub use page::Page; | pub use page::Page; | ||||
pub use section::Section; | pub use section::Section; | ||||
pub use sorting::{sort_pages, populate_previous_and_next_pages}; | |||||
pub use sorting::{sort_pages, populate_siblings}; |
@@ -44,10 +44,14 @@ pub struct Page { | |||||
/// When <!-- more --> is found in the text, will take the content up to that part | /// When <!-- more --> is found in the text, will take the content up to that part | ||||
/// as summary | /// as summary | ||||
pub summary: Option<String>, | pub summary: Option<String>, | ||||
/// The previous page, by whatever sorting is used for the index/section | |||||
pub previous: Option<Box<Page>>, | |||||
/// The next page, by whatever sorting is used for the index/section | |||||
pub next: Option<Box<Page>>, | |||||
/// The earlier page, for pages sorted by date | |||||
pub earlier: Option<Box<Page>>, | |||||
/// The later page, for pages sorted by date | |||||
pub later: Option<Box<Page>>, | |||||
/// The lighter page, for pages sorted by weight | |||||
pub lighter: Option<Box<Page>>, | |||||
/// The heavier page, for pages sorted by weight | |||||
pub heavier: Option<Box<Page>>, | |||||
/// Toc made from the headers of the markdown file | /// Toc made from the headers of the markdown file | ||||
pub toc: Vec<Header>, | pub toc: Vec<Header>, | ||||
} | } | ||||
@@ -68,8 +72,10 @@ impl Page { | |||||
components: vec![], | components: vec![], | ||||
permalink: "".to_string(), | permalink: "".to_string(), | ||||
summary: None, | summary: None, | ||||
previous: None, | |||||
next: None, | |||||
earlier: None, | |||||
later: None, | |||||
lighter: None, | |||||
heavier: None, | |||||
toc: vec![], | toc: vec![], | ||||
} | } | ||||
} | } | ||||
@@ -229,8 +235,10 @@ impl Default for Page { | |||||
components: vec![], | components: vec![], | ||||
permalink: "".to_string(), | permalink: "".to_string(), | ||||
summary: None, | summary: None, | ||||
previous: None, | |||||
next: None, | |||||
earlier: None, | |||||
later: None, | |||||
lighter: None, | |||||
heavier: None, | |||||
toc: vec![], | toc: vec![], | ||||
} | } | ||||
} | } | ||||
@@ -263,8 +271,10 @@ impl ser::Serialize for Page { | |||||
let (word_count, reading_time) = get_reading_analytics(&self.raw_content); | let (word_count, reading_time) = get_reading_analytics(&self.raw_content); | ||||
state.serialize_field("word_count", &word_count)?; | state.serialize_field("word_count", &word_count)?; | ||||
state.serialize_field("reading_time", &reading_time)?; | state.serialize_field("reading_time", &reading_time)?; | ||||
state.serialize_field("previous", &self.previous)?; | |||||
state.serialize_field("next", &self.next)?; | |||||
state.serialize_field("earlier", &self.earlier)?; | |||||
state.serialize_field("later", &self.later)?; | |||||
state.serialize_field("lighter", &self.lighter)?; | |||||
state.serialize_field("heavier", &self.heavier)?; | |||||
state.serialize_field("toc", &self.toc)?; | state.serialize_field("toc", &self.toc)?; | ||||
state.serialize_field("draft", &self.is_draft())?; | state.serialize_field("draft", &self.is_draft())?; | ||||
let assets = self.serialize_assets(); | let assets = self.serialize_assets(); | ||||
@@ -7,7 +7,7 @@ use front_matter::SortBy; | |||||
/// Sort pages by the given criteria | /// Sort pages by the given criteria | ||||
/// | /// | ||||
/// Any pages that doesn't have a the required field when the sorting method is other than none | |||||
/// Any pages that doesn't have a required field when the sorting method is other than none | |||||
/// will be ignored. | /// will be ignored. | ||||
pub fn sort_pages(pages: Vec<Page>, sort_by: SortBy) -> (Vec<Page>, Vec<Page>) { | pub fn sort_pages(pages: Vec<Page>, sort_by: SortBy) -> (Vec<Page>, Vec<Page>) { | ||||
if sort_by == SortBy::None { | if sort_by == SortBy::None { | ||||
@@ -19,7 +19,6 @@ pub fn sort_pages(pages: Vec<Page>, sort_by: SortBy) -> (Vec<Page>, Vec<Page>) { | |||||
.partition(|page| { | .partition(|page| { | ||||
match sort_by { | match sort_by { | ||||
SortBy::Date => page.meta.date.is_some(), | SortBy::Date => page.meta.date.is_some(), | ||||
SortBy::Order => page.meta.order.is_some(), | |||||
SortBy::Weight => page.meta.weight.is_some(), | SortBy::Weight => page.meta.weight.is_some(), | ||||
_ => unreachable!() | _ => unreachable!() | ||||
} | } | ||||
@@ -36,16 +35,6 @@ pub fn sort_pages(pages: Vec<Page>, sort_by: SortBy) -> (Vec<Page>, Vec<Page>) { | |||||
} | } | ||||
}) | }) | ||||
}, | }, | ||||
SortBy::Order => { | |||||
can_be_sorted.par_sort_unstable_by(|a, b| { | |||||
let ord = b.meta.order().cmp(&a.meta.order()); | |||||
if ord == Ordering::Equal { | |||||
a.permalink.cmp(&b.permalink) | |||||
} else { | |||||
ord | |||||
} | |||||
}) | |||||
}, | |||||
SortBy::Weight => { | SortBy::Weight => { | ||||
can_be_sorted.par_sort_unstable_by(|a, b| { | can_be_sorted.par_sort_unstable_by(|a, b| { | ||||
let ord = a.meta.weight().cmp(&b.meta.weight()); | let ord = a.meta.weight().cmp(&b.meta.weight()); | ||||
@@ -64,7 +53,7 @@ pub fn sort_pages(pages: Vec<Page>, sort_by: SortBy) -> (Vec<Page>, Vec<Page>) { | |||||
/// Horribly inefficient way to set previous and next on each pages that skips drafts | /// Horribly inefficient way to set previous and next on each pages that skips drafts | ||||
/// So many clones | /// So many clones | ||||
pub fn populate_previous_and_next_pages(input: &[Page]) -> Vec<Page> { | |||||
pub fn populate_siblings(input: &[Page], sort_by: SortBy) -> Vec<Page> { | |||||
let mut res = Vec::with_capacity(input.len()); | let mut res = Vec::with_capacity(input.len()); | ||||
// The input is already sorted | // The input is already sorted | ||||
@@ -91,9 +80,20 @@ pub fn populate_previous_and_next_pages(input: &[Page]) -> Vec<Page> { | |||||
// Remove prev/next otherwise we serialise the whole thing... | // Remove prev/next otherwise we serialise the whole thing... | ||||
let mut next_page = input[j].clone(); | let mut next_page = input[j].clone(); | ||||
next_page.previous = None; | |||||
next_page.next = None; | |||||
new_page.next = Some(Box::new(next_page)); | |||||
match sort_by { | |||||
SortBy::Weight => { | |||||
next_page.lighter = None; | |||||
next_page.heavier = None; | |||||
new_page.lighter = Some(Box::new(next_page)); | |||||
}, | |||||
SortBy::Date => { | |||||
next_page.earlier = None; | |||||
next_page.later = None; | |||||
new_page.later = Some(Box::new(next_page)); | |||||
}, | |||||
SortBy::None => {} | |||||
} | |||||
break; | break; | ||||
} | } | ||||
} | } | ||||
@@ -113,9 +113,20 @@ pub fn populate_previous_and_next_pages(input: &[Page]) -> Vec<Page> { | |||||
// Remove prev/next otherwise we serialise the whole thing... | // Remove prev/next otherwise we serialise the whole thing... | ||||
let mut previous_page = input[j].clone(); | let mut previous_page = input[j].clone(); | ||||
previous_page.previous = None; | |||||
previous_page.next = None; | |||||
new_page.previous = Some(Box::new(previous_page)); | |||||
match sort_by { | |||||
SortBy::Weight => { | |||||
previous_page.lighter = None; | |||||
previous_page.heavier = None; | |||||
new_page.heavier = Some(Box::new(previous_page)); | |||||
}, | |||||
SortBy::Date => { | |||||
previous_page.earlier = None; | |||||
previous_page.later = None; | |||||
new_page.earlier = Some(Box::new(previous_page)); | |||||
}, | |||||
SortBy::None => { | |||||
} | |||||
} | |||||
break; | break; | ||||
} | } | ||||
} | } | ||||
@@ -129,7 +140,7 @@ pub fn populate_previous_and_next_pages(input: &[Page]) -> Vec<Page> { | |||||
mod tests { | mod tests { | ||||
use front_matter::{PageFrontMatter, SortBy}; | use front_matter::{PageFrontMatter, SortBy}; | ||||
use page::Page; | use page::Page; | ||||
use super::{sort_pages, populate_previous_and_next_pages}; | |||||
use super::{sort_pages, populate_siblings}; | |||||
fn create_page_with_date(date: &str) -> Page { | fn create_page_with_date(date: &str) -> Page { | ||||
let mut front_matter = PageFrontMatter::default(); | let mut front_matter = PageFrontMatter::default(); | ||||
@@ -137,22 +148,6 @@ mod tests { | |||||
Page::new("content/hello.md", front_matter) | Page::new("content/hello.md", front_matter) | ||||
} | } | ||||
fn create_page_with_order(order: usize, filename: &str) -> Page { | |||||
let mut front_matter = PageFrontMatter::default(); | |||||
front_matter.order = Some(order); | |||||
let mut p = Page::new("content/".to_string() + filename, front_matter); | |||||
// Faking a permalink to test sorting with equal order | |||||
p.permalink = filename.to_string(); | |||||
p | |||||
} | |||||
fn create_draft_page_with_order(order: usize) -> Page { | |||||
let mut front_matter = PageFrontMatter::default(); | |||||
front_matter.order = Some(order); | |||||
front_matter.draft = true; | |||||
Page::new("content/hello.md", front_matter) | |||||
} | |||||
fn create_page_with_weight(weight: usize) -> Page { | fn create_page_with_weight(weight: usize) -> Page { | ||||
let mut front_matter = PageFrontMatter::default(); | let mut front_matter = PageFrontMatter::default(); | ||||
front_matter.weight = Some(weight); | front_matter.weight = Some(weight); | ||||
@@ -173,37 +168,6 @@ mod tests { | |||||
assert_eq!(pages[2].clone().meta.date.unwrap().to_string(), "2017-01-01"); | assert_eq!(pages[2].clone().meta.date.unwrap().to_string(), "2017-01-01"); | ||||
} | } | ||||
#[test] | |||||
fn can_sort_by_order() { | |||||
let input = vec![ | |||||
create_page_with_order(2, "hello.md"), | |||||
create_page_with_order(3, "hello2.md"), | |||||
create_page_with_order(1, "hello3.md"), | |||||
]; | |||||
let (pages, _) = sort_pages(input, SortBy::Order); | |||||
// Should be sorted by order | |||||
assert_eq!(pages[0].clone().meta.order.unwrap(), 3); | |||||
assert_eq!(pages[1].clone().meta.order.unwrap(), 2); | |||||
assert_eq!(pages[2].clone().meta.order.unwrap(), 1); | |||||
} | |||||
#[test] | |||||
fn can_sort_by_order_uses_permalink_to_break_ties() { | |||||
let input = vec![ | |||||
create_page_with_order(3, "b.md"), | |||||
create_page_with_order(3, "a.md"), | |||||
create_page_with_order(3, "c.md"), | |||||
]; | |||||
let (pages, _) = sort_pages(input, SortBy::Order); | |||||
// Should be sorted by order | |||||
assert_eq!(pages[0].clone().meta.order.unwrap(), 3); | |||||
assert_eq!(pages[0].clone().permalink, "a.md"); | |||||
assert_eq!(pages[1].clone().meta.order.unwrap(), 3); | |||||
assert_eq!(pages[1].clone().permalink, "b.md"); | |||||
assert_eq!(pages[2].clone().meta.order.unwrap(), 3); | |||||
assert_eq!(pages[2].clone().permalink, "c.md"); | |||||
} | |||||
#[test] | #[test] | ||||
fn can_sort_by_weight() { | fn can_sort_by_weight() { | ||||
let input = vec![ | let input = vec![ | ||||
@@ -221,80 +185,48 @@ mod tests { | |||||
#[test] | #[test] | ||||
fn can_sort_by_none() { | fn can_sort_by_none() { | ||||
let input = vec![ | let input = vec![ | ||||
create_page_with_order(2, "a.md"), | |||||
create_page_with_order(3, "a.md"), | |||||
create_page_with_order(1, "a.md"), | |||||
create_page_with_weight(2), | |||||
create_page_with_weight(3), | |||||
create_page_with_weight(1), | |||||
]; | ]; | ||||
let (pages, _) = sort_pages(input, SortBy::None); | let (pages, _) = sort_pages(input, SortBy::None); | ||||
// Should be sorted by date | |||||
assert_eq!(pages[0].clone().meta.order.unwrap(), 2); | |||||
assert_eq!(pages[1].clone().meta.order.unwrap(), 3); | |||||
assert_eq!(pages[2].clone().meta.order.unwrap(), 1); | |||||
assert_eq!(pages[0].clone().meta.weight.unwrap(), 2); | |||||
assert_eq!(pages[1].clone().meta.weight.unwrap(), 3); | |||||
assert_eq!(pages[2].clone().meta.weight.unwrap(), 1); | |||||
} | } | ||||
#[test] | #[test] | ||||
fn ignore_page_with_missing_field() { | fn ignore_page_with_missing_field() { | ||||
let input = vec![ | let input = vec![ | ||||
create_page_with_order(2, "a.md"), | |||||
create_page_with_order(3, "a.md"), | |||||
create_page_with_weight(2), | |||||
create_page_with_weight(3), | |||||
create_page_with_date("2019-01-01"), | create_page_with_date("2019-01-01"), | ||||
]; | ]; | ||||
let (pages, unsorted) = sort_pages(input, SortBy::Order); | |||||
let (pages, unsorted) = sort_pages(input, SortBy::Weight); | |||||
assert_eq!(pages.len(), 2); | assert_eq!(pages.len(), 2); | ||||
assert_eq!(unsorted.len(), 1); | assert_eq!(unsorted.len(), 1); | ||||
} | } | ||||
#[test] | #[test] | ||||
fn can_populate_previous_and_next_pages() { | |||||
fn can_populate_siblings() { | |||||
let input = vec![ | let input = vec![ | ||||
create_page_with_order(1, "a.md"), | |||||
create_page_with_order(2, "b.md"), | |||||
create_page_with_order(3, "a.md"), | |||||
]; | |||||
let pages = populate_previous_and_next_pages(&input); | |||||
assert!(pages[0].clone().next.is_none()); | |||||
assert!(pages[0].clone().previous.is_some()); | |||||
assert_eq!(pages[0].clone().previous.unwrap().meta.order.unwrap(), 2); | |||||
assert!(pages[1].clone().next.is_some()); | |||||
assert!(pages[1].clone().previous.is_some()); | |||||
assert_eq!(pages[1].clone().previous.unwrap().meta.order.unwrap(), 3); | |||||
assert_eq!(pages[1].clone().next.unwrap().meta.order.unwrap(), 1); | |||||
assert!(pages[2].clone().next.is_some()); | |||||
assert!(pages[2].clone().previous.is_none()); | |||||
assert_eq!(pages[2].clone().next.unwrap().meta.order.unwrap(), 2); | |||||
} | |||||
#[test] | |||||
fn can_populate_previous_and_next_pages_skip_drafts() { | |||||
let input = vec![ | |||||
create_draft_page_with_order(0), | |||||
create_page_with_order(1, "a.md"), | |||||
create_page_with_order(2, "b.md"), | |||||
create_page_with_order(3, "c.md"), | |||||
create_draft_page_with_order(4), | |||||
create_page_with_weight(1), | |||||
create_page_with_weight(2), | |||||
create_page_with_weight(3), | |||||
]; | ]; | ||||
let pages = populate_previous_and_next_pages(&input); | |||||
assert!(pages[0].clone().next.is_none()); | |||||
assert!(pages[0].clone().previous.is_none()); | |||||
assert!(pages[1].clone().next.is_none()); | |||||
assert!(pages[1].clone().previous.is_some()); | |||||
assert_eq!(pages[1].clone().previous.unwrap().meta.order.unwrap(), 2); | |||||
let pages = populate_siblings(&input, SortBy::Weight); | |||||
assert!(pages[2].clone().next.is_some()); | |||||
assert!(pages[2].clone().previous.is_some()); | |||||
assert_eq!(pages[2].clone().previous.unwrap().meta.order.unwrap(), 3); | |||||
assert_eq!(pages[2].clone().next.unwrap().meta.order.unwrap(), 1); | |||||
assert!(pages[0].clone().lighter.is_none()); | |||||
assert!(pages[0].clone().heavier.is_some()); | |||||
assert_eq!(pages[0].clone().heavier.unwrap().meta.weight.unwrap(), 2); | |||||
assert!(pages[3].clone().next.is_some()); | |||||
assert!(pages[3].clone().previous.is_none()); | |||||
assert_eq!(pages[3].clone().next.unwrap().meta.order.unwrap(), 2); | |||||
assert!(pages[1].clone().heavier.is_some()); | |||||
assert!(pages[1].clone().lighter.is_some()); | |||||
assert_eq!(pages[1].clone().lighter.unwrap().meta.weight.unwrap(), 1); | |||||
assert_eq!(pages[1].clone().heavier.unwrap().meta.weight.unwrap(), 3); | |||||
assert!(pages[4].clone().next.is_none()); | |||||
assert!(pages[4].clone().previous.is_none()); | |||||
assert!(pages[2].clone().lighter.is_some()); | |||||
assert!(pages[2].clone().heavier.is_none()); | |||||
assert_eq!(pages[2].clone().lighter.unwrap().meta.weight.unwrap(), 2); | |||||
} | } | ||||
} | } |
@@ -30,8 +30,6 @@ lazy_static! { | |||||
pub enum SortBy { | pub enum SortBy { | ||||
/// Most recent to oldest | /// Most recent to oldest | ||||
Date, | Date, | ||||
/// Lower order comes last | |||||
Order, | |||||
/// Lower weight comes first | /// Lower weight comes first | ||||
Weight, | Weight, | ||||
/// No sorting | /// No sorting | ||||
@@ -79,7 +79,7 @@ fn can_rebuild_after_simple_change_to_page_content() { | |||||
let file_path = edit_file!(site_path, "content/rebuild/first.md", br#" | let file_path = edit_file!(site_path, "content/rebuild/first.md", br#" | ||||
+++ | +++ | ||||
title = "first" | title = "first" | ||||
order = 1 | |||||
weight = 1 | |||||
date = 2017-01-01 | date = 2017-01-01 | ||||
+++ | +++ | ||||
@@ -97,7 +97,7 @@ fn can_rebuild_after_title_change_page_global_func_usage() { | |||||
let file_path = edit_file!(site_path, "content/rebuild/first.md", br#" | let file_path = edit_file!(site_path, "content/rebuild/first.md", br#" | ||||
+++ | +++ | ||||
title = "Premier" | title = "Premier" | ||||
order = 10 | |||||
weight = 10 | |||||
date = 2017-01-01 | date = 2017-01-01 | ||||
+++ | +++ | ||||
@@ -115,12 +115,12 @@ fn can_rebuild_after_sort_change_in_section() { | |||||
let file_path = edit_file!(site_path, "content/rebuild/_index.md", br#" | let file_path = edit_file!(site_path, "content/rebuild/_index.md", br#" | ||||
+++ | +++ | ||||
paginate_by = 1 | paginate_by = 1 | ||||
sort_by = "order" | |||||
sort_by = "weight" | |||||
template = "rebuild.html" | template = "rebuild.html" | ||||
+++ | +++ | ||||
"#); | "#); | ||||
let res = after_content_change(&mut site, &file_path); | let res = after_content_change(&mut site, &file_path); | ||||
assert!(res.is_ok()); | assert!(res.is_ok()); | ||||
assert!(file_contains!(site_path, "public/rebuild/index.html", "<h1>second</h1><h1>first</h1>")); | |||||
assert!(file_contains!(site_path, "public/rebuild/index.html", "<h1>first</h1><h1>second</h1>")); | |||||
} | } |
@@ -36,7 +36,7 @@ use config::{Config, get_config}; | |||||
use utils::fs::{create_file, copy_directory, create_directory, ensure_directory_exists}; | use utils::fs::{create_file, copy_directory, create_directory, ensure_directory_exists}; | ||||
use utils::templates::{render_template, rewrite_theme_paths}; | use utils::templates::{render_template, rewrite_theme_paths}; | ||||
use utils::net::get_available_port; | use utils::net::get_available_port; | ||||
use content::{Page, Section, populate_previous_and_next_pages, sort_pages}; | |||||
use content::{Page, Section, populate_siblings, sort_pages}; | |||||
use templates::{GUTENBERG_TERA, global_fns, render_redirect_template}; | use templates::{GUTENBERG_TERA, global_fns, render_redirect_template}; | ||||
use front_matter::{SortBy, InsertAnchor}; | use front_matter::{SortBy, InsertAnchor}; | ||||
use taxonomies::{Taxonomy, find_taxonomies}; | use taxonomies::{Taxonomy, find_taxonomies}; | ||||
@@ -401,7 +401,7 @@ impl Site { | |||||
} | } | ||||
let pages = mem::replace(&mut section.pages, vec![]); | let pages = mem::replace(&mut section.pages, vec![]); | ||||
let (sorted_pages, cannot_be_sorted_pages) = sort_pages(pages, section.meta.sort_by); | let (sorted_pages, cannot_be_sorted_pages) = sort_pages(pages, section.meta.sort_by); | ||||
section.pages = populate_previous_and_next_pages(&sorted_pages); | |||||
section.pages = populate_siblings(&sorted_pages, section.meta.sort_by); | |||||
section.ignored_pages = cannot_be_sorted_pages; | section.ignored_pages = cannot_be_sorted_pages; | ||||
} | } | ||||
} | } | ||||
@@ -6,6 +6,22 @@ weight = 30 | |||||
A page is any file ending with `.md` in the `content` directory, except files | A page is any file ending with `.md` in the `content` directory, except files | ||||
named `_index.md`. | named `_index.md`. | ||||
If a file ending with `.md` is named `index.md`, then it will generate a page | |||||
with the name of the containing folder (for example, `/content/about/index.md` would | |||||
create a page at `[base_url]/about`). (Note the lack of an underscore; if the file | |||||
were named `_index.md`, then it would create a **section** at `[base_url]/about`, as | |||||
discussed in the prior part of this documentation. But naming the file `index.md` will | |||||
create a **page** at `[base_url]/about`). | |||||
If the file is given any name *other* than `index.md` or `_index.md`, then it will | |||||
create a page with that name (without the `.md`). So naming a file in the root of your | |||||
content directory `about.md` would also create a page at `[base_url]/about`. | |||||
As you can see, creating an `about.md` file is exactly equivalent to creating an | |||||
`about/index.md` file. The only difference between the two methods is that creating | |||||
the `about` folder allows you to use asset colocation, as discussed in the | |||||
[Overview](./documentation/content/overview.md) section of this documentation. | |||||
## Front-matter | ## Front-matter | ||||
The front-matter is a set of metadata embedded in a file. In Gutenberg, | The front-matter is a set of metadata embedded in a file. In Gutenberg, | ||||
@@ -13,7 +29,8 @@ it is at the beginning of the file, surrounded by `+++` and uses TOML. | |||||
While none of the front-matter variables are mandatory, the opening and closing `+++` are required. | While none of the front-matter variables are mandatory, the opening and closing `+++` are required. | ||||
Here is an example page with all the variables available: | |||||
Here is an example page with all the variables available. The values provided below are the default | |||||
values. | |||||
```md | ```md | ||||
+++ | +++ | ||||
@@ -22,8 +39,15 @@ description = "" | |||||
# The date of the post. | # The date of the post. | ||||
# 2 formats are allowed: YYYY-MM-DD (2012-10-02) and RFC3339 (2002-10-02T15:00:00Z) | # 2 formats are allowed: YYYY-MM-DD (2012-10-02) and RFC3339 (2002-10-02T15:00:00Z) | ||||
# Do not wrap dates in quotes, the line below only indicates that there is no default date | |||||
date = | |||||
# Do not wrap dates in quotes, the line below only indicates that there is no default date. | |||||
# If the section variable `sort_by` is set to `date`, then any page that lacks a `date` | |||||
# will not be rendered. | |||||
date = | |||||
# The weight as defined in the Section page | |||||
# If the section variable `sort_by` is set to `weight`, then any page that lacks a `weight` | |||||
# will not be rendered. | |||||
weight = 0 | |||||
# A draft page will not be present in prev/next pagination | # A draft page will not be present in prev/next pagination | ||||
draft = false | draft = false | ||||
@@ -38,28 +62,18 @@ slug = "" | |||||
# It should not start with a `/` and the slash will be removed if it does | # It should not start with a `/` and the slash will be removed if it does | ||||
path = "" | path = "" | ||||
# A dict of taxonomies: the key is the name of the taxonomy which must match | |||||
# one of the taxonomy defined in `config.toml` and the value is a list of | |||||
# strings | |||||
[taxonomies] | |||||
# An array of strings allowing you to group pages with them | |||||
tags = [] | |||||
# The order as defined in the Section page | |||||
order = 0 | |||||
# The weight as defined in the Section page | |||||
weight = 0 | |||||
# An overarching category name for that page, allowing you to group pages with it | |||||
category = "" | |||||
# Use aliases if you are moving content but want to redirect previous URLs to the | |||||
# current one. Each element in the array of aliases may take one of two forms: | |||||
# * "some/alias/path", which will generate "some/alias/path/index.html" | |||||
# * "some/alias/path.html", which will generate "some/alias/path.html" | |||||
# | |||||
# The former is useful if your previous site had the form "example.com/some/alias/path", | |||||
# the latter is useful if your previous site had the form "example.com/some/alias/path.html" | |||||
# Use aliases if you are moving content but want to redirect previous URLs to the | |||||
# current one. This takes an array of path, not URLs. | |||||
aliases = [] | aliases = [] | ||||
# Whether the page should be in the search index. This is only used if | # Whether the page should be in the search index. This is only used if | ||||
# `build_search_index` is set to true in the config and the parent section | |||||
# `build_search_index` is set to true in the config and the parent section | |||||
# hasn't set `in_search_index` to false in its front-matter | # hasn't set `in_search_index` to false in its front-matter | ||||
in_search_index = true | in_search_index = true | ||||
@@ -75,7 +89,7 @@ Some content | |||||
## Summary | ## Summary | ||||
You can ask Gutenberg to create a summary if you only want to show the first | |||||
You can ask Gutenberg to create a summary if you only want to show the first | |||||
paragraph of each page in a list for example. | paragraph of each page in a list for example. | ||||
To do so, add <code><!-- more --></code> in your content at the point | To do so, add <code><!-- more --></code> in your content at the point | ||||
@@ -83,6 +97,6 @@ where you want the summary to end and the content up to that point will be also | |||||
available separately in the | available separately in the | ||||
[template](./documentation/templates/pages-sections.md#page-variables). | [template](./documentation/templates/pages-sections.md#page-variables). | ||||
An anchor link to this position named `continue-reading` is created so you can link | |||||
An anchor link to this position named `continue-reading` is created so you can link | |||||
directly to it if needed for example: | directly to it if needed for example: | ||||
`<a href="{{ page.permalink }}#continue-reading">Continue Reading</a>` | `<a href="{{ page.permalink }}#continue-reading">Continue Reading</a>` |
@@ -3,22 +3,32 @@ title = "Section" | |||||
weight = 20 | weight = 20 | ||||
+++ | +++ | ||||
A section is automatically created when a folder is found in the `content` section, unless it only | |||||
contains a `index.md` file and is actually a page with assets. | |||||
A section is created whenever a folder (or subfolder) in the `content` section contains an | |||||
`_index.md` file. If a folder does not contain an `_index.md` file, no section will be | |||||
created, but markdown files within that folder will still create pages (known as orphan pages). | |||||
You can add `_index.md` file to a folder to augment a section and give it some metadata and/or content. | |||||
The index page is actually a section created automatically like any other: you can add metadata | |||||
and content by adding `_index.md` at the root of the `content` folder. | |||||
The index page (i.e., the page displayed when a user browses to your `base_url`) is a section, | |||||
which is created whether or not you add an `_index.md` file at the root of your `content` folder. | |||||
If you do not create an `_index.md` file in your content directory, this main content section will | |||||
not have any content or metadata. If you would like to add content or metadata, you can add an | |||||
`_index.md` file at the root of the `content` folder and edit it just as you would edit any other | |||||
`_index.md` file; your `index.html` template will then have access to that content and metadata. | |||||
## Front-matter | ## Front-matter | ||||
The `_index.md` file within a folder defines the content and metadata for that section. To set | |||||
the metadata, add front matter to the file. | |||||
The front-matter is a set of metadata embedded in a file. In Gutenberg, | The front-matter is a set of metadata embedded in a file. In Gutenberg, | ||||
it is at the beginning of the file, surrounded by `+++` and uses TOML. | it is at the beginning of the file, surrounded by `+++` and uses TOML. | ||||
After the closing `+++`, you can add content that will be parsed as markdown and will be available | |||||
to your templates through the `section.content` variable. | |||||
While none of the front-matter variables are mandatory, the opening and closing `+++` are required. | While none of the front-matter variables are mandatory, the opening and closing `+++` are required. | ||||
Here is an example `_index.md` with all the variables available: | |||||
Here is an example `_index.md` with all the variables available. The values provided below are the | |||||
default values. | |||||
```md | ```md | ||||
@@ -27,7 +37,7 @@ title = "" | |||||
description = "" | description = "" | ||||
# Whether to sort by "date", "order", "weight" or "none". More on that below | |||||
# Whether to sort pages by "date", "weight", or "none". More on that below | |||||
sort_by = "none" | sort_by = "none" | ||||
# Used by the parent section to order its subsections. | # Used by the parent section to order its subsections. | ||||
@@ -83,22 +93,66 @@ You can also change the pagination path (the word displayed while paginated in t | |||||
by setting the `paginate_path` variable, which defaults to `page`. | by setting the `paginate_path` variable, which defaults to `page`. | ||||
## Sorting | ## Sorting | ||||
Sections' pages can be sorted three different ways, not counting the unsorted default and | |||||
is enabled by setting the `sort_by` front-matter variable. | |||||
It is very common for Gutenberg templates to iterate over pages or sections | |||||
to display all pages/sections a given directory. Consider a very simple | |||||
example: a `blog` directory with three files: `blog/Post_1.md`, | |||||
`blog/Post_2.md`, and `blog/Post_3.md`. To iterate over these posts and | |||||
create a list of links to the posts, a simple template might look like this: | |||||
```j2 | |||||
{% for post in section.pages %} | |||||
<h1><a href="{{ post.permalink }}">{{ post.title }}</a></h1> | |||||
{% endfor %} | |||||
``` | |||||
Any page that cannot be sorted, for example if missing the date variable while sorting by `date`, will be ignored and | |||||
won't be rendered. The terminal will warn you if this is happening. | |||||
This would iterate over the posts, and would do so in a specific order | |||||
based on the `sort_by` variable set in the `_index.md` page for the | |||||
containing section. The `sort_by` variable can be given three values: `date`, | |||||
`weight`, and `none`. If no `sort_by` method is set, the pages will be | |||||
sorted in the `none` order, which is not intended to be used for sorted content. | |||||
If several pages have the same date/weight/order, their permalink will be used to break the tie following | |||||
an alphabetical order. | |||||
Any page that is missing the data it needs to be sorted will be ignored and | |||||
won't be rendered. For example, if a page is missing the date variable the | |||||
containing section sets `sort_by = "date"`, then that page will be ignored. | |||||
The terminal will warn you if this is happening. | |||||
### `date` | |||||
This will sort all pages by their `date` field, from the most recent to the oldest. | |||||
If several pages have the same date/weight/order, their permalink will be used | |||||
to break the tie following an alphabetical order. | |||||
### `weight` | |||||
This will be sort all pages by their `weight` field. Heavier weights fall at the bottom: 5 would be before 10. | |||||
## Sorting Pages | |||||
The `sort_by` front-matter variable can have the following values: | |||||
### `order` | |||||
This will be sort all pages by their `order` field. Order is the opposite of weight, think of it as enumerating | |||||
the content: this is my first post, my second, etc. A page with `order: 5` will appear after a page with `order: 10` in the sorted list. | |||||
### `date` | |||||
This will sort all pages by their `date` field, from the most recent (at the | |||||
top of the list) to the oldest (at the bottom of the list). Each page will | |||||
get `page.earlier` and `page.later` variables that contain the pages with | |||||
earlier and later dates, respectively. | |||||
### `weight` | |||||
This will be sort all pages by their `weight` field, from lightest weight | |||||
(at the top of the list) to heaviest (at the bottom of the list). Each | |||||
page gets `page.lighter` and `page.heavier` variables that contain the | |||||
pages with lighter and heavier weights, respectively. | |||||
When iterating through pages, you may wish to use the Tera `reverse` filter, | |||||
which reverses the order of the pages. Thus, after using the `reverse` filter, | |||||
pages sorted by weight will be sorted from lightest (at the top) to heaviest | |||||
(at the bottom); pages sorted by date will be sorted from oldest (at the top) | |||||
to newest (at the bottom). | |||||
`reverse` has no effect on `page.later`/`page.earlier`/`page.heavier`/`page.lighter`. | |||||
## Sorting Subsections | |||||
Sorting sections is a bit less flexible: sections are always sorted by `weight`, | |||||
and do not have any variables that point to the next heavier/lighter sections. | |||||
Based on this, by default the lightest (lowest `weight`) subsections will be at | |||||
the top of the list and the heaviest (highest `weight`) will be at the top; | |||||
the `reverse` filter reverses this order. | |||||
**Note**: Unlike pages, permalinks will **not** be used to break ties between | |||||
equally weighted sections. Thus, if the `weight` variable for your section is not set (or if it | |||||
is set in a way that produces ties), then your sections will be sorted in | |||||
**random** order. Moreover, that order is determined at build time and will | |||||
change with each site rebuild. Thus, if there is any chance that you will | |||||
iterate over your sections, you should always assign them weight. |
@@ -32,9 +32,12 @@ extra: HashMap<String, Any>; | |||||
word_count: Number; | word_count: Number; | ||||
// Based on https://help.medium.com/hc/en-us/articles/214991667-Read-time | // Based on https://help.medium.com/hc/en-us/articles/214991667-Read-time | ||||
reading_time: Number; | reading_time: Number; | ||||
// `previous` and `next` are only filled if the content can be sorted | |||||
previous: Page?; | |||||
next: Page?; | |||||
// `earlier` and `later` are only populated if the section variable `sort_by` is set to `date` | |||||
earlier: Page?; | |||||
later: Page?; | |||||
// `heavier` and `lighter` are only populated if the section variable `sort_by` is set to `weight` | |||||
heavier: Page?; | |||||
lighter: Page?; | |||||
// See the Table of contents section below for more details | // See the Table of contents section below for more details | ||||
toc: Array<Header>; | toc: Array<Header>; | ||||
// Year/month/day is only set if the page has a date and month/day are 1-indexed | // Year/month/day is only set if the page has a date and month/day are 1-indexed | ||||
@@ -1,6 +1,6 @@ | |||||
+++ | +++ | ||||
title = "DevOps" | title = "DevOps" | ||||
sort_by = "order" | |||||
sort_by = "weight" | |||||
redirect_to = "posts/tutorials/devops/docker" | redirect_to = "posts/tutorials/devops/docker" | ||||
weight = 10 | weight = 10 | ||||
+++ | +++ |
@@ -1,6 +1,6 @@ | |||||
+++ | +++ | ||||
title = "Docker" | title = "Docker" | ||||
order = 1 | |||||
weight = 1 | |||||
date = 2017-01-01 | date = 2017-01-01 | ||||
+++ | +++ | ||||
@@ -1,6 +1,6 @@ | |||||
+++ | +++ | ||||
title = "Nix" | title = "Nix" | ||||
order = 2 | |||||
weight = 2 | |||||
date = 2017-01-01 | date = 2017-01-01 | ||||
+++ | +++ | ||||
@@ -1,5 +1,5 @@ | |||||
+++ | +++ | ||||
title = "Programming" | title = "Programming" | ||||
sort_by = "order" | |||||
sort_by = "weight" | |||||
weight = 1 | weight = 1 | ||||
+++ | +++ |
@@ -1,6 +1,6 @@ | |||||
+++ | +++ | ||||
title = "Python tutorial" | title = "Python tutorial" | ||||
order = 1 | |||||
weight = 1 | |||||
date = 2017-01-01 | date = 2017-01-01 | ||||
+++ | +++ | ||||
@@ -1,6 +1,6 @@ | |||||
+++ | +++ | ||||
title = "Rust" | title = "Rust" | ||||
order = 2 | |||||
weight = 2 | |||||
date = 2017-01-01 | date = 2017-01-01 | ||||
+++ | +++ | ||||
@@ -1,5 +1,5 @@ | |||||
+++ | +++ | ||||
paginate_by = 1 | paginate_by = 1 | ||||
sort_by = "order" | |||||
sort_by = "weight" | |||||
template = "rebuild.html" | template = "rebuild.html" | ||||
+++ | +++ |
@@ -1,6 +1,6 @@ | |||||
+++ | +++ | ||||
title = "first" | title = "first" | ||||
order = 10 | |||||
weight = 10 | |||||
date = 2017-01-01 | date = 2017-01-01 | ||||
+++ | +++ | ||||
@@ -1,6 +1,6 @@ | |||||
+++ | +++ | ||||
title = "second" | title = "second" | ||||
order = 100 | |||||
weight = 100 | |||||
date = 2016-01-01 | date = 2016-01-01 | ||||
+++ | +++ | ||||