Add KCL importing relative to where you're importing from (#7125)

* add test

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* Add importing relative to where you're importing from

* Update output

* Remove runtime panics

* Change to debug_assert

---------

Signed-off-by: Jess Frazelle <github@jessfraz.com>
Co-authored-by: Jess Frazelle <github@jessfraz.com>
This commit is contained in:
Jonathan Tran
2025-05-20 20:47:32 -04:00
committed by GitHub
parent 38a73a603b
commit 4212b95232
19 changed files with 459 additions and 58 deletions

View File

@ -34,9 +34,9 @@ pub(crate) type Universe = HashMap<String, DependencyInfo>;
pub fn import_graph(progs: &Universe, ctx: &ExecutorContext) -> Result<Vec<Vec<String>>, KclError> {
let mut graph = Graph::new();
for (name, (_, _, _, repr)) in progs.iter() {
for (name, (_, _, path, repr)) in progs.iter() {
graph.extend(
import_dependencies(repr, ctx)?
import_dependencies(path, repr, ctx)?
.into_iter()
.map(|(dependency, _, _)| (name.clone(), dependency))
.collect::<Vec<_>>(),
@ -120,17 +120,26 @@ fn topsort(all_modules: &[&str], graph: Graph) -> Result<Vec<Vec<String>>, KclEr
type ImportDependencies = Vec<(String, AstNode<ImportStatement>, ModulePath)>;
pub(crate) fn import_dependencies(repr: &ModuleRepr, ctx: &ExecutorContext) -> Result<ImportDependencies, KclError> {
pub(crate) fn import_dependencies(
path: &ModulePath,
repr: &ModuleRepr,
ctx: &ExecutorContext,
) -> Result<ImportDependencies, KclError> {
let ModuleRepr::Kcl(prog, _) = repr else {
// It has no dependencies, so return an empty list.
return Ok(vec![]);
};
let ret = Arc::new(Mutex::new(vec![]));
fn walk(ret: Arc<Mutex<ImportDependencies>>, node: Node<'_>, ctx: &ExecutorContext) -> Result<(), KclError> {
fn walk(
ret: Arc<Mutex<ImportDependencies>>,
node: Node<'_>,
import_from: &ModulePath,
ctx: &ExecutorContext,
) -> Result<(), KclError> {
if let Node::ImportStatement(is) = node {
// We only care about Kcl and Foreign imports for now.
let resolved_path = ModulePath::from_import_path(&is.path, &ctx.settings.project_directory);
let resolved_path = ModulePath::from_import_path(&is.path, &ctx.settings.project_directory, import_from)?;
match &is.path {
ImportPath::Kcl { filename } => {
// We need to lock the mutex to push the dependency.
@ -160,13 +169,13 @@ pub(crate) fn import_dependencies(repr: &ModuleRepr, ctx: &ExecutorContext) -> R
}
for child in node.children().iter() {
walk(ret.clone(), *child, ctx)?;
walk(ret.clone(), *child, import_from, ctx)?;
}
Ok(())
}
walk(ret.clone(), prog.into(), ctx)?;
walk(ret.clone(), prog.into(), path, ctx)?;
let ret = ret.lock().map_err(|err| {
KclError::Internal(KclErrorDetails::new(
@ -182,11 +191,12 @@ pub(crate) fn import_dependencies(repr: &ModuleRepr, ctx: &ExecutorContext) -> R
/// only `repr`'s non-transitive imports.
pub(crate) async fn import_universe(
ctx: &ExecutorContext,
path: &ModulePath,
repr: &ModuleRepr,
out: &mut Universe,
exec_state: &mut ExecState,
) -> Result<UniverseMap, KclError> {
let modules = import_dependencies(repr, ctx)?;
let modules = import_dependencies(path, repr, ctx)?;
let mut module_imports = HashMap::new();
for (filename, import_stmt, module_path) in modules {
match &module_path {
@ -208,7 +218,7 @@ pub(crate) async fn import_universe(
let source_range = SourceRange::from(&import_stmt);
let attrs = &import_stmt.outer_attrs;
let module_id = ctx
.open_module(&import_stmt.path, attrs, exec_state, source_range)
.open_module(&import_stmt.path, attrs, &module_path, exec_state, source_range)
.await?;
let repr = {
@ -221,8 +231,8 @@ pub(crate) async fn import_universe(
module_info.repr.clone()
};
out.insert(filename, (import_stmt, module_id, module_path, repr.clone()));
Box::pin(import_universe(ctx, &repr, out, exec_state)).await?;
out.insert(filename, (import_stmt, module_id, module_path.clone(), repr.clone()));
Box::pin(import_universe(ctx, &module_path, &repr, out, exec_state)).await?;
}
Ok(module_imports)