Repository: clap-rs/clap
Pull request #3699 (2022-05-06 14:18:16)

add support for dashed arg names and values in arg! macro
While migrating from clap2 to clap3 I ran into lack of support for dashed arg names and values in [clap::arg!](https://docs.rs/clap/3.1.16/clap/macro.arg.html) macro
meanwhile such definitions were supported but functions like [arg_from_usage](https://docs.rs/clap/2.33.4/clap/struct.App.html#method.arg_from_usage)

Here are some examples of such problematic argument usage descriptions
```
[num-readers] --num-readers=<n> 'Number of reader threads'
[num-loaders] --num-loaders=<n> 'Number of loader threads'
```
(another problem I found is that arg! macro does not allow [flag] before any of -f or --flag but that is a different story)

so my proposed just repeats what is already done for longarg
makes it possible to wrap arg name and arg value in double quotes so that it is interpreted as literal rather than an ident

thus arg!([some_arg] "some arg") becomes possible this way arg!(["some-arg"] "some arg")
and arg!(<some_val> "some val") becomes possible this way arg!(<"some-val"> "some val")

P.S some tests provided



Patch:
diff --git a/clap_complete/CHANGELOG.md b/clap_complete/CHANGELOG.md
index 3e423357..11fd7873 100644
--- a/clap_complete/CHANGELOG.md
+++ b/clap_complete/CHANGELOG.md
@@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
 <!-- next-header -->
 ## [Unreleased] - ReleaseDate
 
+## [3.1.4] - 2022-05-06
+
+### Fixes
+
+- *(bash)* Complete visible `PossibleValue`s, rather than hidden
+
 ## [3.1.3] - 2022-04-30
 
 ### Fixes
@@ -53,7 +59,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
 ## [3.0.1] - 2022-01-03
 
 <!-- next-url -->
-[Unreleased]: https://github.com/clap-rs/clap/compare/clap_complete-v3.1.3...HEAD
+[Unreleased]: https://github.com/clap-rs/clap/compare/clap_complete-v3.1.4...HEAD
+[3.1.4]: https://github.com/clap-rs/clap/compare/clap_complete-v3.1.3...clap_complete-v3.1.4
 [3.1.3]: https://github.com/clap-rs/clap/compare/clap_complete-v3.1.2...clap_complete-v3.1.3
 [3.1.2]: https://github.com/clap-rs/clap/compare/clap_complete-v3.1.1...clap_complete-v3.1.2
 [3.1.1]: https://github.com/clap-rs/clap/compare/clap_complete-v3.1.0...clap_complete-v3.1.1
diff --git a/clap_complete/Cargo.toml b/clap_complete/Cargo.toml
index 67b8b8e2..5f47a97c 100644
--- a/clap_complete/Cargo.toml
+++ b/clap_complete/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "clap_complete"
-version = "3.1.3"
+version = "3.1.4"
 edition = "2018"
 include = [
   "build.rs",
diff --git a/clap_complete/README.md b/clap_complete/README.md
index d57fd4c0..e962aba2 100644
--- a/clap_complete/README.md
+++ b/clap_complete/README.md
@@ -5,16 +5,16 @@
 
 [![Crates.io](https://img.shields.io/crates/v/clap_complete?style=flat-square)](https://crates.io/crates/clap_complete)
 [![Crates.io](https://img.shields.io/crates/d/clap_complete?style=flat-square)](https://crates.io/crates/clap_complete)
-[![License](https://img.shields.io/badge/license-Apache%202.0-blue?style=flat-square)](https://github.com/clap-rs/clap/blob/clap_complete-v3.1.3/LICENSE-APACHE)
-[![License](https://img.shields.io/badge/license-MIT-blue?style=flat-square)](https://github.com/clap-rs/clap/blob/clap_complete-v3.1.3/LICENSE-MIT)
+[![License](https://img.shields.io/badge/license-Apache%202.0-blue?style=flat-square)](https://github.com/clap-rs/clap/blob/clap_complete-v3.1.4/LICENSE-APACHE)
+[![License](https://img.shields.io/badge/license-MIT-blue?style=flat-square)](https://github.com/clap-rs/clap/blob/clap_complete-v3.1.4/LICENSE-MIT)
 
 Dual-licensed under [Apache 2.0](LICENSE-APACHE) or [MIT](LICENSE-MIT).
 
 1. [About](#about)
 2. [API Reference](https://docs.rs/clap_complete)
 3. [Questions & Discussions](https://github.com/clap-rs/clap/discussions)
-4. [CONTRIBUTING](https://github.com/clap-rs/clap/blob/clap_complete-v3.1.3/clap_complete/CONTRIBUTING.md)
-5. [Sponsors](https://github.com/clap-rs/clap/blob/clap_complete-v3.1.3/README.md#sponsors)
+4. [CONTRIBUTING](https://github.com/clap-rs/clap/blob/clap_complete-v3.1.4/clap_complete/CONTRIBUTING.md)
+5. [Sponsors](https://github.com/clap-rs/clap/blob/clap_complete-v3.1.4/README.md#sponsors)
 
 ## About
 
diff --git a/clap_complete/src/shells/bash.rs b/clap_complete/src/shells/bash.rs
index 08bf1190..a95047df 100644
--- a/clap_complete/src/shells/bash.rs
+++ b/clap_complete/src/shells/bash.rs
@@ -178,7 +178,7 @@ fn vals_for(o: &Arg) -> String {
         format!(
             "$(compgen -W \"{}\" -- \"${{cur}}\")",
             vals.iter()
-                .filter(|pv| pv.is_hide_set())
+                .filter(|pv| !pv.is_hide_set())
                 .map(PossibleValue::get_name)
                 .collect::<Vec<_>>()
                 .join(" ")
diff --git a/src/macros.rs b/src/macros.rs
index f43573b4..13921b14 100644
--- a/src/macros.rs
+++ b/src/macros.rs
@@ -512,6 +512,32 @@ macro_rules! arg_impl {
             $($tail)*
         }
     };
+    (
+        @arg
+        ($arg:expr)
+        <$value_name:literal>
+        $($tail:tt)*
+    ) => {
+        $crate::arg_impl! {
+            @arg
+            ({
+                debug_assert!(!$arg.is_multiple_occurrences_set(), "Values should precede `...`");
+                debug_assert_eq!($arg.get_value_names(), None, "Multiple values not yet supported");
+
+                let mut arg = $arg;
+
+                arg = arg.required(true);
+                arg = arg.takes_value(true);
+
+                let value_name = $crate::arg_impl! { @string $value_name };
+                if arg.get_id().is_empty() {
+                    arg = arg.id(value_name);
+                }
+                arg.value_name(value_name)
+            })
+            $($tail)*
+        }
+    };
     (
         @arg
         ($arg:expr)
@@ -542,6 +568,36 @@ macro_rules! arg_impl {
             $($tail)*
         }
     };
+    (
+        @arg
+        ($arg:expr)
+        [$value_name:literal]
+        $($tail:tt)*
+    ) => {
+        $crate::arg_impl! {
+            @arg
+            ({
+                debug_assert!(!$arg.is_multiple_occurrences_set(), "Values should precede `...`");
+                debug_assert_eq!($arg.get_value_names(), None, "Multiple values not yet supported");
+
+                let mut arg = $arg;
+
+                if arg.get_long().is_none() && arg.get_short().is_none() {
+                    arg = arg.required(false);
+                } else {
+                    arg = arg.min_values(0).max_values(1);
+                }
+                arg = arg.takes_value(true);
+
+                let value_name = $crate::arg_impl! { @string $value_name };
+                if arg.get_id().is_empty() {
+                    arg = arg.id(value_name);
+                }
+                arg.value_name(value_name)
+            })
+            $($tail)*
+        }
+    };
     (
         @arg
         ($arg:expr)


Test patch:
diff --git a/clap_complete/tests/snapshots/sub_subcommands.bash b/clap_complete/tests/snapshots/sub_subcommands.bash
index c39eb589..85729508 100644
--- a/clap_complete/tests/snapshots/sub_subcommands.bash
+++ b/clap_complete/tests/snapshots/sub_subcommands.bash
@@ -94,7 +94,7 @@ _my-app() {
             fi
             case "${prev}" in
                 --config)
-                    COMPREPLY=($(compgen -W "" -- "${cur}"))
+                    COMPREPLY=($(compgen -W "Lest quotes aren't escaped." -- "${cur}"))
                     return 0
                     ;;
                 *)
diff --git a/clap_complete/tests/snapshots/value_hint.bash b/clap_complete/tests/snapshots/value_hint.bash
index 4138007a..39e8208b 100644
--- a/clap_complete/tests/snapshots/value_hint.bash
+++ b/clap_complete/tests/snapshots/value_hint.bash
@@ -26,7 +26,7 @@ _my-app() {
             fi
             case "${prev}" in
                 --choice)
-                    COMPREPLY=($(compgen -W "" -- "${cur}"))
+                    COMPREPLY=($(compgen -W "bash fish zsh" -- "${cur}"))
                     return 0
                     ;;
                 --unknown)
diff --git a/tests/macros.rs b/tests/macros.rs
index 075724b9..caa7b270 100644
--- a/tests/macros.rs
+++ b/tests/macros.rs
@@ -469,4 +469,60 @@ mod arg_impl {
         let actual = clap::arg_impl! { @char 'o' };
         assert_eq!(actual, expected);
     }
+
+    #[test]
+    // allow double quoted dashed arg name in square brackets (e.g ["some-arg"])
+    fn arg_name_dashed() {
+        let arg = clap::arg!(["some-arg"] "some arg");
+        assert_eq!(arg, clap::Arg::new("some-arg").help("some arg"));
+
+        let m = clap::Command::new("flag")
+            .arg(arg)
+            .try_get_matches_from(vec!["", "some-val"])
+            .unwrap();
+        assert!(m.is_present("some-arg"));
+        assert_eq!(m.value_of("some-arg").unwrap(), "some-val");
+    }
+
+    #[test]
+    // allow double quoted dashed arg value in triangle brackets (e.g <"some-val">)
+    // test in combination with short argument name (e.g. -v)
+    fn arg_value_dashed_with_short_arg() {
+        let arg = clap::arg!(-a <"some-val"> "some arg");
+        assert_eq!(
+            arg,
+            clap::Arg::new("some-val")
+                .short('a')
+                .long("arg")
+                .value_name("some-val")
+        );
+
+        let m = clap::Command::new("cmd")
+            .arg(arg)
+            .try_get_matches_from(vec!["", "-a", "val"])
+            .unwrap();
+        assert!(m.is_present("some-val"));
+        assert_eq!(m.value_of("some-val").unwrap(), "val");
+    }
+
+    #[test]
+    // allow double quoted dashed arg value in triangle brackets (e.g <"some-val">)
+    // test in combination with long argument name (e.g. --value)
+    fn arg_value_dashed_with_long_arg() {
+        let arg = clap::arg!(-a --arg <"some-val"> "some arg");
+        assert_eq!(
+            arg,
+            clap::Arg::new("arg")
+                .short('a')
+                .long("arg")
+                .value_name("some-val")
+        );
+
+        let m = clap::Command::new("cmd")
+            .arg(arg)
+            .try_get_matches_from(vec!["", "--arg", "some-val"])
+            .unwrap();
+        assert!(m.is_present("arg"));
+        assert_eq!(m.value_of("arg").unwrap(), "some-val");
+    }
 }