Repository: casey/just
Pull request #2056 (2024-05-19 06:36:14)

Fix imported recipe working directory


Patch:
diff --git a/src/analyzer.rs b/src/analyzer.rs
index 0180fbe0..eb85b4f1 100644
--- a/src/analyzer.rs
+++ b/src/analyzer.rs
@@ -133,7 +133,7 @@ impl<'src> Analyzer<'src> {
       define(recipe.name, "recipe", settings.allow_duplicate_recipes)?;
       if recipe_table
         .get(recipe.name.lexeme())
-        .map_or(true, |original| recipe.depth <= original.depth)
+        .map_or(true, |original| recipe.file_depth <= original.file_depth)
       {
         recipe_table.insert(recipe.clone());
       }
diff --git a/src/compiler.rs b/src/compiler.rs
index 6305c543..a1e987a1 100644
--- a/src/compiler.rs
+++ b/src/compiler.rs
@@ -21,9 +21,10 @@ impl Compiler {
       loaded.push(relative.into());
       let tokens = Lexer::lex(relative, src)?;
       let mut ast = Parser::parse(
+        current.file_depth,
         &current.path,
         &current.namepath,
-        current.depth,
+        current.submodule_depth,
         &tokens,
         &current.working_directory,
       )?;
@@ -169,6 +170,7 @@ impl Compiler {
   pub(crate) fn test_compile(src: &str) -> CompileResult<Justfile> {
     let tokens = Lexer::test_lex(src)?;
     let ast = Parser::parse(
+      0,
       &PathBuf::new(),
       &Namepath::default(),
       0,
diff --git a/src/parser.rs b/src/parser.rs
index 5760a416..e3abcbfb 100644
--- a/src/parser.rs
+++ b/src/parser.rs
@@ -25,6 +25,7 @@ use {super::*, TokenKind::*};
 /// contents of the set is printed in the resultant error message.
 pub(crate) struct Parser<'run, 'src> {
   expected_tokens: BTreeSet<TokenKind>,
+  file_depth: u32,
   file_path: &'run Path,
   module_namepath: &'run Namepath<'src>,
   next_token: usize,
@@ -37,6 +38,7 @@ pub(crate) struct Parser<'run, 'src> {
 impl<'run, 'src> Parser<'run, 'src> {
   /// Parse `tokens` into an `Ast`
   pub(crate) fn parse(
+    file_depth: u32,
     file_path: &'run Path,
     module_namepath: &'run Namepath<'src>,
     submodule_depth: u32,
@@ -45,6 +47,7 @@ impl<'run, 'src> Parser<'run, 'src> {
   ) -> CompileResult<'src, Ast<'src>> {
     Self {
       expected_tokens: BTreeSet::new(),
+      file_depth,
       file_path,
       module_namepath,
       next_token: 0,
@@ -456,7 +459,7 @@ impl<'run, 'src> Parser<'run, 'src> {
     let value = self.parse_expression()?;
     self.expect_eol()?;
     Ok(Assignment {
-      depth: self.submodule_depth,
+      depth: self.file_depth,
       export,
       name,
       value,
@@ -769,8 +772,8 @@ impl<'run, 'src> Parser<'run, 'src> {
       attributes,
       body,
       dependencies,
-      depth: self.submodule_depth,
       doc,
+      file_depth: self.file_depth,
       file_path: self.file_path.into(),
       name,
       namepath: self.module_namepath.join(name),
@@ -778,6 +781,7 @@ impl<'run, 'src> Parser<'run, 'src> {
       priors,
       private: name.lexeme().starts_with('_'),
       quiet,
+      submodule_depth: self.submodule_depth,
       working_directory: self.working_directory.into(),
     })
   }
@@ -1010,6 +1014,7 @@ mod tests {
     let unindented = unindent(text);
     let tokens = Lexer::test_lex(&unindented).expect("lexing failed");
     let justfile = Parser::parse(
+      0,
       &PathBuf::new(),
       &Namepath::default(),
       0,
@@ -1055,6 +1060,7 @@ mod tests {
     let tokens = Lexer::test_lex(src).expect("Lexing failed in parse test...");
 
     match Parser::parse(
+      0,
       &PathBuf::new(),
       &Namepath::default(),
       0,
diff --git a/src/recipe.rs b/src/recipe.rs
index 5834d5b3..48784d94 100644
--- a/src/recipe.rs
+++ b/src/recipe.rs
@@ -22,10 +22,10 @@ pub(crate) struct Recipe<'src, D = Dependency<'src>> {
   pub(crate) attributes: BTreeSet<Attribute<'src>>,
   pub(crate) body: Vec<Line<'src>>,
   pub(crate) dependencies: Vec<D>,
-  #[serde(skip)]
-  pub(crate) depth: u32,
   pub(crate) doc: Option<&'src str>,
   #[serde(skip)]
+  pub(crate) file_depth: u32,
+  #[serde(skip)]
   pub(crate) file_path: PathBuf,
   pub(crate) name: Name<'src>,
   pub(crate) namepath: Namepath<'src>,
@@ -35,6 +35,8 @@ pub(crate) struct Recipe<'src, D = Dependency<'src>> {
   pub(crate) quiet: bool,
   pub(crate) shebang: bool,
   #[serde(skip)]
+  pub(crate) submodule_depth: u32,
+  #[serde(skip)]
   pub(crate) working_directory: PathBuf,
 }
 
@@ -126,7 +128,7 @@ impl<'src, D> Recipe<'src, D> {
 
   fn working_directory<'a>(&'a self, search: &'a Search) -> Option<&Path> {
     if self.change_directory() {
-      Some(if self.depth > 0 {
+      Some(if self.submodule_depth > 0 {
         &self.working_directory
       } else {
         &search.working_directory
diff --git a/src/source.rs b/src/source.rs
index bbb319a1..8826b4d5 100644
--- a/src/source.rs
+++ b/src/source.rs
@@ -1,37 +1,41 @@
 use super::*;
 
 pub(crate) struct Source<'src> {
-  pub(crate) path: PathBuf,
-  pub(crate) depth: u32,
+  pub(crate) file_depth: u32,
   pub(crate) namepath: Namepath<'src>,
+  pub(crate) path: PathBuf,
+  pub(crate) submodule_depth: u32,
   pub(crate) working_directory: PathBuf,
 }
 
 impl<'src> Source<'src> {
   pub(crate) fn root(path: &Path) -> Self {
     Self {
-      path: path.into(),
-      depth: 0,
+      file_depth: 0,
       namepath: Namepath::default(),
+      path: path.into(),
+      submodule_depth: 0,
       working_directory: path.parent().unwrap().into(),
     }
   }
 
   pub(crate) fn import(&self, path: PathBuf) -> Self {
     Self {
-      depth: self.depth + 1,
-      path,
+      file_depth: self.file_depth + 1,
       namepath: self.namepath.clone(),
+      path,
+      submodule_depth: self.submodule_depth,
       working_directory: self.working_directory.clone(),
     }
   }
 
   pub(crate) fn module(&self, name: Name<'src>, path: PathBuf) -> Self {
     Self {
+      file_depth: self.file_depth + 1,
+      namepath: self.namepath.join(name),
+      submodule_depth: self.submodule_depth + 1,
       working_directory: path.parent().unwrap().into(),
       path,
-      depth: self.depth + 1,
-      namepath: self.namepath.join(name),
     }
   }
 }
diff --git a/src/unresolved_recipe.rs b/src/unresolved_recipe.rs
index fe30f151..80d49045 100644
--- a/src/unresolved_recipe.rs
+++ b/src/unresolved_recipe.rs
@@ -48,8 +48,8 @@ impl<'src> UnresolvedRecipe<'src> {
       attributes: self.attributes,
       body: self.body,
       dependencies,
-      depth: self.depth,
       doc: self.doc,
+      file_depth: self.file_depth,
       file_path: self.file_path,
       name: self.name,
       namepath: self.namepath,
@@ -58,6 +58,7 @@ impl<'src> UnresolvedRecipe<'src> {
       private: self.private,
       quiet: self.quiet,
       shebang: self.shebang,
+      submodule_depth: self.submodule_depth,
       working_directory: self.working_directory,
     })
   }


Test patch:
diff --git a/src/testing.rs b/src/testing.rs
index bf3cbbf5..3de2e2bf 100644
--- a/src/testing.rs
+++ b/src/testing.rs
@@ -60,6 +60,7 @@ pub(crate) fn analysis_error(
   let tokens = Lexer::test_lex(src).expect("Lexing failed in parse test...");
 
   let ast = Parser::parse(
+    0,
     &PathBuf::new(),
     &Namepath::default(),
     0,
diff --git a/tests/imports.rs b/tests/imports.rs
index 0d32fadc..ce6dcf94 100644
--- a/tests/imports.rs
+++ b/tests/imports.rs
@@ -344,3 +344,20 @@ fn shebang_recipes_in_imports_in_root_run_in_justfile_directory() {
     .stdout("BAZ")
     .run();
 }
+
+#[test]
+fn recipes_imported_in_root_run_in_command_line_provided_working_directory() {
+  Test::new()
+    .write("subdir/b.justfile", "@b:\n  cat baz")
+    .write("subdir/a.justfile", "import 'b.justfile'\n@a: b\n  cat baz")
+    .write("baz", "BAZ")
+    .args([
+      "--working-directory",
+      ".",
+      "--justfile",
+      "subdir/a.justfile",
+    ])
+    .test_round_trip(false)
+    .stdout("BAZBAZ")
+    .run();
+}