diff --git a/GRAMMAR.md b/GRAMMAR.md index 6e5f818ab9..fc35e5dc62 100644 --- a/GRAMMAR.md +++ b/GRAMMAR.md @@ -60,6 +60,7 @@ export : 'export' assignment setting : 'set' 'dotenv-load' boolean? | 'set' 'export' boolean? + | 'set' 'ignore-recipe-comments' boolean? | 'set' 'positional-arguments' boolean? | 'set' 'allow-duplicate-recipes' boolean? | 'set' 'windows-powershell' boolean? diff --git a/README.md b/README.md index 8eb95f40d7..0f11f43770 100644 --- a/README.md +++ b/README.md @@ -646,6 +646,7 @@ foo: | `allow-duplicate-recipes` | boolean | Allow recipes appearing later in a `justfile` to override earlier recipes with the same name. | | `dotenv-load` | boolean | Load a `.env` file, if present. | | `export` | boolean | Export all variables as environment variables. | +| `ignore-recipe-comments' | boolean | Treat lines beginning with a '#' in the body of a recipe as comments, and do not print them. | | `positional-arguments` | boolean | Pass positional arguments. | | `shell` | `[COMMAND, ARGS…]` | Set the command used to invoke recipes and evaluate backticks. | | `windows-shell` | `[COMMAND, ARGS…]` | Set the command used to invoke recipes and evaluate backticks. | diff --git a/src/analyzer.rs b/src/analyzer.rs index 39e03a5e6d..38a59d4aff 100644 --- a/src/analyzer.rs +++ b/src/analyzer.rs @@ -53,6 +53,9 @@ impl<'src> Analyzer<'src> { Setting::Export(export) => { settings.export = export; } + Setting::IgnoreRecipeComments(ignore) => { + settings.ignore_recipe_comments = ignore; + } Setting::PositionalArguments(positional_arguments) => { settings.positional_arguments = positional_arguments; } diff --git a/src/justfile.rs b/src/justfile.rs index ccad90a707..8a17f814a0 100644 --- a/src/justfile.rs +++ b/src/justfile.rs @@ -324,7 +324,7 @@ impl<'src> Justfile<'src> { )?; } - recipe.run(context, dotenv, scope.child(), search, &positional)?; + recipe.run(context, dotenv, scope.child(), search, &positional, &self.settings)?; { let mut ran = BTreeSet::new(); diff --git a/src/keyword.rs b/src/keyword.rs index 2913e7115a..2c1e92f110 100644 --- a/src/keyword.rs +++ b/src/keyword.rs @@ -10,6 +10,7 @@ pub(crate) enum Keyword { Export, False, If, + IgnoreRecipeComments, PositionalArguments, Set, Shell, diff --git a/src/line.rs b/src/line.rs index 6f26506d52..aaf7807577 100644 --- a/src/line.rs +++ b/src/line.rs @@ -12,6 +12,16 @@ impl<'src> Line<'src> { self.fragments.is_empty() } + pub(crate) fn is_comment(&self) -> bool { + match self.fragments.first() { + Some(Fragment::Text { token }) => { + let lexeme = token.lexeme(); + lexeme.starts_with("#") && !lexeme.starts_with("#!") + }, + _ => false + } + } + pub(crate) fn is_continuation(&self) -> bool { match self.fragments.last() { Some(Fragment::Text { token }) => token.lexeme().ends_with('\\'), diff --git a/src/node.rs b/src/node.rs index 6afc0a4371..1bf7c6946d 100644 --- a/src/node.rs +++ b/src/node.rs @@ -224,7 +224,8 @@ impl<'src> Node<'src> for Set<'src> { | Setting::DotenvLoad(value) | Setting::Export(value) | Setting::PositionalArguments(value) - | Setting::WindowsPowerShell(value) => { + | Setting::WindowsPowerShell(value) + | Setting::IgnoreRecipeComments(value) => { set.push_mut(value.to_string()); } Setting::Shell(Shell { command, arguments }) diff --git a/src/parser.rs b/src/parser.rs index 00ec7e7edf..7c8903e099 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -748,36 +748,23 @@ impl<'tokens, 'src> Parser<'tokens, 'src> { let name = Name::from_identifier(self.presume(Identifier)?); let lexeme = name.lexeme(); - if Keyword::AllowDuplicateRecipes == lexeme { - let value = self.parse_set_bool()?; - return Ok(Set { - value: Setting::AllowDuplicateRecipes(value), - name, - }); - } else if Keyword::DotenvLoad == lexeme { - let value = self.parse_set_bool()?; - return Ok(Set { - value: Setting::DotenvLoad(value), - name, - }); - } else if Keyword::Export == lexeme { - let value = self.parse_set_bool()?; - return Ok(Set { - value: Setting::Export(value), - name, - }); - } else if Keyword::PositionalArguments == lexeme { - let value = self.parse_set_bool()?; - return Ok(Set { - value: Setting::PositionalArguments(value), - name, - }); - } else if Keyword::WindowsPowershell == lexeme { - let value = self.parse_set_bool()?; - return Ok(Set { - value: Setting::WindowsPowerShell(value), - name, - }); + let set_bool: Option = match Keyword::from_lexeme(lexeme) { + Some(kw) => match kw { + Keyword::AllowDuplicateRecipes => Some(Setting::AllowDuplicateRecipes(self.parse_set_bool()?)), + Keyword::DotenvLoad => Some(Setting::DotenvLoad(self.parse_set_bool()?)), + Keyword::Export => Some(Setting::Export(self.parse_set_bool()?)), + Keyword::IgnoreRecipeComments => Some(Setting::IgnoreRecipeComments(self.parse_set_bool()?)), + Keyword::PositionalArguments => Some(Setting::PositionalArguments(self.parse_set_bool()?)), + Keyword::WindowsPowershell => Some(Setting::WindowsPowerShell(self.parse_set_bool()?)), + _ => None, + }, + None => None + }; + + if let Some(value) = set_bool { + return Ok(Set { + value, name + }); } self.expect(ColonEquals)?; diff --git a/src/recipe.rs b/src/recipe.rs index 63787a3f62..6641d96f72 100644 --- a/src/recipe.rs +++ b/src/recipe.rs @@ -72,6 +72,7 @@ impl<'src, D> Recipe<'src, D> { scope: Scope<'src, 'run>, search: &'run Search, positional: &[String], + settings: &Settings, ) -> RunResult<'src, ()> { let config = &context.config; @@ -91,11 +92,11 @@ impl<'src, D> Recipe<'src, D> { if self.shebang { self.run_shebang(context, dotenv, &scope, positional, config, evaluator) } else { - self.run_linewise(context, dotenv, &scope, positional, config, evaluator) + self.run_linewise(context, dotenv, &scope, positional, config, evaluator, settings) } } - pub(crate) fn run_linewise<'run>( + fn run_linewise<'run>( &self, context: &RecipeContext<'src, 'run>, dotenv: &BTreeMap, @@ -103,6 +104,7 @@ impl<'src, D> Recipe<'src, D> { positional: &[String], config: &Config, mut evaluator: Evaluator<'src, 'run>, + settings: &Settings, ) -> RunResult<'src, ()> { let mut lines = self.body.iter().peekable(); let mut line_number = self.line_number() + 1; @@ -114,6 +116,8 @@ impl<'src, D> Recipe<'src, D> { 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 comment_line = lines.peek().map_or(false, |line| line.is_comment()); + loop { if lines.peek().is_none() { break; @@ -138,6 +142,10 @@ impl<'src, D> Recipe<'src, D> { command = &command[1..]; } + if comment_line && settings.ignore_recipe_comments { + continue; + } + if command.is_empty() { continue; } diff --git a/src/setting.rs b/src/setting.rs index 6349cabda6..e4ff863b0d 100644 --- a/src/setting.rs +++ b/src/setting.rs @@ -5,6 +5,7 @@ pub(crate) enum Setting<'src> { AllowDuplicateRecipes(bool), DotenvLoad(bool), Export(bool), + IgnoreRecipeComments(bool), PositionalArguments(bool), Shell(Shell<'src>), WindowsPowerShell(bool), @@ -16,6 +17,7 @@ impl<'src> Display for Setting<'src> { match self { Setting::AllowDuplicateRecipes(value) | Setting::DotenvLoad(value) + | Setting::IgnoreRecipeComments(value) | Setting::Export(value) | Setting::PositionalArguments(value) | Setting::WindowsPowerShell(value) => write!(f, "{}", value), diff --git a/src/settings.rs b/src/settings.rs index c755a60459..521e982a24 100644 --- a/src/settings.rs +++ b/src/settings.rs @@ -10,6 +10,7 @@ pub(crate) struct Settings<'src> { pub(crate) allow_duplicate_recipes: bool, pub(crate) dotenv_load: Option, pub(crate) export: bool, + pub(crate) ignore_recipe_comments: bool, pub(crate) positional_arguments: bool, pub(crate) shell: Option>, pub(crate) windows_powershell: bool, @@ -22,6 +23,7 @@ impl<'src> Settings<'src> { allow_duplicate_recipes: false, dotenv_load: None, export: false, + ignore_recipe_comments: false, positional_arguments: false, shell: None, windows_powershell: false, diff --git a/tests/ignore_recipe_comments.rs b/tests/ignore_recipe_comments.rs new file mode 100644 index 0000000000..6a7a5f6905 --- /dev/null +++ b/tests/ignore_recipe_comments.rs @@ -0,0 +1,53 @@ +use super::*; + +#[test] +fn ignore_recipe_comments() { + Test::new() + .justfile( + " + set ignore-recipe-comments + + some_recipe: + # A recipe-internal comment + echo something-useful + ", + ) + .stdout("something-useful\n") + .stderr("echo something-useful\n") + .run(); +} + +#[test] +fn dont_ignore_recipe_comments() { + Test::new() + .justfile( + " + set ignore-recipe-comments := false + + some_recipe: + # A recipe-internal comment + echo something-useful + ", + ) + .stdout("something-useful\n") + .stderr("# A recipe-internal comment\necho something-useful\n") + .run(); +} + +#[test] +fn ignore_recipe_comments_with_shell_setting() { + Test::new() + .justfile( + " + set shell := ['echo', '-n'] + set ignore-recipe-comments + + some_recipe: + # Alternate shells still ignore comments + echo something-useful + ", + ) + .stdout("something-useful\n") + .stderr("echo something-useful\n") + .run(); +} diff --git a/tests/json.rs b/tests/json.rs index e312111df9..2d1f3890b0 100644 --- a/tests/json.rs +++ b/tests/json.rs @@ -42,6 +42,7 @@ fn alias() { "allow_duplicate_recipes": false, "dotenv_load": null, "export": false, + "ignore_recipe_comments": false, "positional_arguments": false, "shell": null, "windows_powershell": false, @@ -71,6 +72,7 @@ fn assignment() { "allow_duplicate_recipes": false, "dotenv_load": null, "export": false, + "ignore_recipe_comments": false, "positional_arguments": false, "shell": null, "windows_powershell": false, @@ -113,6 +115,7 @@ fn body() { "allow_duplicate_recipes": false, "dotenv_load": null, "export": false, + "ignore_recipe_comments": false, "positional_arguments": false, "shell": null, "windows_powershell": false, @@ -165,6 +168,7 @@ fn dependencies() { "allow_duplicate_recipes": false, "dotenv_load": null, "export": false, + "ignore_recipe_comments": false, "positional_arguments": false, "shell": null, "windows_powershell": false, @@ -254,6 +258,7 @@ fn dependency_argument() { "allow_duplicate_recipes": false, "dotenv_load": null, "export": false, + "ignore_recipe_comments": false, "positional_arguments": false, "shell": null, "windows_powershell": false, @@ -307,6 +312,7 @@ fn duplicate_recipes() { "allow_duplicate_recipes": true, "dotenv_load": null, "export": false, + "ignore_recipe_comments": false, "positional_arguments": false, "shell": null, "windows_powershell": false, @@ -342,6 +348,7 @@ fn doc_comment() { "allow_duplicate_recipes": false, "dotenv_load": null, "export": false, + "ignore_recipe_comments": false, "positional_arguments": false, "shell": null, "windows_powershell": false, @@ -365,6 +372,7 @@ fn empty_justfile() { "allow_duplicate_recipes": false, "dotenv_load": null, "export": false, + "ignore_recipe_comments": false, "positional_arguments": false, "shell": null, "windows_powershell": false, @@ -497,6 +505,7 @@ fn parameters() { "allow_duplicate_recipes": false, "dotenv_load": null, "export": false, + "ignore_recipe_comments": false, "positional_arguments": false, "shell": null, "windows_powershell": false, @@ -568,6 +577,7 @@ fn priors() { "allow_duplicate_recipes": false, "dotenv_load": null, "export": false, + "ignore_recipe_comments": false, "positional_arguments": false, "shell": null, "windows_powershell": false, @@ -603,6 +613,7 @@ fn private() { "allow_duplicate_recipes": false, "dotenv_load": null, "export": false, + "ignore_recipe_comments": false, "positional_arguments": false, "shell": null, "windows_powershell": false, @@ -638,6 +649,7 @@ fn quiet() { "allow_duplicate_recipes": false, "dotenv_load": null, "export": false, + "ignore_recipe_comments": false, "positional_arguments": false, "shell": null, "windows_powershell": false, @@ -665,6 +677,7 @@ fn settings() { set dotenv-load set export set positional-arguments + set ignore-recipe-comments := true set shell := ['a', 'b', 'c'] foo: @@ -691,6 +704,7 @@ fn settings() { "allow_duplicate_recipes": false, "dotenv_load": true, "export": true, + "ignore_recipe_comments": true, "positional_arguments": true, "shell": { "arguments": ["b", "c"], @@ -732,6 +746,7 @@ fn shebang() { "allow_duplicate_recipes": false, "dotenv_load": null, "export": false, + "ignore_recipe_comments": false, "positional_arguments": false, "shell": null, "windows_powershell": false, @@ -767,6 +782,7 @@ fn simple() { "allow_duplicate_recipes": false, "dotenv_load": null, "export": false, + "ignore_recipe_comments": false, "positional_arguments": false, "shell": null, "windows_powershell": false, diff --git a/tests/lib.rs b/tests/lib.rs index b6fa7a4efd..90fe18584d 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -50,6 +50,7 @@ mod export; mod fall_back_to_parent; mod fmt; mod functions; +mod ignore_recipe_comments; mod init; #[cfg(unix)] mod interrupts;