From 31eb7c9b2eb4f4f90beb9e6ebb4dea7d315f22c7 Mon Sep 17 00:00:00 2001 From: Chris Cochrun Date: Tue, 31 Mar 2026 14:42:18 -0500 Subject: [PATCH] some hard coded working output. Depends on my fork of orgize in order to not get hung up on blocks having empty lines --- Cargo.lock | 34 ++++++++++++++++++++++++++++------ Cargo.toml | 2 +- src/main.rs | 31 ++++++++++++++++++++----------- src/org/exporter.rs | 33 ++++++++++++++++++++++++++++++++- src/publish/publish.rs | 40 +++++++++++++++++++++++++++++++++++----- src/watcher/mod.rs | 17 ++++++++++------- 6 files changed, 126 insertions(+), 31 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 92c83a0..139b316 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -488,6 +488,15 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "file-id" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1fc6a637b6dc58414714eddd9170ff187ecb0933d4c7024d1abbd23a3cc26e9" +dependencies = [ + "windows-sys 0.60.2", +] + [[package]] name = "find-msvc-tools" version = "0.1.9" @@ -1044,6 +1053,19 @@ dependencies = [ "windows-sys 0.60.2", ] +[[package]] +name = "notify-debouncer-full" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c02b49179cfebc9932238d04d6079912d26de0379328872846118a0fa0dbb302" +dependencies = [ + "file-id", + "log", + "notify", + "notify-types", + "walkdir", +] + [[package]] name = "notify-types" version = "2.1.0" @@ -1159,7 +1181,7 @@ dependencies = [ "jetscii", "lazy_static", "miette", - "notify", + "notify-debouncer-full", "orgize", "pretty_assertions", "rayon", @@ -1608,7 +1630,7 @@ checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" [[package]] name = "steel-core" version = "0.8.2" -source = "git+https://github.com/mattwparas/steel.git?branch=master#b3ae8b95d8fc0a2eef13c7f618abe4fcd5e3b1f7" +source = "git+https://github.com/mattwparas/steel.git?branch=master#657eb333cfc50c6cfa2c1a39546789e5eca741d0" dependencies = [ "arc-swap", "bigdecimal", @@ -1660,7 +1682,7 @@ dependencies = [ [[package]] name = "steel-derive" version = "0.8.2" -source = "git+https://github.com/mattwparas/steel.git?branch=master#b3ae8b95d8fc0a2eef13c7f618abe4fcd5e3b1f7" +source = "git+https://github.com/mattwparas/steel.git?branch=master#657eb333cfc50c6cfa2c1a39546789e5eca741d0" dependencies = [ "proc-macro2", "quote", @@ -1670,7 +1692,7 @@ dependencies = [ [[package]] name = "steel-gen" version = "0.8.2" -source = "git+https://github.com/mattwparas/steel.git?branch=master#b3ae8b95d8fc0a2eef13c7f618abe4fcd5e3b1f7" +source = "git+https://github.com/mattwparas/steel.git?branch=master#657eb333cfc50c6cfa2c1a39546789e5eca741d0" dependencies = [ "codegen", "serde", @@ -1679,7 +1701,7 @@ dependencies = [ [[package]] name = "steel-parser" version = "0.8.2" -source = "git+https://github.com/mattwparas/steel.git?branch=master#b3ae8b95d8fc0a2eef13c7f618abe4fcd5e3b1f7" +source = "git+https://github.com/mattwparas/steel.git?branch=master#657eb333cfc50c6cfa2c1a39546789e5eca741d0" dependencies = [ "compact_str", "dashmap", @@ -1700,7 +1722,7 @@ dependencies = [ [[package]] name = "steel-quickscope" version = "0.3.2" -source = "git+https://github.com/mattwparas/steel.git?branch=master#b3ae8b95d8fc0a2eef13c7f618abe4fcd5e3b1f7" +source = "git+https://github.com/mattwparas/steel.git?branch=master#657eb333cfc50c6cfa2c1a39546789e5eca741d0" dependencies = [ "indexmap 2.13.0", "smallvec", diff --git a/Cargo.toml b/Cargo.toml index 44415e1..7729a00 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,7 +14,7 @@ tracing-subscriber = { version = "0.3.18", features = ["fmt", "std", "chrono", " orgize = { git="https://git.tfcconnection.org/chris/orgize", branch = "v0.11", features = ["chrono", "indexmap", "tracing"] } steel-core = { git="https://github.com/mattwparas/steel.git", branch = "master" } chrono = "0.4.44" -notify = "8.2.0" +notify-debouncer-full = "0.7.0" walkdir = "2.5.0" jetscii = "0.5.3" lazy_static = "1.5.0" diff --git a/src/main.rs b/src/main.rs index 118ab25..c2936be 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,10 +2,12 @@ pub mod org; pub mod publish; pub mod watcher; +use std::time::Duration; use std::{path::PathBuf, sync::mpsc}; use clap::Parser; -use notify::{Event, RecursiveMode, Result, Watcher}; +use notify_debouncer_full::notify::{Event, RecursiveMode, Result, Watcher}; +use notify_debouncer_full::{DebouncedEvent, new_debouncer}; use tracing::{error, info, level_filters::LevelFilter}; use tracing_subscriber::EnvFilter; @@ -41,21 +43,28 @@ fn main() -> Result<()> { .init(); if let Some(path) = args.path { - let (tx, rx) = mpsc::channel::>(); + let (tx, rx) = mpsc::channel(); // Automatically select the best implementation for your platform. - let mut watcher = notify::recommended_watcher(tx)?; + let mut watcher = new_debouncer(Duration::from_millis(500), None, tx).unwrap(); watcher.watch(&path, RecursiveMode::Recursive)?; - for res in rx { - match res { - Ok(event) => match watcher::watch(event, &path) { - Ok(()) => (), - Err(e) => error!("internal error: {:?}", e), - }, + loop { + match rx.recv() { + Ok(event) => event.map_or_else( + |e| error!("Error: {:?}", e), + |events| { + for event in events { + match watcher::watch(&event, &path) { + Ok(()) => (), + Err(e) => error!("internal error: {:?}", e), + } + } + }, + ), Err(e) => error!("watch error: {:?}", e), - }; + } } } else { - info!("Nothing to do") + info!("Nothing to do"); } Ok(()) diff --git a/src/org/exporter.rs b/src/org/exporter.rs index cd634e4..f5e33d4 100644 --- a/src/org/exporter.rs +++ b/src/org/exporter.rs @@ -2,6 +2,8 @@ use slugify::slugify; use std::cmp::min; use std::fmt; use std::fmt::Write as _; +use std::sync::Arc; +use tracing::{debug, info}; use orgize::export::{Container, Event, TraversalContext, Traverser}; use orgize::{SyntaxElement, SyntaxKind, SyntaxNode}; @@ -52,6 +54,8 @@ pub struct OrgHtmlExporter { in_descriptive_list: Vec, table_row: TableRow, + + files: Arc>, } #[derive(Default, PartialEq, Eq)] @@ -64,6 +68,12 @@ enum TableRow { } impl OrgHtmlExporter { + pub fn new(files: Arc>) -> Self { + let mut new = Self::default(); + new.files = files; + new + } + pub fn push_str(&mut self, s: impl AsRef) { self.output += s.as_ref(); } @@ -87,6 +97,26 @@ impl OrgHtmlExporter { let mut ctx = TraversalContext::default(); self.element(SyntaxElement::Node(node.clone()), &mut ctx); } + + fn convert_denote_links(&self, link: impl AsRef) -> String { + let link = link.as_ref().to_string(); + if !link.starts_with("denote") { + return link; + }; + link.as_str() + .to_string() + .strip_prefix("denote:") + .map_or(link.clone(), |key| { + self.files + .iter() + .find(|file| file.contains(key)) + .map_or(link, |s| { + let mut final_link = "/".to_string(); + final_link.push_str(s); + final_link + }) + }) + } } impl Traverser for OrgHtmlExporter { @@ -281,7 +311,7 @@ impl Traverser for OrgHtmlExporter { Event::Leave(Container::OrgTableCell(_)) => self.output += "", Event::Enter(Container::Link(link)) => { - let path = link.path(); + let path = self.convert_denote_links(link.path()); let path = path.trim_start_matches("file:"); let path = if let Some(path) = path.strip_suffix("org") { let mut path = path.to_string(); @@ -290,6 +320,7 @@ impl Traverser for OrgHtmlExporter { } else { path.to_string() }; + let path = path.trim_start_matches("//"); if link.is_image() { let _ = write!(&mut self.output, r#""#, HtmlEscape(&path)); diff --git a/src/publish/publish.rs b/src/publish/publish.rs index af480ce..773b985 100644 --- a/src/publish/publish.rs +++ b/src/publish/publish.rs @@ -4,6 +4,7 @@ use std::collections::HashMap; use std::fs; use std::io::Error as IOError; use std::path::{Path, PathBuf}; +use std::sync::Arc; use tracing::{debug, error}; use walkdir::{DirEntry, Error as WDError, WalkDir}; @@ -57,7 +58,22 @@ pub fn build_site( } } if base_path.is_dir() { - WalkDir::new(&base_path) + let files = WalkDir::new(&base_path) + .follow_links(true) + .into_iter() + .filter_map(|e| { + e.ok().map(|e| { + let path = e + .path() + .strip_prefix(&base_path) + .unwrap_or_else(|_| e.path()); + path.to_string_lossy().to_string() + }) + }) + .collect::>(); + let files = Arc::new(files); + let walk_dir = WalkDir::new(&base_path); + walk_dir .follow_links(true) .into_iter() .collect::>>() @@ -67,8 +83,8 @@ pub fn build_site( let path = entry.path(); let file_name = path.file_name().unwrap_or_default().to_string_lossy(); if path.is_file() && !path.ends_with("~") && !file_name.starts_with(".#") { - let mut handler = OrgHtmlExporter::default(); - debug!(?path); + let mut handler = OrgHtmlExporter::new(Arc::clone(&files)); + // debug!(?path); if path .extension() .iter() @@ -76,7 +92,7 @@ pub fn build_site( .collect::() == "org" { - debug!("It ends with org"); + // debug!("It ends with org"); let org_string = fs::read_to_string(path).unwrap_or_default(); let org = orgize::Org::parse(&org_string); let mut keywords = org.keywords(); @@ -154,7 +170,17 @@ fn build_html(inner_html: String, keywords: HashMap) -> String { display: none; } "#; - format!("{head}\n{inner_html}\n") + let preamble = r#" +"#; + format!("{head}\n{preamble}{inner_html}\n") } fn extract_keywords(keywords: &mut I) -> HashMap @@ -168,3 +194,7 @@ where } map } + +fn conver_denote_links(inner_html: String) -> String { + todo!() +} diff --git a/src/watcher/mod.rs b/src/watcher/mod.rs index c073df8..620642b 100644 --- a/src/watcher/mod.rs +++ b/src/watcher/mod.rs @@ -1,11 +1,11 @@ use std::path::{Path, PathBuf}; -use notify::{Event, EventKind}; +use notify_debouncer_full::{DebouncedEvent, notify::EventKind}; use tracing::{debug, info}; use crate::publish::publish::{Error, build_site}; -pub fn watch(event: Event, path: impl AsRef) -> Result<(), Error> { +pub fn watch(event: &DebouncedEvent, path: impl AsRef) -> Result<(), Error> { if !event .paths .clone() @@ -16,7 +16,10 @@ pub fn watch(event: Event, path: impl AsRef) -> Result<(), Error> { false } else { e.file_name().is_some_and(|base_name| { - let name = base_name.to_os_string().into_string().unwrap_or("".into()); + let name = base_name + .to_os_string() + .into_string() + .unwrap_or_else(|_| String::new()); !(name.starts_with(".#") | name.ends_with("~")) }) } @@ -28,14 +31,14 @@ pub fn watch(event: Event, path: impl AsRef) -> Result<(), Error> { match event.kind { EventKind::Create(_) | EventKind::Modify(_) | EventKind::Remove(_) => { // rebuild function - debug!(?event); - for path in event.paths { + for path in &event.paths { if !path.exists() { debug!("file deleted"); } } let begin = chrono::Local::now(); let mut base_path = path.as_ref().to_path_buf(); + info!("Rebuilding site at: {:?}", base_path); base_path.push("content"); let mut publish_path = path.as_ref().to_path_buf(); publish_path.push("public"); @@ -43,10 +46,10 @@ pub fn watch(event: Event, path: impl AsRef) -> Result<(), Error> { let end = chrono::Local::now(); let elapsed = end - begin; info!( - "Time to build: {:?} minutes, {:?} seconds, and {:?} nanoseconds", + "Time to build: {:?} minutes, {:?} seconds, and {:?} milliseconds", elapsed.num_minutes(), elapsed.num_seconds(), - elapsed.num_nanoseconds() + elapsed.subsec_millis() ); } _ => (),