Allow listing recipes in submodules with --list-submodules (#2113)

This commit is contained in:
Casey Rodarmor 2024-05-30 12:28:54 -05:00 committed by GitHub
parent d2b10e04d3
commit d38c1add13
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 176 additions and 14 deletions

View File

@ -30,7 +30,7 @@ _just() {
case "${cmd}" in case "${cmd}" in
just) just)
opts="-n -f -q -u -v -d -c -e -l -s -E -g -h -V --check --chooser --color --command-color --yes --dry-run --dump-format --highlight --list-heading --list-prefix --no-aliases --no-deps --no-dotenv --no-highlight --justfile --quiet --set --shell --shell-arg --shell-command --clear-shell-args --unsorted --unstable --verbose --working-directory --changelog --choose --command --completions --dump --edit --evaluate --fmt --init --list --groups --man --show --summary --variables --dotenv-filename --dotenv-path --global-justfile --timestamp --timestamp-format --help --version [ARGUMENTS]..." opts="-n -f -q -u -v -d -c -e -l -s -E -g -h -V --check --chooser --color --command-color --yes --dry-run --dump-format --highlight --list-heading --list-prefix --list-submodules --no-aliases --no-deps --no-dotenv --no-highlight --justfile --quiet --set --shell --shell-arg --shell-command --clear-shell-args --unsorted --unstable --verbose --working-directory --changelog --choose --command --completions --dump --edit --evaluate --fmt --init --list --groups --man --show --summary --variables --dotenv-filename --dotenv-path --global-justfile --timestamp --timestamp-format --help --version [ARGUMENTS]..."
if [[ ${cur} == -* ]] ; then if [[ ${cur} == -* ]] ; then
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
return 0 return 0

View File

@ -46,6 +46,7 @@ set edit:completion:arg-completer[just] = {|@words|
cand -n 'Print what just would do without doing it' cand -n 'Print what just would do without doing it'
cand --dry-run 'Print what just would do without doing it' cand --dry-run 'Print what just would do without doing it'
cand --highlight 'Highlight echoed recipe lines in bold' cand --highlight 'Highlight echoed recipe lines in bold'
cand --list-submodules 'List recipes in submodules'
cand --no-aliases 'Don''t show aliases in list' cand --no-aliases 'Don''t show aliases in list'
cand --no-deps 'Don''t run recipe dependencies' cand --no-deps 'Don''t run recipe dependencies'
cand --no-dotenv 'Don''t load `.env` file' cand --no-dotenv 'Don''t load `.env` file'

View File

@ -57,6 +57,7 @@ complete -c just -l check -d 'Run `--fmt` in \'check\' mode. Exits with 0 if jus
complete -c just -l yes -d 'Automatically confirm all recipes.' complete -c just -l yes -d 'Automatically confirm all recipes.'
complete -c just -s n -l dry-run -d 'Print what just would do without doing it' complete -c just -s n -l dry-run -d 'Print what just would do without doing it'
complete -c just -l highlight -d 'Highlight echoed recipe lines in bold' complete -c just -l highlight -d 'Highlight echoed recipe lines in bold'
complete -c just -l list-submodules -d 'List recipes in submodules'
complete -c just -l no-aliases -d 'Don\'t show aliases in list' complete -c just -l no-aliases -d 'Don\'t show aliases in list'
complete -c just -l no-deps -d 'Don\'t run recipe dependencies' complete -c just -l no-deps -d 'Don\'t run recipe dependencies'
complete -c just -l no-dotenv -d 'Don\'t load `.env` file' complete -c just -l no-dotenv -d 'Don\'t load `.env` file'

View File

@ -49,6 +49,7 @@ Register-ArgumentCompleter -Native -CommandName 'just' -ScriptBlock {
[CompletionResult]::new('-n', 'n', [CompletionResultType]::ParameterName, 'Print what just would do without doing it') [CompletionResult]::new('-n', 'n', [CompletionResultType]::ParameterName, 'Print what just would do without doing it')
[CompletionResult]::new('--dry-run', 'dry-run', [CompletionResultType]::ParameterName, 'Print what just would do without doing it') [CompletionResult]::new('--dry-run', 'dry-run', [CompletionResultType]::ParameterName, 'Print what just would do without doing it')
[CompletionResult]::new('--highlight', 'highlight', [CompletionResultType]::ParameterName, 'Highlight echoed recipe lines in bold') [CompletionResult]::new('--highlight', 'highlight', [CompletionResultType]::ParameterName, 'Highlight echoed recipe lines in bold')
[CompletionResult]::new('--list-submodules', 'list-submodules', [CompletionResultType]::ParameterName, 'List recipes in submodules')
[CompletionResult]::new('--no-aliases', 'no-aliases', [CompletionResultType]::ParameterName, 'Don''t show aliases in list') [CompletionResult]::new('--no-aliases', 'no-aliases', [CompletionResultType]::ParameterName, 'Don''t show aliases in list')
[CompletionResult]::new('--no-deps', 'no-deps', [CompletionResultType]::ParameterName, 'Don''t run recipe dependencies') [CompletionResult]::new('--no-deps', 'no-deps', [CompletionResultType]::ParameterName, 'Don''t run recipe dependencies')
[CompletionResult]::new('--no-dotenv', 'no-dotenv', [CompletionResultType]::ParameterName, 'Don''t load `.env` file') [CompletionResult]::new('--no-dotenv', 'no-dotenv', [CompletionResultType]::ParameterName, 'Don''t load `.env` file')

View File

@ -44,6 +44,7 @@ _just() {
'(-q --quiet)-n[Print what just would do without doing it]' \ '(-q --quiet)-n[Print what just would do without doing it]' \
'(-q --quiet)--dry-run[Print what just would do without doing it]' \ '(-q --quiet)--dry-run[Print what just would do without doing it]' \
'--highlight[Highlight echoed recipe lines in bold]' \ '--highlight[Highlight echoed recipe lines in bold]' \
'--list-submodules[List recipes in submodules]' \
'--no-aliases[Don'\''t show aliases in list]' \ '--no-aliases[Don'\''t show aliases in list]' \
'--no-deps[Don'\''t run recipe dependencies]' \ '--no-deps[Don'\''t run recipe dependencies]' \
'--no-dotenv[Don'\''t load \`.env\` file]' \ '--no-dotenv[Don'\''t load \`.env\` file]' \

View File

@ -32,6 +32,7 @@ pub(crate) struct Config {
pub(crate) invocation_directory: PathBuf, pub(crate) invocation_directory: PathBuf,
pub(crate) list_heading: String, pub(crate) list_heading: String,
pub(crate) list_prefix: String, pub(crate) list_prefix: String,
pub(crate) list_submodules: bool,
pub(crate) load_dotenv: bool, pub(crate) load_dotenv: bool,
pub(crate) no_aliases: bool, pub(crate) no_aliases: bool,
pub(crate) no_dependencies: bool, pub(crate) no_dependencies: bool,
@ -97,11 +98,12 @@ mod arg {
pub(crate) const DOTENV_PATH: &str = "DOTENV-PATH"; pub(crate) const DOTENV_PATH: &str = "DOTENV-PATH";
pub(crate) const DRY_RUN: &str = "DRY-RUN"; pub(crate) const DRY_RUN: &str = "DRY-RUN";
pub(crate) const DUMP_FORMAT: &str = "DUMP-FORMAT"; pub(crate) const DUMP_FORMAT: &str = "DUMP-FORMAT";
pub(crate) const GLOBAL_JUSTFILE: &str = "GLOBAL_JUSTFILE"; pub(crate) const GLOBAL_JUSTFILE: &str = "GLOBAL-JUSTFILE";
pub(crate) const HIGHLIGHT: &str = "HIGHLIGHT"; pub(crate) const HIGHLIGHT: &str = "HIGHLIGHT";
pub(crate) const JUSTFILE: &str = "JUSTFILE"; pub(crate) const JUSTFILE: &str = "JUSTFILE";
pub(crate) const LIST_HEADING: &str = "LIST-HEADING"; pub(crate) const LIST_HEADING: &str = "LIST-HEADING";
pub(crate) const LIST_PREFIX: &str = "LIST-PREFIX"; pub(crate) const LIST_PREFIX: &str = "LIST-PREFIX";
pub(crate) const LIST_SUBMODULES: &str = "LIST-SUBMODULES";
pub(crate) const NO_ALIASES: &str = "NO-ALIASES"; pub(crate) const NO_ALIASES: &str = "NO-ALIASES";
pub(crate) const NO_DEPS: &str = "NO-DEPS"; pub(crate) const NO_DEPS: &str = "NO-DEPS";
pub(crate) const NO_DOTENV: &str = "NO-DOTENV"; pub(crate) const NO_DOTENV: &str = "NO-DOTENV";
@ -112,7 +114,7 @@ mod arg {
pub(crate) const SHELL_ARG: &str = "SHELL-ARG"; pub(crate) const SHELL_ARG: &str = "SHELL-ARG";
pub(crate) const SHELL_COMMAND: &str = "SHELL-COMMAND"; pub(crate) const SHELL_COMMAND: &str = "SHELL-COMMAND";
pub(crate) const TIMESTAMP: &str = "TIMESTAMP"; pub(crate) const TIMESTAMP: &str = "TIMESTAMP";
pub(crate) const TIMESTAMP_FORMAT: &str = "TIMESTAMP_FORMAT"; pub(crate) const TIMESTAMP_FORMAT: &str = "TIMESTAMP-FORMAT";
pub(crate) const UNSORTED: &str = "UNSORTED"; pub(crate) const UNSORTED: &str = "UNSORTED";
pub(crate) const UNSTABLE: &str = "UNSTABLE"; pub(crate) const UNSTABLE: &str = "UNSTABLE";
pub(crate) const VERBOSE: &str = "VERBOSE"; pub(crate) const VERBOSE: &str = "VERBOSE";
@ -236,6 +238,13 @@ impl Config {
.value_name("TEXT") .value_name("TEXT")
.action(ArgAction::Set), .action(ArgAction::Set),
) )
.arg(
Arg::new(arg::LIST_SUBMODULES)
.long("list-submodules")
.help("List recipes in submodules")
.action(ArgAction::SetTrue)
.env("JUST_LIST_SUBMODULES"),
)
.arg( .arg(
Arg::new(arg::NO_ALIASES) Arg::new(arg::NO_ALIASES)
.long("no-aliases") .long("no-aliases")
@ -754,6 +763,7 @@ impl Config {
list_prefix: matches list_prefix: matches
.get_one::<String>(arg::LIST_PREFIX) .get_one::<String>(arg::LIST_PREFIX)
.map_or_else(|| " ".into(), Into::into), .map_or_else(|| " ".into(), Into::into),
list_submodules: matches.get_flag(arg::LIST_SUBMODULES),
load_dotenv: !matches.get_flag(arg::NO_DOTENV), load_dotenv: !matches.get_flag(arg::NO_DOTENV),
no_aliases: matches.get_flag(arg::NO_ALIASES), no_aliases: matches.get_flag(arg::NO_ALIASES),
no_dependencies: matches.get_flag(arg::NO_DEPS), no_dependencies: matches.get_flag(arg::NO_DEPS),

View File

@ -488,6 +488,12 @@ impl Subcommand {
.ok_or_else(|| Error::UnknownSubmodule { path: path.clone() })?; .ok_or_else(|| Error::UnknownSubmodule { path: path.clone() })?;
} }
Self::list_module(config, module, 0);
Ok(())
}
fn list_module(config: &Config, module: &Justfile, depth: usize) {
let aliases = if config.no_aliases { let aliases = if config.no_aliases {
BTreeMap::new() BTreeMap::new()
} else { } else {
@ -532,7 +538,11 @@ impl Subcommand {
.max() .max()
.unwrap_or(0); .unwrap_or(0);
let list_prefix = config.list_prefix.repeat(depth + 1);
if depth == 0 {
print!("{}", config.list_heading); print!("{}", config.list_heading);
}
let groups = { let groups = {
let mut groups = BTreeMap::<Option<String>, Vec<&Recipe>>::new(); let mut groups = BTreeMap::<Option<String>, Vec<&Recipe>>::new();
@ -557,7 +567,7 @@ impl Subcommand {
let no_groups = groups.contains_key(&None) && groups.len() == 1; let no_groups = groups.contains_key(&None) && groups.len() == 1;
if !no_groups { if !no_groups {
print!("{}", config.list_prefix); print!("{list_prefix}");
if let Some(group_name) = group { if let Some(group_name) = group {
println!("[{group_name}]"); println!("[{group_name}]");
} else { } else {
@ -580,8 +590,7 @@ impl Subcommand {
if doc.lines().count() > 1 { if doc.lines().count() > 1 {
for line in doc.lines() { for line in doc.lines() {
println!( println!(
"{}{} {}", "{list_prefix}{} {}",
config.list_prefix,
config.color.stdout().doc().paint("#"), config.color.stdout().doc().paint("#"),
config.color.stdout().doc().paint(line), config.color.stdout().doc().paint(line),
); );
@ -590,8 +599,7 @@ impl Subcommand {
} }
print!( print!(
"{}{}", "{list_prefix}{}",
config.list_prefix,
RecipeSignature { name, recipe }.color_display(config.color.stdout()) RecipeSignature { name, recipe }.color_display(config.color.stdout())
); );
@ -611,11 +619,21 @@ impl Subcommand {
} }
} }
for submodule in module.modules(config) { if config.list_submodules {
println!("{}{} ...", config.list_prefix, submodule.name(),); for (i, submodule) in module.modules(config).into_iter().enumerate() {
if i + groups.len() > 0 {
println!();
} }
Ok(()) println!("{list_prefix}{}:", submodule.name());
Self::list_module(config, submodule, depth + 1);
}
} else {
for submodule in module.modules(config) {
println!("{list_prefix}{} ...", submodule.name(),);
}
}
} }
fn show<'src>( fn show<'src>(

View File

@ -223,3 +223,133 @@ fn list_unknown_submodule() {
.status(1) .status(1)
.run(); .run();
} }
#[test]
fn list_with_groups_in_modules() {
Test::new()
.justfile(
"
[group('FOO')]
foo:
mod bar
",
)
.write("bar.just", "[group('BAZ')]\nbaz:")
.test_round_trip(false)
.args(["--unstable", "--list", "--list-submodules"])
.stdout(
"
Available recipes:
[FOO]
foo
bar:
[BAZ]
baz
",
)
.run();
}
#[test]
fn list_displays_recipes_in_submodules() {
Test::new()
.write("foo.just", "bar:\n @echo FOO")
.justfile(
"
mod foo
",
)
.test_round_trip(false)
.args(["--unstable", "--list", "--list-submodules"])
.stdout(
"
Available recipes:
foo:
bar
",
)
.run();
}
#[test]
fn modules_are_space_separated_in_output() {
Test::new()
.write("foo.just", "foo:")
.write("bar.just", "bar:")
.justfile(
"
mod foo
mod bar
",
)
.test_round_trip(false)
.args(["--unstable", "--list", "--list-submodules"])
.stdout(
"
Available recipes:
bar:
bar
foo:
foo
",
)
.run();
}
#[test]
fn module_recipe_list_alignment_ignores_private_recipes() {
Test::new()
.write(
"foo.just",
"
# foos
foo:
@echo FOO
[private]
barbarbar:
@echo BAR
@_bazbazbaz:
@echo BAZ
",
)
.justfile("mod foo")
.test_round_trip(false)
.args(["--unstable", "--list", "--list-submodules"])
.stdout(
"
Available recipes:
foo:
foo # foos
",
)
.run();
}
#[test]
fn nested_modules_are_properly_indented() {
Test::new()
.write("foo.just", "mod bar")
.write("bar.just", "baz:\n @echo FOO")
.justfile(
"
mod foo
",
)
.test_round_trip(false)
.args(["--unstable", "--list", "--list-submodules"])
.stdout(
"
Available recipes:
foo:
bar:
baz
",
)
.run();
}

View File

@ -258,7 +258,7 @@ impl Test {
} }
} }
if !compare("status", output.status.code().unwrap(), self.status) if !compare("status", output.status.code(), Some(self.status))
| (self.stdout_regex.is_none() && !compare("stdout", output_stdout, &stdout)) | (self.stdout_regex.is_none() && !compare("stdout", output_stdout, &stdout))
| (self.stderr_regex.is_none() && !compare("stderr", output_stderr, &stderr)) | (self.stderr_regex.is_none() && !compare("stderr", output_stderr, &stderr))
{ {