summaryrefslogtreecommitdiff
path: root/support/texlab/crates/base-db/src/deps/discover.rs
diff options
context:
space:
mode:
Diffstat (limited to 'support/texlab/crates/base-db/src/deps/discover.rs')
-rw-r--r--support/texlab/crates/base-db/src/deps/discover.rs117
1 files changed, 117 insertions, 0 deletions
diff --git a/support/texlab/crates/base-db/src/deps/discover.rs b/support/texlab/crates/base-db/src/deps/discover.rs
new file mode 100644
index 0000000000..06905905a5
--- /dev/null
+++ b/support/texlab/crates/base-db/src/deps/discover.rs
@@ -0,0 +1,117 @@
+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<PathBuf>,
+) {
+ 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<PathBuf>) {
+ 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<PathBuf>) -> 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::<FxHashSet<_>>();
+
+ 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_path(&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<PathBuf>) -> 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::<FxHashSet<_>>();
+
+ let mut changed = false;
+ for file in files {
+ let language = Language::from_path(&file).unwrap_or(Language::Tex);
+
+ if workspace.lookup_path(&file).is_none() && file.exists() {
+ changed |= workspace.load(&file, language).is_ok();
+ checked_paths.insert(file);
+ }
+ }
+
+ changed
+}