Fix slugified taxonomies rendering

Closes #1177
This commit is contained in:
Vincent Prouillet 2020-09-21 21:43:02 +02:00
parent f9ae897190
commit b313c97ceb
16 changed files with 294 additions and 159 deletions

View File

@ -1,7 +1,10 @@
# Changelog # Changelog
## 0.13.0 (unreleased) ## 0.12.1 (unreleased)
- Add line
- Fix the new `zola serve` being broken on Windows
- Fix slugified taxonomies not being rendered at the right path
## 0.12.0 (2020-09-04) ## 0.12.0 (2020-09-04)

View File

@ -17,10 +17,7 @@ lazy_static! {
} }
/// Returns the highlighter and whether it was found in the extra or not /// Returns the highlighter and whether it was found in the extra or not
pub fn get_highlighter( pub fn get_highlighter(language: Option<&str>, config: &Config) -> (HighlightLines<'static>, bool) {
language: Option<&str>,
config: &Config
) -> (HighlightLines<'static>, bool) {
let theme = &THEME_SET.themes[&config.highlight_theme]; let theme = &THEME_SET.themes[&config.highlight_theme];
let mut in_extra = false; let mut in_extra = false;

View File

@ -103,7 +103,7 @@ impl<'a> Paginator<'a> {
paginate_reversed: false, paginate_reversed: false,
root: PaginationRoot::Taxonomy(taxonomy, item), root: PaginationRoot::Taxonomy(taxonomy, item),
permalink: item.permalink.clone(), permalink: item.permalink.clone(),
path: format!("/{}/{}/", taxonomy.kind.name, item.slug), path: format!("/{}/{}/", taxonomy.slug, item.slug),
paginate_path: taxonomy paginate_path: taxonomy
.kind .kind
.paginate_path .paginate_path
@ -129,7 +129,7 @@ impl<'a> Paginator<'a> {
} }
for key in self.all_pages.to_mut().iter_mut() { for key in self.all_pages.to_mut().iter_mut() {
let page = library.get_page_by_key(key.clone()); let page = library.get_page_by_key(*key);
current_page.push(page.to_serialized_basic(library)); current_page.push(page.to_serialized_basic(library));
if current_page.len() == self.paginate_by { if current_page.len() == self.paginate_by {
@ -416,7 +416,11 @@ mod tests {
permalink: "https://vincent.is/tags/something/".to_string(), permalink: "https://vincent.is/tags/something/".to_string(),
pages: library.pages().keys().collect(), pages: library.pages().keys().collect(),
}; };
let taxonomy = Taxonomy { kind: taxonomy_def, items: vec![taxonomy_item.clone()] }; let taxonomy = Taxonomy {
kind: taxonomy_def,
slug: "tags".to_string(),
items: vec![taxonomy_item.clone()],
};
let paginator = Paginator::from_taxonomy(&taxonomy, &taxonomy_item, &library); let paginator = Paginator::from_taxonomy(&taxonomy, &taxonomy_item, &library);
assert_eq!(paginator.pagers.len(), 2); assert_eq!(paginator.pagers.len(), 2);
@ -431,6 +435,39 @@ mod tests {
assert_eq!(paginator.pagers[1].path, "/tags/something/page/2/"); assert_eq!(paginator.pagers[1].path, "/tags/something/page/2/");
} }
#[test]
fn test_can_create_paginator_for_slugified_taxonomy() {
let (_, library) = create_library(false, 3, false);
let taxonomy_def = TaxonomyConfig {
name: "some tags".to_string(),
paginate_by: Some(2),
..TaxonomyConfig::default()
};
let taxonomy_item = TaxonomyItem {
name: "Something".to_string(),
slug: "something".to_string(),
permalink: "https://vincent.is/some-tags/something/".to_string(),
pages: library.pages().keys().collect(),
};
let taxonomy = Taxonomy {
kind: taxonomy_def,
slug: "some-tags".to_string(),
items: vec![taxonomy_item.clone()],
};
let paginator = Paginator::from_taxonomy(&taxonomy, &taxonomy_item, &library);
assert_eq!(paginator.pagers.len(), 2);
assert_eq!(paginator.pagers[0].index, 1);
assert_eq!(paginator.pagers[0].pages.len(), 2);
assert_eq!(paginator.pagers[0].permalink, "https://vincent.is/some-tags/something/");
assert_eq!(paginator.pagers[0].path, "/some-tags/something/");
assert_eq!(paginator.pagers[1].index, 2);
assert_eq!(paginator.pagers[1].pages.len(), 2);
assert_eq!(paginator.pagers[1].permalink, "https://vincent.is/some-tags/something/page/2/");
assert_eq!(paginator.pagers[1].path, "/some-tags/something/page/2/");
}
// https://github.com/getzola/zola/issues/866 // https://github.com/getzola/zola/issues/866
#[test] #[test]
fn works_with_empty_paginate_path() { fn works_with_empty_paginate_path() {

View File

@ -53,6 +53,7 @@ impl TaxonomyItem {
pub fn new( pub fn new(
name: &str, name: &str,
taxonomy: &TaxonomyConfig, taxonomy: &TaxonomyConfig,
taxo_slug: &str,
config: &Config, config: &Config,
keys: Vec<DefaultKey>, keys: Vec<DefaultKey>,
library: &Library, library: &Library,
@ -72,7 +73,6 @@ impl TaxonomyItem {
.collect(); .collect();
let (mut pages, ignored_pages) = sort_pages_by_date(data); let (mut pages, ignored_pages) = sort_pages_by_date(data);
let item_slug = slugify_paths(name, config.slugify.taxonomies); let item_slug = slugify_paths(name, config.slugify.taxonomies);
let taxo_slug = slugify_paths(&taxonomy.name, config.slugify.taxonomies);
let permalink = if taxonomy.lang != config.default_language { let permalink = if taxonomy.lang != config.default_language {
config.make_permalink(&format!("/{}/{}/{}", taxonomy.lang, taxo_slug, item_slug)) config.make_permalink(&format!("/{}/{}/{}", taxonomy.lang, taxo_slug, item_slug))
} else { } else {
@ -118,6 +118,7 @@ impl<'a> SerializedTaxonomy<'a> {
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub struct Taxonomy { pub struct Taxonomy {
pub kind: TaxonomyConfig, pub kind: TaxonomyConfig,
pub slug: String,
// this vec is sorted by the count of item // this vec is sorted by the count of item
pub items: Vec<TaxonomyItem>, pub items: Vec<TaxonomyItem>,
} }
@ -130,8 +131,9 @@ impl Taxonomy {
library: &Library, library: &Library,
) -> Taxonomy { ) -> Taxonomy {
let mut sorted_items = vec![]; let mut sorted_items = vec![];
let slug = slugify_paths(&kind.name, config.slugify.taxonomies);
for (name, pages) in items { for (name, pages) in items {
sorted_items.push(TaxonomyItem::new(&name, &kind, config, pages, library)); sorted_items.push(TaxonomyItem::new(&name, &kind, &slug, config, pages, library));
} }
//sorted_items.sort_by(|a, b| a.name.cmp(&b.name)); //sorted_items.sort_by(|a, b| a.name.cmp(&b.name));
sorted_items.sort_by(|a, b| match a.slug.cmp(&b.slug) { sorted_items.sort_by(|a, b| match a.slug.cmp(&b.slug) {
@ -150,7 +152,7 @@ impl Taxonomy {
false false
} }
}); });
Taxonomy { kind, items: sorted_items } Taxonomy { kind, slug, items: sorted_items }
} }
pub fn len(&self) -> usize { pub fn len(&self) -> usize {

View File

@ -1,11 +1,11 @@
use syntect::html::{IncludeBackground, styled_line_to_highlighted_html};
use syntect::easy::HighlightLines;
use syntect::parsing::SyntaxSet;
use syntect::highlighting::{Color, Theme, Style};
use config::Config;
use config::highlighting::{get_highlighter, SYNTAX_SET, THEME_SET}; use config::highlighting::{get_highlighter, SYNTAX_SET, THEME_SET};
use config::Config;
use std::cmp::min; use std::cmp::min;
use std::collections::HashSet; use std::collections::HashSet;
use syntect::easy::HighlightLines;
use syntect::highlighting::{Color, Style, Theme};
use syntect::html::{styled_line_to_highlighted_html, IncludeBackground};
use syntect::parsing::SyntaxSet;
use super::fence::{FenceSettings, Range}; use super::fence::{FenceSettings, Range};
@ -22,11 +22,7 @@ pub struct CodeBlock<'config> {
} }
impl<'config> CodeBlock<'config> { impl<'config> CodeBlock<'config> {
pub fn new( pub fn new(fence_info: &str, config: &'config Config, background: IncludeBackground) -> Self {
fence_info: &str,
config: &'config Config,
background: IncludeBackground,
) -> Self {
let fence_info = FenceSettings::new(fence_info); let fence_info = FenceSettings::new(fence_info);
let theme = &THEME_SET.themes[&config.highlight_theme]; let theme = &THEME_SET.themes[&config.highlight_theme];
let (highlighter, in_extra) = get_highlighter(fence_info.language, config); let (highlighter, in_extra) = get_highlighter(fence_info.language, config);
@ -45,10 +41,8 @@ impl<'config> CodeBlock<'config> {
} }
pub fn highlight(&mut self, text: &str) -> String { pub fn highlight(&mut self, text: &str) -> String {
let highlighted = self.highlighter.highlight( let highlighted =
text, self.highlighter.highlight(text, self.extra_syntax_set.unwrap_or(&SYNTAX_SET));
self.extra_syntax_set.unwrap_or(&SYNTAX_SET),
);
let line_boundaries = self.find_line_boundaries(&highlighted); let line_boundaries = self.find_line_boundaries(&highlighted);
// First we make sure that `highlighted` is split at every line // First we make sure that `highlighted` is split at every line
@ -61,9 +55,8 @@ impl<'config> CodeBlock<'config> {
// we don't use it later. // we don't use it later.
let mut highlighted = perform_split(&highlighted, line_boundaries); let mut highlighted = perform_split(&highlighted, line_boundaries);
let hl_background = self.theme.settings.line_highlight let hl_background =
.unwrap_or(Color { r: 255, g: 255, b: 0, a: 0 }); self.theme.settings.line_highlight.unwrap_or(Color { r: 255, g: 255, b: 0, a: 0 });
let hl_lines = self.get_highlighted_lines(); let hl_lines = self.get_highlighted_lines();
color_highlighted_lines(&mut highlighted, &hl_lines, hl_background); color_highlighted_lines(&mut highlighted, &hl_lines, hl_background);
@ -76,10 +69,7 @@ impl<'config> CodeBlock<'config> {
for (vec_idx, (_style, s)) in styled.iter().enumerate() { for (vec_idx, (_style, s)) in styled.iter().enumerate() {
for (str_idx, character) in s.char_indices() { for (str_idx, character) in s.char_indices() {
if character == '\n' { if character == '\n' {
boundaries.push(StyledIdx { boundaries.push(StyledIdx { vec_idx, str_idx });
vec_idx,
str_idx,
});
} }
} }
} }
@ -90,7 +80,7 @@ impl<'config> CodeBlock<'config> {
fn get_highlighted_lines(&self) -> HashSet<usize> { fn get_highlighted_lines(&self) -> HashSet<usize> {
let mut lines = HashSet::new(); let mut lines = HashSet::new();
for range in &self.highlight_lines { for range in &self.highlight_lines {
for line in range.from ..= min(range.to, self.num_lines) { for line in range.from..=min(range.to, self.num_lines) {
// Ranges are one-indexed // Ranges are one-indexed
lines.insert(line.saturating_sub(1)); lines.insert(line.saturating_sub(1));
} }
@ -123,7 +113,7 @@ fn get_str_idx_if_vec_idx_is(idx: Option<&StyledIdx>, vec_idx: usize) -> Option<
/// the `StyledIdx` type. /// the `StyledIdx` type.
fn perform_split<'b>( fn perform_split<'b>(
split: &[(Style, &'b str)], split: &[(Style, &'b str)],
line_boundaries: Vec<StyledIdx> line_boundaries: Vec<StyledIdx>,
) -> Vec<(Style, &'b str)> { ) -> Vec<(Style, &'b str)> {
let mut result = Vec::new(); let mut result = Vec::new();
@ -172,11 +162,7 @@ fn perform_split<'b>(
result result
} }
fn color_highlighted_lines( fn color_highlighted_lines(data: &mut [(Style, &str)], lines: &HashSet<usize>, background: Color) {
data: &mut [(Style, &str)],
lines: &HashSet<usize>,
background: Color,
) {
if lines.is_empty() { if lines.is_empty() {
return; return;
} }

View File

@ -9,22 +9,16 @@ impl Range {
match s.find('-') { match s.find('-') {
Some(dash) => { Some(dash) => {
let mut from = s[..dash].parse().ok()?; let mut from = s[..dash].parse().ok()?;
let mut to = s[dash+1..].parse().ok()?; let mut to = s[dash + 1..].parse().ok()?;
if to < from { if to < from {
std::mem::swap(&mut from, &mut to); std::mem::swap(&mut from, &mut to);
} }
Some(Range { Some(Range { from, to })
from, }
to,
})
},
None => { None => {
let val = s.parse().ok()?; let val = s.parse().ok()?;
Some(Range { Some(Range { from: val, to: val })
from: val, }
to: val,
})
},
} }
} }
} }
@ -37,11 +31,7 @@ pub struct FenceSettings<'a> {
} }
impl<'a> FenceSettings<'a> { impl<'a> FenceSettings<'a> {
pub fn new(fence_info: &'a str) -> Self { pub fn new(fence_info: &'a str) -> Self {
let mut me = Self { let mut me = Self { language: None, line_numbers: false, highlight_lines: Vec::new() };
language: None,
line_numbers: false,
highlight_lines: Vec::new(),
};
for token in FenceIter::new(fence_info) { for token in FenceIter::new(fence_info) {
match token { match token {
@ -67,9 +57,7 @@ struct FenceIter<'a> {
} }
impl<'a> FenceIter<'a> { impl<'a> FenceIter<'a> {
fn new(fence_info: &'a str) -> Self { fn new(fence_info: &'a str) -> Self {
Self { Self { split: fence_info.split(',') }
split: fence_info.split(','),
}
} }
} }
@ -92,10 +80,10 @@ impl<'a> Iterator for FenceIter<'a> {
} }
} }
return Some(FenceToken::HighlightLines(ranges)); return Some(FenceToken::HighlightLines(ranges));
}, }
lang => { lang => {
return Some(FenceToken::Language(lang)); return Some(FenceToken::Language(lang));
}, }
} }
} }
} }

View File

@ -5,8 +5,6 @@ use tera::Tera;
use config::Config; use config::Config;
use front_matter::InsertAnchor; use front_matter::InsertAnchor;
use rendering::{render_content, RenderContext}; use rendering::{render_content, RenderContext};
use templates::ZOLA_TERA;
use utils::slugs::SlugifyStrategy;
macro_rules! colored_html_line { macro_rules! colored_html_line {
( @no $s:expr ) => {{ ( @no $s:expr ) => {{
@ -41,19 +39,26 @@ fn hl_lines_simple() {
let mut config = Config::default(); let mut config = Config::default();
config.highlight_code = true; config.highlight_code = true;
let context = RenderContext::new(&tera_ctx, &config, "", &permalinks_ctx, InsertAnchor::None); let context = RenderContext::new(&tera_ctx, &config, "", &permalinks_ctx, InsertAnchor::None);
let res = render_content(r#" let res = render_content(
r#"
```hl_lines=2 ```hl_lines=2
foo foo
bar bar
bar bar
baz baz
``` ```
"#, &context).unwrap(); "#,
assert_eq!(res.body, colored_html!( &context,
)
.unwrap();
assert_eq!(
res.body,
colored_html!(
@no "foo", @no "foo",
@hl "bar", @hl "bar",
@no "bar\nbaz", @no "bar\nbaz",
)); )
);
} }
#[test] #[test]
@ -63,19 +68,26 @@ fn hl_lines_in_middle() {
let mut config = Config::default(); let mut config = Config::default();
config.highlight_code = true; config.highlight_code = true;
let context = RenderContext::new(&tera_ctx, &config, "", &permalinks_ctx, InsertAnchor::None); let context = RenderContext::new(&tera_ctx, &config, "", &permalinks_ctx, InsertAnchor::None);
let res = render_content(r#" let res = render_content(
r#"
```hl_lines=2-3 ```hl_lines=2-3
foo foo
bar bar
bar bar
baz baz
``` ```
"#, &context).unwrap(); "#,
assert_eq!(res.body, colored_html!( &context,
)
.unwrap();
assert_eq!(
res.body,
colored_html!(
@no "foo", @no "foo",
@hl "bar\nbar", @hl "bar\nbar",
@no "baz", @no "baz",
)); )
);
} }
#[test] #[test]
@ -85,17 +97,24 @@ fn hl_lines_all() {
let mut config = Config::default(); let mut config = Config::default();
config.highlight_code = true; config.highlight_code = true;
let context = RenderContext::new(&tera_ctx, &config, "", &permalinks_ctx, InsertAnchor::None); let context = RenderContext::new(&tera_ctx, &config, "", &permalinks_ctx, InsertAnchor::None);
let res = render_content(r#" let res = render_content(
r#"
```hl_lines=1-4 ```hl_lines=1-4
foo foo
bar bar
bar bar
baz baz
``` ```
"#, &context).unwrap(); "#,
assert_eq!(res.body, colored_html!( &context,
)
.unwrap();
assert_eq!(
res.body,
colored_html!(
@hl "foo\nbar\nbar\nbaz", @hl "foo\nbar\nbar\nbaz",
)); )
);
} }
#[test] #[test]
@ -105,18 +124,25 @@ fn hl_lines_start_from_one() {
let mut config = Config::default(); let mut config = Config::default();
config.highlight_code = true; config.highlight_code = true;
let context = RenderContext::new(&tera_ctx, &config, "", &permalinks_ctx, InsertAnchor::None); let context = RenderContext::new(&tera_ctx, &config, "", &permalinks_ctx, InsertAnchor::None);
let res = render_content(r#" let res = render_content(
r#"
```hl_lines=1-3 ```hl_lines=1-3
foo foo
bar bar
bar bar
baz baz
``` ```
"#, &context).unwrap(); "#,
assert_eq!(res.body, colored_html!( &context,
)
.unwrap();
assert_eq!(
res.body,
colored_html!(
@hl "foo\nbar\nbar", @hl "foo\nbar\nbar",
@no "baz", @no "baz",
)); )
);
} }
#[test] #[test]
@ -126,18 +152,25 @@ fn hl_lines_start_from_zero() {
let mut config = Config::default(); let mut config = Config::default();
config.highlight_code = true; config.highlight_code = true;
let context = RenderContext::new(&tera_ctx, &config, "", &permalinks_ctx, InsertAnchor::None); let context = RenderContext::new(&tera_ctx, &config, "", &permalinks_ctx, InsertAnchor::None);
let res = render_content(r#" let res = render_content(
r#"
```hl_lines=0-3 ```hl_lines=0-3
foo foo
bar bar
bar bar
baz baz
``` ```
"#, &context).unwrap(); "#,
assert_eq!(res.body, colored_html!( &context,
)
.unwrap();
assert_eq!(
res.body,
colored_html!(
@hl "foo\nbar\nbar", @hl "foo\nbar\nbar",
@no "baz", @no "baz",
)); )
);
} }
#[test] #[test]
@ -147,18 +180,25 @@ fn hl_lines_end() {
let mut config = Config::default(); let mut config = Config::default();
config.highlight_code = true; config.highlight_code = true;
let context = RenderContext::new(&tera_ctx, &config, "", &permalinks_ctx, InsertAnchor::None); let context = RenderContext::new(&tera_ctx, &config, "", &permalinks_ctx, InsertAnchor::None);
let res = render_content(r#" let res = render_content(
r#"
```hl_lines=3-4 ```hl_lines=3-4
foo foo
bar bar
bar bar
baz baz
``` ```
"#, &context).unwrap(); "#,
assert_eq!(res.body, colored_html!( &context,
)
.unwrap();
assert_eq!(
res.body,
colored_html!(
@no "foo\nbar", @no "foo\nbar",
@hl "bar\nbaz", @hl "bar\nbaz",
)); )
);
} }
#[test] #[test]
@ -168,18 +208,25 @@ fn hl_lines_end_out_of_bounds() {
let mut config = Config::default(); let mut config = Config::default();
config.highlight_code = true; config.highlight_code = true;
let context = RenderContext::new(&tera_ctx, &config, "", &permalinks_ctx, InsertAnchor::None); let context = RenderContext::new(&tera_ctx, &config, "", &permalinks_ctx, InsertAnchor::None);
let res = render_content(r#" let res = render_content(
r#"
```hl_lines=3-4294967295 ```hl_lines=3-4294967295
foo foo
bar bar
bar bar
baz baz
``` ```
"#, &context).unwrap(); "#,
assert_eq!(res.body, colored_html!( &context,
)
.unwrap();
assert_eq!(
res.body,
colored_html!(
@no "foo\nbar", @no "foo\nbar",
@hl "bar\nbaz", @hl "bar\nbaz",
)); )
);
} }
#[test] #[test]
@ -189,18 +236,25 @@ fn hl_lines_overlap() {
let mut config = Config::default(); let mut config = Config::default();
config.highlight_code = true; config.highlight_code = true;
let context = RenderContext::new(&tera_ctx, &config, "", &permalinks_ctx, InsertAnchor::None); let context = RenderContext::new(&tera_ctx, &config, "", &permalinks_ctx, InsertAnchor::None);
let res = render_content(r#" let res = render_content(
r#"
```hl_lines=2-3 1-2 ```hl_lines=2-3 1-2
foo foo
bar bar
bar bar
baz baz
``` ```
"#, &context).unwrap(); "#,
assert_eq!(res.body, colored_html!( &context,
)
.unwrap();
assert_eq!(
res.body,
colored_html!(
@hl "foo\nbar\nbar", @hl "foo\nbar\nbar",
@no "baz", @no "baz",
)); )
);
} }
#[test] #[test]
fn hl_lines_multiple() { fn hl_lines_multiple() {
@ -209,18 +263,25 @@ fn hl_lines_multiple() {
let mut config = Config::default(); let mut config = Config::default();
config.highlight_code = true; config.highlight_code = true;
let context = RenderContext::new(&tera_ctx, &config, "", &permalinks_ctx, InsertAnchor::None); let context = RenderContext::new(&tera_ctx, &config, "", &permalinks_ctx, InsertAnchor::None);
let res = render_content(r#" let res = render_content(
r#"
```hl_lines=2-3,hl_lines=1-2 ```hl_lines=2-3,hl_lines=1-2
foo foo
bar bar
bar bar
baz baz
``` ```
"#, &context).unwrap(); "#,
assert_eq!(res.body, colored_html!( &context,
)
.unwrap();
assert_eq!(
res.body,
colored_html!(
@hl "foo\nbar\nbar", @hl "foo\nbar\nbar",
@no "baz", @no "baz",
)); )
);
} }
#[test] #[test]
@ -230,18 +291,25 @@ fn hl_lines_extra_spaces() {
let mut config = Config::default(); let mut config = Config::default();
config.highlight_code = true; config.highlight_code = true;
let context = RenderContext::new(&tera_ctx, &config, "", &permalinks_ctx, InsertAnchor::None); let context = RenderContext::new(&tera_ctx, &config, "", &permalinks_ctx, InsertAnchor::None);
let res = render_content(r#" let res = render_content(
r#"
``` hl_lines = 2 - 3 1 - 2 ``` hl_lines = 2 - 3 1 - 2
foo foo
bar bar
bar bar
baz baz
``` ```
"#, &context).unwrap(); "#,
assert_eq!(res.body, colored_html!( &context,
)
.unwrap();
assert_eq!(
res.body,
colored_html!(
@hl "foo\nbar\nbar", @hl "foo\nbar\nbar",
@no "baz", @no "baz",
)); )
);
} }
#[test] #[test]
@ -251,19 +319,26 @@ fn hl_lines_int_and_range() {
let mut config = Config::default(); let mut config = Config::default();
config.highlight_code = true; config.highlight_code = true;
let context = RenderContext::new(&tera_ctx, &config, "", &permalinks_ctx, InsertAnchor::None); let context = RenderContext::new(&tera_ctx, &config, "", &permalinks_ctx, InsertAnchor::None);
let res = render_content(r#" let res = render_content(
r#"
```hl_lines=1 3-4 ```hl_lines=1 3-4
foo foo
bar bar
bar bar
baz baz
``` ```
"#, &context).unwrap(); "#,
assert_eq!(res.body, colored_html!( &context,
)
.unwrap();
assert_eq!(
res.body,
colored_html!(
@hl "foo", @hl "foo",
@no "bar", @no "bar",
@hl "bar\nbaz", @hl "bar\nbaz",
)); )
);
} }
#[test] #[test]
@ -273,22 +348,28 @@ fn hl_lines_single_line_range() {
let mut config = Config::default(); let mut config = Config::default();
config.highlight_code = true; config.highlight_code = true;
let context = RenderContext::new(&tera_ctx, &config, "", &permalinks_ctx, InsertAnchor::None); let context = RenderContext::new(&tera_ctx, &config, "", &permalinks_ctx, InsertAnchor::None);
let res = render_content(r#" let res = render_content(
r#"
```hl_lines=2-2 ```hl_lines=2-2
foo foo
bar bar
bar bar
baz baz
``` ```
"#, &context).unwrap(); "#,
assert_eq!(res.body, colored_html!( &context,
)
.unwrap();
assert_eq!(
res.body,
colored_html!(
@no "foo", @no "foo",
@hl "bar", @hl "bar",
@no "bar\nbaz", @no "bar\nbaz",
)); )
);
} }
#[test] #[test]
fn hl_lines_reverse_range() { fn hl_lines_reverse_range() {
let tera_ctx = Tera::default(); let tera_ctx = Tera::default();
@ -296,17 +377,24 @@ fn hl_lines_reverse_range() {
let mut config = Config::default(); let mut config = Config::default();
config.highlight_code = true; config.highlight_code = true;
let context = RenderContext::new(&tera_ctx, &config, "", &permalinks_ctx, InsertAnchor::None); let context = RenderContext::new(&tera_ctx, &config, "", &permalinks_ctx, InsertAnchor::None);
let res = render_content(r#" let res = render_content(
r#"
```hl_lines=3-2 ```hl_lines=3-2
foo foo
bar bar
bar bar
baz baz
``` ```
"#, &context).unwrap(); "#,
assert_eq!(res.body, colored_html!( &context,
)
.unwrap();
assert_eq!(
res.body,
colored_html!(
@no "foo", @no "foo",
@hl "bar\nbar", @hl "bar\nbar",
@no "baz", @no "baz",
)); )
);
} }

View File

@ -452,12 +452,17 @@ impl Site {
let cfg = &Cfg { minify_js: false }; let cfg = &Cfg { minify_js: false };
let mut input_bytes = html.as_bytes().to_vec(); let mut input_bytes = html.as_bytes().to_vec();
match with_friendly_error(&mut input_bytes, cfg) { match with_friendly_error(&mut input_bytes, cfg) {
Ok(_len) => match std::str::from_utf8(&mut input_bytes) { Ok(_len) => match std::str::from_utf8(&input_bytes) {
Ok(result) => Ok(result.to_string()), Ok(result) => Ok(result.to_string()),
Err(err) => bail!("Failed to convert bytes to string : {}", err), Err(err) => bail!("Failed to convert bytes to string : {}", err),
}, },
Err(minify_error) => { Err(minify_error) => {
bail!("Failed to truncate html at character {}: {} \n {}", minify_error.position, minify_error.message, minify_error.code_context); bail!(
"Failed to truncate html at character {}: {} \n {}",
minify_error.position,
minify_error.message,
minify_error.code_context
);
} }
} }
} }
@ -548,7 +553,7 @@ impl Site {
let site_path = let site_path =
if filename != "index.html" { site_path.join(filename) } else { site_path }; if filename != "index.html" { site_path.join(filename) } else { site_path };
&SITE_CONTENT.write().unwrap().insert(site_path, final_content); SITE_CONTENT.write().unwrap().insert(site_path, final_content);
} }
} }
@ -763,7 +768,7 @@ impl Site {
components.push(taxonomy.kind.lang.as_ref()); components.push(taxonomy.kind.lang.as_ref());
} }
components.push(taxonomy.kind.name.as_ref()); components.push(taxonomy.slug.as_ref());
let list_output = let list_output =
taxonomy.render_all_terms(&self.tera, &self.config, &self.library.read().unwrap())?; taxonomy.render_all_terms(&self.tera, &self.config, &self.library.read().unwrap())?;
@ -793,7 +798,7 @@ impl Site {
if taxonomy.kind.feed { if taxonomy.kind.feed {
self.render_feed( self.render_feed(
item.pages.iter().map(|p| library.get_page_by_key(*p)).collect(), item.pages.iter().map(|p| library.get_page_by_key(*p)).collect(),
Some(&PathBuf::from(format!("{}/{}", taxonomy.kind.name, item.slug))), Some(&PathBuf::from(format!("{}/{}", taxonomy.slug, item.slug))),
if self.config.is_multilingual() && !taxonomy.kind.lang.is_empty() { if self.config.is_multilingual() && !taxonomy.kind.lang.is_empty() {
&taxonomy.kind.lang &taxonomy.kind.lang
} else { } else {

View File

@ -154,6 +154,10 @@ fn can_build_site_without_live_reload() {
assert_eq!(file_exists!(public, "categories/index.html"), true); assert_eq!(file_exists!(public, "categories/index.html"), true);
assert_eq!(file_exists!(public, "categories/a-category/index.html"), true); assert_eq!(file_exists!(public, "categories/a-category/index.html"), true);
assert_eq!(file_exists!(public, "categories/a-category/atom.xml"), true); assert_eq!(file_exists!(public, "categories/a-category/atom.xml"), true);
// and podcast_authors (https://github.com/getzola/zola/issues/1177)
assert_eq!(file_exists!(public, "podcast-authors/index.html"), true);
assert_eq!(file_exists!(public, "podcast-authors/some-person/index.html"), true);
assert_eq!(file_exists!(public, "podcast-authors/some-person/atom.xml"), true);
// But no tags // But no tags
assert_eq!(file_exists!(public, "tags/index.html"), false); assert_eq!(file_exists!(public, "tags/index.html"), false);

View File

@ -572,6 +572,7 @@ mod tests {
let tag = TaxonomyItem::new( let tag = TaxonomyItem::new(
"Programming", "Programming",
&taxo_config, &taxo_config,
"tags",
&config, &config,
vec![], vec![],
&library.read().unwrap(), &library.read().unwrap(),
@ -579,12 +580,14 @@ mod tests {
let tag_fr = TaxonomyItem::new( let tag_fr = TaxonomyItem::new(
"Programmation", "Programmation",
&taxo_config_fr, &taxo_config_fr,
"tags",
&config, &config,
vec![], vec![],
&library.read().unwrap(), &library.read().unwrap(),
); );
let tags = Taxonomy { kind: taxo_config, items: vec![tag] }; let tags = Taxonomy { kind: taxo_config, slug: "tags".to_string(), items: vec![tag] };
let tags_fr = Taxonomy { kind: taxo_config_fr, items: vec![tag_fr] }; let tags_fr =
Taxonomy { kind: taxo_config_fr, slug: "tags".to_string(), items: vec![tag_fr] };
let taxonomies = vec![tags.clone(), tags_fr.clone()]; let taxonomies = vec![tags.clone(), tags_fr.clone()];
let static_fn = let static_fn =
@ -647,10 +650,12 @@ mod tests {
..TaxonomyConfig::default() ..TaxonomyConfig::default()
}; };
let library = Library::new(0, 0, false); let library = Library::new(0, 0, false);
let tag = TaxonomyItem::new("Programming", &taxo_config, &config, vec![], &library); let tag = TaxonomyItem::new("Programming", &taxo_config, "tags", &config, vec![], &library);
let tag_fr = TaxonomyItem::new("Programmation", &taxo_config_fr, &config, vec![], &library); let tag_fr =
let tags = Taxonomy { kind: taxo_config, items: vec![tag] }; TaxonomyItem::new("Programmation", &taxo_config_fr, "tags", &config, vec![], &library);
let tags_fr = Taxonomy { kind: taxo_config_fr, items: vec![tag_fr] }; let tags = Taxonomy { kind: taxo_config, slug: "tags".to_string(), items: vec![tag] };
let tags_fr =
Taxonomy { kind: taxo_config_fr, slug: "tags".to_string(), items: vec![tag_fr] };
let taxonomies = vec![tags.clone(), tags_fr.clone()]; let taxonomies = vec![tags.clone(), tags_fr.clone()];
let static_fn = let static_fn =

View File

@ -94,7 +94,7 @@ async fn handle_request(req: Request<Body>, root: PathBuf) -> Result<Response<Bo
ResolveResult::MethodNotMatched => return Ok(method_not_allowed()), ResolveResult::MethodNotMatched => return Ok(method_not_allowed()),
ResolveResult::NotFound | ResolveResult::UriNotMatched => { ResolveResult::NotFound | ResolveResult::UriNotMatched => {
let not_found_path = RelativePath::new("404.html"); let not_found_path = RelativePath::new("404.html");
let content_404 = SITE_CONTENT.read().unwrap().get(not_found_path).map(|x| x.clone()); let content_404 = SITE_CONTENT.read().unwrap().get(not_found_path).cloned();
return Ok(not_found(content_404)); return Ok(not_found(content_404));
} }
_ => (), _ => (),

View File

@ -16,7 +16,7 @@ fn main() {
"." => env::current_dir().unwrap(), "." => env::current_dir().unwrap(),
path => PathBuf::from(path) path => PathBuf::from(path)
.canonicalize() .canonicalize()
.expect(&format!("Cannot find root directory: {}", path)), .unwrap_or_else(|_| panic!("Cannot find root directory: {}", path)),
}; };
let config_file = match matches.value_of("config") { let config_file = match matches.value_of("config") {
Some(path) => PathBuf::from(path), Some(path) => PathBuf::from(path),

View File

@ -4,16 +4,21 @@ highlight_code = true
compile_sass = true compile_sass = true
generate_feed = true generate_feed = true
theme = "sample" theme = "sample"
slugify_paths = true
taxonomies = [ taxonomies = [
{name = "categories", feed = true}, {name = "categories", feed = true},
{name = "podcast_authors", feed = true},
] ]
extra_syntaxes = ["syntaxes"] extra_syntaxes = ["syntaxes"]
ignored_content = ["*/ignored.md"] ignored_content = ["*/ignored.md"]
[slugify]
paths = "on"
taxonomies = "on"
anchors = "on"
[link_checker] [link_checker]
skip_prefixes = [ skip_prefixes = [
"http://[2001:db8::]/", "http://[2001:db8::]/",

View File

@ -5,6 +5,7 @@ date = 2017-01-01
[taxonomies] [taxonomies]
categories = ["a-category"] categories = ["a-category"]
podcast_authors = ["Some Person"]
+++ +++
# A title # A title

View File

@ -0,0 +1,4 @@
{% for term in terms %}
{{ term.name }} {{ term.slug }} {{ term.pages | length }}
{% endfor %}
Current path: {{ current_path }}

View File

@ -0,0 +1,10 @@
Category: {{ term.name }}
{% for page in term.pages %}
<article>
<h3 class="post__title"><a href="{{ page.permalink }}">{{ page.title }}</a></h3>
</article>
{% endfor %}
Current path: {{ current_path }}