Repository: kbknapp/clap-rs
Pull request #1045 (2017-09-13 19:28:19)

Issues 1043,1044


<!-- Reviewable:start -->
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/kbknapp/clap-rs/1045)
<!-- Reviewable:end -->



Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index b5d2b8ba..99e16d31 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,21 @@
+<a name="v2.26.1"></a>
+### v2.26.1 (2017-09-14)
+
+
+#### Bug Fixes
+
+*   fixes using require_equals(true) and min_values(0) together ([10ae208f](https://github.com/kbknapp/clap-rs/commit/10ae208f68518eff6e98166724065745f4083174), closes [#1044](https://github.com/kbknapp/clap-rs/issues/1044))
+*   escape special characters in zsh and fish completions ([87e019fc](https://github.com/kbknapp/clap-rs/commit/87e019fc84ba6193a8c4ddc26c61eb99efffcd25))
+*   avoid panic generating default help msg if term width set to 0 due to bug in textwrap 0.7.0 ([b3eadb0d](https://github.com/kbknapp/clap-rs/commit/b3eadb0de516106db4e08f078ad32e8f6d6e7a57))
+*   Change `who's` -> `whose` ([53c1ffe8](https://github.com/kbknapp/clap-rs/commit/53c1ffe87f38b05d8804a0f7832412a952845349))
+*   adds a debug assertion to ensure all args added to groups actually exist ([7ad123e2](https://github.com/kbknapp/clap-rs/commit/7ad123e2c02577e3ca30f7e205181e896b157d11), closes [#917](https://github.com/kbknapp/clap-rs/issues/917))
+*   fixes a bug where args that allow values to start with a hyphen couldnt contain a double hyphen -- as a value ([ab2f4c9e](https://github.com/kbknapp/clap-rs/commit/ab2f4c9e563e36ec739a4b55d5a5b76fdb9e9fa4), closes [#960](https://github.com/kbknapp/clap-rs/issues/960))
+*   fixes a bug where positional argument help text is misaligned ([54c16836](https://github.com/kbknapp/clap-rs/commit/54c16836dea4651806a2cfad53146a83fa3abf21))
+* **Help Message:**  fixes long_about not being usable ([a8257ea0](https://github.com/kbknapp/clap-rs/commit/a8257ea0ffb812e552aca256c4a3d2aebfd8065b), closes [#1043](https://github.com/kbknapp/clap-rs/issues/1043))
+* **Suggestions:**  output for flag after subcommand ([434ea5ba](https://github.com/kbknapp/clap-rs/commit/434ea5ba71395d8c1afcf88e69f0b0d8339b01a1))
+
+
+
 <a name="v2.26.0"></a>
 ## v2.26.0 (2017-07-29)
 
diff --git a/Cargo.toml b/Cargo.toml
index 40e798f1..fec6f56b 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,7 +1,7 @@
 [package]
 
 name = "clap"
-version = "2.26.0"
+version = "2.26.1"
 authors = ["Kevin K. <kbknapp@gmail.com>"]
 exclude = ["examples/*", "clap-test/*", "tests/*", "benches/*", "*.png", "clap-perf/*", "*.dot"]
 repository = "https://github.com/kbknapp/clap-rs.git"
@@ -23,7 +23,6 @@ appveyor = { repository = "kbknapp/clap-rs" }
 bitflags              = "0.9"
 vec_map               = "0.8"
 unicode-width         = "0.1.4"
-unicode-segmentation  = "1.2.0"
 textwrap              = "0.8.0"
 strsim    = { version = "0.6.0",  optional = true }
 ansi_term = { version = "0.9.0",  optional = true }
diff --git a/README.md b/README.md
index 02ec05dc..e9c1b0af 100644
--- a/README.md
+++ b/README.md
@@ -45,15 +45,21 @@ Created by [gh-md-toc](https://github.com/ekalinin/github-markdown-toc)
 
 ## What's New
 
-Here's the highlights for v2.26.0
+Here's what's new in 2.26.1:
 
-*   **The minimum required version of Rust is now 1.13.0 (Stable)**
-*   bumps unicode-segmentation to v1.2 
-*   update textwrap to version 0.7.0 which increases the performance of writing help strings
+*   fixes using require_equals(true) and min_values(0) together 
+*   escape special characters in zsh and fish completions 
+*   avoid panic generating default help msg if term width set to 0 due to bug in textwrap 0.7.0 
+*   Change `who's` -> `whose` in documentation
+* **Help Message:**  fixes `App::long_about` not being displayed
+* **Suggestions:**  output for flag after subcommand 
 
 
-Here's the highlights for v2.21.0 to v2.25.1
+Here's the highlights for v2.21.0 to v2.26.0
 
+*   **The minimum required version of Rust is now 1.13.0 (Stable)**
+*   bumps unicode-segmentation to v1.2 
+*   update textwrap to version 0.7.0 which increases the performance of writing help strings
 * impl Default for Values + OsValues for any lifetime. 
 * use textwrap crate for wrapping help texts
 * suggests to use flag after subcommand when applicable
diff --git a/src/app/help.rs b/src/app/help.rs
index db34ceab..223e7ae5 100644
--- a/src/app/help.rs
+++ b/src/app/help.rs
@@ -657,8 +657,22 @@ impl<'a> Help<'a> {
         if let Some(author) = parser.meta.author {
             write_thing!(author)
         }
-        if let Some(about) = parser.meta.about {
-            write_thing!(about)
+        if self.use_long {
+            if let Some(about) = parser.meta.long_about {
+                debugln!("Help::write_default_help: writing long about");
+                write_thing!(about)
+            } else if let Some(about) = parser.meta.about {
+                debugln!("Help::write_default_help: writing about");
+                write_thing!(about)
+            }
+        } else {
+            if let Some(about) = parser.meta.about {
+                debugln!("Help::write_default_help: writing about");
+                write_thing!(about)
+            } else if let Some(about) = parser.meta.long_about {
+                debugln!("Help::write_default_help: writing long about");
+                write_thing!(about)
+            }
         }
 
         color!(self, "\nUSAGE:", warning)?;
@@ -861,6 +875,11 @@ impl<'a> Help<'a> {
                                 "{}",
                                 parser.meta.about.unwrap_or("unknown about"))?;
                 }
+                b"long-about" => {
+                    write!(self.writer,
+                                "{}",
+                                parser.meta.long_about.unwrap_or("unknown about"))?;
+                }
                 b"usage" => {
                     write!(self.writer, "{}", usage::create_usage_no_title(parser, &[]))?;
                 }
diff --git a/src/app/parser.rs b/src/app/parser.rs
index c20ab0b6..bc5f43e4 100644
--- a/src/app/parser.rs
+++ b/src/app/parser.rs
@@ -1339,7 +1339,8 @@ impl<'a, 'b> Parser<'a, 'b>
 
     #[cfg_attr(feature = "cargo-clippy", allow(let_and_return))]
     fn use_long_help(&self) -> bool {
-        let ul = self.flags.iter().any(|f| f.b.long_help.is_some()) ||
+        let ul = self.meta.long_about.is_some() ||
+                 self.flags.iter().any(|f| f.b.long_help.is_some()) ||
                  self.opts.iter().any(|o| o.b.long_help.is_some()) ||
                  self.positionals.values().any(|p| p.b.long_help.is_some()) ||
                  self.subcommands
@@ -1529,13 +1530,17 @@ impl<'a, 'b> Parser<'a, 'b>
         debugln!("Parser::parse_opt; opt={}, val={:?}", opt.b.name, val);
         debugln!("Parser::parse_opt; opt.settings={:?}", opt.b.settings);
         let mut has_eq = false;
+        let no_val = val.is_none();
+        let empty_vals = opt.is_set(ArgSettings::EmptyValues);
+        let min_vals_zero = opt.v.min_vals.unwrap_or(1) == 0;
+        let needs_eq = opt.is_set(ArgSettings::RequireEquals);
 
         debug!("Parser::parse_opt; Checking for val...");
         if let Some(fv) = val {
             has_eq = fv.starts_with(&[b'=']) || had_eq;
             let v = fv.trim_left_matches(b'=');
-            if !opt.is_set(ArgSettings::EmptyValues) &&
-               (v.len_() == 0 || (opt.is_set(ArgSettings::RequireEquals) && !has_eq)) {
+            if !empty_vals &&
+               (v.len_() == 0 || (needs_eq && !has_eq)) {
                 sdebugln!("Found Empty - Error");
                 return Err(Error::empty_value(opt,
                                               &*usage::create_error_usage(self, matcher, None),
@@ -1546,7 +1551,7 @@ impl<'a, 'b> Parser<'a, 'b>
                      fv,
                      fv.starts_with(&[b'=']));
             self.add_val_to_arg(opt, v, matcher)?;
-        } else if opt.is_set(ArgSettings::RequireEquals) && !opt.is_set(ArgSettings::EmptyValues) {
+        } else if needs_eq && !(empty_vals || min_vals_zero) {
             sdebugln!("None, but requires equals...Error");
             return Err(Error::empty_value(opt,
                                           &*usage::create_error_usage(self, matcher, None),
@@ -1561,10 +1566,12 @@ impl<'a, 'b> Parser<'a, 'b>
         self.groups_for_arg(opt.b.name)
             .and_then(|vec| Some(matcher.inc_occurrences_of(&*vec)));
 
-        if val.is_none() ||
-           !has_eq &&
-           (opt.is_set(ArgSettings::Multiple) && !opt.is_set(ArgSettings::RequireDelimiter) &&
-            matcher.needs_more_vals(opt)) {
+        let needs_delim = opt.is_set(ArgSettings::RequireDelimiter);
+        let mult = opt.is_set(ArgSettings::Multiple);
+        if no_val && min_vals_zero && !has_eq && needs_eq {
+            debugln!("Parser::parse_opt: More arg vals not required...");
+            return Ok(ParseResult::ValuesDone);
+        } else if no_val || (mult && !needs_delim) && !has_eq && matcher.needs_more_vals(opt) { 
             debugln!("Parser::parse_opt: More arg vals required...");
             return Ok(ParseResult::Opt(opt.b.name));
         }
diff --git a/src/app/validator.rs b/src/app/validator.rs
index 33b98c01..6227c060 100644
--- a/src/app/validator.rs
+++ b/src/app/validator.rs
@@ -294,9 +294,9 @@ impl<'a, 'b, 'z> Validator<'a, 'b, 'z> {
                                                   self.0.color()));
             }
         }
-        if let Some(num) = a.min_vals() {
+        let min_vals_zero = if let Some(num) = a.min_vals() {
             debugln!("Validator::validate_arg_num_vals: min_vals set: {}", num);
-            if (ma.vals.len() as u64) < num {
+            if (ma.vals.len() as u64) < num && num != 0 {
                 debugln!("Validator::validate_arg_num_vals: Sending error TooFewValues");
                 return Err(Error::too_few_values(a,
                                                  num,
@@ -306,9 +306,10 @@ impl<'a, 'b, 'z> Validator<'a, 'b, 'z> {
                                                                              None),
                                                  self.0.color()));
             }
-        }
+            num == 0
+        } else { false };
         // Issue 665 (https://github.com/kbknapp/clap-rs/issues/665)
-        if a.takes_value() && !a.is_set(ArgSettings::EmptyValues) && ma.vals.is_empty() {
+        if a.takes_value() && !(a.is_set(ArgSettings::EmptyValues) || min_vals_zero)  && ma.vals.is_empty() {
             return Err(Error::empty_value(a,
                                           &*usage::create_error_usage(self.0, matcher, None),
                                           self.0.color()));
diff --git a/src/lib.rs b/src/lib.rs
index 73e74f11..468dd596 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -542,7 +542,6 @@ extern crate vec_map;
 #[cfg(feature = "wrap_help")]
 extern crate term_size;
 extern crate textwrap;
-extern crate unicode_segmentation;
 #[cfg(feature = "color")]
 extern crate atty;
 


Test patch:
diff --git a/tests/help.rs b/tests/help.rs
index f84580d0..d92a56a5 100644
--- a/tests/help.rs
+++ b/tests/help.rs
@@ -440,6 +440,26 @@ FLAGS:
     -h, --help       Prints help information
     -V, --version    Prints version information";
 
+static LONG_ABOUT: &'static str = "myapp 1.0
+foo
+something really really long, with
+multiple lines of text
+that should be displayed
+
+USAGE:
+    myapp [arg1]
+
+FLAGS:
+    -h, --help       
+            Prints help information
+
+    -V, --version    
+            Prints version information
+
+
+ARGS:
+    <arg1>    
+            some option";
 #[test]
 fn help_short() {
     let m = App::new("test")
@@ -784,6 +804,17 @@ fn issue_702_multiple_values() {
     assert!(test::compare_output(app, "myapp --help", ISSUE_702, false));
 }
 
+#[test]
+fn long_about() {
+    let app = App::new("myapp")
+        .version("1.0")
+        .author("foo")
+        .about("bar")
+        .long_about("something really really long, with\nmultiple lines of text\nthat should be displayed")
+        .arg(Arg::with_name("arg1").help("some option"));
+    assert!(test::compare_output(app, "myapp --help", LONG_ABOUT, false));
+}
+
 #[test]
 fn issue_760() {
     let app = App::new("ctest")
diff --git a/tests/opts.rs b/tests/opts.rs
index 31f39512..6dccb94b 100644
--- a/tests/opts.rs
+++ b/tests/opts.rs
@@ -28,6 +28,24 @@ fn require_equals_fail() {
     assert_eq!(res.unwrap_err().kind, ErrorKind::EmptyValue);
 }
 
+#[test]
+fn require_equals_min_values_zero() {
+    let res = App::new("prog")
+        .arg(Arg::with_name("cfg")
+            .require_equals(true)
+            .takes_value(true)
+            .min_values(0)
+            .long("config"))
+        .arg(Arg::with_name("cmd"))
+        .get_matches_from_safe(vec![
+            "prog", "--config", "cmd"
+        ]);
+    assert!(res.is_ok());
+    let m = res.unwrap();
+    assert!(m.is_present("cfg"));
+    assert_eq!(m.value_of("cmd"), Some("cmd"));
+}
+
 #[test]
 fn double_hyphen_as_value() {
     let res = App::new("prog")