use std::path::PathBuf; use distro::Language; use itertools::Itertools; use rustc_hash::FxHashSet; use crate::Workspace; use super::ProjectRoot; pub fn watch( workspace: &mut Workspace, watcher: &mut dyn notify::Watcher, watched_dirs: &mut FxHashSet, ) { let roots = workspace .iter() .map(|document| &document.dir) .filter(|dir| dir.scheme() == "file") .unique() .map(|dir| ProjectRoot::walk_and_find(workspace, dir)); for root in roots { for uri in [&root.src_dir, &root.aux_dir, &root.log_dir, &root.pdf_dir] { if let Ok(path) = uri.to_file_path() { if watched_dirs.insert(path.clone()) { let _ = watcher.watch(&path, notify::RecursiveMode::NonRecursive); } } } } } pub fn discover(workspace: &mut Workspace, checked_paths: &mut FxHashSet) { loop { let mut changed = false; changed |= discover_parents(workspace, checked_paths); changed |= discover_children(workspace, checked_paths); if !changed { break; } } } fn discover_parents(workspace: &mut Workspace, checked_paths: &mut FxHashSet) -> bool { let dirs = workspace .iter() .filter(|document| document.language != Language::Bib) .filter_map(|document| document.path.as_deref()) .flat_map(|path| path.ancestors().skip(1)) .filter(|path| workspace.contains(path)) .map(|path| path.to_path_buf()) .collect::>(); let mut changed = false; for dir in dirs { if workspace .iter() .filter(|document| matches!(document.language, Language::Root | Language::Tectonic)) .filter_map(|document| document.path.as_deref()) .filter_map(|path| path.parent()) .any(|marker| dir.starts_with(marker)) { continue; } let Ok(entries) = std::fs::read_dir(dir) else { continue; }; for file in entries .flatten() .filter(|entry| entry.file_type().map_or(false, |type_| type_.is_file())) .map(|entry| entry.path()) { let Some(lang) = Language::from_path(&file) else { continue; }; if !matches!( lang, Language::Tex | Language::Root | Language::Tectonic | Language::Latexmkrc ) { continue; } if workspace.lookup_file(&file).is_none() && file.exists() { changed |= workspace.load(&file, lang).is_ok(); checked_paths.insert(file); } } } changed } fn discover_children(workspace: &mut Workspace, checked_paths: &mut FxHashSet) -> bool { let files = workspace .graphs() .values() .flat_map(|graph| graph.missing.iter()) .filter(|uri| uri.scheme() == "file") .flat_map(|uri| uri.to_file_path()) .collect::>(); let mut changed = false; for file in files { let language = Language::from_path(&file).unwrap_or(Language::Tex); if workspace.lookup_file(&file).is_none() && file.exists() { changed |= workspace.load(&file, language).is_ok(); checked_paths.insert(file); } } changed }