Repository: clap-rs/clap
Pull request #1920 (2020-05-11 23:14:48)

Cherry pick missed commits from v2
<!--
If your PR closes some issues, please write `Closes #XXXX`
where `XXXX` is the number of the issue you want to fix.
Each issue goes on it's own line.
-->
See #1919 



Patch:
diff --git a/clap_generate/src/generators/shells/bash.rs b/clap_generate/src/generators/shells/bash.rs
index 40bd33aa..3e2d6be3 100644
--- a/clap_generate/src/generators/shells/bash.rs
+++ b/clap_generate/src/generators/shells/bash.rs
@@ -43,7 +43,7 @@ impl Generator for Bash {
         {cmd})
             opts=\"{name_opts}\"
             if [[ ${{cur}} == -* || ${{COMP_CWORD}} -eq 1 ]] ; then
-                COMPREPLY=( $(compgen -W \"${{opts}}\" -- ${{cur}}) )
+                COMPREPLY=( $(compgen -W \"${{opts}}\" -- \"${{cur}}\") )
                 return 0
             fi
             case \"${{prev}}\" in
@@ -52,7 +52,7 @@ impl Generator for Bash {
                     COMPREPLY=()
                     ;;
             esac
-            COMPREPLY=( $(compgen -W \"${{opts}}\" -- ${{cur}}) )
+            COMPREPLY=( $(compgen -W \"${{opts}}\" -- \"${{cur}}\") )
             return 0
             ;;
         {subcmd_details}
@@ -117,7 +117,7 @@ fn subcommand_details(app: &App) -> String {
         {subcmd})
             opts=\"{sc_opts}\"
             if [[ ${{cur}} == -* || ${{COMP_CWORD}} -eq {level} ]] ; then
-                COMPREPLY=( $(compgen -W \"${{opts}}\" -- ${{cur}}) )
+                COMPREPLY=( $(compgen -W \"${{opts}}\" -- \"${{cur}}\") )
                 return 0
             fi
             case \"${{prev}}\" in
@@ -126,7 +126,7 @@ fn subcommand_details(app: &App) -> String {
                     COMPREPLY=()
                     ;;
             esac
-            COMPREPLY=( $(compgen -W \"${{opts}}\" -- ${{cur}}) )
+            COMPREPLY=( $(compgen -W \"${{opts}}\" -- \"${{cur}}\") )
             return 0
             ;;",
             subcmd_dets,
@@ -181,9 +181,9 @@ fn vals_for(o: &Arg) -> String {
     debug!("vals_for: o={}", o.get_name());
 
     if let Some(ref vals) = o.get_possible_values() {
-        format!("$(compgen -W \"{}\" -- ${{cur}})", vals.join(" "))
+        format!("$(compgen -W \"{}\" -- \"${{cur}}\")", vals.join(" "))
     } else {
-        String::from("$(compgen -f ${cur})")
+        String::from("$(compgen -f \"${cur}\")")
     }
 }
 
diff --git a/examples/04_using_matches.rs b/examples/04_using_matches.rs
index 97f27c93..ab5b8563 100644
--- a/examples/04_using_matches.rs
+++ b/examples/04_using_matches.rs
@@ -44,8 +44,8 @@ fn main() {
         println!("Debugging is turned on");
     }
 
-    // If we wanted to some custom initialization based off some configuration file provided
-    // by the user, we could get the file (A string of the file)
+    // If we wanted to do some custom initialization based off some configuration file
+    // provided by the user, we could get the file (A string of the file)
     if let Some(ref file) = matches.value_of("config") {
         println!("Using config file: {}", file);
     }
diff --git a/src/build/app/mod.rs b/src/build/app/mod.rs
index ba8b4166..12126f11 100644
--- a/src/build/app/mod.rs
+++ b/src/build/app/mod.rs
@@ -1811,7 +1811,8 @@ impl<'b> App<'b> {
             .args
             .args
             .iter()
-            .any(|x| x.long == Some("help") || x.id == Id::help_hash()))
+            .any(|x| x.long == Some("help") || x.id == Id::help_hash())
+            || self.is_set(AppSettings::DisableHelpFlags))
         {
             debug!("App::_create_help_and_version: Building --help");
             let mut help = Arg::with_name("help")
@@ -2128,6 +2129,7 @@ impl<'a> From<&'a Yaml> for App<'a> {
         }
 
         yaml_str!(a, yaml, version);
+        yaml_str!(a, yaml, long_version);
         yaml_str!(a, yaml, author);
         yaml_str!(a, yaml, bin_name);
         yaml_str!(a, yaml, about);
diff --git a/src/build/app/settings.rs b/src/build/app/settings.rs
index e66e2263..39d644a9 100644
--- a/src/build/app/settings.rs
+++ b/src/build/app/settings.rs
@@ -48,6 +48,7 @@ bitflags! {
         const ARGS_OVERRIDE_SELF             = 1 << 39;
         const HELP_REQUIRED                  = 1 << 40;
         const SUBCOMMAND_PRECEDENCE_OVER_ARG = 1 << 41;
+        const DISABLE_HELP_FLAGS             = 1 << 42;
     }
 }
 
@@ -100,6 +101,8 @@ impl_settings! { AppSettings, AppFlags,
         => Flags::DERIVE_DISP_ORDER,
     DisableHelpSubcommand("disablehelpsubcommand")
         => Flags::DISABLE_HELP_SC,
+    DisableHelpFlags("disablehelpflags")
+        => Flags::DISABLE_HELP_FLAGS,
     DisableVersion("disableversion")
         => Flags::DISABLE_VERSION,
     GlobalVersion("globalversion")
@@ -601,6 +604,37 @@ pub enum AppSettings {
     /// [`Arg::use_delimiter(false)`]: ./struct.Arg.html#method.use_delimiter
     DontDelimitTrailingValues,
 
+    /// Disables `-h` and `--help` [`App`] without affecting any of the [`SubCommand`]s
+    /// (Defaults to `false`; application *does* have help flags)
+    ///
+    /// # Examples
+    ///
+    /// ```rust
+    /// # use clap::{App, AppSettings, ErrorKind};
+    /// let res = App::new("myprog")
+    ///     .setting(AppSettings::DisableHelpFlags)
+    ///     .try_get_matches_from(vec![
+    ///         "myprog", "-h"
+    ///     ]);
+    /// assert!(res.is_err());
+    /// assert_eq!(res.unwrap_err().kind, ErrorKind::UnknownArgument);
+    /// ```
+    ///
+    /// ```rust
+    /// # use clap::{App, AppSettings, ErrorKind};
+    /// let res = App::new("myprog")
+    ///     .setting(AppSettings::DisableHelpFlags)
+    ///     .subcommand(App::new("test"))
+    ///     .try_get_matches_from(vec![
+    ///         "myprog", "test", "-h"
+    ///     ]);
+    /// assert!(res.is_err());
+    /// assert_eq!(res.unwrap_err().kind, ErrorKind::HelpDisplayed);
+    /// ```
+    /// [`SubCommand`]: ./struct.SubCommand.html
+    /// [`App`]: ./struct.App.html
+    DisableHelpFlags,
+
     /// Disables the `help` subcommand
     ///
     /// # Examples
@@ -967,7 +1001,7 @@ pub enum AppSettings {
     /// Disables `-V` and `--version` for all [``]s
     /// (Defaults to `false`; subcommands *do* have version flags.)
     ///
-    /// **NOTE:** This setting must be set **prior** adding any subcommands
+    /// **NOTE:** This setting must be set **prior** to adding any subcommands.
     ///
     /// # Examples
     ///
@@ -1040,6 +1074,10 @@ mod test {
     #[allow(clippy::cognitive_complexity)]
     #[test]
     fn app_settings_fromstr() {
+        assert_eq!(
+            "disablehelpflags".parse::<AppSettings>().unwrap(),
+            AppSettings::DisableHelpFlags
+        );
         assert_eq!(
             "argsnegatesubcommands".parse::<AppSettings>().unwrap(),
             AppSettings::ArgsNegateSubcommands
diff --git a/src/build/arg/mod.rs b/src/build/arg/mod.rs
index 17d4b5c1..03641489 100644
--- a/src/build/arg/mod.rs
+++ b/src/build/arg/mod.rs
@@ -236,6 +236,7 @@ impl<'help> Arg<'help> {
                 "hide_default_value" => yaml_to_bool!(a, v, hide_default_value),
                 "overrides_with" => yaml_vec_or_str!(v, a, overrides_with),
                 "possible_values" => yaml_vec_or_str!(v, a, possible_value),
+                "case_insensitive" => yaml_to_bool!(a, v, case_insensitive),
                 "required_unless_one" => yaml_vec_or_str!(v, a, required_unless),
                 "required_unless_all" => {
                     a = yaml_vec_or_str!(v, a, required_unless);
diff --git a/src/build/arg_group.rs b/src/build/arg_group.rs
index 5201e970..e1b19916 100644
--- a/src/build/arg_group.rs
+++ b/src/build/arg_group.rs
@@ -43,7 +43,7 @@ use crate::util::{Id, Key};
 ///     .arg("--minor         'auto increase minor'")
 ///     .arg("--patch         'auto increase patch'")
 ///     .group(ArgGroup::with_name("vers")
-///          .args(&["set-ver", "major", "minor","patch"])
+///          .args(&["set-ver", "major", "minor", "patch"])
 ///          .required(true))
 ///     .try_get_matches_from(vec!["app", "--major", "--patch"]);
 /// // Because we used two args in the group it's an error
diff --git a/src/macros.rs b/src/macros.rs
index 537dde03..a063e015 100644
--- a/src/macros.rs
+++ b/src/macros.rs
@@ -262,7 +262,8 @@ macro_rules! app_from_crate {
 ///         (author: "Someone E. <someone_else@other.com>")
 ///         (@arg verbose: -v --verbose "Print test information verbosely")
 ///     )
-/// );
+/// )
+/// .get_matches();
 /// # }
 /// ```
 ///
@@ -494,9 +495,13 @@ macro_rules! clap_app {
     (@arg ($arg:expr) $modes:tt $ident:ident[$($target:ident)*] $($tail:tt)*) => {
         $crate::clap_app!{ @arg ($arg $( .$ident(stringify!($target)) )*) $modes $($tail)* }
     };
-    // Inherit builder's functions
-    (@arg ($arg:expr) $modes:tt $ident:ident($($expr:expr)*) $($tail:tt)*) => {
-        $crate::clap_app!{ @arg ($arg.$ident($($expr)*)) $modes $($tail)* }
+    // Inherit builder's functions, e.g. `index(2)`, `requires_if("val", "arg")`
+    (@arg ($arg:expr) $modes:tt $ident:ident($($expr:expr),*) $($tail:tt)*) => {
+        $crate::clap_app!{ @arg ($arg.$ident($($expr),*)) $modes $($tail)* }
+    };
+    // Inherit builder's functions with trailing comma, e.g. `index(2,)`, `requires_if("val", "arg",)`
+    (@arg ($arg:expr) $modes:tt $ident:ident($($expr:expr,)*) $($tail:tt)*) => {
+        $crate::clap_app!{ @arg ($arg.$ident($($expr),*)) $modes $($tail)* }
     };
     // Build a subcommand outside of an app.
     (@subcommand ($name:expr) => $($tail:tt)*) => {


Test patch:
diff --git a/clap_generate/tests/completions.rs b/clap_generate/tests/completions.rs
index 73e96d39..74046645 100644
--- a/clap_generate/tests/completions.rs
+++ b/clap_generate/tests/completions.rs
@@ -47,7 +47,7 @@ static BASH: &str = r#"_myapp() {
         myapp)
             opts=" -h -V  --help --version  <file> test help"
             if [[ ${cur} == -* || ${COMP_CWORD} -eq 1 ]] ; then
-                COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
+                COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
                 return 0
             fi
             case "${prev}" in
@@ -56,14 +56,14 @@ static BASH: &str = r#"_myapp() {
                     COMPREPLY=()
                     ;;
             esac
-            COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
+            COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
             return 0
             ;;
         
         myapp__help)
             opts=" -h -V  --help --version  "
             if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then
-                COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
+                COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
                 return 0
             fi
             case "${prev}" in
@@ -72,26 +72,26 @@ static BASH: &str = r#"_myapp() {
                     COMPREPLY=()
                     ;;
             esac
-            COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
+            COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
             return 0
             ;;
         myapp__test)
             opts=" -h -V  --case --help --version  "
             if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then
-                COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
+                COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
                 return 0
             fi
             case "${prev}" in
                 
                 --case)
-                    COMPREPLY=($(compgen -f ${cur}))
+                    COMPREPLY=($(compgen -f "${cur}"))
                     return 0
                     ;;
                 *)
                     COMPREPLY=()
                     ;;
             esac
-            COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
+            COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
             return 0
             ;;
     esac
@@ -574,7 +574,7 @@ static BASH_SPECIAL_CMDS: &str = r#"_my_app() {
         my_app)
             opts=" -h -V  --help --version  <file> test some_cmd some-cmd-with-hypens help"
             if [[ ${cur} == -* || ${COMP_CWORD} -eq 1 ]] ; then
-                COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
+                COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
                 return 0
             fi
             case "${prev}" in
@@ -583,14 +583,14 @@ static BASH_SPECIAL_CMDS: &str = r#"_my_app() {
                     COMPREPLY=()
                     ;;
             esac
-            COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
+            COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
             return 0
             ;;
         
         my_app__help)
             opts=" -h -V  --help --version  "
             if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then
-                COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
+                COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
                 return 0
             fi
             case "${prev}" in
@@ -599,13 +599,13 @@ static BASH_SPECIAL_CMDS: &str = r#"_my_app() {
                     COMPREPLY=()
                     ;;
             esac
-            COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
+            COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
             return 0
             ;;
         my_app__some__cmd__with__hypens)
             opts=" -h -V  --help --version  "
             if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then
-                COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
+                COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
                 return 0
             fi
             case "${prev}" in
@@ -614,45 +614,45 @@ static BASH_SPECIAL_CMDS: &str = r#"_my_app() {
                     COMPREPLY=()
                     ;;
             esac
-            COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
+            COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
             return 0
             ;;
         my_app__some_cmd)
             opts=" -h -V  --config --help --version  "
             if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then
-                COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
+                COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
                 return 0
             fi
             case "${prev}" in
                 
                 --config)
-                    COMPREPLY=($(compgen -f ${cur}))
+                    COMPREPLY=($(compgen -f "${cur}"))
                     return 0
                     ;;
                 *)
                     COMPREPLY=()
                     ;;
             esac
-            COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
+            COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
             return 0
             ;;
         my_app__test)
             opts=" -h -V  --case --help --version  "
             if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then
-                COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
+                COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
                 return 0
             fi
             case "${prev}" in
                 
                 --case)
-                    COMPREPLY=($(compgen -f ${cur}))
+                    COMPREPLY=($(compgen -f "${cur}"))
                     return 0
                     ;;
                 *)
                     COMPREPLY=()
                     ;;
             esac
-            COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
+            COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
             return 0
             ;;
     esac
diff --git a/tests/app_settings.rs b/tests/app_settings.rs
index cbc50a32..40ad2aaf 100644
--- a/tests/app_settings.rs
+++ b/tests/app_settings.rs
@@ -724,6 +724,25 @@ fn issue_1093_allow_ext_sc() {
     ));
 }
 
+#[test]
+fn allow_ext_sc_when_sc_required() {
+    let res = App::new("clap-test")
+        .version("v1.4.8")
+        .setting(AppSettings::AllowExternalSubcommands)
+        .setting(AppSettings::SubcommandRequiredElseHelp)
+        .try_get_matches_from(vec!["clap-test", "external-cmd", "foo"]);
+
+    assert!(res.is_ok());
+
+    match res.unwrap().subcommand() {
+        (name, Some(args)) => {
+            assert_eq!(name, "external-cmd");
+            assert_eq!(args.values_of_lossy(""), Some(vec!["foo".to_string()]));
+        }
+        _ => unreachable!(),
+    }
+}
+
 #[test]
 fn external_subcommand_looks_like_built_in() {
     let res = App::new("cargo")
diff --git a/tests/fixtures/app.yml b/tests/fixtures/app.yml
index 87b2e7ac..416c2424 100644
--- a/tests/fixtures/app.yml
+++ b/tests/fixtures/app.yml
@@ -107,8 +107,12 @@ args:
         long: exclusive
         about: Tests 3 exclusive
         exclusive: true
-    
-    
+    - case_insensitive:
+        index: 4
+        help: Test case_insensitive
+        possible_values: [test123, test321]
+        case_insensitive: true
+
 arg_groups:
     - test:
         args:
diff --git a/tests/macros.rs b/tests/macros.rs
index 526723f5..32cad5de 100755
--- a/tests/macros.rs
+++ b/tests/macros.rs
@@ -330,3 +330,31 @@ fn literals() {
         false
     ));
 }
+
+#[test]
+fn multiarg() {
+    let app = clap_app!(claptests =>
+        (@arg flag: --flag "value")
+        (@arg multiarg: --multiarg
+            default_value("flag-unset") default_value_if("flag", None, "flag-set")
+            "multiarg")
+        (@arg multiarg2: --multiarg2
+            default_value("flag-unset") default_value_if("flag", None, "flag-set",)
+            "multiarg2")
+    );
+
+    let matches = app
+        .clone()
+        .try_get_matches_from(vec!["bin_name"])
+        .expect("match failed");
+
+    assert_eq!(matches.value_of("multiarg"), Some("flag-unset"));
+    assert_eq!(matches.value_of("multiarg2"), Some("flag-unset"));
+
+    let matches = app
+        .try_get_matches_from(vec!["bin_name", "--flag"])
+        .expect("match failed");
+
+    assert_eq!(matches.value_of("multiarg"), Some("flag-set"));
+    assert_eq!(matches.value_of("multiarg2"), Some("flag-set"));
+}