mirror of
https://github.com/clap-rs/clap
synced 2025-03-04 23:37:32 +00:00
Allow for nested subcommands with ZSH generation.
This commit is contained in:
parent
6a56a82629
commit
60dfcf13b5
3 changed files with 139 additions and 9 deletions
|
@ -119,7 +119,8 @@ _{bin_name_underscore}_commands() {{
|
|||
}}",
|
||||
bin_name_underscore = bin_name.replace(" ", "__"),
|
||||
bin_name = bin_name,
|
||||
subcommands_and_args = subcommands_of(parser_of(p, bin_name))
|
||||
subcommands_and_args =
|
||||
subcommands_of(parser_of(p, bin_name).expect(INTERNAL_ERROR_MSG))
|
||||
));
|
||||
}
|
||||
|
||||
|
@ -217,14 +218,20 @@ fn get_subcommands_of(p: &App) -> String {
|
|||
let mut subcmds = vec![];
|
||||
|
||||
for &(ref name, ref bin_name) in &sc_names {
|
||||
debug!(
|
||||
"get_subcommands_of:iter: p={}, name={}, bin_name={}",
|
||||
p.get_name(),
|
||||
name,
|
||||
bin_name,
|
||||
);
|
||||
let mut v = vec![format!("({})", name)];
|
||||
let subcommand_args = get_args_of(parser_of(p, &*bin_name));
|
||||
let subcommand_args = get_args_of(parser_of(p, &*bin_name).expect(INTERNAL_ERROR_MSG));
|
||||
|
||||
if !subcommand_args.is_empty() {
|
||||
v.push(subcommand_args);
|
||||
}
|
||||
|
||||
let subcommands = get_subcommands_of(parser_of(p, &*bin_name));
|
||||
let subcommands = get_subcommands_of(parser_of(p, &*bin_name).expect(INTERNAL_ERROR_MSG));
|
||||
|
||||
if !subcommands.is_empty() {
|
||||
v.push(subcommands);
|
||||
|
@ -252,15 +259,24 @@ esac",
|
|||
)
|
||||
}
|
||||
|
||||
fn parser_of<'help, 'app>(p: &'app App<'help>, mut sc: &str) -> &'app App<'help> {
|
||||
debug!("parser_of: sc={}", sc);
|
||||
// Get the App for a given subcommand tree.
|
||||
//
|
||||
// Given the bin_name "a b c" and the App for "a" this returns the "c" App.
|
||||
// Given the bin_name "a b c" and the App for "b" this returns the "c" App.
|
||||
fn parser_of<'help, 'app>(p: &'app App<'help>, bin_name: &str) -> Option<&'app App<'help>> {
|
||||
debug!("parser_of: p={}, bin_name={}", p.get_name(), bin_name);
|
||||
|
||||
if sc == p.get_bin_name().unwrap_or(&String::new()) {
|
||||
return p;
|
||||
if bin_name == p.get_bin_name().unwrap_or(&String::new()) {
|
||||
return Some(p);
|
||||
}
|
||||
|
||||
sc = sc.split(' ').last().unwrap();
|
||||
p.find_subcommand(sc).expect(INTERNAL_ERROR_MSG)
|
||||
for sc in p.get_subcommands() {
|
||||
if let Some(ret) = parser_of(sc, bin_name) {
|
||||
return Some(ret);
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
// Writes out the args section, which ends up being the flags, opts and positionals, and a jump to
|
||||
|
|
|
@ -712,6 +712,108 @@ _my_app_commands() {
|
|||
|
||||
_my_app "$@""#;
|
||||
|
||||
static ZSH_NESTED_SUBCOMMANDS: &str = r#"#compdef my_app
|
||||
|
||||
autoload -U is-at-least
|
||||
|
||||
_my_app() {
|
||||
typeset -A opt_args
|
||||
typeset -a _arguments_options
|
||||
local ret=1
|
||||
|
||||
if is-at-least 5.2; then
|
||||
_arguments_options=(-s -S -C)
|
||||
else
|
||||
_arguments_options=(-s -C)
|
||||
fi
|
||||
|
||||
local context curcontext="$curcontext" state line
|
||||
_arguments "${_arguments_options[@]}" \
|
||||
'-h[Prints help information]' \
|
||||
'--help[Prints help information]' \
|
||||
'-V[Prints version information]' \
|
||||
'--version[Prints version information]' \
|
||||
":: :_my_app_commands" \
|
||||
"*::: :->first" \
|
||||
&& ret=0
|
||||
case $state in
|
||||
(first)
|
||||
words=($line[1] "${words[@]}")
|
||||
(( CURRENT += 1 ))
|
||||
curcontext="${curcontext%:*:*}:my_app-command-$line[1]:"
|
||||
case $line[1] in
|
||||
(second)
|
||||
_arguments "${_arguments_options[@]}" \
|
||||
'-h[Prints help information]' \
|
||||
'--help[Prints help information]' \
|
||||
'-V[Prints version information]' \
|
||||
'--version[Prints version information]' \
|
||||
":: :_my_app__second_commands" \
|
||||
"*::: :->second" \
|
||||
&& ret=0
|
||||
case $state in
|
||||
(second)
|
||||
words=($line[1] "${words[@]}")
|
||||
(( CURRENT += 1 ))
|
||||
curcontext="${curcontext%:*:*}:my_app-second-command-$line[1]:"
|
||||
case $line[1] in
|
||||
(third)
|
||||
_arguments "${_arguments_options[@]}" \
|
||||
'-h[Prints help information]' \
|
||||
'--help[Prints help information]' \
|
||||
'-V[Prints version information]' \
|
||||
'--version[Prints version information]' \
|
||||
&& ret=0
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
(help)
|
||||
_arguments "${_arguments_options[@]}" \
|
||||
'-h[Prints help information]' \
|
||||
'--help[Prints help information]' \
|
||||
'-V[Prints version information]' \
|
||||
'--version[Prints version information]' \
|
||||
&& ret=0
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
(( $+functions[_my_app_commands] )) ||
|
||||
_my_app_commands() {
|
||||
local commands; commands=(
|
||||
"second:" \
|
||||
"help:Prints this message or the help of the given subcommand(s)" \
|
||||
)
|
||||
_describe -t commands 'my_app commands' commands "$@"
|
||||
}
|
||||
(( $+functions[_my_app__help_commands] )) ||
|
||||
_my_app__help_commands() {
|
||||
local commands; commands=(
|
||||
|
||||
)
|
||||
_describe -t commands 'my_app help commands' commands "$@"
|
||||
}
|
||||
(( $+functions[_my_app__second_commands] )) ||
|
||||
_my_app__second_commands() {
|
||||
local commands; commands=(
|
||||
"third:" \
|
||||
)
|
||||
_describe -t commands 'my_app second commands' commands "$@"
|
||||
}
|
||||
(( $+functions[_my_app__second__third_commands] )) ||
|
||||
_my_app__second__third_commands() {
|
||||
local commands; commands=(
|
||||
|
||||
)
|
||||
_describe -t commands 'my_app second third commands' commands "$@"
|
||||
}
|
||||
|
||||
_my_app "$@""#;
|
||||
|
||||
fn build_app() -> App<'static> {
|
||||
build_app_with_name("myapp")
|
||||
}
|
||||
|
@ -777,6 +879,10 @@ fn build_app_special_help() -> App<'static> {
|
|||
)
|
||||
}
|
||||
|
||||
fn build_app_nested_subcommands() -> App<'static> {
|
||||
App::new("first").subcommand(App::new("second").subcommand(App::new("third")))
|
||||
}
|
||||
|
||||
pub fn common<G: Generator>(app: &mut App, name: &str, fixture: &str) {
|
||||
let mut buf = vec![];
|
||||
generate::<G, _>(app, name, &mut buf);
|
||||
|
@ -856,3 +962,9 @@ fn zsh_with_special_help() {
|
|||
let mut app = build_app_special_help();
|
||||
common::<Zsh>(&mut app, "my_app", ZSH_SPECIAL_HELP);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn zsh_with_nested_subcommands() {
|
||||
let mut app = build_app_nested_subcommands();
|
||||
common::<Zsh>(&mut app, "my_app", ZSH_NESTED_SUBCOMMANDS);
|
||||
}
|
||||
|
|
|
@ -263,6 +263,8 @@ impl<'help> App<'help> {
|
|||
}
|
||||
|
||||
/// Find subcommand such that its name or one of aliases equals `name`.
|
||||
///
|
||||
/// This does not recurse through subcommands of subcommands.
|
||||
#[inline]
|
||||
pub fn find_subcommand<T>(&self, name: &T) -> Option<&App<'help>>
|
||||
where
|
||||
|
|
Loading…
Add table
Reference in a new issue