#![cfg(feature = "code")]
use std::path::Path;
use std::sync::Arc;
use notify::Watcher as _;
use crate::code::lang;
use crate::kg::GraphHandle;
pub fn spawn_watcher(kg_arc: Arc<GraphHandle>, path: String, project: &str) {
let _ = std::thread::Builder::new()
.name(format!("watcher-{project}"))
.spawn(move || {
let kg = kg_arc;
let (tx, rx) = std::sync::mpsc::channel::<notify::Event>();
let Ok(mut watcher) = notify::recommended_watcher(move |res| {
if let Ok(event) = res {
let _ = tx.send(event);
}
}) else {
return;
};
if watcher
.watch(Path::new(&path), notify::RecursiveMode::Recursive)
.is_err()
{
return;
}
use std::collections::BTreeSet;
use std::path::PathBuf;
use std::time::{Duration, Instant};
const DEBOUNCE_MS: u64 = 2000;
let base = crate::actions::code::canonical_base();
let mut pending: BTreeSet<PathBuf> = BTreeSet::new();
let mut last_event = Instant::now();
loop {
let elapsed = last_event.elapsed().as_millis() as u64;
let timeout = Duration::from_millis(DEBOUNCE_MS.saturating_sub(elapsed));
match rx.recv_timeout(timeout) {
Ok(first) => {
let mut collect = |event: notify::Event| {
for p in &event.paths {
if lang::detect(p).is_some() {
pending.insert(p.clone());
}
}
};
collect(first);
while let Ok(event) = rx.try_recv() {
collect(event);
}
last_event = Instant::now();
}
Err(_) if !pending.is_empty() => {
let mut to_index: Vec<PathBuf> = Vec::new();
for p in std::mem::take(&mut pending) {
if p.exists() {
to_index.push(p);
} else {
let name = crate::actions::code::file_entity_name(&p, &base);
let _ = kg.code_purge_file(&name);
}
}
if !to_index.is_empty() {
let _ = crate::actions::code::index_paths(
kg.as_ref(),
to_index,
&base,
false,
);
}
}
Err(_) => {}
}
}
});
}