commit
d6437856a4
10
README.md
10
README.md
|
@ -123,14 +123,18 @@ which template will be used to render that section.
|
||||||
Sections will also automatically pick up their subsections, allowing you to make some complex pages layout and
|
Sections will also automatically pick up their subsections, allowing you to make some complex pages layout and
|
||||||
table of contents.
|
table of contents.
|
||||||
|
|
||||||
|
A special case is the `_index.md` at the root of the `content` directory which represents the homepage. It is only there
|
||||||
|
to control pagination and sorting of the homepage.
|
||||||
|
|
||||||
### Code highlighting themes
|
### Code highlighting themes
|
||||||
Code highlighting can be turned on by setting `highlight_code = true` in `config.toml`.
|
Code highlighting can be turned on by setting `highlight_code = true` in `config.toml`.
|
||||||
|
|
||||||
When turned on, all text between backticks will be highlighted, like the example below.
|
When turned on, all text between backticks will be highlighted, like the example below.
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
let site = Site::new();
|
let site = Site::new();
|
||||||
```
|
```
|
||||||
|
|
||||||
If the name of the language is not given, it will default to plain-text highlighting.
|
If the name of the language is not given, it will default to plain-text highlighting.
|
||||||
|
|
||||||
Gutenberg uses Sublime Text themes for syntax highlighting. It comes with the following theme
|
Gutenberg uses Sublime Text themes for syntax highlighting. It comes with the following theme
|
||||||
|
|
|
@ -37,7 +37,7 @@ lazy_static!{
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A ShortCode that has a body
|
/// A shortcode that has a body
|
||||||
/// Called by having some content like {% ... %} body {% end %}
|
/// Called by having some content like {% ... %} body {% end %}
|
||||||
/// We need the struct to hold the data while we're processing the markdown
|
/// We need the struct to hold the data while we're processing the markdown
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -62,7 +62,7 @@ impl ShortCode {
|
||||||
|
|
||||||
pub fn render(&self, tera: &Tera) -> Result<String> {
|
pub fn render(&self, tera: &Tera) -> Result<String> {
|
||||||
let mut context = Context::new();
|
let mut context = Context::new();
|
||||||
for (key, value) in self.args.iter() {
|
for (key, value) in &self.args {
|
||||||
context.add(key, value);
|
context.add(key, value);
|
||||||
}
|
}
|
||||||
context.add("body", &self.body);
|
context.add("body", &self.body);
|
||||||
|
@ -132,7 +132,7 @@ pub fn markdown_to_html(content: &str, permalinks: &HashMap<String, String>, ter
|
||||||
// for example an article could have several titles named Example
|
// for example an article could have several titles named Example
|
||||||
// We add a counter after the slug if the slug is already present, which
|
// We add a counter after the slug if the slug is already present, which
|
||||||
// means we will have example, example-1, example-2 etc
|
// means we will have example, example-1, example-2 etc
|
||||||
fn find_anchor(anchors: &Vec<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.to_string();
|
||||||
}
|
}
|
||||||
|
@ -164,16 +164,14 @@ pub fn markdown_to_html(content: &str, permalinks: &HashMap<String, String>, ter
|
||||||
}
|
}
|
||||||
|
|
||||||
// Shortcode without body
|
// Shortcode without body
|
||||||
if shortcode_block.is_none() && text.starts_with("{{") && text.ends_with("}}") {
|
if shortcode_block.is_none() && text.starts_with("{{") && text.ends_with("}}") && SHORTCODE_RE.is_match(&text) {
|
||||||
if SHORTCODE_RE.is_match(&text) {
|
let (name, args) = parse_shortcode(&text);
|
||||||
let (name, args) = parse_shortcode(&text);
|
added_shortcode = true;
|
||||||
added_shortcode = true;
|
match render_simple_shortcode(tera, &name, &args) {
|
||||||
match render_simple_shortcode(tera, &name, &args) {
|
Ok(s) => return Event::Html(Owned(format!("</p>{}", s))),
|
||||||
Ok(s) => return Event::Html(Owned(format!("</p>{}", s))),
|
Err(e) => {
|
||||||
Err(e) => {
|
error = Some(e);
|
||||||
error = Some(e);
|
return Event::Html(Owned("".to_string()));
|
||||||
return Event::Html(Owned("".to_string()));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// non-matching will be returned normally below
|
// non-matching will be returned normally below
|
||||||
|
@ -277,7 +275,7 @@ pub fn markdown_to_html(content: &str, permalinks: &HashMap<String, String>, ter
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
return Event::Start(Tag::Link(link.clone(), title.clone()));
|
Event::Start(Tag::Link(link.clone(), title.clone()))
|
||||||
},
|
},
|
||||||
// need to know when we are in a code block to disable shortcodes in them
|
// need to know when we are in a code block to disable shortcodes in them
|
||||||
Event::Start(Tag::Code) => {
|
Event::Start(Tag::Code) => {
|
||||||
|
@ -291,7 +289,7 @@ pub fn markdown_to_html(content: &str, permalinks: &HashMap<String, String>, ter
|
||||||
Event::Start(Tag::Header(num)) => {
|
Event::Start(Tag::Header(num)) => {
|
||||||
in_header = true;
|
in_header = true;
|
||||||
// ugly eh
|
// ugly eh
|
||||||
return Event::Html(Owned(format!("<h{} ", num)));
|
Event::Html(Owned(format!("<h{} ", num)))
|
||||||
},
|
},
|
||||||
Event::End(Tag::Header(_)) => {
|
Event::End(Tag::Header(_)) => {
|
||||||
in_header = false;
|
in_header = false;
|
||||||
|
|
25
src/page.rs
25
src/page.rs
|
@ -152,22 +152,21 @@ impl Page {
|
||||||
// Pages with custom urls exists outside of sections
|
// Pages with custom urls exists outside of sections
|
||||||
if let Some(ref u) = page.meta.url {
|
if let Some(ref u) = page.meta.url {
|
||||||
page.path = u.trim().to_string();
|
page.path = u.trim().to_string();
|
||||||
} else {
|
} else if !page.components.is_empty() {
|
||||||
if !page.components.is_empty() {
|
// If we have a folder with an asset, don't consider it as a component
|
||||||
// If we have a folder with an asset, don't consider it as a component
|
if page.file_name == "index" {
|
||||||
if page.file_name == "index" {
|
page.components.pop();
|
||||||
page.components.pop();
|
// also set parent_path to grandparent instead
|
||||||
// also set parent_path to grandparent instead
|
page.parent_path = page.parent_path.parent().unwrap().to_path_buf();
|
||||||
page.parent_path = page.parent_path.parent().unwrap().to_path_buf();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Don't add a trailing slash to sections
|
|
||||||
page.path = format!("{}/{}", page.components.join("/"), page.slug);
|
|
||||||
} else {
|
|
||||||
page.path = page.slug.clone();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Don't add a trailing slash to sections
|
||||||
|
page.path = format!("{}/{}", page.components.join("/"), page.slug);
|
||||||
|
} else {
|
||||||
|
page.path = page.slug.clone();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
page.permalink = config.make_permalink(&page.path);
|
page.permalink = config.make_permalink(&page.path);
|
||||||
|
|
||||||
Ok(page)
|
Ok(page)
|
||||||
|
|
15
src/site.rs
15
src/site.rs
|
@ -64,6 +64,7 @@ pub struct Site {
|
||||||
pub config: Config,
|
pub config: Config,
|
||||||
pub pages: HashMap<PathBuf, Page>,
|
pub pages: HashMap<PathBuf, Page>,
|
||||||
pub sections: BTreeMap<PathBuf, Section>,
|
pub sections: BTreeMap<PathBuf, Section>,
|
||||||
|
pub index: Option<Section>,
|
||||||
pub tera: Tera,
|
pub tera: Tera,
|
||||||
live_reload: bool,
|
live_reload: bool,
|
||||||
output_path: PathBuf,
|
output_path: PathBuf,
|
||||||
|
@ -91,6 +92,7 @@ impl Site {
|
||||||
config: get_config(path, config_file),
|
config: get_config(path, config_file),
|
||||||
pages: HashMap::new(),
|
pages: HashMap::new(),
|
||||||
sections: BTreeMap::new(),
|
sections: BTreeMap::new(),
|
||||||
|
index: None,
|
||||||
tera: tera,
|
tera: tera,
|
||||||
live_reload: false,
|
live_reload: false,
|
||||||
output_path: path.join("public"),
|
output_path: path.join("public"),
|
||||||
|
@ -117,16 +119,21 @@ impl Site {
|
||||||
/// Reads all .md files in the `content` directory and create pages/sections
|
/// Reads all .md files in the `content` directory and create pages/sections
|
||||||
/// out of them
|
/// out of them
|
||||||
pub fn load(&mut self) -> Result<()> {
|
pub fn load(&mut self) -> Result<()> {
|
||||||
let path = self.base_path.to_string_lossy().replace("\\", "/");
|
let base_path = self.base_path.to_string_lossy().replace("\\", "/");
|
||||||
let content_glob = format!("{}/{}", path, "content/**/*.md");
|
let content_glob = format!("{}/{}", base_path, "content/**/*.md");
|
||||||
|
|
||||||
// TODO: make that parallel, that's the main bottleneck
|
// TODO: make that parallel, that's the main bottleneck
|
||||||
// `add_section` and `add_page` can't be used in the parallel version afaik
|
// `add_section` and `add_page` can't be used in the parallel version afaik
|
||||||
for entry in glob(&content_glob).unwrap().filter_map(|e| e.ok()) {
|
for entry in glob(&content_glob).unwrap().filter_map(|e| e.ok()) {
|
||||||
let path = entry.as_path();
|
let path = entry.as_path();
|
||||||
|
|
||||||
if path.file_name().unwrap() == "_index.md" {
|
if path.file_name().unwrap() == "_index.md" {
|
||||||
self.add_section(path)?;
|
// Index section
|
||||||
|
if path.parent().unwrap() == self.base_path.join("content") {
|
||||||
|
self.index = Some(Section::from_file(path, &self.config)?);
|
||||||
|
} else {
|
||||||
|
// all the other sections
|
||||||
|
self.add_section(path)?;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
self.add_page(path)?;
|
self.add_page(path)?;
|
||||||
}
|
}
|
||||||
|
|
4
test_site/content/_index.md
Normal file
4
test_site/content/_index.md
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
+++
|
||||||
|
title = "Home"
|
||||||
|
description = ""
|
||||||
|
+++
|
|
@ -53,10 +53,10 @@ authors = ["Bob", "Alice"]"#;
|
||||||
assert_eq!(res.title, "Hello".to_string());
|
assert_eq!(res.title, "Hello".to_string());
|
||||||
assert_eq!(res.slug.unwrap(), "hello-world".to_string());
|
assert_eq!(res.slug.unwrap(), "hello-world".to_string());
|
||||||
let extra = res.extra.unwrap();
|
let extra = res.extra.unwrap();
|
||||||
assert_eq!(extra.get("language").unwrap(), &to_value("en").unwrap());
|
assert_eq!(extra["language"], to_value("en").unwrap());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
extra.get("authors").unwrap(),
|
extra["authors"],
|
||||||
&to_value(["Bob".to_string(), "Alice".to_string()]).unwrap()
|
to_value(["Bob".to_string(), "Alice".to_string()]).unwrap()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,9 @@ fn test_can_parse_site() {
|
||||||
assert_eq!(site.pages.len(), 10);
|
assert_eq!(site.pages.len(), 10);
|
||||||
let posts_path = path.join("content").join("posts");
|
let posts_path = path.join("content").join("posts");
|
||||||
|
|
||||||
|
// We have an index page
|
||||||
|
assert!(site.index.is_some());
|
||||||
|
|
||||||
// Make sure we remove all the pwd + content from the sections
|
// Make sure we remove all the pwd + content from the sections
|
||||||
let basic = &site.pages[&posts_path.join("simple.md")];
|
let basic = &site.pages[&posts_path.join("simple.md")];
|
||||||
assert_eq!(basic.components, vec!["posts".to_string()]);
|
assert_eq!(basic.components, vec!["posts".to_string()]);
|
||||||
|
|
Loading…
Reference in a new issue