nebius/SWE-rebench-V2-PRs huggingface dataset:
Repository: casey/just
Pull request #636 (2020-06-09 17:06:23)

Ignores '@' returned from interpolation evaluation
Just now interprets this literally rather than propagating it to make the line run quietly.


Patch:
diff --git a/src/line.rs b/src/line.rs
index b03897c5..bbc0a4e7 100644
--- a/src/line.rs
+++ b/src/line.rs
@@ -24,4 +24,11 @@ impl<'src> Line<'src> {
       _ => false,
     }
   }
+
+  pub(crate) fn is_quiet(&self) -> bool {
+    match self.fragments.first() {
+      Some(Fragment::Text { token }) => token.lexeme().starts_with('@'),
+      _ => false,
+    }
+  }
 }
diff --git a/src/recipe.rs b/src/recipe.rs
index 43aece3a..50c2d39c 100644
--- a/src/recipe.rs
+++ b/src/recipe.rs
@@ -206,6 +206,7 @@ impl<'src, D> Recipe<'src, D> {
         }
         let mut evaluated = String::new();
         let mut continued = false;
+        let quiet_command = lines.peek().map(|line| line.is_quiet()).unwrap_or(false);
         loop {
           if lines.peek().is_none() {
             break;
@@ -221,7 +222,6 @@ impl<'src, D> Recipe<'src, D> {
           }
         }
         let mut command = evaluated.as_str();
-        let quiet_command = command.starts_with('@');
         if quiet_command {
           command = &command[1..];
         }


Test patch:
diff --git a/tests/integration.rs b/tests/integration.rs
index c73b354c..beacdd6e 100644
--- a/tests/integration.rs
+++ b/tests/integration.rs
@@ -2516,3 +2516,36 @@ test! {
   stderr: "",
   shell: false,
 }
+
+test! {
+  name: interpolation_evaluation_ignore_quiet,
+  justfile: r#"
+    foo:
+      {{"@echo foo 2>/dev/null"}}
+  "#,
+  args: (),
+  stdout: "",
+  stderr: "
+    @echo foo 2>/dev/null
+    error: Recipe `foo` failed on line 2 with exit code 127
+  ",
+  status: 127,
+  shell: false,
+}
+
+test! {
+  name: interpolation_evaluation_ignore_quiet_continuation,
+  justfile: r#"
+    foo:
+      {{""}}\
+      @echo foo 2>/dev/null
+  "#,
+  args: (),
+  stdout: "",
+  stderr: "
+    @echo foo 2>/dev/null
+    error: Recipe `foo` failed on line 3 with exit code 127
+  ",
+  status: 127,
+  shell: false,
+}
Repository: casey/just
Pull request #661 (2020-07-19 11:58:22)

Search for `.env` file from working directory
Search for a `.env` file starting in the  working directory, instead of
the invocation directory.


Patch:
diff --git a/src/justfile.rs b/src/justfile.rs
index b78a4b47..f6383a14 100644
--- a/src/justfile.rs
+++ b/src/justfile.rs
@@ -91,7 +91,7 @@ impl<'src> Justfile<'src> {
     }
 
     let dotenv = if config.load_dotenv {
-      load_dotenv()?
+      load_dotenv(&search.working_directory)?
     } else {
       BTreeMap::new()
     };
diff --git a/src/load_dotenv.rs b/src/load_dotenv.rs
index 0702f49c..22cb1ea4 100644
--- a/src/load_dotenv.rs
+++ b/src/load_dotenv.rs
@@ -1,25 +1,26 @@
 use crate::common::*;
 
-pub(crate) fn load_dotenv() -> RunResult<'static, BTreeMap<String, String>> {
-  // `dotenv::dotenv_iter` should eventually be un-deprecated, see:
+pub(crate) fn load_dotenv(
+  working_directory: &Path,
+) -> RunResult<'static, BTreeMap<String, String>> {
+  // `dotenv::from_path_iter` should eventually be un-deprecated, see:
   // https://github.com/dotenv-rs/dotenv/issues/13
   #![allow(deprecated)]
-  match dotenv::dotenv_iter() {
-    Ok(iter) => {
+  for directory in working_directory.ancestors() {
+    let path = directory.join(".env");
+
+    if path.is_file() {
+      let iter = dotenv::from_path_iter(&path)?;
       let mut dotenv = BTreeMap::new();
       for result in iter {
-        let (key, value) = result.map_err(|dotenv_error| RuntimeError::Dotenv { dotenv_error })?;
+        let (key, value) = result?;
         if env::var_os(&key).is_none() {
           dotenv.insert(key, value);
         }
       }
-      Ok(dotenv)
-    },
-    Err(dotenv_error) =>
-      if dotenv_error.not_found() {
-        Ok(BTreeMap::new())
-      } else {
-        Err(RuntimeError::Dotenv { dotenv_error })
-      },
+      return Ok(dotenv);
+    }
   }
+
+  Ok(BTreeMap::new())
 }
diff --git a/src/runtime_error.rs b/src/runtime_error.rs
index 511ae455..cea2fbc9 100644
--- a/src/runtime_error.rs
+++ b/src/runtime_error.rs
@@ -395,3 +395,9 @@ impl<'src> Display for RuntimeError<'src> {
     Ok(())
   }
 }
+
+impl<'src> From<dotenv::Error> for RuntimeError<'src> {
+  fn from(dotenv_error: dotenv::Error) -> RuntimeError<'src> {
+    RuntimeError::Dotenv { dotenv_error }
+  }
+}


Test patch:
diff --git a/tests/dotenv.rs b/tests/dotenv.rs
new file mode 100644
index 00000000..288d3dd1
--- /dev/null
+++ b/tests/dotenv.rs
@@ -0,0 +1,28 @@
+use executable_path::executable_path;
+use std::{process, str};
+
+use test_utilities::tmptree;
+
+#[test]
+fn dotenv() {
+  let tmp = tmptree! {
+    ".env": "KEY=ROOT",
+    sub: {
+      ".env": "KEY=SUB",
+      justfile: "default:\n\techo KEY=$KEY",
+    },
+  };
+
+  let binary = executable_path("just");
+
+  let output = process::Command::new(binary)
+    .current_dir(tmp.path())
+    .arg("sub/default")
+    .output()
+    .expect("just invocation failed");
+
+  assert_eq!(output.status.code().unwrap(), 0);
+
+  let stdout = str::from_utf8(&output.stdout).unwrap();
+  assert_eq!(stdout, "KEY=SUB\n");
+}
Repository: casey/just
Pull request #687 (2020-10-03 12:43:25)

Allow Suppressing of Failing Commands with `-` Prefix
Similar to `make` if a command is prefixed with `-` then the exit
status is ignored.

Fix for  #660

iwillspeak: I’m not sure what’s going on here with the snafu-derive error. It doesn’t happen locally on macOS. Could it be a spurious build failure ?

Patch:
diff --git a/src/line.rs b/src/line.rs
index bbc0a4e7..f296627d 100644
--- a/src/line.rs
+++ b/src/line.rs
@@ -31,4 +31,11 @@ impl<'src> Line<'src> {
       _ => false,
     }
   }
+
+  pub(crate) fn is_infallable(&self) -> bool {
+    match self.fragments.first() {
+      Some(Fragment::Text { token }) => token.lexeme().starts_with('-'),
+      _ => false,
+    }
+  }
 }
diff --git a/src/recipe.rs b/src/recipe.rs
index aa3548a2..d854dd5d 100644
--- a/src/recipe.rs
+++ b/src/recipe.rs
@@ -211,6 +211,10 @@ impl<'src, D> Recipe<'src, D> {
         let mut evaluated = String::new();
         let mut continued = false;
         let quiet_command = lines.peek().map(|line| line.is_quiet()).unwrap_or(false);
+        let infallable_command = lines
+          .peek()
+          .map(|line| line.is_infallable())
+          .unwrap_or(false);
         loop {
           if lines.peek().is_none() {
             break;
@@ -226,7 +230,7 @@ impl<'src, D> Recipe<'src, D> {
           }
         }
         let mut command = evaluated.as_str();
-        if quiet_command {
+        if quiet_command || infallable_command {
           command = &command[1..];
         }
 
@@ -266,7 +270,7 @@ impl<'src, D> Recipe<'src, D> {
         match InterruptHandler::guard(|| cmd.status()) {
           Ok(exit_status) =>
             if let Some(code) = exit_status.code() {
-              if code != 0 {
+              if code != 0 && !infallable_command {
                 return Err(RuntimeError::Code {
                   recipe: self.name(),
                   line_number: Some(line_number),


Test patch:
diff --git a/tests/misc.rs b/tests/misc.rs
index 2a025739..58bed437 100644
--- a/tests/misc.rs
+++ b/tests/misc.rs
@@ -1285,6 +1285,30 @@ test! {
   status:   EXIT_FAILURE,
 }
 
+test! {
+  name: infallable_command,
+  justfile: r#"
+infallable:
+  -exit 101
+"#,
+  stderr: "exit 101\n",
+  status: EXIT_SUCCESS,
+}
+
+test! {
+  name: infallable_with_failing,
+  justfile: r#"
+infallable:
+  -exit 101
+  exit 202
+"#,
+  stderr: r#"exit 101
+exit 202
+error: Recipe `infallable` failed on line 4 with exit code 202
+"#,
+  status: 202,
+}
+
 test! {
   name:     quiet_recipe,
   justfile: r#"
Repository: casey/just
Pull request #789 (2021-04-04 02:35:52)

Test multi-line strings in interpolation
The behavior here is strange, unintentional, but not obviously wrong.
Add a test to make sure it doesn't accidentally change in the future.


Patch:
diff --git a/src/lexer.rs b/src/lexer.rs
index bbec4bb5..9b1e181d 100644
--- a/src/lexer.rs
+++ b/src/lexer.rs
@@ -1431,6 +1431,22 @@ mod tests {
     ),
   }
 
+  test! {
+    name: interpolation_raw_multiline_string,
+    text: "hello:\n echo {{'\n'}}",
+    tokens: (
+      Identifier:"hello",
+      Colon,
+      Eol,
+      Indent:" ",
+      Text:"echo ",
+      InterpolationStart,
+      StringRaw:"'\n'",
+      InterpolationEnd,
+      Dedent,
+    ),
+  }
+
   test! {
     name: tokenize_names,
     text: "


Test patch:
diff --git a/tests/misc.rs b/tests/misc.rs
index e9e3c175..eb9ef9e7 100644
--- a/tests/misc.rs
+++ b/tests/misc.rs
@@ -2452,6 +2452,19 @@ test! {
   ",
 }
 
+test! {
+  name: multi_line_string_in_interpolation,
+  justfile: "
+    foo:
+      echo {{'a
+      echo b
+      echo c'}}z
+      echo baz
+  ",
+  stdout: "a\nb\ncz\nbaz\n",
+  stderr: "echo a\n  echo b\n  echo cz\necho baz\n",
+}
+
 #[cfg(windows)]
 test! {
   name: windows_interpreter_path_no_base,
Repository: casey/just
Pull request #794 (2021-04-06 04:10:52)

Allow evaluating an empty justfile


Patch:
diff --git a/src/justfile.rs b/src/justfile.rs
index 7255c673..36c4472d 100644
--- a/src/justfile.rs
+++ b/src/justfile.rs
@@ -61,23 +61,6 @@ impl<'src> Justfile<'src> {
     overrides: &'run BTreeMap<String, String>,
     arguments: &'run [String],
   ) -> RunResult<'run, ()> {
-    let argvec: Vec<&str> = if !arguments.is_empty() {
-      arguments.iter().map(String::as_str).collect()
-    } else if let Some(recipe) = self.first() {
-      let min_arguments = recipe.min_arguments();
-      if min_arguments > 0 {
-        return Err(RuntimeError::DefaultRecipeRequiresArguments {
-          recipe: recipe.name.lexeme(),
-          min_arguments,
-        });
-      }
-      vec![recipe.name()]
-    } else {
-      return Err(RuntimeError::NoRecipes);
-    };
-
-    let arguments = argvec.as_slice();
-
     let unknown_overrides = overrides
       .keys()
       .filter(|name| !self.assignments.contains_key(name.as_str()))
@@ -142,6 +125,23 @@ impl<'src> Justfile<'src> {
       return Ok(());
     }
 
+    let argvec: Vec<&str> = if !arguments.is_empty() {
+      arguments.iter().map(String::as_str).collect()
+    } else if let Some(recipe) = self.first() {
+      let min_arguments = recipe.min_arguments();
+      if min_arguments > 0 {
+        return Err(RuntimeError::DefaultRecipeRequiresArguments {
+          recipe: recipe.name.lexeme(),
+          min_arguments,
+        });
+      }
+      vec![recipe.name()]
+    } else {
+      return Err(RuntimeError::NoRecipes);
+    };
+
+    let arguments = argvec.as_slice();
+
     let mut missing = vec![];
     let mut grouped = vec![];
     let mut rest = arguments;


Test patch:
diff --git a/tests/evaluate.rs b/tests/evaluate.rs
new file mode 100644
index 00000000..4f11d77e
--- /dev/null
+++ b/tests/evaluate.rs
@@ -0,0 +1,29 @@
+test! {
+  name:     evaluate,
+  justfile: r#"
+foo := "a\t"
+hello := "c"
+bar := "b\t"
+ab := foo + bar + hello
+
+wut:
+  touch /this/is/not/a/file
+"#,
+  args:     ("--evaluate"),
+  stdout:   r#"ab    := "a	b	c"
+bar   := "b	"
+foo   := "a	"
+hello := "c"
+"#,
+}
+
+test! {
+  name:     evaluate_empty,
+  justfile: "
+    a := 'foo'
+  ",
+  args:     ("--evaluate"),
+  stdout:   r#"
+    a := "foo"
+  "#,
+}
diff --git a/tests/lib.rs b/tests/lib.rs
index ef906786..2ec5a2fa 100644
--- a/tests/lib.rs
+++ b/tests/lib.rs
@@ -10,6 +10,7 @@ mod delimiters;
 mod dotenv;
 mod edit;
 mod error_messages;
+mod evaluate;
 mod examples;
 mod export;
 mod init;
diff --git a/tests/misc.rs b/tests/misc.rs
index 69e4b1c1..55c52ba7 100644
--- a/tests/misc.rs
+++ b/tests/misc.rs
@@ -569,25 +569,6 @@ echo `echo command interpolation`
 ",
 }
 
-test! {
-  name:     evaluate,
-  justfile: r#"
-foo := "a\t"
-hello := "c"
-bar := "b\t"
-ab := foo + bar + hello
-
-wut:
-  touch /this/is/not/a/file
-"#,
-  args:     ("--evaluate"),
-  stdout:   r#"ab    := "a	b	c"
-bar   := "b	"
-foo   := "a	"
-hello := "c"
-"#,
-}
-
 test! {
   name:     line_error_spacing,
   justfile: r#"
Repository: casey/just
Pull request #398 (2019-04-11 22:41:01)

Omit aliases that begin with `_` from --list`
It seems reasonable to treat aliases that start with `_` as private, the same as recipes.


Patch:
diff --git a/README.adoc b/README.adoc
index d6df2c89..f123b894 100644
--- a/README.adoc
+++ b/README.adoc
@@ -608,7 +608,7 @@ Run `just --help` to see all the options.
 
 === Private Recipes
 
-Recipes whose name starts with a `_` are omitted from `just --list`:
+Recipes and aliases whose name starts with a `_` are omitted from `just --list`:
 
 ```make
 test: _test-helper
diff --git a/src/alias.rs b/src/alias.rs
index c33c202d..ec733da3 100644
--- a/src/alias.rs
+++ b/src/alias.rs
@@ -4,6 +4,7 @@ pub struct Alias<'a> {
   pub name: &'a str,
   pub target: &'a str,
   pub line_number: usize,
+  pub private: bool,
 }
 
 impl<'a> Display for Alias<'a> {
diff --git a/src/parser.rs b/src/parser.rs
index 497434e0..19b6a924 100644
--- a/src/parser.rs
+++ b/src/parser.rs
@@ -372,6 +372,7 @@ impl<'a> Parser<'a> {
       Alias {
         name: name.lexeme,
         line_number: name.line,
+        private: name.lexeme.starts_with('_'),
         target,
       },
     );
diff --git a/src/run.rs b/src/run.rs
index 0f2c2f7d..afa55bcf 100644
--- a/src/run.rs
+++ b/src/run.rs
@@ -358,6 +358,10 @@ pub fn run() {
     // Construct a target to alias map.
     let mut recipe_aliases: BTreeMap<&str, Vec<&str>> = BTreeMap::new();
     for alias in justfile.aliases.values() {
+      if alias.private {
+        continue;
+      }
+
       if !recipe_aliases.contains_key(alias.target) {
         recipe_aliases.insert(alias.target, vec![alias.name]);
       } else {


Test patch:
diff --git a/tests/integration.rs b/tests/integration.rs
index f94f7e1e..aa8af3d3 100644
--- a/tests/integration.rs
+++ b/tests/integration.rs
@@ -132,6 +132,17 @@ integration_test! {
   status: EXIT_SUCCESS,
 }
 
+integration_test! {
+  name: alias_listing_private,
+  justfile: "foo PARAM='foo':\n  echo {{PARAM}}\nalias _f = foo",
+  args: ("--list"),
+  stdout: "Available recipes:
+    foo PARAM='foo'
+",
+  stderr: "",
+  status: EXIT_SUCCESS,
+}
+
 integration_test! {
   name: alias,
   justfile: "foo:\n  echo foo\nalias f = foo",
Repository: casey/just
Pull request #566 (2019-12-13 01:42:57)

Fix regression in error message color printing
Make the word "error" print in red instead of plain text.


Patch:
diff --git a/src/error_result_ext.rs b/src/error_result_ext.rs
index d52880bd..0ab5c870 100644
--- a/src/error_result_ext.rs
+++ b/src/error_result_ext.rs
@@ -10,7 +10,7 @@ impl<T, E: Error> ErrorResultExt<T> for Result<T, E> {
       Ok(ok) => Ok(ok),
       Err(error) => {
         if color.stderr().active() {
-          eprintln!("{} {:#}", color.error().paint("error:"), error);
+          eprintln!("{}: {:#}", color.stderr().error().paint("error"), error);
         } else {
           eprintln!("error: {}", error);
         }


Test patch:
diff --git a/tests/integration.rs b/tests/integration.rs
index d3942ca4..7176e145 100644
--- a/tests/integration.rs
+++ b/tests/integration.rs
@@ -970,7 +970,7 @@ test! {
   justfile: "b := a\na := `exit 100`\nbar:\n echo '{{`exit 200`}}'",
   args:     ("--color", "always"),
   stdout:   "",
-  stderr:   "\u{1b}[1;31merror:\u{1b}[0m \u{1b}[1mBacktick failed with exit code 100
+  stderr:   "\u{1b}[1;31merror\u{1b}[0m: \u{1b}[1mBacktick failed with exit code 100
 \u{1b}[0m  |\n2 | a := `exit 100`\n  |      \u{1b}[1;31m^^^^^^^^^^\u{1b}[0m\n",
   status:   100,
 }
@@ -1008,7 +1008,7 @@ recipe:
   @exit 100",
   args:     ("--color=always"),
   stdout:   "",
-  stderr:   "\u{1b}[1;31merror:\u{1b}[0m \u{1b}[1m\
+  stderr:   "\u{1b}[1;31merror\u{1b}[0m: \u{1b}[1m\
 Recipe `recipe` failed on line 3 with exit code 100\u{1b}[0m\n",
   status:   100,
 }
Repository: casey/just
Pull request #596 (2020-02-20 14:06:06)

Don't require justfile to print completions
Currently, the `--completions` subcommand will fail if there is no
justfile present. A justfile isn't needed to print completions, so fix
this.


Patch:
diff --git a/src/config.rs b/src/config.rs
index 9c6dadc4..48296bb7 100644
--- a/src/config.rs
+++ b/src/config.rs
@@ -388,6 +388,10 @@ impl Config {
       return self.init();
     }
 
+    if let Completions { shell } = self.subcommand {
+      return Self::completions(&shell);
+    }
+
     let search =
       Search::find(&self.search_config, &self.invocation_directory).eprint(self.color)?;
 
@@ -414,7 +418,6 @@ impl Config {
 
     match &self.subcommand {
       Dump => Self::dump(justfile),
-      Completions { shell } => Self::completions(&shell),
       Evaluate { overrides } => self.run(justfile, &search, overrides, &Vec::new()),
       Run {
         arguments,
@@ -423,7 +426,7 @@ impl Config {
       List => self.list(justfile),
       Show { ref name } => Self::show(&name, justfile),
       Summary => Self::summary(justfile),
-      Edit | Init => unreachable!(),
+      Completions { .. } | Edit | Init => unreachable!(),
     }
   }
 


Test patch:
diff --git a/tests/completions.rs b/tests/completions.rs
index 0121e1d5..4b172cf7 100644
--- a/tests/completions.rs
+++ b/tests/completions.rs
@@ -1,12 +1,16 @@
 use std::process::Command;
 
 use executable_path::executable_path;
+use tempfile::tempdir;
 
 #[test]
 fn output() {
+  let tempdir = tempdir().unwrap();
+
   let output = Command::new(executable_path("just"))
     .arg("--completions")
     .arg("bash")
+    .current_dir(tempdir.path())
     .output()
     .unwrap();
Repository: casey/just
Pull request #2136 (2024-06-07 00:45:27)

Add [attribute: value] shorthand
https://github.com/casey/just/issues/2086

Adds a shorthand syntax `[attribute: value]` for attributes with a single argument, that is equivalent to `[attribute(value)]`.


Patch:
diff --git a/src/attribute.rs b/src/attribute.rs
index bf657a06..f9c5bfcd 100644
--- a/src/attribute.rs
+++ b/src/attribute.rs
@@ -57,9 +57,7 @@ impl<'src> Attribute<'src> {
       })?;
 
     let found = argument.as_ref().iter().count();
-
     let range = discriminant.argument_range();
-
     if !range.contains(&found) {
       return Err(
         name.error(CompileErrorKind::AttributeArgumentCountMismatch {
diff --git a/src/parser.rs b/src/parser.rs
index 857284da..3e4bb022 100644
--- a/src/parser.rs
+++ b/src/parser.rs
@@ -987,15 +987,18 @@ impl<'run, 'src> Parser<'run, 'src> {
       loop {
         let name = self.parse_name()?;
 
-        let argument = if self.accepted(ParenL)? {
-          let argument = self.parse_string_literal()?;
+        let maybe_argument = if self.accepted(Colon)? {
+          let arg = self.parse_string_literal()?;
+          Some(arg)
+        } else if self.accepted(ParenL)? {
+          let arg = self.parse_string_literal()?;
           self.expect(ParenR)?;
-          Some(argument)
+          Some(arg)
         } else {
           None
         };
 
-        let attribute = Attribute::new(name, argument)?;
+        let attribute = Attribute::new(name, maybe_argument)?;
 
         if let Some(line) = attributes.get(&attribute) {
           return Err(name.error(CompileErrorKind::DuplicateAttribute {
@@ -1158,6 +1161,18 @@ mod tests {
     tree: (justfile (alias t test)),
   }
 
+  test! {
+    name: single_argument_attribute_shorthand,
+    text: "[group: 'some-group']\nalias t := test",
+    tree: (justfile (alias t test)),
+  }
+
+  test! {
+    name: single_argument_attribute_shorthand_multiple_same_line,
+    text: "[group: 'some-group', group: 'some-other-group']\nalias t := test",
+    tree: (justfile (alias t test)),
+  }
+
   test! {
     name: aliases_multiple,
     text: "alias t := test\nalias b := build",


Test patch:
diff --git a/tests/attributes.rs b/tests/attributes.rs
index 9c79bb77..a6047772 100644
--- a/tests/attributes.rs
+++ b/tests/attributes.rs
@@ -72,7 +72,7 @@ fn multiple_attributes_one_line_error_message() {
     )
     .stderr(
       "
-        error: Expected ']', ',', or '(', but found identifier
+        error: Expected ']', ':', ',', or '(', but found identifier
          ——▶ justfile:1:17
           │
         1 │ [macos, windows linux]
diff --git a/tests/groups.rs b/tests/groups.rs
index 6c072e86..58343331 100644
--- a/tests/groups.rs
+++ b/tests/groups.rs
@@ -144,3 +144,26 @@ fn list_groups_with_custom_prefix() {
     )
     .run();
 }
+
+#[test]
+fn list_groups_with_shorthand_syntax() {
+  Test::new()
+    .justfile(
+      "
+        [group: 'B']
+        foo:
+
+        [group: 'A', group: 'B']
+        bar:
+      ",
+    )
+    .args(["--groups", "--list-prefix", "..."])
+    .stdout(
+      "
+      Recipe groups:
+      ...A
+      ...B
+      ",
+    )
+    .run();
+}
Repository: casey/just
Pull request #2141 (2024-06-09 00:57:54)

Fix `fzf` chooser preview with space-separated module paths


Patch:
diff --git a/src/config.rs b/src/config.rs
index d2056d5d..34b72f38 100644
--- a/src/config.rs
+++ b/src/config.rs
@@ -11,14 +11,6 @@ const CHOOSE_HELP: &str = "Select one or more recipes to run using a binary choo
                            If `--chooser` is not passed the chooser defaults to the \
                            value of $JUST_CHOOSER, falling back to `fzf`";
 
-pub(crate) fn chooser_default(justfile: &Path) -> OsString {
-  let mut chooser = OsString::new();
-  chooser.push("fzf --multi --preview 'just --unstable --color always --justfile \"");
-  chooser.push(justfile);
-  chooser.push("\" --show {}'");
-  chooser
-}
-
 #[derive(Debug, PartialEq)]
 pub(crate) struct Config {
   pub(crate) check: bool,
@@ -568,15 +560,20 @@ impl Config {
     }
   }
 
-  fn parse_module_path(path: ValuesRef<String>) -> ConfigResult<ModulePath> {
+  fn parse_module_path(values: ValuesRef<String>) -> ConfigResult<ModulePath> {
+    let path = values.clone().map(|s| (*s).as_str()).collect::<Vec<&str>>();
+
+    let path = if path.len() == 1 && path[0].contains(' ') {
+      path[0].split_whitespace().collect::<Vec<&str>>()
+    } else {
+      path
+    };
+
     path
-      .clone()
-      .map(|s| (*s).as_str())
-      .collect::<Vec<&str>>()
       .as_slice()
       .try_into()
       .map_err(|()| ConfigError::ModulePath {
-        path: path.cloned().collect(),
+        path: values.cloned().collect(),
       })
   }
 
diff --git a/src/subcommand.rs b/src/subcommand.rs
index e1433154..450ffa33 100644
--- a/src/subcommand.rs
+++ b/src/subcommand.rs
@@ -231,7 +231,15 @@ impl Subcommand {
       return Err(Error::NoChoosableRecipes);
     }
 
-    let chooser = chooser.map_or_else(|| config::chooser_default(&search.justfile), From::from);
+    let chooser = if let Some(chooser) = chooser {
+      OsString::from(chooser)
+    } else {
+      let mut chooser = OsString::new();
+      chooser.push("fzf --multi --preview 'just --unstable --color always --justfile \"");
+      chooser.push(&search.justfile);
+      chooser.push("\" --show {}'");
+      chooser
+    };
 
     let result = justfile
       .settings


Test patch:
diff --git a/tests/show.rs b/tests/show.rs
index e9fe5e6a..17aaa473 100644
--- a/tests/show.rs
+++ b/tests/show.rs
@@ -124,3 +124,18 @@ fn show_invalid_path() {
     .status(1)
     .run();
 }
+
+#[test]
+fn show_space_separated_path() {
+  Test::new()
+    .write("foo.just", "bar:\n @echo MODULE")
+    .justfile(
+      "
+        mod foo
+      ",
+    )
+    .test_round_trip(false)
+    .args(["--unstable", "--show", "foo bar"])
+    .stdout("bar:\n    @echo MODULE\n")
+    .run();
+}