Partial reloading of content on change
This commit is contained in:
parent
b0cc1ac042
commit
8b5183d4ad
|
@ -6,6 +6,6 @@ use gutenberg::Site;
|
||||||
|
|
||||||
pub fn build() -> Result<()> {
|
pub fn build() -> Result<()> {
|
||||||
let mut site = Site::new(env::current_dir().unwrap())?;
|
let mut site = Site::new(env::current_dir().unwrap())?;
|
||||||
site.parse()?;
|
site.load()?;
|
||||||
site.build()
|
site.build()
|
||||||
}
|
}
|
||||||
|
|
|
@ -70,7 +70,7 @@ pub fn serve(interface: &str, port: &str) -> Result<()> {
|
||||||
format!("http://{}", address)
|
format!("http://{}", address)
|
||||||
};
|
};
|
||||||
|
|
||||||
site.parse()?;
|
site.load()?;
|
||||||
site.enable_live_reload();
|
site.enable_live_reload();
|
||||||
site.build()?;
|
site.build()?;
|
||||||
report_elapsed_time(start);
|
report_elapsed_time(start);
|
||||||
|
@ -129,7 +129,7 @@ pub fn serve(interface: &str, port: &str) -> Result<()> {
|
||||||
(ChangeKind::Content, _) => {
|
(ChangeKind::Content, _) => {
|
||||||
println!("-> Content changed {}", path.display());
|
println!("-> Content changed {}", path.display());
|
||||||
// Force refresh
|
// Force refresh
|
||||||
rebuild_done_handling(&broadcaster, site.rebuild_after_content_change(), "/x.js");
|
rebuild_done_handling(&broadcaster, site.rebuild_after_content_change(&path), "/x.js");
|
||||||
},
|
},
|
||||||
(ChangeKind::Templates, _) => {
|
(ChangeKind::Templates, _) => {
|
||||||
println!("-> Template changed {}", path.display());
|
println!("-> Template changed {}", path.display());
|
||||||
|
|
80
src/site.rs
80
src/site.rs
|
@ -101,39 +101,60 @@ impl Site {
|
||||||
self.output_path = path.as_ref().to_path_buf();
|
self.output_path = path.as_ref().to_path_buf();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Reads all .md files in the `content` directory and create pages
|
/// Reads all .md files in the `content` directory and create pages/sections
|
||||||
/// out of them
|
/// out of them
|
||||||
pub fn parse(&mut self) -> Result<()> {
|
pub fn load(&mut self) -> Result<()> {
|
||||||
let path = self.base_path.to_string_lossy().replace("\\", "/");
|
let path = self.base_path.to_string_lossy().replace("\\", "/");
|
||||||
let content_glob = format!("{}/{}", path, "content/**/*.md");
|
let content_glob = format!("{}/{}", path, "content/**/*.md");
|
||||||
|
|
||||||
// parent_dir -> Section
|
// TODO: make that parallel, that's the main bottleneck
|
||||||
let mut sections = BTreeMap::new();
|
// `add_section` and `add_page` can't be used in the parallel version afaik
|
||||||
|
|
||||||
// Glob is giving us the result order so _index will show up first
|
|
||||||
// for each directory
|
|
||||||
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" {
|
||||||
let section = Section::from_file(&path, &self.config)?;
|
self.add_section(&path)?;
|
||||||
sections.insert(section.parent_path.clone(), section);
|
|
||||||
} else {
|
} else {
|
||||||
|
self.add_page(&path)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.populate_sections();
|
||||||
|
self.populate_tags_and_categories();
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Simple wrapper fn to avoid repeating that code in several places
|
||||||
|
fn add_page(&mut self, path: &Path) -> Result<()> {
|
||||||
let page = Page::from_file(&path, &self.config)?;
|
let page = Page::from_file(&path, &self.config)?;
|
||||||
if sections.contains_key(&page.parent_path) {
|
|
||||||
sections.get_mut(&page.parent_path).unwrap().pages.push(page.clone());
|
|
||||||
}
|
|
||||||
self.pages.insert(page.file_path.clone(), page);
|
self.pages.insert(page.file_path.clone(), page);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Simple wrapper fn to avoid repeating that code in several places
|
||||||
|
fn add_section(&mut self, path: &Path) -> Result<()> {
|
||||||
|
let section = Section::from_file(path, &self.config)?;
|
||||||
|
self.sections.insert(section.parent_path.clone(), section);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Find out the direct subsections of each subsection if there are some
|
||||||
|
/// as well as the pages for each section
|
||||||
|
fn populate_sections(&mut self) {
|
||||||
|
for page in self.pages.values() {
|
||||||
|
if self.sections.contains_key(&page.parent_path) {
|
||||||
|
self.sections.get_mut(&page.parent_path).unwrap().pages.push(page.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Find out the direct subsections of each subsection if there are some
|
|
||||||
let mut grandparent_paths = HashMap::new();
|
let mut grandparent_paths = HashMap::new();
|
||||||
for section in sections.values() {
|
for section in self.sections.values() {
|
||||||
let grand_parent = section.parent_path.parent().unwrap().to_path_buf();
|
let grand_parent = section.parent_path.parent().unwrap().to_path_buf();
|
||||||
grandparent_paths.entry(grand_parent).or_insert_with(|| vec![]).push(section.clone());
|
grandparent_paths.entry(grand_parent).or_insert_with(|| vec![]).push(section.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
for (parent_path, section) in &mut sections {
|
for (parent_path, section) in &mut self.sections {
|
||||||
section.pages.sort_by(|a, b| a.partial_cmp(b).unwrap());
|
section.pages.sort_by(|a, b| a.partial_cmp(b).unwrap());
|
||||||
|
|
||||||
match grandparent_paths.get(parent_path) {
|
match grandparent_paths.get(parent_path) {
|
||||||
|
@ -141,15 +162,10 @@ impl Site {
|
||||||
None => continue,
|
None => continue,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
self.sections = sections;
|
|
||||||
self.parse_tags_and_categories();
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Separated from `parse` for easier testing
|
/// Separated from `parse` for easier testing
|
||||||
pub fn parse_tags_and_categories(&mut self) {
|
pub fn populate_tags_and_categories(&mut self) {
|
||||||
for page in self.pages.values() {
|
for page in self.pages.values() {
|
||||||
if let Some(ref category) = page.meta.category {
|
if let Some(ref category) = page.meta.category {
|
||||||
self.categories
|
self.categories
|
||||||
|
@ -221,8 +237,26 @@ impl Site {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn rebuild_after_content_change(&mut self) -> Result<()> {
|
pub fn rebuild_after_content_change(&mut self, path: &Path) -> Result<()> {
|
||||||
self.parse()?;
|
if path.exists() {
|
||||||
|
// file exists, either a new one or updating content
|
||||||
|
if self.pages.contains_key(path) {
|
||||||
|
if path.ends_with("_index.md") {
|
||||||
|
self.add_section(path)?;
|
||||||
|
} else {
|
||||||
|
// probably just an update so just re-parse that page
|
||||||
|
self.add_page(path)?;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// new file?
|
||||||
|
self.add_page(path)?;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// File doesn't exist -> a deletion so we remove it from
|
||||||
|
self.pages.remove(path);
|
||||||
|
}
|
||||||
|
self.populate_sections();
|
||||||
|
self.populate_tags_and_categories();
|
||||||
self.build()
|
self.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,7 @@ fn test_can_parse_site() {
|
||||||
let mut path = env::current_dir().unwrap().to_path_buf();
|
let mut path = env::current_dir().unwrap().to_path_buf();
|
||||||
path.push("test_site");
|
path.push("test_site");
|
||||||
let mut site = Site::new(&path).unwrap();
|
let mut site = Site::new(&path).unwrap();
|
||||||
site.parse().unwrap();
|
site.load().unwrap();
|
||||||
|
|
||||||
// Correct number of pages (sections are pages too)
|
// Correct number of pages (sections are pages too)
|
||||||
assert_eq!(site.pages.len(), 10);
|
assert_eq!(site.pages.len(), 10);
|
||||||
|
@ -89,7 +89,7 @@ fn test_can_build_site_without_live_reload() {
|
||||||
let mut path = env::current_dir().unwrap().to_path_buf();
|
let mut path = env::current_dir().unwrap().to_path_buf();
|
||||||
path.push("test_site");
|
path.push("test_site");
|
||||||
let mut site = Site::new(&path).unwrap();
|
let mut site = Site::new(&path).unwrap();
|
||||||
site.parse().unwrap();
|
site.load().unwrap();
|
||||||
let tmp_dir = TempDir::new("example").expect("create temp dir");
|
let tmp_dir = TempDir::new("example").expect("create temp dir");
|
||||||
let public = &tmp_dir.path().join("public");
|
let public = &tmp_dir.path().join("public");
|
||||||
site.set_output_path(&public);
|
site.set_output_path(&public);
|
||||||
|
@ -130,7 +130,7 @@ fn test_can_build_site_with_live_reload() {
|
||||||
let mut path = env::current_dir().unwrap().to_path_buf();
|
let mut path = env::current_dir().unwrap().to_path_buf();
|
||||||
path.push("test_site");
|
path.push("test_site");
|
||||||
let mut site = Site::new(&path).unwrap();
|
let mut site = Site::new(&path).unwrap();
|
||||||
site.parse().unwrap();
|
site.load().unwrap();
|
||||||
let tmp_dir = TempDir::new("example").expect("create temp dir");
|
let tmp_dir = TempDir::new("example").expect("create temp dir");
|
||||||
let public = &tmp_dir.path().join("public");
|
let public = &tmp_dir.path().join("public");
|
||||||
site.set_output_path(&public);
|
site.set_output_path(&public);
|
||||||
|
@ -168,7 +168,7 @@ fn test_can_build_site_with_categories() {
|
||||||
let mut path = env::current_dir().unwrap().to_path_buf();
|
let mut path = env::current_dir().unwrap().to_path_buf();
|
||||||
path.push("test_site");
|
path.push("test_site");
|
||||||
let mut site = Site::new(&path).unwrap();
|
let mut site = Site::new(&path).unwrap();
|
||||||
site.parse().unwrap();
|
site.load().unwrap();
|
||||||
|
|
||||||
for (i, page) in site.pages.values_mut().enumerate() {
|
for (i, page) in site.pages.values_mut().enumerate() {
|
||||||
page.meta.category = if i % 2 == 0 {
|
page.meta.category = if i % 2 == 0 {
|
||||||
|
@ -177,7 +177,7 @@ fn test_can_build_site_with_categories() {
|
||||||
Some("B".to_string())
|
Some("B".to_string())
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
site.parse_tags_and_categories();
|
site.populate_tags_and_categories();
|
||||||
let tmp_dir = TempDir::new("example").expect("create temp dir");
|
let tmp_dir = TempDir::new("example").expect("create temp dir");
|
||||||
let public = &tmp_dir.path().join("public");
|
let public = &tmp_dir.path().join("public");
|
||||||
site.set_output_path(&public);
|
site.set_output_path(&public);
|
||||||
|
@ -219,7 +219,7 @@ fn test_can_build_site_with_tags() {
|
||||||
let mut path = env::current_dir().unwrap().to_path_buf();
|
let mut path = env::current_dir().unwrap().to_path_buf();
|
||||||
path.push("test_site");
|
path.push("test_site");
|
||||||
let mut site = Site::new(&path).unwrap();
|
let mut site = Site::new(&path).unwrap();
|
||||||
site.parse().unwrap();
|
site.load().unwrap();
|
||||||
|
|
||||||
for (i, page) in site.pages.values_mut().enumerate() {
|
for (i, page) in site.pages.values_mut().enumerate() {
|
||||||
page.meta.tags = if i % 2 == 0 {
|
page.meta.tags = if i % 2 == 0 {
|
||||||
|
@ -228,7 +228,7 @@ fn test_can_build_site_with_tags() {
|
||||||
Some(vec!["tag with space".to_string()])
|
Some(vec!["tag with space".to_string()])
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
site.parse_tags_and_categories();
|
site.populate_tags_and_categories();
|
||||||
|
|
||||||
let tmp_dir = TempDir::new("example").expect("create temp dir");
|
let tmp_dir = TempDir::new("example").expect("create temp dir");
|
||||||
let public = &tmp_dir.path().join("public");
|
let public = &tmp_dir.path().join("public");
|
||||||
|
|
Loading…
Reference in a new issue