Show recipes in submodules with --show RECIPE::PATH
(#2111)
This commit is contained in:
parent
77a6e02964
commit
de1256f1bd
@ -33,10 +33,10 @@ set edit:completion:arg-completer[just] = {|@words|
|
|||||||
cand -c 'Run an arbitrary command with the working directory, `.env`, overrides, and exports set'
|
cand -c 'Run an arbitrary command with the working directory, `.env`, overrides, and exports set'
|
||||||
cand --command 'Run an arbitrary command with the working directory, `.env`, overrides, and exports set'
|
cand --command 'Run an arbitrary command with the working directory, `.env`, overrides, and exports set'
|
||||||
cand --completions 'Print shell completion script for <SHELL>'
|
cand --completions 'Print shell completion script for <SHELL>'
|
||||||
cand -l 'List available recipes and their arguments'
|
cand -l 'List available recipes'
|
||||||
cand --list 'List available recipes and their arguments'
|
cand --list 'List available recipes'
|
||||||
cand -s 'Show information about <RECIPE>'
|
cand -s 'Show recipe at <PATH>'
|
||||||
cand --show 'Show information about <RECIPE>'
|
cand --show 'Show recipe at <PATH>'
|
||||||
cand --dotenv-filename 'Search for environment file named <DOTENV-FILENAME> instead of `.env`'
|
cand --dotenv-filename 'Search for environment file named <DOTENV-FILENAME> instead of `.env`'
|
||||||
cand -E 'Load <DOTENV-PATH> as environment file instead of searching for one'
|
cand -E 'Load <DOTENV-PATH> as environment file instead of searching for one'
|
||||||
cand --dotenv-path 'Load <DOTENV-PATH> as environment file instead of searching for one'
|
cand --dotenv-path 'Load <DOTENV-PATH> as environment file instead of searching for one'
|
||||||
|
@ -48,8 +48,8 @@ complete -c just -l shell-arg -d 'Invoke shell with <SHELL-ARG> as an argument'
|
|||||||
complete -c just -s d -l working-directory -d 'Use <WORKING-DIRECTORY> as working directory. --justfile must also be set' -r -F
|
complete -c just -s d -l working-directory -d 'Use <WORKING-DIRECTORY> as working directory. --justfile must also be set' -r -F
|
||||||
complete -c just -s c -l command -d 'Run an arbitrary command with the working directory, `.env`, overrides, and exports set' -r
|
complete -c just -s c -l command -d 'Run an arbitrary command with the working directory, `.env`, overrides, and exports set' -r
|
||||||
complete -c just -l completions -d 'Print shell completion script for <SHELL>' -r -f -a "{bash '',elvish '',fish '',powershell '',zsh ''}"
|
complete -c just -l completions -d 'Print shell completion script for <SHELL>' -r -f -a "{bash '',elvish '',fish '',powershell '',zsh ''}"
|
||||||
complete -c just -s l -l list -d 'List available recipes and their arguments' -r
|
complete -c just -s l -l list -d 'List available recipes' -r
|
||||||
complete -c just -s s -l show -d 'Show information about <RECIPE>' -r
|
complete -c just -s s -l show -d 'Show recipe at <PATH>' -r
|
||||||
complete -c just -l dotenv-filename -d 'Search for environment file named <DOTENV-FILENAME> instead of `.env`' -r
|
complete -c just -l dotenv-filename -d 'Search for environment file named <DOTENV-FILENAME> instead of `.env`' -r
|
||||||
complete -c just -s E -l dotenv-path -d 'Load <DOTENV-PATH> as environment file instead of searching for one' -r -F
|
complete -c just -s E -l dotenv-path -d 'Load <DOTENV-PATH> as environment file instead of searching for one' -r -F
|
||||||
complete -c just -l timestamp-format -d 'Timestamp format string' -r
|
complete -c just -l timestamp-format -d 'Timestamp format string' -r
|
||||||
|
@ -36,10 +36,10 @@ Register-ArgumentCompleter -Native -CommandName 'just' -ScriptBlock {
|
|||||||
[CompletionResult]::new('-c', 'c', [CompletionResultType]::ParameterName, 'Run an arbitrary command with the working directory, `.env`, overrides, and exports set')
|
[CompletionResult]::new('-c', 'c', [CompletionResultType]::ParameterName, 'Run an arbitrary command with the working directory, `.env`, overrides, and exports set')
|
||||||
[CompletionResult]::new('--command', 'command', [CompletionResultType]::ParameterName, 'Run an arbitrary command with the working directory, `.env`, overrides, and exports set')
|
[CompletionResult]::new('--command', 'command', [CompletionResultType]::ParameterName, 'Run an arbitrary command with the working directory, `.env`, overrides, and exports set')
|
||||||
[CompletionResult]::new('--completions', 'completions', [CompletionResultType]::ParameterName, 'Print shell completion script for <SHELL>')
|
[CompletionResult]::new('--completions', 'completions', [CompletionResultType]::ParameterName, 'Print shell completion script for <SHELL>')
|
||||||
[CompletionResult]::new('-l', 'l', [CompletionResultType]::ParameterName, 'List available recipes and their arguments')
|
[CompletionResult]::new('-l', 'l', [CompletionResultType]::ParameterName, 'List available recipes')
|
||||||
[CompletionResult]::new('--list', 'list', [CompletionResultType]::ParameterName, 'List available recipes and their arguments')
|
[CompletionResult]::new('--list', 'list', [CompletionResultType]::ParameterName, 'List available recipes')
|
||||||
[CompletionResult]::new('-s', 's', [CompletionResultType]::ParameterName, 'Show information about <RECIPE>')
|
[CompletionResult]::new('-s', 's', [CompletionResultType]::ParameterName, 'Show recipe at <PATH>')
|
||||||
[CompletionResult]::new('--show', 'show', [CompletionResultType]::ParameterName, 'Show information about <RECIPE>')
|
[CompletionResult]::new('--show', 'show', [CompletionResultType]::ParameterName, 'Show recipe at <PATH>')
|
||||||
[CompletionResult]::new('--dotenv-filename', 'dotenv-filename', [CompletionResultType]::ParameterName, 'Search for environment file named <DOTENV-FILENAME> instead of `.env`')
|
[CompletionResult]::new('--dotenv-filename', 'dotenv-filename', [CompletionResultType]::ParameterName, 'Search for environment file named <DOTENV-FILENAME> instead of `.env`')
|
||||||
[CompletionResult]::new('-E', 'E ', [CompletionResultType]::ParameterName, 'Load <DOTENV-PATH> as environment file instead of searching for one')
|
[CompletionResult]::new('-E', 'E ', [CompletionResultType]::ParameterName, 'Load <DOTENV-PATH> as environment file instead of searching for one')
|
||||||
[CompletionResult]::new('--dotenv-path', 'dotenv-path', [CompletionResultType]::ParameterName, 'Load <DOTENV-PATH> as environment file instead of searching for one')
|
[CompletionResult]::new('--dotenv-path', 'dotenv-path', [CompletionResultType]::ParameterName, 'Load <DOTENV-PATH> as environment file instead of searching for one')
|
||||||
|
@ -31,10 +31,10 @@ _just() {
|
|||||||
'*-c+[Run an arbitrary command with the working directory, \`.env\`, overrides, and exports set]: : ' \
|
'*-c+[Run an arbitrary command with the working directory, \`.env\`, overrides, and exports set]: : ' \
|
||||||
'*--command=[Run an arbitrary command with the working directory, \`.env\`, overrides, and exports set]: : ' \
|
'*--command=[Run an arbitrary command with the working directory, \`.env\`, overrides, and exports set]: : ' \
|
||||||
'*--completions=[Print shell completion script for <SHELL>]:SHELL:(bash elvish fish powershell zsh)' \
|
'*--completions=[Print shell completion script for <SHELL>]:SHELL:(bash elvish fish powershell zsh)' \
|
||||||
'-l+[List available recipes and their arguments]' \
|
'()-l+[List available recipes]' \
|
||||||
'--list=[List available recipes and their arguments]' \
|
'()--list=[List available recipes]' \
|
||||||
'-s+[Show information about <RECIPE>]: :(_just_commands)' \
|
'-s+[Show recipe at <PATH>]: :(_just_commands)' \
|
||||||
'--show=[Show information about <RECIPE>]: :(_just_commands)' \
|
'--show=[Show recipe at <PATH>]: :(_just_commands)' \
|
||||||
'(-E --dotenv-path)--dotenv-filename=[Search for environment file named <DOTENV-FILENAME> instead of \`.env\`]: : ' \
|
'(-E --dotenv-path)--dotenv-filename=[Search for environment file named <DOTENV-FILENAME> instead of \`.env\`]: : ' \
|
||||||
'-E+[Load <DOTENV-PATH> as environment file instead of searching for one]: :_files' \
|
'-E+[Load <DOTENV-PATH> as environment file instead of searching for one]: :_files' \
|
||||||
'--dotenv-path=[Load <DOTENV-PATH> as environment file instead of searching for one]: :_files' \
|
'--dotenv-path=[Load <DOTENV-PATH> as environment file instead of searching for one]: :_files' \
|
||||||
|
@ -47,10 +47,10 @@ pub(crate) const ZSH_COMPLETION_REPLACEMENTS: &[(&str, &str)] = &[
|
|||||||
r"'*--set=[Override <VARIABLE> with <VALUE>]: :(_just_variables)' \",
|
r"'*--set=[Override <VARIABLE> with <VALUE>]: :(_just_variables)' \",
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
r"'()-s+[Show information about <RECIPE>]:RECIPE: ' \
|
r"'()-s+[Show recipe at <PATH>]:PATH: ' \
|
||||||
'()--show=[Show information about <RECIPE>]:RECIPE: ' \",
|
'()--show=[Show recipe at <PATH>]:PATH: ' \",
|
||||||
r"'-s+[Show information about <RECIPE>]: :(_just_commands)' \
|
r"'-s+[Show recipe at <PATH>]: :(_just_commands)' \
|
||||||
'--show=[Show information about <RECIPE>]: :(_just_commands)' \",
|
'--show=[Show recipe at <PATH>]: :(_just_commands)' \",
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
"'*::ARGUMENTS -- Overrides and recipe(s) to run, defaulting to the first recipe in the \
|
"'*::ARGUMENTS -- Overrides and recipe(s) to run, defaulting to the first recipe in the \
|
||||||
|
@ -2,6 +2,7 @@ use {
|
|||||||
super::*,
|
super::*,
|
||||||
clap::{
|
clap::{
|
||||||
builder::{styling::AnsiColor, FalseyValueParser, PossibleValuesParser, Styles},
|
builder::{styling::AnsiColor, FalseyValueParser, PossibleValuesParser, Styles},
|
||||||
|
parser::ValuesRef,
|
||||||
value_parser, Arg, ArgAction, ArgGroup, ArgMatches, Command,
|
value_parser, Arg, ArgAction, ArgGroup, ArgMatches, Command,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@ -421,7 +422,8 @@ impl Config {
|
|||||||
.num_args(0..)
|
.num_args(0..)
|
||||||
.value_name("PATH")
|
.value_name("PATH")
|
||||||
.action(ArgAction::Set)
|
.action(ArgAction::Set)
|
||||||
.help("List available recipes and their arguments"),
|
.conflicts_with(arg::ARGUMENTS)
|
||||||
|
.help("List available recipes"),
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::new(cmd::GROUPS)
|
Arg::new(cmd::GROUPS)
|
||||||
@ -439,10 +441,11 @@ impl Config {
|
|||||||
Arg::new(cmd::SHOW)
|
Arg::new(cmd::SHOW)
|
||||||
.short('s')
|
.short('s')
|
||||||
.long("show")
|
.long("show")
|
||||||
|
.num_args(1..)
|
||||||
.action(ArgAction::Set)
|
.action(ArgAction::Set)
|
||||||
.value_name("RECIPE")
|
.value_name("PATH")
|
||||||
.conflicts_with(arg::ARGUMENTS)
|
.conflicts_with(arg::ARGUMENTS)
|
||||||
.help("Show information about <RECIPE>"),
|
.help("Show recipe at <PATH>"),
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::new(cmd::SUMMARY)
|
Arg::new(cmd::SUMMARY)
|
||||||
@ -557,6 +560,18 @@ impl Config {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn parse_module_path(path: ValuesRef<String>) -> ConfigResult<ModulePath> {
|
||||||
|
path
|
||||||
|
.clone()
|
||||||
|
.map(|s| (*s).as_str())
|
||||||
|
.collect::<Vec<&str>>()
|
||||||
|
.as_slice()
|
||||||
|
.try_into()
|
||||||
|
.map_err(|()| ConfigError::ModulePath {
|
||||||
|
path: path.cloned().collect(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
fn search_config(matches: &ArgMatches, positional: &Positional) -> ConfigResult<SearchConfig> {
|
fn search_config(matches: &ArgMatches, positional: &Positional) -> ConfigResult<SearchConfig> {
|
||||||
if matches.get_flag(arg::GLOBAL_JUSTFILE) {
|
if matches.get_flag(arg::GLOBAL_JUSTFILE) {
|
||||||
return Ok(SearchConfig::GlobalJustfile);
|
return Ok(SearchConfig::GlobalJustfile);
|
||||||
@ -676,22 +691,16 @@ impl Config {
|
|||||||
Subcommand::Init
|
Subcommand::Init
|
||||||
} else if let Some(path) = matches.get_many::<String>(cmd::LIST) {
|
} else if let Some(path) = matches.get_many::<String>(cmd::LIST) {
|
||||||
Subcommand::List {
|
Subcommand::List {
|
||||||
path: path
|
path: Self::parse_module_path(path)?,
|
||||||
.clone()
|
|
||||||
.map(|s| (*s).as_str())
|
|
||||||
.collect::<Vec<&str>>()
|
|
||||||
.as_slice()
|
|
||||||
.try_into()
|
|
||||||
.map_err(|()| ConfigError::ListPath {
|
|
||||||
path: path.cloned().collect(),
|
|
||||||
})?,
|
|
||||||
}
|
}
|
||||||
} else if matches.get_flag(cmd::GROUPS) {
|
} else if matches.get_flag(cmd::GROUPS) {
|
||||||
Subcommand::Groups
|
Subcommand::Groups
|
||||||
} else if matches.get_flag(cmd::MAN) {
|
} else if matches.get_flag(cmd::MAN) {
|
||||||
Subcommand::Man
|
Subcommand::Man
|
||||||
} else if let Some(name) = matches.get_one::<String>(cmd::SHOW).map(Into::into) {
|
} else if let Some(path) = matches.get_many::<String>(cmd::SHOW) {
|
||||||
Subcommand::Show { name }
|
Subcommand::Show {
|
||||||
|
path: Self::parse_module_path(path)?,
|
||||||
|
}
|
||||||
} else if matches.get_flag(cmd::EVALUATE) {
|
} else if matches.get_flag(cmd::EVALUATE) {
|
||||||
if positional.arguments.len() > 1 {
|
if positional.arguments.len() > 1 {
|
||||||
return Err(ConfigError::SubcommandArguments {
|
return Err(ConfigError::SubcommandArguments {
|
||||||
@ -1316,18 +1325,24 @@ mod tests {
|
|||||||
test! {
|
test! {
|
||||||
name: subcommand_show_long,
|
name: subcommand_show_long,
|
||||||
args: ["--show", "build"],
|
args: ["--show", "build"],
|
||||||
subcommand: Subcommand::Show { name: String::from("build") },
|
subcommand: Subcommand::Show { path: ModulePath { path: vec!["build".into()], spaced: false } },
|
||||||
}
|
}
|
||||||
|
|
||||||
test! {
|
test! {
|
||||||
name: subcommand_show_short,
|
name: subcommand_show_short,
|
||||||
args: ["-s", "build"],
|
args: ["-s", "build"],
|
||||||
subcommand: Subcommand::Show { name: String::from("build") },
|
subcommand: Subcommand::Show { path: ModulePath { path: vec!["build".into()], spaced: false } },
|
||||||
}
|
}
|
||||||
|
|
||||||
error! {
|
test! {
|
||||||
name: subcommand_show_no_arg,
|
name: subcommand_show_multiple_args,
|
||||||
args: ["--show"],
|
args: ["--show", "foo", "bar"],
|
||||||
|
subcommand: Subcommand::Show {
|
||||||
|
path: ModulePath {
|
||||||
|
path: vec!["foo".into(), "bar".into()],
|
||||||
|
spaced: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
test! {
|
test! {
|
||||||
@ -1602,20 +1617,6 @@ mod tests {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
error_matches! {
|
|
||||||
name: show_arguments,
|
|
||||||
args: ["--show", "foo", "bar"],
|
|
||||||
error: error,
|
|
||||||
check: {
|
|
||||||
assert_eq!(error.kind(), clap::error::ErrorKind::ArgumentConflict);
|
|
||||||
assert_eq!(error.context().collect::<Vec<_>>(), vec![
|
|
||||||
(ContextKind::InvalidArg, &ContextValue::String("--show <RECIPE>".into())),
|
|
||||||
(ContextKind::PriorArg, &ContextValue::String("[ARGUMENTS]...".into())),
|
|
||||||
(ContextKind::Usage, &ContextValue::StyledStr("\u{1b}[33mUsage:\u{1b}[0m \u{1b}[32mjust\u{1b}[0m \u{1b}[32m--show\u{1b}[0m\u{1b}[32m \u{1b}[0m\u{1b}[32m<RECIPE>\u{1b}[0m \u{1b}[32m[ARGUMENTS]...\u{1b}[0m".into())),
|
|
||||||
]);
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
error! {
|
error! {
|
||||||
name: summary_arguments,
|
name: summary_arguments,
|
||||||
args: ["--summary", "bar"],
|
args: ["--summary", "bar"],
|
||||||
|
@ -11,7 +11,7 @@ pub(crate) enum ConfigError {
|
|||||||
))]
|
))]
|
||||||
Internal { message: String },
|
Internal { message: String },
|
||||||
#[snafu(display("Invalid module path `{}`", path.join(" ")))]
|
#[snafu(display("Invalid module path `{}`", path.join(" ")))]
|
||||||
ListPath { path: Vec<String> },
|
ModulePath { path: Vec<String> },
|
||||||
#[snafu(display(
|
#[snafu(display(
|
||||||
"Path-prefixed recipes may not be used with `--working-directory` or `--justfile`."
|
"Path-prefixed recipes may not be used with `--working-directory` or `--justfile`."
|
||||||
))]
|
))]
|
||||||
|
@ -40,7 +40,7 @@ pub(crate) enum Subcommand {
|
|||||||
overrides: BTreeMap<String, String>,
|
overrides: BTreeMap<String, String>,
|
||||||
},
|
},
|
||||||
Show {
|
Show {
|
||||||
name: String,
|
path: ModulePath,
|
||||||
},
|
},
|
||||||
Summary,
|
Summary,
|
||||||
Variables,
|
Variables,
|
||||||
@ -91,7 +91,7 @@ impl Subcommand {
|
|||||||
Format => Self::format(config, &search, src, ast)?,
|
Format => Self::format(config, &search, src, ast)?,
|
||||||
Groups => Self::groups(config, justfile),
|
Groups => Self::groups(config, justfile),
|
||||||
List { path } => Self::list_module(config, justfile, path)?,
|
List { path } => Self::list_module(config, justfile, path)?,
|
||||||
Show { ref name } => Self::show(config, name, justfile)?,
|
Show { path } => Self::show(config, justfile, path)?,
|
||||||
Summary => Self::summary(config, justfile),
|
Summary => Self::summary(config, justfile),
|
||||||
Variables => Self::variables(justfile),
|
Variables => Self::variables(justfile),
|
||||||
Changelog | Completions { .. } | Edit | Init | Man | Run { .. } => unreachable!(),
|
Changelog | Completions { .. } | Edit | Init | Man | Run { .. } => unreachable!(),
|
||||||
@ -636,19 +636,32 @@ impl Subcommand {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn show<'src>(config: &Config, name: &str, justfile: &Justfile<'src>) -> Result<(), Error<'src>> {
|
fn show<'src>(
|
||||||
if let Some(alias) = justfile.get_alias(name) {
|
config: &Config,
|
||||||
let recipe = justfile.get_recipe(alias.target.name.lexeme()).unwrap();
|
mut module: &Justfile<'src>,
|
||||||
|
path: &ModulePath,
|
||||||
|
) -> Result<(), Error<'src>> {
|
||||||
|
for name in &path.path[0..path.path.len() - 1] {
|
||||||
|
module = module
|
||||||
|
.modules
|
||||||
|
.get(name)
|
||||||
|
.ok_or_else(|| Error::UnknownSubmodule { path: path.clone() })?;
|
||||||
|
}
|
||||||
|
|
||||||
|
let name = path.path.last().unwrap();
|
||||||
|
|
||||||
|
if let Some(alias) = module.get_alias(name) {
|
||||||
|
let recipe = module.get_recipe(alias.target.name.lexeme()).unwrap();
|
||||||
println!("{alias}");
|
println!("{alias}");
|
||||||
println!("{}", recipe.color_display(config.color.stdout()));
|
println!("{}", recipe.color_display(config.color.stdout()));
|
||||||
Ok(())
|
Ok(())
|
||||||
} else if let Some(recipe) = justfile.get_recipe(name) {
|
} else if let Some(recipe) = module.get_recipe(name) {
|
||||||
println!("{}", recipe.color_display(config.color.stdout()));
|
println!("{}", recipe.color_display(config.color.stdout()));
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
Err(Error::UnknownRecipes {
|
Err(Error::UnknownRecipes {
|
||||||
recipes: vec![name.to_owned()],
|
recipes: vec![name.to_owned()],
|
||||||
suggestion: justfile.suggest_recipe(name),
|
suggestion: module.suggest_recipe(name),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -100,3 +100,27 @@ a Z="\t z":
|
|||||||
stderr: "error: Justfile does not contain recipe `fooooooo`.\n",
|
stderr: "error: Justfile does not contain recipe `fooooooo`.\n",
|
||||||
status: EXIT_FAILURE,
|
status: EXIT_FAILURE,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn show_recipe_at_path() {
|
||||||
|
Test::new()
|
||||||
|
.write("foo.just", "bar:\n @echo MODULE")
|
||||||
|
.justfile(
|
||||||
|
"
|
||||||
|
mod foo
|
||||||
|
",
|
||||||
|
)
|
||||||
|
.test_round_trip(false)
|
||||||
|
.args(["--unstable", "--show", "foo::bar"])
|
||||||
|
.stdout("bar:\n @echo MODULE\n")
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn show_invalid_path() {
|
||||||
|
Test::new()
|
||||||
|
.args(["--show", "$hello"])
|
||||||
|
.stderr("error: Invalid module path `$hello`\n")
|
||||||
|
.status(1)
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user