commit
3685ed1672
|
@ -16,7 +16,7 @@ matrix:
|
||||||
|
|
||||||
# The earliest stable Rust version that works
|
# The earliest stable Rust version that works
|
||||||
- env: TARGET=x86_64-unknown-linux-gnu
|
- env: TARGET=x86_64-unknown-linux-gnu
|
||||||
rust: 1.29.0
|
rust: 1.30.0
|
||||||
|
|
||||||
|
|
||||||
before_install: set -e
|
before_install: set -e
|
||||||
|
|
|
@ -1,5 +1,14 @@
|
||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 0.5.1 (2018-12-14)
|
||||||
|
|
||||||
|
- Fix deleting markdown file in `zola serve`
|
||||||
|
- Fix pagination for taxonomies being broken and add missing documentation for it
|
||||||
|
- Add missing pager pages from the sitemap
|
||||||
|
- Allow and parse full RFC339 datetimes in filenames
|
||||||
|
- Live reload is now enabled for the 404 page on serve
|
||||||
|
|
||||||
|
|
||||||
## 0.5.0 (2018-11-17)
|
## 0.5.0 (2018-11-17)
|
||||||
|
|
||||||
### Breaking
|
### Breaking
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "zola"
|
name = "zola"
|
||||||
version = "0.5.0"
|
version = "0.5.1"
|
||||||
authors = ["Vincent Prouillet <prouillet.vincent@gmail.com>"]
|
authors = ["Vincent Prouillet <prouillet.vincent@gmail.com>"]
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
|
|
|
@ -37,6 +37,14 @@ impl Taxonomy {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn paginate_path(&self) -> &str {
|
||||||
|
if let Some(ref path) = self.paginate_path {
|
||||||
|
path
|
||||||
|
} else {
|
||||||
|
"page"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Taxonomy {
|
impl Default for Taxonomy {
|
||||||
|
|
|
@ -20,8 +20,11 @@ use content::file_info::FileInfo;
|
||||||
use content::ser::SerializingPage;
|
use content::ser::SerializingPage;
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
// Check whether a string starts with yyyy-mm-dd{-,_}
|
// Based on https://regex101.com/r/H2n38Z/1/tests
|
||||||
static ref DATE_IN_FILENAME: Regex = Regex::new(r"^^([12]\d{3}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01]))(_|-)").unwrap();
|
// A regex parsing RFC3339 date followed by {_,-}, some characters and ended by .md
|
||||||
|
static ref RFC3339_DATE: Regex = Regex::new(
|
||||||
|
r"^(?P<datetime>(\d{4})-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])(T([01][0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9]|60)(\.[0-9]+)?(Z|(\+|-)([01][0-9]|2[0-3]):([0-5][0-9])))?)(_|-)(?P<slug>.+$)"
|
||||||
|
).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
@ -113,11 +116,11 @@ impl Page {
|
||||||
page.word_count = Some(word_count);
|
page.word_count = Some(word_count);
|
||||||
page.reading_time = Some(reading_time);
|
page.reading_time = Some(reading_time);
|
||||||
|
|
||||||
let mut has_date_in_name = false;
|
let mut slug_from_dated_filename = None;
|
||||||
if DATE_IN_FILENAME.is_match(&page.file.name) {
|
if let Some(ref caps) = RFC3339_DATE.captures(&page.file.name.replace(".md", "")) {
|
||||||
has_date_in_name = true;
|
slug_from_dated_filename = Some(caps.name("slug").unwrap().as_str().to_string());
|
||||||
if page.meta.date.is_none() {
|
if page.meta.date.is_none() {
|
||||||
page.meta.date = Some(page.file.name[..10].to_string());
|
page.meta.date = Some(caps.name("datetime").unwrap().as_str().to_string());
|
||||||
page.meta.date_to_datetime();
|
page.meta.date_to_datetime();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -132,9 +135,8 @@ impl Page {
|
||||||
slugify(&page.file.name)
|
slugify(&page.file.name)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if has_date_in_name {
|
if let Some(slug) = slug_from_dated_filename {
|
||||||
// skip the date + the {_,-}
|
slugify(&slug)
|
||||||
slugify(&page.file.name[11..])
|
|
||||||
} else {
|
} else {
|
||||||
slugify(&page.file.name)
|
slugify(&page.file.name)
|
||||||
}
|
}
|
||||||
|
@ -507,7 +509,7 @@ Hello world
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn can_get_date_from_filename() {
|
fn can_get_date_from_short_date_in_filename() {
|
||||||
let config = Config::default();
|
let config = Config::default();
|
||||||
let content = r#"
|
let content = r#"
|
||||||
+++
|
+++
|
||||||
|
@ -523,6 +525,23 @@ Hello world
|
||||||
assert_eq!(page.slug, "hello");
|
assert_eq!(page.slug, "hello");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn can_get_date_from_full_rfc3339_date_in_filename() {
|
||||||
|
let config = Config::default();
|
||||||
|
let content = r#"
|
||||||
|
+++
|
||||||
|
+++
|
||||||
|
Hello world
|
||||||
|
<!-- more -->"#
|
||||||
|
.to_string();
|
||||||
|
let res = Page::parse(Path::new("2018-10-02T15:00:00Z-hello.md"), &content, &config);
|
||||||
|
assert!(res.is_ok());
|
||||||
|
let page = res.unwrap();
|
||||||
|
|
||||||
|
assert_eq!(page.meta.date, Some("2018-10-02T15:00:00Z".to_string()));
|
||||||
|
assert_eq!(page.slug, "hello");
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn frontmatter_date_override_filename_date() {
|
fn frontmatter_date_override_filename_date() {
|
||||||
let config = Config::default();
|
let config = Config::default();
|
||||||
|
|
|
@ -80,7 +80,7 @@ impl Section {
|
||||||
pub fn parse(file_path: &Path, content: &str, config: &Config) -> Result<Section> {
|
pub fn parse(file_path: &Path, content: &str, config: &Config) -> Result<Section> {
|
||||||
let (meta, content) = split_section_content(file_path, content)?;
|
let (meta, content) = split_section_content(file_path, content)?;
|
||||||
let mut section = Section::new(file_path, meta);
|
let mut section = Section::new(file_path, meta);
|
||||||
section.raw_content = content.clone();
|
section.raw_content = content;
|
||||||
let (word_count, reading_time) = get_reading_analytics(§ion.raw_content);
|
let (word_count, reading_time) = get_reading_analytics(§ion.raw_content);
|
||||||
section.word_count = Some(word_count);
|
section.word_count = Some(word_count);
|
||||||
section.reading_time = Some(reading_time);
|
section.reading_time = Some(reading_time);
|
||||||
|
|
|
@ -14,7 +14,7 @@ use taxonomies::{Taxonomy, TaxonomyItem};
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
enum PaginationRoot<'a> {
|
enum PaginationRoot<'a> {
|
||||||
Section(&'a Section),
|
Section(&'a Section),
|
||||||
Taxonomy(&'a Taxonomy),
|
Taxonomy(&'a Taxonomy, &'a TaxonomyItem),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A list of all the pages in the paginator with their index and links
|
/// A list of all the pages in the paginator with their index and links
|
||||||
|
@ -93,14 +93,14 @@ impl<'a> Paginator<'a> {
|
||||||
all_pages: &item.pages,
|
all_pages: &item.pages,
|
||||||
pagers: Vec::with_capacity(item.pages.len() / paginate_by),
|
pagers: Vec::with_capacity(item.pages.len() / paginate_by),
|
||||||
paginate_by,
|
paginate_by,
|
||||||
root: PaginationRoot::Taxonomy(taxonomy),
|
root: PaginationRoot::Taxonomy(taxonomy, item),
|
||||||
permalink: item.permalink.clone(),
|
permalink: item.permalink.clone(),
|
||||||
path: format!("{}/{}", taxonomy.kind.name, item.slug),
|
path: format!("{}/{}", taxonomy.kind.name, item.slug),
|
||||||
paginate_path: taxonomy
|
paginate_path: taxonomy
|
||||||
.kind
|
.kind
|
||||||
.paginate_path
|
.paginate_path
|
||||||
.clone()
|
.clone()
|
||||||
.unwrap_or_else(|| "pages".to_string()),
|
.unwrap_or_else(|| "page".to_string()),
|
||||||
is_index: false,
|
is_index: false,
|
||||||
template: format!("{}/single.html", taxonomy.kind.name),
|
template: format!("{}/single.html", taxonomy.kind.name),
|
||||||
};
|
};
|
||||||
|
@ -212,8 +212,9 @@ impl<'a> Paginator<'a> {
|
||||||
context
|
context
|
||||||
.insert("section", &SerializingSection::from_section_basic(s, Some(library)));
|
.insert("section", &SerializingSection::from_section_basic(s, Some(library)));
|
||||||
}
|
}
|
||||||
PaginationRoot::Taxonomy(t) => {
|
PaginationRoot::Taxonomy(t, item) => {
|
||||||
context.insert("taxonomy", &t.kind);
|
context.insert("taxonomy", &t.kind);
|
||||||
|
context.insert("term", &item.serialize(library));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
context.insert("current_url", &pager.permalink);
|
context.insert("current_url", &pager.permalink);
|
||||||
|
@ -349,7 +350,7 @@ mod tests {
|
||||||
|
|
||||||
assert_eq!(paginator.pagers[1].index, 2);
|
assert_eq!(paginator.pagers[1].index, 2);
|
||||||
assert_eq!(paginator.pagers[1].pages.len(), 1);
|
assert_eq!(paginator.pagers[1].pages.len(), 1);
|
||||||
assert_eq!(paginator.pagers[1].permalink, "https://vincent.is/tags/something/pages/2/");
|
assert_eq!(paginator.pagers[1].permalink, "https://vincent.is/tags/something/page/2/");
|
||||||
assert_eq!(paginator.pagers[1].path, "tags/something/pages/2/");
|
assert_eq!(paginator.pagers[1].path, "tags/something/page/2/");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,7 @@ use library::Library;
|
||||||
use sorting::sort_pages_by_date;
|
use sorting::sort_pages_by_date;
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Serialize)]
|
#[derive(Debug, Clone, PartialEq, Serialize)]
|
||||||
struct SerializedTaxonomyItem<'a> {
|
pub struct SerializedTaxonomyItem<'a> {
|
||||||
name: &'a str,
|
name: &'a str,
|
||||||
slug: &'a str,
|
slug: &'a str,
|
||||||
permalink: &'a str,
|
permalink: &'a str,
|
||||||
|
@ -71,6 +71,10 @@ impl TaxonomyItem {
|
||||||
|
|
||||||
TaxonomyItem { name: name.to_string(), permalink, slug, pages }
|
TaxonomyItem { name: name.to_string(), permalink, slug, pages }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn serialize<'a>(&'a self, library: &'a Library) -> SerializedTaxonomyItem<'a> {
|
||||||
|
SerializedTaxonomyItem::from_item(self, library)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Serialize)]
|
#[derive(Debug, Clone, PartialEq, Serialize)]
|
||||||
|
|
|
@ -311,7 +311,7 @@ pub fn after_content_change(site: &mut Site, path: &Path) -> Result<()> {
|
||||||
if is_md {
|
if is_md {
|
||||||
// only delete if it was able to be added in the first place
|
// only delete if it was able to be added in the first place
|
||||||
if !index.exists() && !path.exists() {
|
if !index.exists() && !path.exists() {
|
||||||
delete_element(site, path, is_section)?;
|
return delete_element(site, path, is_section);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Added another .md in a assets directory
|
// Added another .md in a assets directory
|
||||||
|
@ -352,6 +352,7 @@ pub fn after_template_change(site: &mut Site, path: &Path) -> Result<()> {
|
||||||
site.render_orphan_pages()
|
site.render_orphan_pages()
|
||||||
}
|
}
|
||||||
"section.html" => site.render_sections(),
|
"section.html" => site.render_sections(),
|
||||||
|
"404.html" => site.render_404(),
|
||||||
// Either the index or some unknown template changed
|
// Either the index or some unknown template changed
|
||||||
// We can't really know what this change affects so rebuild all
|
// We can't really know what this change affects so rebuild all
|
||||||
// the things
|
// the things
|
||||||
|
|
|
@ -228,9 +228,22 @@ fn can_rebuild_after_renaming_section_folder() {
|
||||||
fn can_rebuild_after_renaming_non_md_asset_in_colocated_folder() {
|
fn can_rebuild_after_renaming_non_md_asset_in_colocated_folder() {
|
||||||
let tmp_dir = tempdir().expect("create temp dir");
|
let tmp_dir = tempdir().expect("create temp dir");
|
||||||
let (site_path, mut site) = load_and_build_site!(tmp_dir);
|
let (site_path, mut site) = load_and_build_site!(tmp_dir);
|
||||||
let (old_path, new_path) = rename!(site_path, "content/posts/with-assets/zola.png", "gutenberg.png");
|
let (old_path, new_path) =
|
||||||
|
rename!(site_path, "content/posts/with-assets/zola.png", "gutenberg.png");
|
||||||
|
|
||||||
// Testing that we don't try to load some images as markdown or something
|
// Testing that we don't try to load some images as markdown or something
|
||||||
let res = after_content_rename(&mut site, &old_path, &new_path);
|
let res = after_content_rename(&mut site, &old_path, &new_path);
|
||||||
assert!(res.is_ok());
|
assert!(res.is_ok());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn can_rebuild_after_deleting_file() {
|
||||||
|
let tmp_dir = tempdir().expect("create temp dir");
|
||||||
|
let (site_path, mut site) = load_and_build_site!(tmp_dir);
|
||||||
|
let path = site_path.join("content").join("posts").join("fixed-slug.md");
|
||||||
|
fs::remove_file(&path).unwrap();
|
||||||
|
|
||||||
|
let res = after_content_change(&mut site, &path);
|
||||||
|
println!("{:?}", res);
|
||||||
|
assert!(res.is_ok());
|
||||||
|
}
|
||||||
|
|
|
@ -31,7 +31,7 @@ pub struct Rendered {
|
||||||
// means we will have example, example-1, example-2 etc
|
// means we will have example, example-1, example-2 etc
|
||||||
fn find_anchor(anchors: &[String], name: String, level: u8) -> String {
|
fn find_anchor(anchors: &[String], name: String, level: u8) -> String {
|
||||||
if level == 0 && !anchors.contains(&name) {
|
if level == 0 && !anchors.contains(&name) {
|
||||||
return name.to_string();
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
let new_anchor = format!("{}-{}", name, level + 1);
|
let new_anchor = format!("{}-{}", name, level + 1);
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use pest::iterators::Pair;
|
use pest::iterators::Pair;
|
||||||
use pest::Parser;
|
use pest::Parser;
|
||||||
use tera::{to_value, Context, Map, Value};
|
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
|
use tera::{to_value, Context, Map, Value};
|
||||||
|
|
||||||
use context::RenderContext;
|
use context::RenderContext;
|
||||||
use errors::{Result, ResultExt};
|
use errors::{Result, ResultExt};
|
||||||
|
@ -20,9 +20,9 @@ lazy_static! {
|
||||||
|
|
||||||
fn replace_string_markers(input: &str) -> String {
|
fn replace_string_markers(input: &str) -> String {
|
||||||
match input.chars().next().unwrap() {
|
match input.chars().next().unwrap() {
|
||||||
'"' => input.replace('"', "").to_string(),
|
'"' => input.replace('"', ""),
|
||||||
'\'' => input.replace('\'', "").to_string(),
|
'\'' => input.replace('\'', ""),
|
||||||
'`' => input.replace('`', "").to_string(),
|
'`' => input.replace('`', ""),
|
||||||
_ => unreachable!("How did you even get there"),
|
_ => unreachable!("How did you even get there"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -627,10 +627,8 @@ impl Site {
|
||||||
ensure_directory_exists(&self.output_path)?;
|
ensure_directory_exists(&self.output_path)?;
|
||||||
let mut context = Context::new();
|
let mut context = Context::new();
|
||||||
context.insert("config", &self.config);
|
context.insert("config", &self.config);
|
||||||
create_file(
|
let output = render_template("404.html", &self.tera, &context, &self.config.theme)?;
|
||||||
&self.output_path.join("404.html"),
|
create_file(&self.output_path.join("404.html"), &self.inject_livereload(output))
|
||||||
&render_template("404.html", &self.tera, &context, &self.config.theme)?,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Renders robots.txt
|
/// Renders robots.txt
|
||||||
|
@ -646,7 +644,6 @@ impl Site {
|
||||||
|
|
||||||
/// Renders all taxonomies with at least one non-draft post
|
/// Renders all taxonomies with at least one non-draft post
|
||||||
pub fn render_taxonomies(&self) -> Result<()> {
|
pub fn render_taxonomies(&self) -> Result<()> {
|
||||||
// TODO: make parallel?
|
|
||||||
for taxonomy in &self.taxonomies {
|
for taxonomy in &self.taxonomies {
|
||||||
self.render_taxonomy(taxonomy)?;
|
self.render_taxonomy(taxonomy)?;
|
||||||
}
|
}
|
||||||
|
@ -669,24 +666,26 @@ impl Site {
|
||||||
.items
|
.items
|
||||||
.par_iter()
|
.par_iter()
|
||||||
.map(|item| {
|
.map(|item| {
|
||||||
|
let path = output_path.join(&item.slug);
|
||||||
|
if taxonomy.kind.is_paginated() {
|
||||||
|
self.render_paginated(
|
||||||
|
&path,
|
||||||
|
&Paginator::from_taxonomy(&taxonomy, item, &self.library),
|
||||||
|
)?;
|
||||||
|
} else {
|
||||||
|
let single_output =
|
||||||
|
taxonomy.render_term(item, &self.tera, &self.config, &self.library)?;
|
||||||
|
create_directory(&path)?;
|
||||||
|
create_file(&path.join("index.html"), &self.inject_livereload(single_output))?;
|
||||||
|
}
|
||||||
|
|
||||||
if taxonomy.kind.rss {
|
if taxonomy.kind.rss {
|
||||||
self.render_rss_feed(
|
self.render_rss_feed(
|
||||||
item.pages.iter().map(|p| self.library.get_page_by_key(*p)).collect(),
|
item.pages.iter().map(|p| self.library.get_page_by_key(*p)).collect(),
|
||||||
Some(&PathBuf::from(format!("{}/{}", taxonomy.kind.name, item.slug))),
|
Some(&PathBuf::from(format!("{}/{}", taxonomy.kind.name, item.slug))),
|
||||||
)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
if taxonomy.kind.is_paginated() {
|
|
||||||
self.render_paginated(
|
|
||||||
&output_path,
|
|
||||||
&Paginator::from_taxonomy(&taxonomy, item, &self.library),
|
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
let single_output =
|
Ok(())
|
||||||
taxonomy.render_term(item, &self.tera, &self.config, &self.library)?;
|
|
||||||
let path = output_path.join(&item.slug);
|
|
||||||
create_directory(&path)?;
|
|
||||||
create_file(&path.join("index.html"), &self.inject_livereload(single_output))
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.collect::<Result<()>>()
|
.collect::<Result<()>>()
|
||||||
|
@ -720,6 +719,18 @@ impl Site {
|
||||||
.iter()
|
.iter()
|
||||||
.map(|s| SitemapEntry::new(s.permalink.clone(), None))
|
.map(|s| SitemapEntry::new(s.permalink.clone(), None))
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
for section in
|
||||||
|
self.library.sections_values().iter().filter(|s| s.meta.paginate_by.is_some())
|
||||||
|
{
|
||||||
|
let number_pagers = (section.pages.len() as f64
|
||||||
|
/ section.meta.paginate_by.unwrap() as f64)
|
||||||
|
.ceil() as isize;
|
||||||
|
for i in 1..number_pagers + 1 {
|
||||||
|
let permalink =
|
||||||
|
format!("{}{}/{}/", section.permalink, section.meta.paginate_path, i);
|
||||||
|
sections.push(SitemapEntry::new(permalink, None))
|
||||||
|
}
|
||||||
|
}
|
||||||
sections.sort_by(|a, b| a.permalink.cmp(&b.permalink));
|
sections.sort_by(|a, b| a.permalink.cmp(&b.permalink));
|
||||||
context.insert("sections", §ions);
|
context.insert("sections", §ions);
|
||||||
|
|
||||||
|
@ -733,12 +744,29 @@ impl Site {
|
||||||
self.config.make_permalink(&format!("{}/{}", &name, item.slug)),
|
self.config.make_permalink(&format!("{}/{}", &name, item.slug)),
|
||||||
None,
|
None,
|
||||||
));
|
));
|
||||||
|
|
||||||
|
if taxonomy.kind.is_paginated() {
|
||||||
|
let number_pagers = (item.pages.len() as f64
|
||||||
|
/ taxonomy.kind.paginate_by.unwrap() as f64)
|
||||||
|
.ceil() as isize;
|
||||||
|
for i in 1..number_pagers + 1 {
|
||||||
|
let permalink = self.config.make_permalink(&format!(
|
||||||
|
"{}/{}/{}/{}",
|
||||||
|
name,
|
||||||
|
item.slug,
|
||||||
|
taxonomy.kind.paginate_path(),
|
||||||
|
i
|
||||||
|
));
|
||||||
|
terms.push(SitemapEntry::new(permalink, None))
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
terms.sort_by(|a, b| a.permalink.cmp(&b.permalink));
|
terms.sort_by(|a, b| a.permalink.cmp(&b.permalink));
|
||||||
taxonomies.push(terms);
|
taxonomies.push(terms);
|
||||||
}
|
}
|
||||||
context.insert("taxonomies", &taxonomies);
|
|
||||||
|
|
||||||
|
context.insert("taxonomies", &taxonomies);
|
||||||
context.insert("config", &self.config);
|
context.insert("config", &self.config);
|
||||||
|
|
||||||
let sitemap = &render_template("sitemap.xml", &self.tera, &context, &self.config.theme)?;
|
let sitemap = &render_template("sitemap.xml", &self.tera, &context, &self.config.theme)?;
|
||||||
|
@ -771,7 +799,7 @@ impl Site {
|
||||||
|
|
||||||
pages.par_sort_unstable_by(sort_actual_pages_by_date);
|
pages.par_sort_unstable_by(sort_actual_pages_by_date);
|
||||||
|
|
||||||
context.insert("last_build_date", &pages[0].meta.date.clone().map(|d| d.to_string()));
|
context.insert("last_build_date", &pages[0].meta.date.clone());
|
||||||
// limit to the last n elements if the limit is set; otherwise use all.
|
// limit to the last n elements if the limit is set; otherwise use all.
|
||||||
let num_entries = self.config.rss_limit.unwrap_or(pages.len());
|
let num_entries = self.config.rss_limit.unwrap_or(pages.len());
|
||||||
let p = pages
|
let p = pages
|
||||||
|
@ -794,7 +822,7 @@ impl Site {
|
||||||
let feed = &render_template("rss.xml", &self.tera, &context, &self.config.theme)?;
|
let feed = &render_template("rss.xml", &self.tera, &context, &self.config.theme)?;
|
||||||
|
|
||||||
if let Some(ref base) = base_path {
|
if let Some(ref base) = base_path {
|
||||||
let mut output_path = self.output_path.clone().to_path_buf();
|
let mut output_path = self.output_path.clone();
|
||||||
for component in base.components() {
|
for component in base.components() {
|
||||||
output_path.push(component);
|
output_path.push(component);
|
||||||
if !output_path.exists() {
|
if !output_path.exists() {
|
||||||
|
@ -805,16 +833,13 @@ impl Site {
|
||||||
} else {
|
} else {
|
||||||
create_file(&self.output_path.join("rss.xml"), feed)?;
|
create_file(&self.output_path.join("rss.xml"), feed)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Renders a single section
|
/// Renders a single section
|
||||||
pub fn render_section(&self, section: &Section, render_pages: bool) -> Result<()> {
|
pub fn render_section(&self, section: &Section, render_pages: bool) -> Result<()> {
|
||||||
ensure_directory_exists(&self.output_path)?;
|
ensure_directory_exists(&self.output_path)?;
|
||||||
let public = self.output_path.clone();
|
let mut output_path = self.output_path.clone();
|
||||||
|
|
||||||
let mut output_path = public.to_path_buf();
|
|
||||||
for component in §ion.file.components {
|
for component in §ion.file.components {
|
||||||
output_path.push(component);
|
output_path.push(component);
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
extern crate config;
|
||||||
extern crate site;
|
extern crate site;
|
||||||
extern crate tempfile;
|
extern crate tempfile;
|
||||||
|
|
||||||
|
@ -7,6 +8,7 @@ use std::fs::File;
|
||||||
use std::io::prelude::*;
|
use std::io::prelude::*;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
|
use config::Taxonomy;
|
||||||
use site::Site;
|
use site::Site;
|
||||||
use tempfile::tempdir;
|
use tempfile::tempdir;
|
||||||
|
|
||||||
|
@ -465,6 +467,13 @@ fn can_build_site_with_pagination_for_section() {
|
||||||
"posts/page/4/index.html",
|
"posts/page/4/index.html",
|
||||||
"Last: https://replace-this-with-your-url.com/posts/page/5/"
|
"Last: https://replace-this-with-your-url.com/posts/page/5/"
|
||||||
));
|
));
|
||||||
|
|
||||||
|
// sitemap contains the pager pages
|
||||||
|
assert!(file_contains!(
|
||||||
|
public,
|
||||||
|
"sitemap.xml",
|
||||||
|
"<loc>https://replace-this-with-your-url.com/posts/page/4/</loc>"
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -510,6 +519,93 @@ fn can_build_site_with_pagination_for_index() {
|
||||||
assert!(file_contains!(public, "index.html", "Last: https://replace-this-with-your-url.com/"));
|
assert!(file_contains!(public, "index.html", "Last: https://replace-this-with-your-url.com/"));
|
||||||
assert_eq!(file_contains!(public, "index.html", "has_prev"), false);
|
assert_eq!(file_contains!(public, "index.html", "has_prev"), false);
|
||||||
assert_eq!(file_contains!(public, "index.html", "has_next"), false);
|
assert_eq!(file_contains!(public, "index.html", "has_next"), false);
|
||||||
|
|
||||||
|
// sitemap contains the pager pages
|
||||||
|
assert!(file_contains!(
|
||||||
|
public,
|
||||||
|
"sitemap.xml",
|
||||||
|
"<loc>https://replace-this-with-your-url.com/page/1/</loc>"
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn can_build_site_with_pagination_for_taxonomy() {
|
||||||
|
let mut path = env::current_dir().unwrap().parent().unwrap().parent().unwrap().to_path_buf();
|
||||||
|
path.push("test_site");
|
||||||
|
let mut site = Site::new(&path, "config.toml").unwrap();
|
||||||
|
site.config.taxonomies.push(Taxonomy {
|
||||||
|
name: "tags".to_string(),
|
||||||
|
paginate_by: Some(2),
|
||||||
|
paginate_path: None,
|
||||||
|
rss: true,
|
||||||
|
});
|
||||||
|
site.load().unwrap();
|
||||||
|
|
||||||
|
for (i, (_, page)) in site.library.pages_mut().iter_mut().enumerate() {
|
||||||
|
page.meta.taxonomies = {
|
||||||
|
let mut taxonomies = HashMap::new();
|
||||||
|
taxonomies
|
||||||
|
.insert("tags".to_string(), vec![if i % 2 == 0 { "A" } else { "B" }.to_string()]);
|
||||||
|
taxonomies
|
||||||
|
};
|
||||||
|
}
|
||||||
|
site.populate_taxonomies().unwrap();
|
||||||
|
|
||||||
|
let tmp_dir = tempdir().expect("create temp dir");
|
||||||
|
let public = &tmp_dir.path().join("public");
|
||||||
|
site.set_output_path(&public);
|
||||||
|
site.build().unwrap();
|
||||||
|
|
||||||
|
assert!(Path::new(&public).exists());
|
||||||
|
|
||||||
|
assert!(file_exists!(public, "index.html"));
|
||||||
|
assert!(file_exists!(public, "sitemap.xml"));
|
||||||
|
assert!(file_exists!(public, "robots.txt"));
|
||||||
|
assert!(file_exists!(public, "a-fixed-url/index.html"));
|
||||||
|
assert!(file_exists!(public, "posts/python/index.html"));
|
||||||
|
assert!(file_exists!(public, "posts/tutorials/devops/nix/index.html"));
|
||||||
|
assert!(file_exists!(public, "posts/with-assets/index.html"));
|
||||||
|
|
||||||
|
// Tags
|
||||||
|
assert!(file_exists!(public, "tags/index.html"));
|
||||||
|
// With RSS
|
||||||
|
assert!(file_exists!(public, "tags/a/rss.xml"));
|
||||||
|
assert!(file_exists!(public, "tags/b/rss.xml"));
|
||||||
|
// And pagination!
|
||||||
|
assert!(file_exists!(public, "tags/a/page/1/index.html"));
|
||||||
|
assert!(file_exists!(public, "tags/b/page/1/index.html"));
|
||||||
|
assert!(file_exists!(public, "tags/a/page/2/index.html"));
|
||||||
|
assert!(file_exists!(public, "tags/b/page/2/index.html"));
|
||||||
|
|
||||||
|
// should redirect to posts/
|
||||||
|
assert!(file_contains!(
|
||||||
|
public,
|
||||||
|
"tags/a/page/1/index.html",
|
||||||
|
"http-equiv=\"refresh\" content=\"0;url=https://replace-this-with-your-url.com/tags/a/\""
|
||||||
|
));
|
||||||
|
assert!(file_contains!(public, "tags/a/index.html", "Num pagers: 6"));
|
||||||
|
assert!(file_contains!(public, "tags/a/index.html", "Page size: 2"));
|
||||||
|
assert!(file_contains!(public, "tags/a/index.html", "Current index: 1"));
|
||||||
|
assert!(!file_contains!(public, "tags/a/index.html", "has_prev"));
|
||||||
|
assert!(file_contains!(public, "tags/a/index.html", "has_next"));
|
||||||
|
assert!(file_contains!(
|
||||||
|
public,
|
||||||
|
"tags/a/index.html",
|
||||||
|
"First: https://replace-this-with-your-url.com/tags/a/"
|
||||||
|
));
|
||||||
|
assert!(file_contains!(
|
||||||
|
public,
|
||||||
|
"tags/a/index.html",
|
||||||
|
"Last: https://replace-this-with-your-url.com/tags/a/page/6/"
|
||||||
|
));
|
||||||
|
assert_eq!(file_contains!(public, "tags/a/index.html", "has_prev"), false);
|
||||||
|
|
||||||
|
// sitemap contains the pager pages
|
||||||
|
assert!(file_contains!(
|
||||||
|
public,
|
||||||
|
"sitemap.xml",
|
||||||
|
"<loc>https://replace-this-with-your-url.com/tags/a/page/6/</loc>"
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
@ -144,7 +144,7 @@ pub fn make_get_taxonomy(all_taxonomies: &[Taxonomy], library: &Library) -> Glob
|
||||||
None => {
|
None => {
|
||||||
return Err(
|
return Err(
|
||||||
format!("`get_taxonomy` received an unknown taxonomy as kind: {}", kind).into()
|
format!("`get_taxonomy` received an unknown taxonomy as kind: {}", kind).into()
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -180,12 +180,12 @@ pub fn make_get_taxonomy_url(all_taxonomies: &[Taxonomy]) -> GlobalFn {
|
||||||
"`get_taxonomy_url` received an unknown taxonomy as kind: {}",
|
"`get_taxonomy_url` received an unknown taxonomy as kind: {}",
|
||||||
kind
|
kind
|
||||||
)
|
)
|
||||||
.into())
|
.into());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(ref permalink) = container.get(&name) {
|
if let Some(permalink) = container.get(&name) {
|
||||||
return Ok(to_value(permalink.clone()).unwrap());
|
return Ok(to_value(permalink).unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
Err(format!("`get_taxonomy_url`: couldn't find `{}` in `{}` taxonomy", name, kind).into())
|
Err(format!("`get_taxonomy_url`: couldn't find `{}` in `{}` taxonomy", name, kind).into())
|
||||||
|
@ -226,7 +226,7 @@ pub fn make_resize_image(imageproc: Arc<Mutex<imageproc::Processor>>) -> GlobalF
|
||||||
return Err(format!("`resize_image`: Cannot find path: {}", path).into());
|
return Err(format!("`resize_image`: Cannot find path: {}", path).into());
|
||||||
}
|
}
|
||||||
|
|
||||||
let imageop = imageproc::ImageOp::from_args(path.clone(), &op, width, height, quality)
|
let imageop = imageproc::ImageOp::from_args(path, &op, width, height, quality)
|
||||||
.map_err(|e| format!("`resize_image`: {}", e))?;
|
.map_err(|e| format!("`resize_image`: {}", e))?;
|
||||||
let url = imageproc.insert(imageop);
|
let url = imageproc.insert(imageop);
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,7 @@ create a **page** at `[base_url]/about`).
|
||||||
If the file is given any name *other* than `index.md` or `_index.md`, then it will
|
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
|
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`.
|
content directory `about.md` would also create a page at `[base_url]/about`.
|
||||||
Another exception to that rule is that a filename starting with a YYYY-mm-dd date followed by
|
Another exception to that rule is that a filename starting with a datetime (YYYY-mm-dd or [a RFC3339 datetime](https://www.ietf.org/rfc/rfc3339.txt)) followed by
|
||||||
an underscore (`_`) or a dash (`-`) will use that date as the page date, unless already set
|
an underscore (`_`) or a dash (`-`) will use that date as the page date, unless already set
|
||||||
in the front-matter. The page name will be anything after `_`/`-` so a filename like `2018-10-10-hello-world.md` will
|
in the front-matter. The page name will be anything after `_`/`-` so a filename like `2018-10-10-hello-world.md` will
|
||||||
be available at `[base_url]/hello-world`
|
be available at `[base_url]/hello-world`
|
||||||
|
|
|
@ -26,6 +26,7 @@ Here is a full list of the supported languages and the short names you can use:
|
||||||
- Plain Text -> ["txt"]
|
- Plain Text -> ["txt"]
|
||||||
- Assembly x86 (NASM) -> ["asm", "inc", "nasm"]
|
- Assembly x86 (NASM) -> ["asm", "inc", "nasm"]
|
||||||
- Crystal -> ["cr"]
|
- Crystal -> ["cr"]
|
||||||
|
- Dart -> ["dart"]
|
||||||
- Elixir -> ["ex", "exs"]
|
- Elixir -> ["ex", "exs"]
|
||||||
- fsharp -> ["fs"]
|
- fsharp -> ["fs"]
|
||||||
- Handlebars -> ["handlebars", "handlebars.html", "hbr", "hbrs", "hbs", "hdbs", "hjs", "mu", "mustache", "rac", "stache", "template", "tmpl"]
|
- Handlebars -> ["handlebars", "handlebars.html", "hbr", "hbrs", "hbs", "hdbs", "hjs", "mu", "mustache", "rac", "stache", "template", "tmpl"]
|
||||||
|
|
|
@ -14,7 +14,9 @@ Zola is available on [Brew](https://brew.sh):
|
||||||
$ brew install zola
|
$ brew install zola
|
||||||
```
|
```
|
||||||
|
|
||||||
## Linux
|
## From source
|
||||||
|
To build it from source, you will need to have Git, [Rust (at least 1.30) and Cargo](https://www.rust-lang.org/)
|
||||||
|
installed. You will also need additional dependencies to compile [libsass](https://github.com/sass/libsass):
|
||||||
|
|
||||||
### Arch Linux
|
### Arch Linux
|
||||||
|
|
||||||
|
|
|
@ -7,10 +7,9 @@ Two things can get paginated: a section or a taxonomy term.
|
||||||
|
|
||||||
A paginated section gets the same `section` variable as a normal
|
A paginated section gets the same `section` variable as a normal
|
||||||
[section page](./documentation/templates/pages-sections.md#section-variables) minus its pages
|
[section page](./documentation/templates/pages-sections.md#section-variables) minus its pages
|
||||||
while a paginated taxonomy gets the a `taxonomy` variable of type `TaxonomyConfig`, equivalent
|
while
|
||||||
to the taxonomy definition in the `config.toml`.
|
|
||||||
|
|
||||||
In addition, a paginated page gets a `paginator` variable of the `Pager` type:
|
Both get a paginated page gets a `paginator` variable of the `Pager` type:
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
// How many items per page
|
// How many items per page
|
||||||
|
@ -33,3 +32,17 @@ pages: Array<Page>;
|
||||||
// Which page are we on
|
// Which page are we on
|
||||||
current_index: Number;
|
current_index: Number;
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Section
|
||||||
|
|
||||||
|
A paginated section gets the same `section` variable as a normal
|
||||||
|
[section page](./documentation/templates/pages-sections.md#section-variables) minus its pages.
|
||||||
|
|
||||||
|
## Taxonomy term
|
||||||
|
|
||||||
|
A paginated taxonomy gets two variables:
|
||||||
|
|
||||||
|
- a `taxonomy` variable of type `TaxonomyConfig`
|
||||||
|
- a `term` variable of type `TaxonomyTerm`.
|
||||||
|
|
||||||
|
See the [taxonomies page](./documentation/templates/taxonomies.md) for a detailed version of the types.
|
||||||
|
|
|
@ -17,8 +17,35 @@ permalink: String;
|
||||||
pages: Array<Page>;
|
pages: Array<Page>;
|
||||||
```
|
```
|
||||||
|
|
||||||
## Non-paginated taxonomies
|
and a `TaxonomyConfig`:
|
||||||
If a taxonomy is not paginated, the templates get the following variables:
|
|
||||||
|
```ts
|
||||||
|
name: String,
|
||||||
|
slug: String,
|
||||||
|
paginate_by: Number?;
|
||||||
|
paginate_path: String?;
|
||||||
|
rss: Bool;
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
### Taxonomy list (`list.html`)
|
||||||
|
|
||||||
|
This template is never paginated and therefore get the following variables in all cases.
|
||||||
|
|
||||||
|
```ts
|
||||||
|
// The site config
|
||||||
|
config: Config;
|
||||||
|
// The data of the taxonomy, from the config
|
||||||
|
taxonomy: TaxonomyConfig;
|
||||||
|
// The current full permalink for that page
|
||||||
|
current_url: String;
|
||||||
|
// The current path for that page
|
||||||
|
current_path: String;
|
||||||
|
// All terms for that taxonomy
|
||||||
|
terms: Array<TaxonomyTerm>;
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
### Single term (`single.html`)
|
### Single term (`single.html`)
|
||||||
```ts
|
```ts
|
||||||
|
@ -34,18 +61,5 @@ current_path: String;
|
||||||
term: TaxonomyTerm;
|
term: TaxonomyTerm;
|
||||||
```
|
```
|
||||||
|
|
||||||
### Taxonomy list (`list.html`)
|
A paginated taxonomy term will also get a `paginator` variable, see the [pagination page](./documentation/templates/pagination.md)
|
||||||
```ts
|
for more details on that.
|
||||||
// The site config
|
|
||||||
config: Config;
|
|
||||||
// The data of the taxonomy, from the config
|
|
||||||
taxonomy: TaxonomyConfig;
|
|
||||||
// The current full permalink for that page
|
|
||||||
current_url: String;
|
|
||||||
// The current path for that page
|
|
||||||
current_path: String;
|
|
||||||
// All terms for that taxonomy
|
|
||||||
terms: Array<TaxonomyTerm>;
|
|
||||||
```
|
|
||||||
|
|
||||||
## Paginated taxonomies
|
|
||||||
|
|
|
@ -12,6 +12,7 @@ apps:
|
||||||
zola:
|
zola:
|
||||||
command: zola
|
command: zola
|
||||||
plugs:
|
plugs:
|
||||||
|
- home
|
||||||
- network
|
- network
|
||||||
- network-bind
|
- network-bind
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,202 @@
|
||||||
|
%YAML 1.2
|
||||||
|
---
|
||||||
|
# http://www.sublimetext.com/docs/3/syntax.html
|
||||||
|
name: Dart
|
||||||
|
file_extensions:
|
||||||
|
- dart
|
||||||
|
scope: source.dart
|
||||||
|
contexts:
|
||||||
|
main:
|
||||||
|
- match: ^(#!.*)$
|
||||||
|
scope: meta.preprocessor.script.dart
|
||||||
|
- match: ^\w*\b(library|import|part of|part|export)\b
|
||||||
|
captures:
|
||||||
|
0: keyword.other.import.dart
|
||||||
|
push:
|
||||||
|
- meta_scope: meta.declaration.dart
|
||||||
|
- match: ;
|
||||||
|
captures:
|
||||||
|
0: punctuation.terminator.dart
|
||||||
|
pop: true
|
||||||
|
- include: strings
|
||||||
|
- include: comments
|
||||||
|
- match: \b(as|show|hide)\b
|
||||||
|
scope: keyword.other.import.dart
|
||||||
|
- include: comments
|
||||||
|
- include: punctuation
|
||||||
|
- include: annotations
|
||||||
|
- include: keywords
|
||||||
|
- include: constants-and-special-vars
|
||||||
|
- include: strings
|
||||||
|
annotations:
|
||||||
|
- match: "@[a-zA-Z]+"
|
||||||
|
scope: storage.type.annotation.dart
|
||||||
|
comments:
|
||||||
|
- match: /\*\*/
|
||||||
|
scope: comment.block.empty.dart
|
||||||
|
captures:
|
||||||
|
0: punctuation.definition.comment.dart
|
||||||
|
- include: comments-doc-oldschool
|
||||||
|
- include: comments-doc
|
||||||
|
- include: comments-inline
|
||||||
|
comments-doc:
|
||||||
|
- match: ///
|
||||||
|
scope: comment.block.documentation.dart
|
||||||
|
comments-doc-oldschool:
|
||||||
|
- match: /\*\*
|
||||||
|
push:
|
||||||
|
- meta_scope: comment.block.documentation.dart
|
||||||
|
- match: \*/
|
||||||
|
pop: true
|
||||||
|
- include: dartdoc
|
||||||
|
comments-inline:
|
||||||
|
- match: /\*
|
||||||
|
push:
|
||||||
|
- meta_scope: comment.block.dart
|
||||||
|
- match: \*/
|
||||||
|
pop: true
|
||||||
|
- match: ((//).*)$
|
||||||
|
captures:
|
||||||
|
1: comment.line.double-slash.dart
|
||||||
|
constants-and-special-vars:
|
||||||
|
- match: (?<!\$)\b(true|false|null)\b(?!\$)
|
||||||
|
scope: constant.language.dart
|
||||||
|
- match: (?<!\$)\b(this|super)\b(?!\$)
|
||||||
|
scope: variable.language.dart
|
||||||
|
- match: '(?<!\$)\b((0(x|X)[0-9a-fA-F]*)|(([0-9]+\.?[0-9]*)|(\.[0-9]+))((e|E)(\+|-)?[0-9]+)?)\b(?!\$)'
|
||||||
|
scope: constant.numeric.dart
|
||||||
|
- match: "(?<![a-zA-Z0-9_$])[_$]*[A-Z][a-zA-Z0-9_$]*"
|
||||||
|
scope: support.class.dart
|
||||||
|
- match: '([_$]*[a-z][a-zA-Z0-9_$]*)(\(|\s+=>)'
|
||||||
|
captures:
|
||||||
|
1: entity.name.function.dart
|
||||||
|
dartdoc:
|
||||||
|
- match: '(\[.*?\])'
|
||||||
|
captures:
|
||||||
|
0: variable.name.source.dart
|
||||||
|
- match: " .*"
|
||||||
|
captures:
|
||||||
|
0: variable.name.source.dart
|
||||||
|
- match: "```.*?$"
|
||||||
|
push:
|
||||||
|
- meta_content_scope: variable.other.source.dart
|
||||||
|
- match: "```"
|
||||||
|
pop: true
|
||||||
|
- match: (`.*?`)
|
||||||
|
captures:
|
||||||
|
0: variable.other.source.dart
|
||||||
|
- match: (`.*?`)
|
||||||
|
captures:
|
||||||
|
0: variable.other.source.dart
|
||||||
|
- match: (\* (( ).*))$
|
||||||
|
captures:
|
||||||
|
2: variable.other.source.dart
|
||||||
|
- match: (\* .*)$
|
||||||
|
keywords:
|
||||||
|
- match: (?<!\$)\bas\b(?!\$)
|
||||||
|
scope: keyword.cast.dart
|
||||||
|
- match: (?<!\$)\b(try|on|catch|finally|throw|rethrow)\b(?!\$)
|
||||||
|
scope: keyword.control.catch-exception.dart
|
||||||
|
- match: (?<!\$)\b(break|case|continue|default|do|else|for|if|in|return|switch|while)\b(?!\$)
|
||||||
|
scope: keyword.control.dart
|
||||||
|
- match: (?<!\$)\b(sync(\*)?|async(\*)?|await|yield(\*)?)\b(?!\$)
|
||||||
|
scope: keyword.control.dart
|
||||||
|
- match: (?<!\$)\bassert\b(?!\$)
|
||||||
|
scope: keyword.control.dart
|
||||||
|
- match: (?<!\$)\b(new)\b(?!\$)
|
||||||
|
scope: keyword.control.new.dart
|
||||||
|
- match: (?<!\$)\b(abstract|class|enum|extends|external|factory|implements|get|mixin|native|operator|set|typedef|with)\b(?!\$)
|
||||||
|
scope: keyword.declaration.dart
|
||||||
|
- match: (?<!\$)\b(is\!?)\b(?!\$)
|
||||||
|
scope: keyword.operator.dart
|
||||||
|
- match: '\?|:'
|
||||||
|
scope: keyword.operator.ternary.dart
|
||||||
|
- match: (<<|>>>?|~|\^|\||&)
|
||||||
|
scope: keyword.operator.bitwise.dart
|
||||||
|
- match: ((&|\^|\||<<|>>>?)=)
|
||||||
|
scope: keyword.operator.assignment.bitwise.dart
|
||||||
|
- match: (=>)
|
||||||
|
scope: keyword.operator.closure.dart
|
||||||
|
- match: (==|!=|<=?|>=?)
|
||||||
|
scope: keyword.operator.comparison.dart
|
||||||
|
- match: '(([+*/%-]|\~)=)'
|
||||||
|
scope: keyword.operator.assignment.arithmetic.dart
|
||||||
|
- match: (=)
|
||||||
|
scope: keyword.operator.assignment.dart
|
||||||
|
- match: (\-\-|\+\+)
|
||||||
|
scope: keyword.operator.increment-decrement.dart
|
||||||
|
- match: (\-|\+|\*|\/|\~\/|%)
|
||||||
|
scope: keyword.operator.arithmetic.dart
|
||||||
|
- match: (!|&&|\|\|)
|
||||||
|
scope: keyword.operator.logical.dart
|
||||||
|
- match: (?<!\$)\b(static|final|const)\b(?!\$)
|
||||||
|
scope: storage.modifier.dart
|
||||||
|
- match: (?<!\$)\b(?:void|bool|num|int|double|dynamic|var)\b(?!\$)
|
||||||
|
scope: storage.type.primitive.dart
|
||||||
|
punctuation:
|
||||||
|
- match: ","
|
||||||
|
scope: punctuation.comma.dart
|
||||||
|
- match: ;
|
||||||
|
scope: punctuation.terminator.dart
|
||||||
|
- match: \.
|
||||||
|
scope: punctuation.dot.dart
|
||||||
|
string-interp:
|
||||||
|
- match: '\$((\w+)|\{([^{}]+)\})'
|
||||||
|
captures:
|
||||||
|
2: variable.parameter.dart
|
||||||
|
3: variable.parameter.dart
|
||||||
|
- match: \\.
|
||||||
|
scope: constant.character.escape.dart
|
||||||
|
strings:
|
||||||
|
- match: (?<!r)"""
|
||||||
|
push:
|
||||||
|
- meta_scope: string.interpolated.triple.double.dart
|
||||||
|
- match: '"""(?!")'
|
||||||
|
pop: true
|
||||||
|
- include: string-interp
|
||||||
|
- match: (?<!r)'''
|
||||||
|
push:
|
||||||
|
- meta_scope: string.interpolated.triple.single.dart
|
||||||
|
- match: "'''(?!')"
|
||||||
|
pop: true
|
||||||
|
- include: string-interp
|
||||||
|
- match: r"""
|
||||||
|
push:
|
||||||
|
- meta_scope: string.quoted.triple.double.dart
|
||||||
|
- match: '"""(?!")'
|
||||||
|
pop: true
|
||||||
|
- match: r'''
|
||||||
|
push:
|
||||||
|
- meta_scope: string.quoted.triple.single.dart
|
||||||
|
- match: "'''(?!')"
|
||||||
|
pop: true
|
||||||
|
- match: (?<!\|r)"
|
||||||
|
push:
|
||||||
|
- meta_scope: string.interpolated.double.dart
|
||||||
|
- match: '"'
|
||||||
|
pop: true
|
||||||
|
- match: \n
|
||||||
|
scope: invalid.string.newline
|
||||||
|
- include: string-interp
|
||||||
|
- match: r"
|
||||||
|
push:
|
||||||
|
- meta_scope: string.quoted.double.dart
|
||||||
|
- match: '"'
|
||||||
|
pop: true
|
||||||
|
- match: \n
|
||||||
|
scope: invalid.string.newline
|
||||||
|
- match: (?<!\|r)'
|
||||||
|
push:
|
||||||
|
- meta_scope: string.interpolated.single.dart
|
||||||
|
- match: "'"
|
||||||
|
pop: true
|
||||||
|
- match: \n
|
||||||
|
scope: invalid.string.newline
|
||||||
|
- include: string-interp
|
||||||
|
- match: r'
|
||||||
|
push:
|
||||||
|
- meta_scope: string.quoted.single.dart
|
||||||
|
- match: "'"
|
||||||
|
pop: true
|
||||||
|
- match: \n
|
||||||
|
scope: invalid.string.newline
|
Binary file not shown.
|
@ -1,7 +1,21 @@
|
||||||
|
{% if not paginator %}
|
||||||
Tag: {{ term.name }}
|
Tag: {{ term.name }}
|
||||||
|
|
||||||
{% for page in term.pages %}
|
{% for page in term.pages %}
|
||||||
<article>
|
<article>
|
||||||
<h3 class="post__title"><a href="{{ page.permalink }}">{{ page.title }}</a></h3>
|
<h3 class="post__title"><a href="{{ page.permalink | safe }}">{{ page.title | safe }}</a></h3>
|
||||||
</article>
|
</article>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
{% else %}
|
||||||
|
Tag: {{ term.name }}
|
||||||
|
{% for page in paginator.pages %}
|
||||||
|
{{page.title|safe}}
|
||||||
|
{% endfor %}
|
||||||
|
Num pagers: {{ paginator.number_pagers }}
|
||||||
|
Page size: {{ paginator.paginate_by }}
|
||||||
|
Current index: {{ paginator.current_index }}
|
||||||
|
First: {{ paginator.first | safe }}
|
||||||
|
Last: {{ paginator.last | safe }}
|
||||||
|
{% if paginator.previous %}has_prev{% endif%}
|
||||||
|
{% if paginator.next %}has_next{% endif%}
|
||||||
|
{% endif %}
|
||||||
|
|
Loading…
Reference in New Issue