tinymist_query/syntax/
module.rsuse std::sync::Once;
use regex::RegexSet;
use crate::prelude::*;
#[derive(Debug, Clone)]
pub struct ModuleDependency {
pub dependencies: EcoVec<TypstFileId>,
pub dependents: EcoVec<TypstFileId>,
}
pub fn construct_module_dependencies(
ctx: &mut LocalContext,
) -> HashMap<TypstFileId, ModuleDependency> {
let mut dependencies = HashMap::new();
let mut dependents = HashMap::new();
for file_id in ctx.source_files().clone() {
let source = match ctx.shared.source_by_id(file_id) {
Ok(source) => source,
Err(err) => {
static WARN_ONCE: Once = Once::new();
WARN_ONCE.call_once(|| {
log::warn!("construct_module_dependencies: {err:?}");
});
continue;
}
};
let file_id = source.id();
let ei = ctx.shared.expr_stage(&source);
dependencies
.entry(file_id)
.or_insert_with(|| ModuleDependency {
dependencies: ei.imports.keys().cloned().collect(),
dependents: EcoVec::default(),
});
for (dep, _) in ei.imports.clone() {
dependents
.entry(dep)
.or_insert_with(EcoVec::new)
.push(file_id);
}
}
for (file_id, dependents) in dependents {
if let Some(dep) = dependencies.get_mut(&file_id) {
dep.dependents = dependents;
}
}
dependencies
}
fn is_hidden(entry: &walkdir::DirEntry) -> bool {
entry
.file_name()
.to_str()
.map(|s| s.starts_with('.'))
.unwrap_or(false)
}
pub(crate) fn scan_workspace_files<T>(
root: &Path,
ext: &RegexSet,
f: impl Fn(&Path) -> T,
) -> Vec<T> {
let mut res = vec![];
let mut it = walkdir::WalkDir::new(root).follow_links(false).into_iter();
loop {
let de = match it.next() {
None => break,
Some(Err(_err)) => continue,
Some(Ok(entry)) => entry,
};
if is_hidden(&de) {
if de.file_type().is_dir() {
it.skip_current_dir();
}
continue;
}
static IGNORE_REGEX: LazyLock<RegexSet> = LazyLock::new(|| {
RegexSet::new([
r#"^build$"#,
r#"^target$"#,
r#"^node_modules$"#,
r#"^out$"#,
r#"^dist$"#,
])
.unwrap()
});
if de
.path()
.file_name()
.and_then(|s| s.to_str())
.is_some_and(|s| IGNORE_REGEX.is_match(s))
{
if de.file_type().is_dir() {
it.skip_current_dir();
}
continue;
}
if !de.file_type().is_file() {
continue;
}
if !de
.path()
.extension()
.and_then(|err| err.to_str())
.is_some_and(|err| ext.is_match(err))
{
continue;
}
let path = de.path();
let relative_path = match path.strip_prefix(root) {
Ok(path) => path,
Err(err) => {
log::warn!("failed to strip prefix, path: {path:?}, root: {root:?}: {err}");
continue;
}
};
res.push(f(relative_path));
if res.len() >= (u16::MAX as usize) {
break;
}
}
res
}