diff --git a/Cargo.lock b/Cargo.lock index 7f29534..af0753a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1951,6 +1951,7 @@ dependencies = [ "jotdown", "log", "rand", + "rayon", "regex", "serde", "serde_json", diff --git a/crates/treehouse/Cargo.toml b/crates/treehouse/Cargo.toml index 08f278c..c74bbb0 100644 --- a/crates/treehouse/Cargo.toml +++ b/crates/treehouse/Cargo.toml @@ -25,6 +25,7 @@ indexmap = { version = "2.2.6", features = ["serde"] } jotdown = { version = "0.4.1", default-features = false } log = { workspace = true } rand = "0.8.5" +rayon = "1.10.0" regex = "1.10.3" serde = { version = "1.0.183", features = ["derive"] } serde_json = "1.0.105" diff --git a/crates/treehouse/src/generate.rs b/crates/treehouse/src/generate.rs index 21bc43f..614057a 100644 --- a/crates/treehouse/src/generate.rs +++ b/crates/treehouse/src/generate.rs @@ -514,7 +514,9 @@ pub fn target(dirs: Arc, sources: Arc) -> DynDir { let dir_index = DirIndex::new(sources.parsed_trees.keys().map(|x| &**x)); let tree_view = TreehouseDir::new(dirs, sources, dir_index); + let tree_view = ContentCache::new(tree_view); + tree_view.warm_up(); let tree_view = HtmlCanonicalize::new(tree_view); Overlay::new(tree_view.to_dyn(), root.to_dyn()).to_dyn() diff --git a/crates/treehouse/src/vfs/content_cache.rs b/crates/treehouse/src/vfs/content_cache.rs index 8c4a44a..ae7e16d 100644 --- a/crates/treehouse/src/vfs/content_cache.rs +++ b/crates/treehouse/src/vfs/content_cache.rs @@ -1,8 +1,13 @@ -use std::fmt::{self, Debug}; +use std::{ + fmt::{self, Debug}, + ops::ControlFlow, +}; use dashmap::DashMap; +use log::debug; +use rayon::iter::{IntoParallelRefIterator, ParallelIterator}; -use super::{Dir, DirEntry, EditPath, ImageSize, VPath, VPathBuf}; +use super::{walk_dir_rec, Dir, DirEntry, EditPath, ImageSize, VPath, VPathBuf}; pub struct ContentCache { inner: T, @@ -18,6 +23,22 @@ impl ContentCache { } } +impl ContentCache +where + T: Dir + Send + Sync, +{ + pub fn warm_up(&self) { + debug!("warm_up({self:?})"); + let mut paths = vec![]; + walk_dir_rec(&self.inner, VPath::ROOT, &mut |path| { + paths.push(path.to_owned()); + ControlFlow::Continue(()) + }); + + paths.par_iter().for_each(|path| _ = self.content(path)); + } +} + impl Dir for ContentCache where T: Dir, diff --git a/crates/treehouse/src/vfs/path.rs b/crates/treehouse/src/vfs/path.rs index 6d9707f..baec51a 100644 --- a/crates/treehouse/src/vfs/path.rs +++ b/crates/treehouse/src/vfs/path.rs @@ -91,10 +91,16 @@ impl VPath { } pub fn segments(&self) -> impl Iterator { - self.as_str().split(Self::SEPARATOR).map(|s| unsafe { - // SAFETY: Since we're splitting on the separator, the path cannot start or end with it. - Self::new_unchecked(s) - }) + if self.is_root() { + None.into_iter().flatten() + } else { + Some(self.as_str().split(Self::SEPARATOR).map(|s| unsafe { + // SAFETY: Since we're splitting on the separator, the path cannot start or end with it. + Self::new_unchecked(s) + })) + .into_iter() + .flatten() + } } pub fn rsegments(&self) -> impl Iterator { @@ -203,7 +209,9 @@ impl VPathBuf { pub fn push(&mut self, sub: &VPath) { if !sub.is_empty() { - self.path.push('/'); + if !self.is_empty() { + self.path.push('/'); + } self.path.push_str(&sub.path); } }