diff --git a/crates/treehouse/src/generate.rs b/crates/treehouse/src/generate.rs index 339505d..21bc43f 100644 --- a/crates/treehouse/src/generate.rs +++ b/crates/treehouse/src/generate.rs @@ -20,7 +20,10 @@ use crate::{ parse::parse_tree_with_diagnostics, state::{report_diagnostics, Source}, tree::SemaRoots, - vfs::{self, Cd, Dir, DirEntry, DynDir, MemDir, Overlay, ToDynDir, VPath, VPathBuf}, + vfs::{ + self, Cd, ContentCache, Dir, DirEntry, DynDir, EditPath, ImageSize, MemDir, Overlay, + ToDynDir, VPath, VPathBuf, + }, }; use crate::state::{FileId, Treehouse}; @@ -415,14 +418,10 @@ impl Dir for TreehouseDir { } else { path }; - let mut path = path.to_owned(); - if path.extension() == Some("html") { - path.set_extension(""); - } self.sources .parsed_trees - .get(&path) + .get(path) .map(|parsed_tree| { generate_tree_or_error(&self.sources, &self.dirs, &self.handlebars, parsed_tree) .into() @@ -456,12 +455,67 @@ impl fmt::Debug for TreehouseDir { } } +struct HtmlCanonicalize { + inner: T, +} + +impl HtmlCanonicalize { + pub fn new(inner: T) -> Self { + Self { inner } + } +} + +impl Dir for HtmlCanonicalize +where + T: Dir, +{ + fn dir(&self, path: &VPath) -> Vec { + self.inner.dir(path) + } + + fn content(&self, path: &VPath) -> Option> { + let mut path = path.to_owned(); + if path.extension() == Some("html") { + path.set_extension(""); + } + + self.inner.content(&path) + } + + fn content_version(&self, path: &VPath) -> Option { + self.inner.content_version(path) + } + + fn image_size(&self, path: &VPath) -> Option { + self.inner.image_size(path) + } + + fn anchor(&self, path: &VPath) -> Option { + self.inner.anchor(path) + } + + fn edit_path(&self, path: &VPath) -> Option { + self.inner.edit_path(path) + } +} + +impl fmt::Debug for HtmlCanonicalize +where + T: fmt::Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "HtmlCanonicalize({:?})", self.inner) + } +} + pub fn target(dirs: Arc, sources: Arc) -> DynDir { let mut root = MemDir::new(); root.add(VPath::new("static"), dirs.static_.clone()); 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); + 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.rs b/crates/treehouse/src/vfs.rs index 124e730..9ff75d9 100644 --- a/crates/treehouse/src/vfs.rs +++ b/crates/treehouse/src/vfs.rs @@ -52,6 +52,7 @@ use std::{ mod anchored; pub mod asynch; mod cd; +mod content_cache; mod content_version_cache; mod edit; mod empty; @@ -64,6 +65,7 @@ mod physical; pub use anchored::*; pub use cd::*; +pub use content_cache::*; pub use content_version_cache::*; pub use edit::*; pub use empty::*; diff --git a/crates/treehouse/src/vfs/content_cache.rs b/crates/treehouse/src/vfs/content_cache.rs new file mode 100644 index 0000000..8c4a44a --- /dev/null +++ b/crates/treehouse/src/vfs/content_cache.rs @@ -0,0 +1,60 @@ +use std::fmt::{self, Debug}; + +use dashmap::DashMap; + +use super::{Dir, DirEntry, EditPath, ImageSize, VPath, VPathBuf}; + +pub struct ContentCache { + inner: T, + cache: DashMap>>, +} + +impl ContentCache { + pub fn new(inner: T) -> Self { + Self { + inner, + cache: DashMap::new(), + } + } +} + +impl Dir for ContentCache +where + T: Dir, +{ + fn dir(&self, path: &VPath) -> Vec { + self.inner.dir(path) + } + + fn content(&self, path: &VPath) -> Option> { + self.cache + .entry(path.to_owned()) + .or_insert_with(|| self.inner.content(path)) + .clone() + } + + fn content_version(&self, path: &VPath) -> Option { + self.inner.content_version(path) + } + + fn image_size(&self, path: &VPath) -> Option { + self.inner.image_size(path) + } + + fn anchor(&self, path: &VPath) -> Option { + self.inner.anchor(path) + } + + fn edit_path(&self, path: &VPath) -> Option { + self.inner.edit_path(path) + } +} + +impl fmt::Debug for ContentCache +where + T: Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "ContentCache({:?})", self.inner) + } +} diff --git a/static/js/tree.js b/static/js/tree.js index a8428f1..ea7e924 100644 --- a/static/js/tree.js +++ b/static/js/tree.js @@ -104,9 +104,7 @@ class LinkedBranch extends Branch { async loadTreePromise(_initiator) { try { - let response = await fetch( - `${TREEHOUSE_SITE}/${this.linkedTree}.html` - ); + let response = await fetch(`${TREEHOUSE_SITE}/${this.linkedTree}`); if (response.status == 404) { throw `Hmm, seems like the tree "${this.linkedTree}" does not exist.`; } @@ -127,7 +125,9 @@ class LinkedBranch extends Branch { // No need to await for the import because we don't use the resulting module. // Just fire and forger 💀 // and let them run in parallel. - let url = URL.createObjectURL(new Blob([script.textContent], { type: "text/javascript" })) + let url = URL.createObjectURL( + new Blob([script.textContent], { type: "text/javascript" }), + ); import(url); } } catch (error) { @@ -257,10 +257,7 @@ async function expandLinkedBranch() { let currentlyHighlightedBranch = getCurrentlyHighlightedBranch(); if (currentlyHighlightedBranch.length > 0) { let linkedBranch = document.getElementById(currentlyHighlightedBranch); - if ( - linkedBranch.children.length > 0 && - linkedBranch.children[0].tagName == "DETAILS" - ) { + if (linkedBranch.children.length > 0 && linkedBranch.children[0].tagName == "DETAILS") { expandDetailsRecursively(linkedBranch.children[0]); } }