Skip to content

Commit

Permalink
Add ignore-recipe-comments setting
Browse files Browse the repository at this point in the history
Add a new setting "ignore-recipe-comments". If set, this causes lines
internal to a non-shebang recipe beginning with the character '#' (other
than '#!' itself) to be treated as comments of the justfile itself. They will
not be echoed to stderr when the recipe executes.
  • Loading branch information
neunenak committed Sep 25, 2022
1 parent 7680b19 commit 3c4e59b
Show file tree
Hide file tree
Showing 14 changed files with 140 additions and 35 deletions.
1 change: 1 addition & 0 deletions GRAMMAR.md
Original file line number Diff line number Diff line change
Expand Up @@ -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?
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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. |
Expand Down
3 changes: 3 additions & 0 deletions src/analyzer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
22 changes: 20 additions & 2 deletions src/justfile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,15 @@ impl<'src> Justfile<'src> {

let mut ran = BTreeSet::new();
for (recipe, arguments) in grouped {
Self::run_recipe(&context, recipe, arguments, &dotenv, search, &mut ran)?;
Self::run_recipe(
&context,
recipe,
arguments,
&dotenv,
search,
&self.settings,
&mut ran,
)?;
}

Ok(())
Expand All @@ -281,6 +289,7 @@ impl<'src> Justfile<'src> {
arguments: &[&str],
dotenv: &BTreeMap<String, String>,
search: &Search,
settings: &Settings,
ran: &mut BTreeSet<Vec<String>>,
) -> RunResult<'src, ()> {
let mut invocation = vec![recipe.name().to_owned()];
Expand Down Expand Up @@ -319,11 +328,19 @@ impl<'src> Justfile<'src> {
&arguments.iter().map(String::as_ref).collect::<Vec<&str>>(),
dotenv,
search,
settings,
ran,
)?;
}

recipe.run(context, dotenv, scope.child(), search, &positional)?;
recipe.run(
context,
dotenv,
scope.child(),
search,
&positional,
settings,
)?;

{
let mut ran = BTreeSet::new();
Expand All @@ -341,6 +358,7 @@ impl<'src> Justfile<'src> {
&evaluated.iter().map(String::as_ref).collect::<Vec<&str>>(),
dotenv,
search,
settings,
&mut ran,
)?;
}
Expand Down
1 change: 1 addition & 0 deletions src/keyword.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ pub(crate) enum Keyword {
Export,
False,
If,
IgnoreRecipeComments,
PositionalArguments,
Set,
Shell,
Expand Down
7 changes: 7 additions & 0 deletions src/line.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,13 @@ impl<'src> Line<'src> {
self.fragments.is_empty()
}

pub(crate) fn is_comment(&self) -> bool {
match self.fragments.first() {
Some(Fragment::Text { token }) => token.lexeme().starts_with("#"),
_ => false,
}
}

pub(crate) fn is_continuation(&self) -> bool {
match self.fragments.last() {
Some(Fragment::Text { token }) => token.lexeme().ends_with('\\'),
Expand Down
3 changes: 2 additions & 1 deletion src/node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,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 })
Expand Down
49 changes: 19 additions & 30 deletions src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -755,36 +755,25 @@ 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<Setting> = 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)?;
Expand Down
14 changes: 12 additions & 2 deletions src/recipe.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -91,18 +92,21 @@ 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<String, String>,
scope: &Scope<'src, 'run>,
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;
Expand All @@ -114,6 +118,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;
Expand All @@ -138,6 +144,10 @@ impl<'src, D> Recipe<'src, D> {
command = &command[1..];
}

if comment_line && settings.ignore_recipe_comments {
continue;
}

if command.is_empty() {
continue;
}
Expand Down
2 changes: 2 additions & 0 deletions src/setting.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ pub(crate) enum Setting<'src> {
AllowDuplicateRecipes(bool),
DotenvLoad(bool),
Export(bool),
IgnoreRecipeComments(bool),
PositionalArguments(bool),
Shell(Shell<'src>),
WindowsPowerShell(bool),
Expand All @@ -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),
Expand Down
2 changes: 2 additions & 0 deletions src/settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ pub(crate) struct Settings<'src> {
pub(crate) allow_duplicate_recipes: bool,
pub(crate) dotenv_load: Option<bool>,
pub(crate) export: bool,
pub(crate) ignore_recipe_comments: bool,
pub(crate) positional_arguments: bool,
pub(crate) shell: Option<Shell<'src>>,
pub(crate) windows_powershell: bool,
Expand All @@ -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,
Expand Down
53 changes: 53 additions & 0 deletions tests/ignore_recipe_comments.rs
Original file line number Diff line number Diff line change
@@ -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();
}
Loading

0 comments on commit 3c4e59b

Please sign in to comment.