diff --git a/GRAMMAR.md b/GRAMMAR.md index 920ad84..4c4a25c 100644 --- a/GRAMMAR.md +++ b/GRAMMAR.md @@ -69,6 +69,7 @@ setting : 'set' 'allow-duplicate-recipes' boolean? | 'set' 'fallback' boolean? | 'set' 'ignore-comments' boolean? | 'set' 'positional-arguments' boolean? + | 'set' 'quiet' boolean? | 'set' 'shell' ':=' '[' string (',' string)* ','? ']' | 'set' 'tempdir ':=' string | 'set' 'windows-powershell' boolean? diff --git a/README.md b/README.md index c854c56..0020dab 100644 --- a/README.md +++ b/README.md @@ -1460,6 +1460,7 @@ Recipes may be annotated with attributes that change their behavior. | `[macos]`1.8.0 | Enable recipe on MacOS. | | `[no-cd]`1.9.0 | Don't change directory before executing recipe. | | `[no-exit-message]`1.7.0 | Don't print an error message if recipe fails. | +| `[no-quiet]`master | Override globally quiet recipes and always echo out the recipe. | | `[private]`1.10.0 | See [Private Recipes](#private-recipes). | | `[unix]`1.8.0 | Enable recipe on Unixes. (Includes MacOS). | | `[windows]`1.8.0 | Enable recipe on Windows. | @@ -2496,6 +2497,31 @@ goodbye # all done! ``` +All recipes in a Justfile can be made quiet with `set quiet`: + +```just +set quiet + +foo: + echo "This is quiet" + +@foo2: + echo "This is also quiet" +``` + +The `[no-quiet]` attribute overrides this setting: + +```just +set quiet + +foo: + echo "This is quiet" + +[no-quiet] +foo2: + echo "This is not quiet" +``` + Shebang recipes are quiet by default: ```just diff --git a/src/attribute.rs b/src/attribute.rs index b4c7166..0c361e3 100644 --- a/src/attribute.rs +++ b/src/attribute.rs @@ -12,6 +12,7 @@ pub(crate) enum Attribute { NoCd, NoExitMessage, Private, + NoQuiet, Unix, Windows, } diff --git a/src/keyword.rs b/src/keyword.rs index 8bde1a8..830207e 100644 --- a/src/keyword.rs +++ b/src/keyword.rs @@ -17,6 +17,7 @@ pub(crate) enum Keyword { Import, Mod, PositionalArguments, + Quiet, Set, Shell, Tempdir, diff --git a/src/node.rs b/src/node.rs index 77309d6..1d98b99 100644 --- a/src/node.rs +++ b/src/node.rs @@ -269,6 +269,7 @@ impl<'src> Node<'src> for Set<'src> { | Setting::Export(value) | Setting::Fallback(value) | Setting::PositionalArguments(value) + | Setting::Quiet(value) | Setting::WindowsPowerShell(value) | Setting::IgnoreComments(value) => { set.push_mut(value.to_string()); diff --git a/src/parser.rs b/src/parser.rs index 6608c5f..3f7deda 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -852,6 +852,7 @@ impl<'run, 'src> Parser<'run, 'src> { Keyword::Fallback => Some(Setting::Fallback(self.parse_set_bool()?)), Keyword::IgnoreComments => Some(Setting::IgnoreComments(self.parse_set_bool()?)), Keyword::PositionalArguments => Some(Setting::PositionalArguments(self.parse_set_bool()?)), + Keyword::Quiet => Some(Setting::Quiet(self.parse_set_bool()?)), Keyword::WindowsPowershell => Some(Setting::WindowsPowerShell(self.parse_set_bool()?)), _ => None, }; @@ -1927,6 +1928,24 @@ mod tests { tree: (justfile (set positional_arguments true)), } + test! { + name: set_quiet_implicit, + text: "set quiet", + tree: (justfile (set quiet true)), + } + + test! { + name: set_quiet_true, + text: "set quiet := true", + tree: (justfile (set quiet true)), + } + + test! { + name: set_quiet_false, + text: "set quiet := false", + tree: (justfile (set quiet false)), + } + test! { name: set_positional_arguments_false, text: "set positional-arguments := false", diff --git a/src/recipe.rs b/src/recipe.rs index 99f90e1..bc06cf4 100644 --- a/src/recipe.rs +++ b/src/recipe.rs @@ -134,6 +134,10 @@ impl<'src, D> Recipe<'src, D> { } } + fn no_quiet(&self) -> bool { + self.attributes.contains(&Attribute::NoQuiet) + } + pub(crate) fn run<'run>( &self, context: &RecipeContext<'src, 'run>, @@ -181,8 +185,8 @@ impl<'src, D> Recipe<'src, D> { } let mut evaluated = String::new(); let mut continued = false; - let quiet_command = lines.peek().map_or(false, |line| line.is_quiet()); - let infallible_command = lines.peek().map_or(false, |line| line.is_infallible()); + let quiet_line = lines.peek().map_or(false, |line| line.is_quiet()); + let infallible_line = lines.peek().map_or(false, |line| line.is_infallible()); let comment_line = context.settings.ignore_comments && lines.peek().map_or(false, |line| line.is_comment()); @@ -210,7 +214,7 @@ impl<'src, D> Recipe<'src, D> { let mut command = evaluated.as_str(); - let sigils = usize::from(infallible_command) + usize::from(quiet_command); + let sigils = usize::from(infallible_line) + usize::from(quiet_line); command = &command[sigils..]; @@ -220,7 +224,9 @@ impl<'src, D> Recipe<'src, D> { if config.dry_run || config.verbosity.loquacious() - || !((quiet_command ^ self.quiet) || config.verbosity.quiet()) + || !((quiet_line ^ self.quiet) + || (context.settings.quiet && !self.no_quiet()) + || config.verbosity.quiet()) { let color = if config.highlight { config.color.command(config.command_color) @@ -257,7 +263,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 && !infallible_command { + if code != 0 && !infallible_line { return Err(Error::Code { recipe: self.name(), line_number: Some(line_number), diff --git a/src/setting.rs b/src/setting.rs index 9b2a9fe..0256ff9 100644 --- a/src/setting.rs +++ b/src/setting.rs @@ -10,6 +10,7 @@ pub(crate) enum Setting<'src> { Fallback(bool), IgnoreComments(bool), PositionalArguments(bool), + Quiet(bool), Shell(Shell<'src>), Tempdir(String), WindowsPowerShell(bool), @@ -25,6 +26,7 @@ impl<'src> Display for Setting<'src> { | Setting::Fallback(value) | Setting::IgnoreComments(value) | Setting::PositionalArguments(value) + | Setting::Quiet(value) | Setting::WindowsPowerShell(value) => write!(f, "{value}"), Setting::Shell(shell) | Setting::WindowsShell(shell) => write!(f, "{shell}"), Setting::DotenvFilename(value) | Setting::DotenvPath(value) | Setting::Tempdir(value) => { diff --git a/src/settings.rs b/src/settings.rs index 5c5e64c..f72cb54 100644 --- a/src/settings.rs +++ b/src/settings.rs @@ -15,6 +15,7 @@ pub(crate) struct Settings<'src> { pub(crate) fallback: bool, pub(crate) ignore_comments: bool, pub(crate) positional_arguments: bool, + pub(crate) quiet: bool, pub(crate) shell: Option>, pub(crate) tempdir: Option, pub(crate) windows_powershell: bool, @@ -51,6 +52,9 @@ impl<'src> Settings<'src> { Setting::PositionalArguments(positional_arguments) => { settings.positional_arguments = positional_arguments; } + Setting::Quiet(quiet) => { + settings.quiet = quiet; + } Setting::Shell(shell) => { settings.shell = Some(shell); } diff --git a/tests/json.rs b/tests/json.rs index c70063f..4c71b43 100644 --- a/tests/json.rs +++ b/tests/json.rs @@ -50,6 +50,7 @@ fn alias() { "export": false, "fallback": false, "positional_arguments": false, + "quiet": false, "shell": null, "tempdir" : null, "ignore_comments": false, @@ -86,6 +87,7 @@ fn assignment() { "fallback": false, "ignore_comments": false, "positional_arguments": false, + "quiet": false, "shell": null, "tempdir" : null, "windows_powershell": false, @@ -136,6 +138,7 @@ fn body() { "fallback": false, "ignore_comments": false, "positional_arguments": false, + "quiet": false, "shell": null, "tempdir" : null, "windows_powershell": false, @@ -198,6 +201,7 @@ fn dependencies() { "fallback": false, "ignore_comments": false, "positional_arguments": false, + "quiet": false, "shell": null, "tempdir" : null, "windows_powershell": false, @@ -297,6 +301,7 @@ fn dependency_argument() { "fallback": false, "ignore_comments": false, "positional_arguments": false, + "quiet": false, "shell": null, "tempdir" : null, "windows_powershell": false, @@ -359,6 +364,7 @@ fn duplicate_recipes() { "fallback": false, "ignore_comments": false, "positional_arguments": false, + "quiet": false, "shell": null, "tempdir" : null, "windows_powershell": false, @@ -402,6 +408,7 @@ fn doc_comment() { "fallback": false, "ignore_comments": false, "positional_arguments": false, + "quiet": false, "shell": null, "tempdir" : null, "windows_powershell": false, @@ -431,6 +438,7 @@ fn empty_justfile() { "fallback": false, "ignore_comments": false, "positional_arguments": false, + "quiet": false, "shell": null, "tempdir" : null, "windows_powershell": false, @@ -581,6 +589,7 @@ fn parameters() { "fallback": false, "ignore_comments": false, "positional_arguments": false, + "quiet": false, "shell": null, "tempdir" : null, "windows_powershell": false, @@ -664,6 +673,7 @@ fn priors() { "fallback": false, "ignore_comments": false, "positional_arguments": false, + "quiet": false, "shell": null, "tempdir" : null, "windows_powershell": false, @@ -707,6 +717,7 @@ fn private() { "fallback": false, "ignore_comments": false, "positional_arguments": false, + "quiet": false, "shell": null, "tempdir" : null, "windows_powershell": false, @@ -750,6 +761,7 @@ fn quiet() { "fallback": false, "ignore_comments": false, "positional_arguments": false, + "quiet": false, "shell": null, "tempdir" : null, "windows_powershell": false, @@ -770,6 +782,7 @@ fn settings() { set export set fallback set positional-arguments + set quiet set ignore-comments set shell := ['a', 'b', 'c'] foo: @@ -804,6 +817,7 @@ fn settings() { "fallback": true, "ignore_comments": true, "positional_arguments": true, + "quiet": true, "shell": { "arguments": ["b", "c"], "command": "a", @@ -853,6 +867,7 @@ fn shebang() { "fallback": false, "ignore_comments": false, "positional_arguments": false, + "quiet": false, "shell": null, "tempdir": null, "windows_powershell": false, @@ -896,6 +911,7 @@ fn simple() { "fallback": false, "ignore_comments": false, "positional_arguments": false, + "quiet": false, "shell": null, "tempdir": null, "windows_powershell": false, @@ -941,6 +957,7 @@ fn attribute() { "export": false, "fallback": false, "positional_arguments": false, + "quiet": false, "shell": null, "tempdir" : null, "ignore_comments": false, @@ -1000,6 +1017,7 @@ fn module() { "export": false, "fallback": false, "positional_arguments": false, + "quiet": false, "shell": null, "tempdir" : null, "ignore_comments": false, @@ -1018,6 +1036,7 @@ fn module() { "export": false, "fallback": false, "positional_arguments": false, + "quiet": false, "shell": null, "tempdir" : null, "ignore_comments": false, diff --git a/tests/quiet.rs b/tests/quiet.rs index 8fc0e4f..59ad41e 100644 --- a/tests/quiet.rs +++ b/tests/quiet.rs @@ -129,3 +129,111 @@ test! { ", args: ("--quiet"), } + +#[test] +fn no_quiet_setting() { + Test::new() + .justfile( + " + foo: + echo FOO + ", + ) + .stdout("FOO\n") + .stderr("echo FOO\n") + .run(); +} + +#[test] +fn quiet_setting() { + Test::new() + .justfile( + " + set quiet + + foo: + echo FOO + ", + ) + .stdout("FOO\n") + .run(); +} + +#[test] +fn quiet_setting_with_no_quiet_attribute() { + Test::new() + .justfile( + " + set quiet + + [no-quiet] + foo: + echo FOO + ", + ) + .stdout("FOO\n") + .stderr("echo FOO\n") + .run(); +} + +#[test] +fn quiet_setting_with_quiet_recipe() { + Test::new() + .justfile( + " + set quiet + + @foo: + echo FOO + ", + ) + .stdout("FOO\n") + .run(); +} + +#[test] +fn quiet_setting_with_quiet_line() { + Test::new() + .justfile( + " + set quiet + + foo: + @echo FOO + ", + ) + .stdout("FOO\n") + .run(); +} + +#[test] +fn quiet_setting_with_no_quiet_attribute_and_quiet_recipe() { + Test::new() + .justfile( + " + set quiet + + [no-quiet] + @foo: + echo FOO + ", + ) + .stdout("FOO\n") + .run(); +} + +#[test] +fn quiet_setting_with_no_quiet_attribute_and_quiet_line() { + Test::new() + .justfile( + " + set quiet + + [no-quiet] + foo: + @echo FOO + ", + ) + .stdout("FOO\n") + .run(); +}