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
34 changes: 25 additions & 9 deletions clap_generate/src/generators/shells/zsh.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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))
));
}

Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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 bin_name == p.get_bin_name().unwrap_or(&String::new()) {
return Some(p);
}

if sc == p.get_bin_name().unwrap_or(&String::new()) {
return p;
for sc in p.get_subcommands() {
if let Some(ret) = parser_of(sc, bin_name) {
return Some(ret);
}
}

sc = sc.split(' ').last().unwrap();
p.find_subcommand(sc).expect(INTERNAL_ERROR_MSG)
None
}

// Writes out the args section, which ends up being the flags, opts and positionals, and a jump to
Expand Down
112 changes: 112 additions & 0 deletions clap_generate/tests/completions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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")
}
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
}
2 changes: 2 additions & 0 deletions src/build/app/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down