@@ -42,6 +42,7 @@ sections up to the index to be used with the `get_section` Tera function | |||||
- Add `transparent` sections, for when you want to separate content by sections but want to group them at a higher level (think a `posts` folder with years | - Add `transparent` sections, for when you want to separate content by sections but want to group them at a higher level (think a `posts` folder with years | ||||
that want to use pagination on the index). | that want to use pagination on the index). | ||||
- Add `page_template` to section front-matter for when you want to specify the template to use for every page under it | - Add `page_template` to section front-matter for when you want to specify the template to use for every page under it | ||||
- Improves to `zola serve`: now handles files/directories renaming | |||||
## 0.4.2 (2018-09-03) | ## 0.4.2 (2018-09-03) | ||||
@@ -86,7 +86,7 @@ dependencies = [ | |||||
"rand 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)", | "rand 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
"regex 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", | "regex 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
"serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)", | "serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
"serde_json 1.0.32 (registry+https://github.com/rust-lang/crates.io-index)", | |||||
"serde_json 1.0.33 (registry+https://github.com/rust-lang/crates.io-index)", | |||||
"serde_urlencoded 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", | "serde_urlencoded 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
"sha1 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", | "sha1 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
"slab 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", | "slab 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
@@ -506,7 +506,7 @@ dependencies = [ | |||||
"rust-stemmers 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", | "rust-stemmers 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
"serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)", | "serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
"serde_derive 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)", | "serde_derive 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
"serde_json 1.0.32 (registry+https://github.com/rust-lang/crates.io-index)", | |||||
"serde_json 1.0.33 (registry+https://github.com/rust-lang/crates.io-index)", | |||||
"strum 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", | "strum 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
"strum_macros 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", | "strum_macros 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
] | ] | ||||
@@ -1140,7 +1140,7 @@ dependencies = [ | |||||
"phf_codegen 0.7.23 (registry+https://github.com/rust-lang/crates.io-index)", | "phf_codegen 0.7.23 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
"serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)", | "serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
"serde_derive 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)", | "serde_derive 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
"serde_json 1.0.32 (registry+https://github.com/rust-lang/crates.io-index)", | |||||
"serde_json 1.0.33 (registry+https://github.com/rust-lang/crates.io-index)", | |||||
"string_cache 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", | "string_cache 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
"string_cache_codegen 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", | "string_cache_codegen 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
"tendril 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", | "tendril 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
@@ -1385,7 +1385,7 @@ dependencies = [ | |||||
[[package]] | [[package]] | ||||
name = "onig" | name = "onig" | ||||
version = "4.2.0" | |||||
version = "4.2.1" | |||||
source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
dependencies = [ | dependencies = [ | ||||
"bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", | "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
@@ -1751,7 +1751,7 @@ dependencies = [ | |||||
"mime_guess 2.0.0-alpha.6 (registry+https://github.com/rust-lang/crates.io-index)", | "mime_guess 2.0.0-alpha.6 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
"native-tls 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", | "native-tls 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
"serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)", | "serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
"serde_json 1.0.32 (registry+https://github.com/rust-lang/crates.io-index)", | |||||
"serde_json 1.0.33 (registry+https://github.com/rust-lang/crates.io-index)", | |||||
"serde_urlencoded 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", | "serde_urlencoded 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
"tokio 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", | "tokio 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
"tokio-io 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", | "tokio-io 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
@@ -1907,7 +1907,7 @@ dependencies = [ | |||||
[[package]] | [[package]] | ||||
name = "serde_json" | name = "serde_json" | ||||
version = "1.0.32" | |||||
version = "1.0.33" | |||||
source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
dependencies = [ | dependencies = [ | ||||
"indexmap 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", | "indexmap 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
@@ -2119,12 +2119,12 @@ dependencies = [ | |||||
"fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", | "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
"lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", | "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
"lazycell 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", | "lazycell 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
"onig 4.2.0 (registry+https://github.com/rust-lang/crates.io-index)", | |||||
"onig 4.2.1 (registry+https://github.com/rust-lang/crates.io-index)", | |||||
"plist 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", | "plist 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
"regex-syntax 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", | "regex-syntax 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
"serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)", | "serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
"serde_derive 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)", | "serde_derive 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
"serde_json 1.0.32 (registry+https://github.com/rust-lang/crates.io-index)", | |||||
"serde_json 1.0.33 (registry+https://github.com/rust-lang/crates.io-index)", | |||||
"walkdir 2.2.6 (registry+https://github.com/rust-lang/crates.io-index)", | "walkdir 2.2.6 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
"yaml-rust 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", | "yaml-rust 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
] | ] | ||||
@@ -2156,7 +2156,7 @@ dependencies = [ | |||||
"library 0.1.0", | "library 0.1.0", | ||||
"pulldown-cmark 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", | "pulldown-cmark 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
"reqwest 0.9.4 (registry+https://github.com/rust-lang/crates.io-index)", | "reqwest 0.9.4 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
"serde_json 1.0.32 (registry+https://github.com/rust-lang/crates.io-index)", | |||||
"serde_json 1.0.33 (registry+https://github.com/rust-lang/crates.io-index)", | |||||
"tera 0.11.19 (registry+https://github.com/rust-lang/crates.io-index)", | "tera 0.11.19 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
"toml 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", | "toml 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
"url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)", | "url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
@@ -2187,7 +2187,7 @@ dependencies = [ | |||||
"pest_derive 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", | "pest_derive 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
"regex 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", | "regex 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
"serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)", | "serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
"serde_json 1.0.32 (registry+https://github.com/rust-lang/crates.io-index)", | |||||
"serde_json 1.0.33 (registry+https://github.com/rust-lang/crates.io-index)", | |||||
"slug 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", | "slug 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
"unic-segment 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", | "unic-segment 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
"url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)", | "url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
@@ -2946,7 +2946,7 @@ dependencies = [ | |||||
"checksum num-rational 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4e96f040177bb3da242b5b1ecf3f54b5d5af3efbbfb18608977a5d2767b22f10" | "checksum num-rational 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4e96f040177bb3da242b5b1ecf3f54b5d5af3efbbfb18608977a5d2767b22f10" | ||||
"checksum num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0b3a5d7cc97d6d30d8b9bc8fa19bf45349ffe46241e8816f50f62f6d6aaabee1" | "checksum num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0b3a5d7cc97d6d30d8b9bc8fa19bf45349ffe46241e8816f50f62f6d6aaabee1" | ||||
"checksum num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c51a3322e4bca9d212ad9a158a02abc6934d005490c054a2778df73a70aa0a30" | "checksum num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c51a3322e4bca9d212ad9a158a02abc6934d005490c054a2778df73a70aa0a30" | ||||
"checksum onig 4.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9f0d9dac9b8a5211103d75f3ce8d0c799e6f4f3f22608eeaefb4419786e4b258" | |||||
"checksum onig 4.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3febe8cb22362af9e662c9c35e4d8a675de50b1b119823aa556892ac967fb776" | |||||
"checksum onig_sys 69.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "78c04019a39ebac42dfd8c7822af0a009043720845a812ddbb95e403298b0183" | "checksum onig_sys 69.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "78c04019a39ebac42dfd8c7822af0a009043720845a812ddbb95e403298b0183" | ||||
"checksum openssl 0.10.15 (registry+https://github.com/rust-lang/crates.io-index)" = "5e1309181cdcbdb51bc3b6bedb33dfac2a83b3d585033d3f6d9e22e8c1928613" | "checksum openssl 0.10.15 (registry+https://github.com/rust-lang/crates.io-index)" = "5e1309181cdcbdb51bc3b6bedb33dfac2a83b3d585033d3f6d9e22e8c1928613" | ||||
"checksum openssl-probe 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de" | "checksum openssl-probe 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de" | ||||
@@ -3001,7 +3001,7 @@ dependencies = [ | |||||
"checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" | "checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" | ||||
"checksum serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)" = "15c141fc7027dd265a47c090bf864cf62b42c4d228bbcf4e51a0c9e2b0d3f7ef" | "checksum serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)" = "15c141fc7027dd265a47c090bf864cf62b42c4d228bbcf4e51a0c9e2b0d3f7ef" | ||||
"checksum serde_derive 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)" = "225de307c6302bec3898c51ca302fc94a7a1697ef0845fcee6448f33c032249c" | "checksum serde_derive 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)" = "225de307c6302bec3898c51ca302fc94a7a1697ef0845fcee6448f33c032249c" | ||||
"checksum serde_json 1.0.32 (registry+https://github.com/rust-lang/crates.io-index)" = "43344e7ce05d0d8280c5940cabb4964bea626aa58b1ec0e8c73fa2a8512a38ce" | |||||
"checksum serde_json 1.0.33 (registry+https://github.com/rust-lang/crates.io-index)" = "c37ccd6be3ed1fdf419ee848f7c758eb31b054d7cd3ae3600e3bae0adf569811" | |||||
"checksum serde_urlencoded 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "aaed41d9fb1e2f587201b863356590c90c1157495d811430a0c0325fe8169650" | "checksum serde_urlencoded 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "aaed41d9fb1e2f587201b863356590c90c1157495d811430a0c0325fe8169650" | ||||
"checksum sha-1 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "51b9d1f3b5de8a167ab06834a7c883bd197f2191e1dda1a22d9ccfeedbf9aded" | "checksum sha-1 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "51b9d1f3b5de8a167ab06834a7c883bd197f2191e1dda1a22d9ccfeedbf9aded" | ||||
"checksum sha1 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2579985fda508104f7587689507983eadd6a6e84dd35d6d115361f530916fa0d" | "checksum sha1 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2579985fda508104f7587689507983eadd6a6e84dd35d6d115361f530916fa0d" | ||||
@@ -277,8 +277,8 @@ impl Library { | |||||
.collect() | .collect() | ||||
} | } | ||||
pub fn find_parent_section(&self, path: &Path) -> Option<&Section> { | |||||
let page_key = self.paths_to_pages[path]; | |||||
pub fn find_parent_section<P: AsRef<Path>>(&self, path: P) -> Option<&Section> { | |||||
let page_key = self.paths_to_pages[path.as_ref()]; | |||||
for s in self.sections.values() { | for s in self.sections.values() { | ||||
if s.pages.contains(&page_key) { | if s.pages.contains(&page_key) { | ||||
return Some(s); | return Some(s); | ||||
@@ -289,16 +289,16 @@ impl Library { | |||||
} | } | ||||
/// Only used in tests | /// Only used in tests | ||||
pub fn get_section_key(&self, path: &PathBuf) -> Option<&Key> { | |||||
self.paths_to_sections.get(path) | |||||
pub fn get_section_key<P: AsRef<Path>>(&self, path: P) -> Option<&Key> { | |||||
self.paths_to_sections.get(path.as_ref()) | |||||
} | } | ||||
pub fn get_section(&self, path: &PathBuf) -> Option<&Section> { | |||||
self.sections.get(self.paths_to_sections.get(path).cloned().unwrap_or_default()) | |||||
pub fn get_section<P: AsRef<Path>>(&self, path: P) -> Option<&Section> { | |||||
self.sections.get(self.paths_to_sections.get(path.as_ref()).cloned().unwrap_or_default()) | |||||
} | } | ||||
pub fn get_section_mut(&mut self, path: &PathBuf) -> Option<&mut Section> { | |||||
self.sections.get_mut(self.paths_to_sections.get(path).cloned().unwrap_or_default()) | |||||
pub fn get_section_mut<P: AsRef<Path>>(&mut self, path: P) -> Option<&mut Section> { | |||||
self.sections.get_mut(self.paths_to_sections.get(path.as_ref()).cloned().unwrap_or_default()) | |||||
} | } | ||||
pub fn get_section_by_key(&self, key: Key) -> &Section { | pub fn get_section_by_key(&self, key: Key) -> &Section { | ||||
@@ -313,8 +313,8 @@ impl Library { | |||||
&self.get_section_by_key(key).file.relative | &self.get_section_by_key(key).file.relative | ||||
} | } | ||||
pub fn get_page(&self, path: &PathBuf) -> Option<&Page> { | |||||
self.pages.get(self.paths_to_pages.get(path).cloned().unwrap_or_default()) | |||||
pub fn get_page<P: AsRef<Path>>(&self, path: P) -> Option<&Page> { | |||||
self.pages.get(self.paths_to_pages.get(path.as_ref()).cloned().unwrap_or_default()) | |||||
} | } | ||||
pub fn get_page_by_key(&self, key: Key) -> &Page { | pub fn get_page_by_key(&self, key: Key) -> &Page { | ||||
@@ -325,16 +325,16 @@ impl Library { | |||||
self.pages.get_mut(key).unwrap() | self.pages.get_mut(key).unwrap() | ||||
} | } | ||||
pub fn remove_section(&mut self, path: &PathBuf) -> Option<Section> { | |||||
if let Some(k) = self.paths_to_sections.remove(path) { | |||||
pub fn remove_section<P: AsRef<Path>>(&mut self, path: P) -> Option<Section> { | |||||
if let Some(k) = self.paths_to_sections.remove(path.as_ref()) { | |||||
self.sections.remove(k) | self.sections.remove(k) | ||||
} else { | } else { | ||||
None | None | ||||
} | } | ||||
} | } | ||||
pub fn remove_page(&mut self, path: &PathBuf) -> Option<Page> { | |||||
if let Some(k) = self.paths_to_pages.remove(path) { | |||||
pub fn remove_page<P: AsRef<Path>>(&mut self, path: P) -> Option<Page> { | |||||
if let Some(k) = self.paths_to_pages.remove(path.as_ref()) { | |||||
self.pages.remove(k) | self.pages.remove(k) | ||||
} else { | } else { | ||||
None | None | ||||
@@ -342,12 +342,12 @@ impl Library { | |||||
} | } | ||||
/// Used in rebuild, to check if we know it already | /// Used in rebuild, to check if we know it already | ||||
pub fn contains_section(&self, path: &PathBuf) -> bool { | |||||
self.paths_to_sections.contains_key(path) | |||||
pub fn contains_section<P: AsRef<Path>>(&self, path: P) -> bool { | |||||
self.paths_to_sections.contains_key(path.as_ref()) | |||||
} | } | ||||
/// Used in rebuild, to check if we know it already | /// Used in rebuild, to check if we know it already | ||||
pub fn contains_page(&self, path: &PathBuf) -> bool { | |||||
self.paths_to_pages.contains_key(path) | |||||
pub fn contains_page<P: AsRef<Path>>(&self, path: P) -> bool { | |||||
self.paths_to_pages.contains_key(path.as_ref()) | |||||
} | } | ||||
} | } |
@@ -240,7 +240,53 @@ fn handle_page_editing(site: &mut Site, path: &Path) -> Result<()> { | |||||
} | } | ||||
} | } | ||||
/// What happens when a section or a page is changed | |||||
/// What happens when we rename a file/folder in the content directory. | |||||
/// Note that this is only called for folders when it isn't empty | |||||
pub fn after_content_rename(site: &mut Site, old: &Path, new: &Path) -> Result<()> { | |||||
let new_path = if new.is_dir() { | |||||
if new.join("_index.md").exists() { | |||||
// This is a section keep the dir folder to differentiate from renaming _index.md | |||||
// which doesn't do the same thing | |||||
new.to_path_buf() | |||||
} else if new.join("index.md").exists() { | |||||
new.join("index.md") | |||||
} else { | |||||
bail!("Got unexpected folder {:?} while handling renaming that was not expected", new); | |||||
} | |||||
} else { | |||||
new.to_path_buf() | |||||
}; | |||||
// A section folder has been renamed: just reload the whole site and rebuild it as we | |||||
// do not really know what needs to be rendered | |||||
if new_path.is_dir() { | |||||
site.load()?; | |||||
return site.build(); | |||||
} | |||||
// Renaming a file to _index.md, let the section editing do something and hope for the best | |||||
if new_path.file_name().unwrap() == "_index.md" { | |||||
// We aren't entirely sure where the original thing was so just try to delete whatever was | |||||
// at the old path | |||||
site.library.remove_page(&old.to_path_buf()); | |||||
site.library.remove_section(&old.to_path_buf()); | |||||
return handle_section_editing(site, &new_path); | |||||
} | |||||
// If it is a page, just delete what was there before and | |||||
// fake it's a new page | |||||
let old_path = if new_path.file_name().unwrap() == "index.md" { | |||||
old.join("index.md") | |||||
} else { | |||||
old.to_path_buf() | |||||
}; | |||||
site.library.remove_page(&old_path); | |||||
return handle_page_editing(site, &new_path); | |||||
} | |||||
/// What happens when a section or a page is created/edited | |||||
pub fn after_content_change(site: &mut Site, path: &Path) -> Result<()> { | pub fn after_content_change(site: &mut Site, path: &Path) -> Result<()> { | ||||
let is_section = path.file_name().unwrap() == "_index.md"; | let is_section = path.file_name().unwrap() == "_index.md"; | ||||
let is_md = path.extension().unwrap() == "md"; | let is_md = path.extension().unwrap() == "md"; | ||||
@@ -4,14 +4,14 @@ extern crate site; | |||||
extern crate tempfile; | extern crate tempfile; | ||||
use std::env; | use std::env; | ||||
use std::fs::{remove_dir_all, File}; | |||||
use std::fs::{self, File}; | |||||
use std::io::prelude::*; | use std::io::prelude::*; | ||||
use fs_extra::dir; | use fs_extra::dir; | ||||
use site::Site; | use site::Site; | ||||
use tempfile::tempdir; | use tempfile::tempdir; | ||||
use rebuild::after_content_change; | |||||
use rebuild::{after_content_change, after_content_rename}; | |||||
// Loads the test_site in a tempdir and build it there | // Loads the test_site in a tempdir and build it there | ||||
// Returns (site_path_in_tempdir, site) | // Returns (site_path_in_tempdir, site) | ||||
@@ -25,10 +25,6 @@ macro_rules! load_and_build_site { | |||||
dir::copy(&path, &$tmp_dir, &options).unwrap(); | dir::copy(&path, &$tmp_dir, &options).unwrap(); | ||||
let site_path = $tmp_dir.path().join("test_site"); | let site_path = $tmp_dir.path().join("test_site"); | ||||
// delete useless sections for those tests | |||||
remove_dir_all(site_path.join("content").join("paginated")).unwrap(); | |||||
remove_dir_all(site_path.join("content").join("posts")).unwrap(); | |||||
let mut site = Site::new(&site_path, "config.toml").unwrap(); | let mut site = Site::new(&site_path, "config.toml").unwrap(); | ||||
site.load().unwrap(); | site.load().unwrap(); | ||||
let public = &site_path.join("public"); | let public = &site_path.join("public"); | ||||
@@ -67,6 +63,22 @@ macro_rules! file_contains { | |||||
}}; | }}; | ||||
} | } | ||||
/// Rename a file or a folder to the new given name | |||||
macro_rules! rename { | |||||
($site_path: expr, $path: expr, $new_name: expr) => {{ | |||||
let mut t = $site_path.clone(); | |||||
for c in $path.split('/') { | |||||
t.push(c); | |||||
} | |||||
let mut new_path = t.parent().unwrap().to_path_buf(); | |||||
new_path.push($new_name); | |||||
fs::rename(&t, &new_path).unwrap(); | |||||
println!("Renamed {:?} to {:?}", t, new_path); | |||||
(t, new_path) | |||||
}}; | |||||
} | |||||
#[test] | #[test] | ||||
fn can_rebuild_after_simple_change_to_page_content() { | fn can_rebuild_after_simple_change_to_page_content() { | ||||
let tmp_dir = tempdir().expect("create temp dir"); | let tmp_dir = tempdir().expect("create temp dir"); | ||||
@@ -135,3 +147,87 @@ template = "rebuild.html" | |||||
"<h1>first</h1><h1>second</h1>" | "<h1>first</h1><h1>second</h1>" | ||||
)); | )); | ||||
} | } | ||||
#[test] | |||||
fn can_rebuild_after_transparent_change() { | |||||
let tmp_dir = tempdir().expect("create temp dir"); | |||||
let (site_path, mut site) = load_and_build_site!(tmp_dir); | |||||
let file_path = edit_file!( | |||||
site_path, | |||||
"content/posts/2018/_index.md", | |||||
br#" | |||||
+++ | |||||
transparent = false | |||||
render = false | |||||
+++ | |||||
"# | |||||
); | |||||
// Also remove pagination from posts section so we check whether the transparent page title | |||||
// is there or not without dealing with pagination | |||||
edit_file!( | |||||
site_path, | |||||
"content/posts/_index.md", | |||||
br#" | |||||
+++ | |||||
template = "section.html" | |||||
insert_anchor_links = "left" | |||||
+++ | |||||
"# | |||||
); | |||||
let res = after_content_change(&mut site, &file_path); | |||||
assert!(res.is_ok()); | |||||
assert!(!file_contains!( | |||||
site_path, | |||||
"public/posts/index.html", | |||||
"A transparent page" | |||||
)); | |||||
} | |||||
#[test] | |||||
fn can_rebuild_after_renaming_page() { | |||||
let tmp_dir = tempdir().expect("create temp dir"); | |||||
let (site_path, mut site) = load_and_build_site!(tmp_dir); | |||||
let (old_path, new_path) = rename!(site_path, "content/posts/simple.md", "hard.md"); | |||||
let res = after_content_rename(&mut site, &old_path, &new_path); | |||||
println!("{:?}", res); | |||||
assert!(res.is_ok()); | |||||
assert!(file_contains!( | |||||
site_path, | |||||
"public/posts/hard/index.html", | |||||
"A simple page" | |||||
)); | |||||
} | |||||
// https://github.com/Keats/gutenberg/issues/385 | |||||
#[test] | |||||
fn can_rebuild_after_renaming_colocated_asset_folder() { | |||||
let tmp_dir = tempdir().expect("create temp dir"); | |||||
let (site_path, mut site) = load_and_build_site!(tmp_dir); | |||||
let (old_path, new_path) = rename!(site_path, "content/posts/with-assets", "with-assets-updated"); | |||||
assert!(file_contains!(site_path, "content/posts/with-assets-updated/index.md", "Hello")); | |||||
let res = after_content_rename(&mut site, &old_path, &new_path); | |||||
println!("{:?}", res); | |||||
assert!(res.is_ok()); | |||||
assert!(file_contains!( | |||||
site_path, | |||||
"public/posts/with-assets-updated/index.html", | |||||
"Hello world" | |||||
)); | |||||
} | |||||
// https://github.com/Keats/gutenberg/issues/385 | |||||
#[test] | |||||
fn can_rebuild_after_renaming_section_folder() { | |||||
let tmp_dir = tempdir().expect("create temp dir"); | |||||
let (site_path, mut site) = load_and_build_site!(tmp_dir); | |||||
let (old_path, new_path) = rename!(site_path, "content/posts", "new-posts"); | |||||
assert!(file_contains!(site_path, "content/new-posts/simple.md", "simple")); | |||||
let res = after_content_rename(&mut site, &old_path, &new_path); | |||||
assert!(res.is_ok()); | |||||
assert!(file_contains!(site_path, "public/new-posts/simple/index.html", "simple")); | |||||
} |
@@ -22,7 +22,7 @@ | |||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||||
use std::env; | use std::env; | ||||
use std::fs::{remove_dir_all, File}; | |||||
use std::fs::{remove_dir_all, File, read_dir}; | |||||
use std::io::{self, Read}; | use std::io::{self, Read}; | ||||
use std::path::{Path, PathBuf, MAIN_SEPARATOR}; | use std::path::{Path, PathBuf, MAIN_SEPARATOR}; | ||||
use std::sync::mpsc::channel; | use std::sync::mpsc::channel; | ||||
@@ -225,7 +225,7 @@ pub fn serve( | |||||
.bind(&address) | .bind(&address) | ||||
.expect("Can't start the webserver") | .expect("Can't start the webserver") | ||||
.shutdown_timeout(20); | .shutdown_timeout(20); | ||||
println!("Web server is available at http://{}", &address); | |||||
println!("Web server is available at http://{}\n", &address); | |||||
s.run(); | s.run(); | ||||
}); | }); | ||||
// The websocket for livereload | // The websocket for livereload | ||||
@@ -286,12 +286,81 @@ pub fn serve( | |||||
use notify::DebouncedEvent::*; | use notify::DebouncedEvent::*; | ||||
let reload_templates = |site: &mut Site, path: &Path| { | |||||
let msg = if path.is_dir() { | |||||
format!("-> Directory in `templates` folder changed {}", path.display()) | |||||
} else { | |||||
format!("-> Template changed {}", path.display()) | |||||
}; | |||||
console::info(&msg); | |||||
if let Some(ref broadcaster) = broadcaster { | |||||
// Force refresh | |||||
rebuild_done_handling( | |||||
broadcaster, | |||||
rebuild::after_template_change(site, &path), | |||||
"/x.js", | |||||
); | |||||
} | |||||
}; | |||||
let reload_sass = |site: &Site, path: &Path, partial_path: &Path| { | |||||
let msg = if path.is_dir() { | |||||
format!("-> Directory in `sass` folder changed {}", path.display()) | |||||
} else { | |||||
format!("-> Sass file changed {}", path.display()) | |||||
}; | |||||
console::info(&msg); | |||||
if let Some(ref broadcaster) = broadcaster { | |||||
rebuild_done_handling( | |||||
&broadcaster, | |||||
site.compile_sass(&site.base_path), | |||||
&partial_path.to_string_lossy(), | |||||
); | |||||
} | |||||
}; | |||||
let copy_static = |site: &Site, path: &Path, partial_path: &Path| { | |||||
// Do nothing if the file/dir was deleted | |||||
if !path.exists() { | |||||
return; | |||||
} | |||||
let msg = if path.is_dir() { | |||||
format!("-> Directory in `static` folder changed {}", path.display()) | |||||
} else { | |||||
format!("-> Static file changed {}", path.display()) | |||||
}; | |||||
console::info(&msg); | |||||
if let Some(ref broadcaster) = broadcaster { | |||||
if path.is_dir() { | |||||
rebuild_done_handling( | |||||
broadcaster, | |||||
site.copy_static_directories(), | |||||
&path.to_string_lossy(), | |||||
); | |||||
} else { | |||||
rebuild_done_handling( | |||||
broadcaster, | |||||
copy_file(&path, &site.output_path, &site.static_path), | |||||
&partial_path.to_string_lossy(), | |||||
); | |||||
} | |||||
} | |||||
}; | |||||
loop { | loop { | ||||
match rx.recv() { | match rx.recv() { | ||||
Ok(event) => { | Ok(event) => { | ||||
match event { | match event { | ||||
Create(path) | Write(path) | Remove(path) | Rename(_, path) => { | |||||
if is_temp_file(&path) || path.is_dir() { | |||||
Rename(old_path, path) => { | |||||
if path.is_file() && is_temp_file(&path) { | |||||
continue; | |||||
} | |||||
let (change_kind, partial_path) = detect_change_kind(&pwd, &path); | |||||
// We only care about changes in non-empty folders | |||||
if path.is_dir() && is_folder_empty(&path) { | |||||
continue; | continue; | ||||
} | } | ||||
@@ -299,55 +368,65 @@ pub fn serve( | |||||
"Change detected @ {}", | "Change detected @ {}", | ||||
Local::now().format("%Y-%m-%d %H:%M:%S").to_string() | Local::now().format("%Y-%m-%d %H:%M:%S").to_string() | ||||
); | ); | ||||
let start = Instant::now(); | let start = Instant::now(); | ||||
match detect_change_kind(&pwd, &path) { | |||||
(ChangeKind::Content, _) => { | |||||
console::info(&format!("-> Content changed {}", path.display())); | |||||
match change_kind { | |||||
ChangeKind::Content => { | |||||
console::info(&format!("-> Content renamed {}", path.display())); | |||||
if let Some(ref broadcaster) = broadcaster { | if let Some(ref broadcaster) = broadcaster { | ||||
// Force refresh | // Force refresh | ||||
rebuild_done_handling( | rebuild_done_handling( | ||||
broadcaster, | broadcaster, | ||||
rebuild::after_content_change(&mut site, &path), | |||||
rebuild::after_content_rename(&mut site, &old_path, &path), | |||||
"/x.js", | "/x.js", | ||||
); | ); | ||||
} | } | ||||
} | } | ||||
(ChangeKind::Templates, _) => { | |||||
console::info(&format!("-> Template changed {}", path.display())); | |||||
ChangeKind::Templates => reload_templates(&mut site, &path), | |||||
ChangeKind::StaticFiles => copy_static(&site, &path, &partial_path), | |||||
ChangeKind::Sass => reload_sass(&site, &path, &partial_path), | |||||
ChangeKind::Config => { | |||||
console::info("-> Config changed. The whole site will be reloaded. The browser needs to be refreshed to make the changes visible."); | |||||
site = create_new_site( | |||||
interface, | |||||
port, | |||||
output_dir, | |||||
base_url, | |||||
config_file, | |||||
) | |||||
.unwrap() | |||||
.0; | |||||
} | |||||
} | |||||
console::report_elapsed_time(start); | |||||
} | |||||
Create(path) | Write(path) | Remove(path) => { | |||||
if is_temp_file(&path) || path.is_dir() { | |||||
continue; | |||||
} | |||||
println!( | |||||
"Change detected @ {}", | |||||
Local::now().format("%Y-%m-%d %H:%M:%S").to_string() | |||||
); | |||||
let start = Instant::now(); | |||||
match detect_change_kind(&pwd, &path) { | |||||
(ChangeKind::Content, _) => { | |||||
console::info(&format!("-> Content changed {}", path.display())); | |||||
if let Some(ref broadcaster) = broadcaster { | if let Some(ref broadcaster) = broadcaster { | ||||
// Force refresh | // Force refresh | ||||
rebuild_done_handling( | rebuild_done_handling( | ||||
broadcaster, | broadcaster, | ||||
rebuild::after_template_change(&mut site, &path), | |||||
rebuild::after_content_change(&mut site, &path), | |||||
"/x.js", | "/x.js", | ||||
); | ); | ||||
} | } | ||||
} | } | ||||
(ChangeKind::StaticFiles, p) => { | |||||
if path.is_file() { | |||||
console::info(&format!( | |||||
"-> Static file changes detected {}", | |||||
path.display() | |||||
)); | |||||
if let Some(ref broadcaster) = broadcaster { | |||||
rebuild_done_handling( | |||||
broadcaster, | |||||
copy_file(&path, &site.output_path, &site.static_path), | |||||
&p.to_string_lossy(), | |||||
); | |||||
} | |||||
} | |||||
} | |||||
(ChangeKind::Sass, p) => { | |||||
console::info(&format!("-> Sass file changed {}", path.display())); | |||||
if let Some(ref broadcaster) = broadcaster { | |||||
rebuild_done_handling( | |||||
&broadcaster, | |||||
site.compile_sass(&site.base_path), | |||||
&p.to_string_lossy(), | |||||
); | |||||
} | |||||
} | |||||
(ChangeKind::Templates, _) => reload_templates(&mut site, &path), | |||||
(ChangeKind::StaticFiles, p) => copy_static(&site, &path, &p), | |||||
(ChangeKind::Sass, p) => reload_sass(&site, &path, &p), | |||||
(ChangeKind::Config, _) => { | (ChangeKind::Config, _) => { | ||||
console::info("-> Config changed. The whole site will be reloaded. The browser needs to be refreshed to make the changes visible."); | console::info("-> Config changed. The whole site will be reloaded. The browser needs to be refreshed to make the changes visible."); | ||||
site = create_new_site( | site = create_new_site( | ||||
@@ -421,6 +500,18 @@ fn detect_change_kind(pwd: &Path, path: &Path) -> (ChangeKind, PathBuf) { | |||||
(change_kind, partial_path) | (change_kind, partial_path) | ||||
} | } | ||||
/// Check if the directory at path contains any file | |||||
fn is_folder_empty(dir: &Path) -> bool { | |||||
// Can panic if we don't have the rights I guess? | |||||
for _ in read_dir(dir).expect("Failed to read a directory to see if it was empty") { | |||||
// If we get there, that means we have a file | |||||
return false; | |||||
} | |||||
true | |||||
} | |||||
#[cfg(test)] | #[cfg(test)] | ||||
mod tests { | mod tests { | ||||
use std::path::{Path, PathBuf}; | use std::path::{Path, PathBuf}; | ||||