diff --git a/CHANGELOG.md b/CHANGELOG.md index b5d2b8ba76a..99e16d31461 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,21 @@ + +### 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)) + + + ## v2.26.0 (2017-07-29) diff --git a/Cargo.toml b/Cargo.toml index 40e798f16bf..fec6f56ba4f 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. "] 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 02ec05dc8f9..e9c1b0afa90 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 db34ceabd06..223e7ae5c64 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 c20ab0b6558..bc5f43e4389 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 33b98c014e3..6227c060cf9 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 73e74f11277..468dd5967e8 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; diff --git a/tests/help.rs b/tests/help.rs index f84580d0588..d92a56a5141 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: + + 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 31f395129f0..6dccb94b20b 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")