Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion completions/just.bash
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ _just() {

case "${cmd}" in
just)
opts="-E -n -g -f -q -u -v -d -c -e -l -s -h -V --alias-style --ceiling --check --chooser --clear-shell-args --color --command-color --cygpath --dotenv-filename --dotenv-path --dry-run --dump-format --explain --global-justfile --highlight --justfile --list-heading --list-prefix --list-submodules --no-aliases --no-deps --no-dotenv --no-highlight --one --quiet --allow-missing --set --shell --shell-arg --shell-command --tempdir --timestamp --timestamp-format --unsorted --unstable --verbose --working-directory --yes --changelog --choose --command --completions --dump --edit --evaluate --fmt --groups --init --list --man --request --show --summary --usage --variables --help --version [ARGUMENTS]..."
opts="-E -n -g -f -q -u -v -d -c -e -l -s -h -V --alias-style --ceiling --check --chooser --clear-shell-args --color --command-color --cygpath --dotenv-filename --dotenv-path --dry-run --dump-format --explain --global-justfile --highlight --justfile --list-heading --list-prefix --list-submodules --group --no-aliases --no-deps --no-dotenv --no-highlight --one --quiet --allow-missing --set --shell --shell-arg --shell-command --tempdir --timestamp --timestamp-format --unsorted --unstable --verbose --working-directory --yes --changelog --choose --command --completions --dump --edit --evaluate --fmt --groups --init --list --man --request --show --summary --usage --variables --help --version [ARGUMENTS]..."
if [[ ${cur} == -* ]] ; then
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
return 0
Expand Down Expand Up @@ -108,6 +108,10 @@ _just() {
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
--group)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
--set)
COMPREPLY=($(compgen -f "${cur}"))
return 0
Expand Down
1 change: 1 addition & 0 deletions completions/just.elvish
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ set edit:completion:arg-completer[just] = {|@words|
cand --justfile 'Use <JUSTFILE> as justfile'
cand --list-heading 'Print <TEXT> before list'
cand --list-prefix 'Print <TEXT> before each list item'
cand --group 'Only list recipes in <GROUP>'
cand --set 'Override <VARIABLE> with <VALUE>'
cand --shell 'Invoke <SHELL> to run recipes'
cand --shell-arg 'Invoke shell with <SHELL-ARG> as an argument'
Expand Down
1 change: 1 addition & 0 deletions completions/just.fish
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ just\t''"
complete -c just -s f -l justfile -d 'Use <JUSTFILE> as justfile' -r -F
complete -c just -l list-heading -d 'Print <TEXT> before list' -r
complete -c just -l list-prefix -d 'Print <TEXT> before each list item' -r
complete -c just -l group -d 'Only list recipes in <GROUP>' -r
complete -c just -l set -d 'Override <VARIABLE> with <VALUE>' -r
complete -c just -l shell -d 'Invoke <SHELL> to run recipes' -r
complete -c just -l shell-arg -d 'Invoke shell with <SHELL-ARG> as an argument' -r
Expand Down
1 change: 1 addition & 0 deletions completions/just.powershell
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ Register-ArgumentCompleter -Native -CommandName 'just' -ScriptBlock {
[CompletionResult]::new('--justfile', '--justfile', [CompletionResultType]::ParameterName, 'Use <JUSTFILE> as justfile')
[CompletionResult]::new('--list-heading', '--list-heading', [CompletionResultType]::ParameterName, 'Print <TEXT> before list')
[CompletionResult]::new('--list-prefix', '--list-prefix', [CompletionResultType]::ParameterName, 'Print <TEXT> before each list item')
[CompletionResult]::new('--group', '--group', [CompletionResultType]::ParameterName, 'Only list recipes in <GROUP>')
[CompletionResult]::new('--set', '--set', [CompletionResultType]::ParameterName, 'Override <VARIABLE> with <VALUE>')
[CompletionResult]::new('--shell', '--shell', [CompletionResultType]::ParameterName, 'Invoke <SHELL> to run recipes')
[CompletionResult]::new('--shell-arg', '--shell-arg', [CompletionResultType]::ParameterName, 'Invoke shell with <SHELL-ARG> as an argument')
Expand Down
1 change: 1 addition & 0 deletions completions/just.zsh
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ _just() {
'--justfile=[Use <JUSTFILE> as justfile]: :_files' \
'--list-heading=[Print <TEXT> before list]:TEXT:_default' \
'--list-prefix=[Print <TEXT> before each list item]:TEXT:_default' \
'*--group=[Only list recipes in <GROUP>]: :_default' \
'*--set=[Override <VARIABLE> with <VALUE>]: :(_just_variables)' \
'--shell=[Invoke <SHELL> to run recipes]: :_default' \
'*--shell-arg=[Invoke shell with <SHELL-ARG> as an argument]: :_default' \
Expand Down
14 changes: 14 additions & 0 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ pub(crate) struct Config {
pub(crate) dry_run: bool,
pub(crate) dump_format: DumpFormat,
pub(crate) explain: bool,
pub(crate) groups: Vec<String>,
pub(crate) highlight: bool,
pub(crate) invocation_directory: PathBuf,
pub(crate) list_heading: String,
Expand Down Expand Up @@ -110,6 +111,7 @@ mod arg {
pub(crate) const GLOBAL_JUSTFILE: &str = "GLOBAL-JUSTFILE";
pub(crate) const HIGHLIGHT: &str = "HIGHLIGHT";
pub(crate) const JUSTFILE: &str = "JUSTFILE";
pub(crate) const GROUP: &str = "GROUP";
pub(crate) const LIST_HEADING: &str = "LIST-HEADING";
pub(crate) const LIST_PREFIX: &str = "LIST-PREFIX";
pub(crate) const LIST_SUBMODULES: &str = "LIST-SUBMODULES";
Expand Down Expand Up @@ -316,6 +318,14 @@ impl Config {
.action(ArgAction::SetTrue)
.requires(cmd::LIST),
)
.arg(
Arg::new(arg::GROUP)
.long("group")
.env("JUST_GROUP")
.help("Only list recipes in <GROUP>")
.action(ArgAction::Append)
.requires(cmd::LIST),
)
.arg(
Arg::new(arg::NO_ALIASES)
.long("no-aliases")
Expand Down Expand Up @@ -827,6 +837,10 @@ impl Config {
explain,
highlight: !matches.get_flag(arg::NO_HIGHLIGHT),
invocation_directory: env::current_dir().context(config_error::CurrentDirContext)?,
groups: matches
.get_many::<String>(arg::GROUP)
.map(|s| s.map(Into::into).collect())
.unwrap_or_default(),
list_heading: matches.get_one::<String>(arg::LIST_HEADING).unwrap().into(),
list_prefix: matches.get_one::<String>(arg::LIST_PREFIX).unwrap().into(),
list_submodules: matches.get_flag(arg::LIST_SUBMODULES),
Expand Down
6 changes: 6 additions & 0 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,9 @@ pub(crate) enum Error<'src> {
recipe: &'src str,
line_number: Option<usize>,
},
UnknownGroup {
group: String,
},
UnknownOption {
recipe: &'src str,
option: Switch,
Expand Down Expand Up @@ -770,6 +773,9 @@ impl ColorDisplay for Error<'_> {
"{count} {overrides} overridden on the command line but not present in justfile",
)?;
}
UnknownGroup { group } => {
write!(f, "Justfile does not contain group `{group}`")?;
}
UnknownRecipe { recipe, suggestion } => {
write!(f, "Justfile does not contain recipe `{recipe}`")?;
if let Some(suggestion) = suggestion {
Expand Down
77 changes: 53 additions & 24 deletions src/subcommand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -478,12 +478,17 @@ impl Subcommand {
})?;
}

Self::list_module(config, module, 0);
Self::list_module(config, 0, &config.groups, module)?;

Ok(())
}

fn list_module(config: &Config, module: &Justfile, depth: usize) {
fn list_module(
config: &Config,
depth: usize,
groups: &[String],
module: &Justfile,
) -> RunResult<'static> {
fn print_doc_and_aliases(
config: &Config,
name: &str,
Expand Down Expand Up @@ -597,54 +602,76 @@ impl Subcommand {

let list_prefix = config.list_prefix.repeat(depth + 1);

if !groups.is_empty() {
let public_groups = module.public_groups(config);
for group in groups {
if !public_groups.contains(group) {
return Err(Error::UnknownGroup {
group: group.clone(),
});
}
}
}

if depth == 0 {
print!("{}", config.list_heading);
}

let recipe_groups = {
let mut groups = BTreeMap::<Option<String>, Vec<&Recipe>>::new();
let mut recipe_groups = BTreeMap::<Option<String>, Vec<&Recipe>>::new();
for recipe in module.public_recipes(config) {
let recipe_groups = recipe.groups();
if recipe_groups.is_empty() {
groups.entry(None).or_default().push(recipe);
let recipe_groups_list = recipe.groups();
if recipe_groups_list.is_empty() {
recipe_groups.entry(None).or_default().push(recipe);
} else {
for group in recipe_groups {
groups.entry(Some(group)).or_default().push(recipe);
for group in recipe_groups_list {
recipe_groups.entry(Some(group)).or_default().push(recipe);
}
}
}
groups
recipe_groups
};

let submodule_groups = {
let mut groups = BTreeMap::<Option<String>, Vec<&Justfile>>::new();
let mut submodule_groups = BTreeMap::<Option<String>, Vec<&Justfile>>::new();
for submodule in module.public_modules(config) {
let submodule_groups = submodule.groups();
if submodule_groups.is_empty() {
groups.entry(None).or_default().push(submodule);
let submodule_groups_list = submodule.groups();
if submodule_groups_list.is_empty() {
submodule_groups.entry(None).or_default().push(submodule);
} else {
for group in submodule_groups {
groups
for group in submodule_groups_list {
submodule_groups
.entry(Some(group.to_string()))
.or_default()
.push(submodule);
}
}
}
groups
submodule_groups
};

let mut ordered_groups = module
.public_groups(config)
.into_iter()
.map(Some)
.collect::<Vec<Option<String>>>();
let mut ordered_groups = if groups.is_empty() {
module
.public_groups(config)
.into_iter()
.map(Some)
.collect::<Vec<Option<String>>>()
} else {
groups
.iter()
.cloned()
.map(Some)
.collect::<Vec<Option<String>>>()
};

if recipe_groups.contains_key(&None) || submodule_groups.contains_key(&None) {
if groups.is_empty()
&& (recipe_groups.contains_key(&None) || submodule_groups.contains_key(&None))
{
ordered_groups.insert(0, None);
}

let no_groups = ordered_groups.len() == 1 && ordered_groups.first() == Some(&None);
let no_groups =
groups.is_empty() && ordered_groups.len() == 1 && ordered_groups.first() == Some(&None);

let groups_count = if no_groups { 0 } else { ordered_groups.len() };

Expand Down Expand Up @@ -720,7 +747,7 @@ impl Subcommand {
}
println!("{list_prefix}{}:", submodule.name());

Self::list_module(config, submodule, depth + 1);
Self::list_module(config, depth + 1, &[], submodule)?;
} else {
print!("{list_prefix}{} ...", submodule.name());
print_doc_and_aliases(
Expand All @@ -735,6 +762,8 @@ impl Subcommand {
}
}
}

Ok(())
}

fn show<'src>(config: &Config, module: &Justfile<'src>, path: &ModulePath) -> RunResult<'src> {
Expand Down
104 changes: 104 additions & 0 deletions tests/groups.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,84 @@
use super::*;

#[test]
fn list_group_unknown() {
Test::new()
.justfile(
"
[group('foo')]
a:
",
)
.args(["--list", "--group", "bar"])
.stderr("error: Justfile does not contain group `bar`\n")
.failure();
}

#[test]
fn list_group() {
Test::new()
.justfile(
"
[group('alpha')]
a:
[group('alpha')]
[group('beta')]
b:
c:
[group('beta')]
d:
",
)
.args(["--list", "--group", "alpha"])
.stdout(
"
Available recipes:
[alpha]
a
b
",
)
.success();
}

#[test]
fn list_multiple_groups() {
Test::new()
.justfile(
"
[group('alpha')]
a:
[group('alpha')]
[group('beta')]
b:
c:
[group('beta')]
d:
[group('gamma')]
e:
",
)
.args([
"--list", "--group", "alpha", "--group", "beta", "--group", "gamma",
])
.stdout(
"
Available recipes:
[alpha]
a
b

[beta]
b
d

[gamma]
e
",
)
.success();
}

#[test]
fn list_with_groups() {
Test::new()
Expand Down Expand Up @@ -282,3 +361,28 @@ fn list_groups_private() {
)
.success();
}

#[test]
fn list_group_with_submodules() {
Test::new()
.justfile(
"
[group('foo')]
a:

b:

mod bar
",
)
.write("bar.just", "c:\nd:")
.args(["--list", "--group", "foo", "--list-submodules"])
.stdout(
"
Available recipes:
[foo]
a
",
)
.success();
}