From eec047a6f62b69ff54554ce7bb3522ff3dbf7218 Mon Sep 17 00:00:00 2001 From: Andrew Shu Date: Fri, 26 Aug 2022 17:40:48 -0700 Subject: [PATCH] fix(help): Do not propagate global args to help This prevents global args from showing in help completions, since help completions should only suggest subcommands. Adds tests to ensure the args still show in the generated help messages of subcommands. --- clap_complete/tests/snapshots/basic.bash | 6 +- clap_complete/tests/snapshots/basic.elvish | 3 - clap_complete/tests/snapshots/basic.fish | 3 - clap_complete/tests/snapshots/basic.ps1 | 3 - clap_complete/tests/snapshots/basic.zsh | 3 - .../tests/snapshots/basic.fig.js | 15 --- src/builder/command.rs | 10 ++ tests/builder/help.rs | 105 ++++++++++++++++++ 8 files changed, 118 insertions(+), 30 deletions(-) diff --git a/clap_complete/tests/snapshots/basic.bash b/clap_complete/tests/snapshots/basic.bash index f55933fd1e2..08b27e0bbde 100644 --- a/clap_complete/tests/snapshots/basic.bash +++ b/clap_complete/tests/snapshots/basic.bash @@ -39,7 +39,7 @@ _my-app() { return 0 ;; my__app__help) - opts="-c test help" + opts="test help" if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 @@ -53,7 +53,7 @@ _my-app() { return 0 ;; my__app__help__help) - opts="-c" + opts="" if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 @@ -67,7 +67,7 @@ _my-app() { return 0 ;; my__app__help__test) - opts="-c" + opts="" if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 diff --git a/clap_complete/tests/snapshots/basic.elvish b/clap_complete/tests/snapshots/basic.elvish index 71494e466e4..2465d3ca750 100644 --- a/clap_complete/tests/snapshots/basic.elvish +++ b/clap_complete/tests/snapshots/basic.elvish @@ -32,15 +32,12 @@ set edit:completion:arg-completer[my-app] = {|@words| cand --help 'Print help information' } &'my-app;help'= { - cand -c 'c' cand test 'Subcommand' cand help 'Print this message or the help of the given subcommand(s)' } &'my-app;help;test'= { - cand -c 'c' } &'my-app;help;help'= { - cand -c 'c' } ] $completions[$command] diff --git a/clap_complete/tests/snapshots/basic.fish b/clap_complete/tests/snapshots/basic.fish index 54415fd69e9..5a719a2a707 100644 --- a/clap_complete/tests/snapshots/basic.fish +++ b/clap_complete/tests/snapshots/basic.fish @@ -6,8 +6,5 @@ complete -c my-app -n "__fish_use_subcommand" -f -a "help" -d 'Print this messag complete -c my-app -n "__fish_seen_subcommand_from test" -s d complete -c my-app -n "__fish_seen_subcommand_from test" -s c complete -c my-app -n "__fish_seen_subcommand_from test" -s h -l help -d 'Print help information' -complete -c my-app -n "__fish_seen_subcommand_from help; and not __fish_seen_subcommand_from test; and not __fish_seen_subcommand_from help" -s c complete -c my-app -n "__fish_seen_subcommand_from help; and not __fish_seen_subcommand_from test; and not __fish_seen_subcommand_from help" -f -a "test" -d 'Subcommand' complete -c my-app -n "__fish_seen_subcommand_from help; and not __fish_seen_subcommand_from test; and not __fish_seen_subcommand_from help" -f -a "help" -d 'Print this message or the help of the given subcommand(s)' -complete -c my-app -n "__fish_seen_subcommand_from help; and __fish_seen_subcommand_from test" -s c -complete -c my-app -n "__fish_seen_subcommand_from help; and __fish_seen_subcommand_from help" -s c diff --git a/clap_complete/tests/snapshots/basic.ps1 b/clap_complete/tests/snapshots/basic.ps1 index 242b7e2d7f4..89aae1dc097 100644 --- a/clap_complete/tests/snapshots/basic.ps1 +++ b/clap_complete/tests/snapshots/basic.ps1 @@ -37,17 +37,14 @@ Register-ArgumentCompleter -Native -CommandName 'my-app' -ScriptBlock { break } 'my-app;help' { - [CompletionResult]::new('-c', 'c', [CompletionResultType]::ParameterName, 'c') [CompletionResult]::new('test', 'test', [CompletionResultType]::ParameterValue, 'Subcommand') [CompletionResult]::new('help', 'help', [CompletionResultType]::ParameterValue, 'Print this message or the help of the given subcommand(s)') break } 'my-app;help;test' { - [CompletionResult]::new('-c', 'c', [CompletionResultType]::ParameterName, 'c') break } 'my-app;help;help' { - [CompletionResult]::new('-c', 'c', [CompletionResultType]::ParameterName, 'c') break } }) diff --git a/clap_complete/tests/snapshots/basic.zsh b/clap_complete/tests/snapshots/basic.zsh index ad6c2aecab1..f7407180d8f 100644 --- a/clap_complete/tests/snapshots/basic.zsh +++ b/clap_complete/tests/snapshots/basic.zsh @@ -38,7 +38,6 @@ _arguments "${_arguments_options[@]}" / ;; (help) _arguments "${_arguments_options[@]}" / -'*-c[]' / ":: :_my-app__help_commands" / "*::: :->help" / && ret=0 @@ -51,12 +50,10 @@ _arguments "${_arguments_options[@]}" / case $line[1] in (test) _arguments "${_arguments_options[@]}" / -'*-c[]' / && ret=0 ;; (help) _arguments "${_arguments_options[@]}" / -'*-c[]' / && ret=0 ;; esac diff --git a/clap_complete_fig/tests/snapshots/basic.fig.js b/clap_complete_fig/tests/snapshots/basic.fig.js index e488df4bd7c..a8cd3fccde3 100644 --- a/clap_complete_fig/tests/snapshots/basic.fig.js +++ b/clap_complete_fig/tests/snapshots/basic.fig.js @@ -26,25 +26,10 @@ const completion: Fig.Spec = { { name: "test", description: "Subcommand", - options: [ - { - name: "-c", - }, - ], }, { name: "help", description: "Print this message or the help of the given subcommand(s)", - options: [ - { - name: "-c", - }, - ], - }, - ], - options: [ - { - name: "-c", }, ], }, diff --git a/src/builder/command.rs b/src/builder/command.rs index d4f6212ff8e..9e0a64e6d00 100644 --- a/src/builder/command.rs +++ b/src/builder/command.rs @@ -4070,7 +4070,17 @@ impl Command { pub(crate) fn _propagate_global_args(&mut self) { debug!("Command::_propagate_global_args:{}", self.name); + let autogenerated_help_subcommand = !self.is_disable_help_subcommand_set(); + for sc in &mut self.subcommands { + if sc.get_name() == "help" && autogenerated_help_subcommand { + // Avoid propagating args to the autogenerated help subtrees used in completion. + // This prevents args from showing up during help completions like + // `myapp help subcmd `, which should only suggest subcommands and not args, + // while still allowing args to show up properly on the generated help message. + continue; + } + for a in self.args.args().filter(|a| a.is_global_set()) { if sc.find(&a.id).is_some() { debug!( diff --git a/tests/builder/help.rs b/tests/builder/help.rs index 3224321c25a..ac68374da1f 100644 --- a/tests/builder/help.rs +++ b/tests/builder/help.rs @@ -2097,6 +2097,111 @@ Arguments: ); } +#[test] +fn global_args_should_show_on_toplevel_help_message() { + static HELP: &str = "myapp\x20 + +Usage: + myapp [OPTIONS] [SUBCOMMAND] + +Subcommands: + subcmd\x20\x20\x20\x20 + help Print this message or the help of the given subcommand(s) + +Options: + -g, --some-global \x20\x20\x20\x20 + -h, --help Print help information +"; + + let cmd = Command::new("myapp") + .arg( + Arg::new("someglobal") + .short('g') + .long("some-global") + .global(true), + ) + .subcommand(Command::new("subcmd").subcommand(Command::new("multi").version("1.0"))); + + utils::assert_output(cmd, "myapp help", HELP, false); +} + +#[test] +fn global_args_should_not_show_on_help_message_for_help_help() { + static HELP_HELP: &str = "myapp-help\x20 +Print this message or the help of the given subcommand(s) + +Usage: + myapp help [SUBCOMMAND]... + +Arguments: + [SUBCOMMAND]... The subcommand whose help message to display +"; + + let cmd = Command::new("myapp") + .arg( + Arg::new("someglobal") + .short('g') + .long("some-global") + .global(true), + ) + .subcommand(Command::new("subcmd").subcommand(Command::new("multi").version("1.0"))); + + utils::assert_output(cmd, "myapp help help", HELP_HELP, false); +} + +#[test] +fn global_args_should_show_on_help_message_for_subcommand() { + static HELP_SUBCMD: &str = "myapp-subcmd\x20 + +Usage: + myapp subcmd [OPTIONS] [SUBCOMMAND] + +Subcommands: + multi\x20\x20\x20\x20 + help Print this message or the help of the given subcommand(s) + +Options: + -g, --some-global \x20\x20\x20\x20 + -h, --help Print help information +"; + + let cmd = Command::new("myapp") + .arg( + Arg::new("someglobal") + .short('g') + .long("some-global") + .global(true), + ) + .subcommand(Command::new("subcmd").subcommand(Command::new("multi").version("1.0"))); + + utils::assert_output(cmd, "myapp help subcmd", HELP_SUBCMD, false); +} + +#[test] +fn global_args_should_show_on_help_message_for_nested_subcommand() { + static HELP_SUB_SUBCMD: &str = "myapp-subcmd-multi 1.0 + +Usage: + myapp subcmd multi [OPTIONS] + +Options: + -g, --some-global \x20\x20\x20\x20 + -h, --help Print help information + -V, --version Print version information +"; + + let cmd = Command::new("myapp") + .arg( + Arg::new("someglobal") + .short('g') + .long("some-global") + .global(true), + ) + .subcommand(Command::new("subcmd").subcommand(Command::new("multi").version("1.0"))); + + utils::assert_output(cmd, "myapp help subcmd multi", HELP_SUB_SUBCMD, false); +} + #[test] fn option_usage_order() { static OPTION_USAGE_ORDER: &str = "order