diff --git a/CHANGELOG.md b/CHANGELOG.md index 2d7d4395..5f224f32 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,36 @@ + +## v2.6.0 (2016-06-14) + + +#### Improvements + +* removes extra newline from help output ([86e61d19](https://github.com/kbknapp/clap-rs/commit/86e61d19a748fb9870fcf1175308984e51ca1115)) +* allows printing version to any io::Write object ([921f5f79](https://github.com/kbknapp/clap-rs/commit/921f5f7916597f1d028cd4a65bfe76a01c801724)) +* removes extra newline when printing version ([7e2e2cbb](https://github.com/kbknapp/clap-rs/commit/7e2e2cbb4a8a0f050bb8072a376f742fc54b8589)) +* **Aliases:** improves readability of asliases in help messages ([ca511de7](https://github.com/kbknapp/clap-rs/commit/ca511de71f5b8c2ac419f1b188658e8c63b67846), closes [#526](https://github.com/kbknapp/clap-rs/issues/526), [#529](https://github.com/kbknapp/clap-rs/issues/529)) +* **Usage Strings:** improves the default usage string when only a single positional arg is present ([ec86f2da](https://github.com/kbknapp/clap-rs/commit/ec86f2dada1545a63fc72355e22fcdc4c466c215), closes [#518](https://github.com/kbknapp/clap-rs/issues/518)) + +#### Features + +* **Help:** allows wrapping at specified term width (Even on Windows!) ([1761dc0d](https://github.com/kbknapp/clap-rs/commit/1761dc0d27d0d621229d792be40c36fbf65c3014), closes [#451](https://github.com/kbknapp/clap-rs/issues/451)) +* **Settings:** + * adds new setting to stop delimiting values with -- or TrailingVarArg ([fc3e0f5a](https://github.com/kbknapp/clap-rs/commit/fc3e0f5afda6d24cdb3c4676614beebe13e1e870), closes [#511](https://github.com/kbknapp/clap-rs/issues/511)) + * one can now set an AppSetting which is propogated down through child subcommands ([e2341835](https://github.com/kbknapp/clap-rs/commit/e23418351a3b98bf08dfd7744bc14377c70d59ee), closes [#519](https://github.com/kbknapp/clap-rs/issues/519)) +* **Subcommands:** adds support for visible aliases ([7b10e7f8](https://github.com/kbknapp/clap-rs/commit/7b10e7f8937a07fdb8d16a6d8df79ce78d080cd3), closes [#522](https://github.com/kbknapp/clap-rs/issues/522)) + +#### Bug Fixes + +* fixes bug where args are printed out of order with templates ([05abb534](https://github.com/kbknapp/clap-rs/commit/05abb534864764102031a0d402e64ac65867aa87)) +* fixes bug where one can't override version or help flags ([90d7d6a2](https://github.com/kbknapp/clap-rs/commit/90d7d6a2ea8240122dd9bf8d82d3c4f5ebb5c703), closes [#514](https://github.com/kbknapp/clap-rs/issues/514)) +* fixes issue where before_help wasn't printed ([b3faff60](https://github.com/kbknapp/clap-rs/commit/b3faff6030f76a23f26afcfa6a90169002ed7106)) +* **Help:** `App::before_help` and `App::after_help` now correctly wrap ([1f4da767](https://github.com/kbknapp/clap-rs/commit/1f4da7676e6e71aa8dda799f3eeefad105a47819), closes [#516](https://github.com/kbknapp/clap-rs/issues/516)) +* **Settings:** fixes bug where new color settings couldn't be converted from strs ([706a7c11](https://github.com/kbknapp/clap-rs/commit/706a7c11b0900be594de6d5a3121938eff197602)) +* **Subcommands:** subcommands with aliases now display help of the aliased subcommand ([5354d14b](https://github.com/kbknapp/clap-rs/commit/5354d14b51f189885ba110e01e6b76cca3752992), closes [#521](https://github.com/kbknapp/clap-rs/issues/521)) +* **Windows:** fixes a failing windows build ([01e7dfd6](https://github.com/kbknapp/clap-rs/commit/01e7dfd6c07228c0be6695b3c7bf9370d82860d4)) +* **YAML:** adds missing YAML methods for App and Arg ([e468faf3](https://github.com/kbknapp/clap-rs/commit/e468faf3f05950fd9f72d84b69aa2061e91c6c64), closes [#528](https://github.com/kbknapp/clap-rs/issues/528)) + + + ### v2.5.2 (2016-05-31) diff --git a/README.md b/README.md index 4d5e130d..2aeb3288 100644 --- a/README.md +++ b/README.md @@ -38,6 +38,24 @@ Created by [gh-md-toc](https://github.com/ekalinin/github-markdown-toc) ## What's New +Here's the highlights for v2.6.0 + +* **Global Settings:** One can now set an `AppSetting` which is propogated down through child subcommands +* **Terminal Wrapping:** Allows wrapping at specified term width (Even on Windows!) (can now set an absolute width to "smart" wrap at) +* **SubCommands/Aliases:** adds support for visible aliases for subcommands (i.e. aliases that are dipslayed in the help message) +* **Subcommands/Aliases:** when viewing the help of an alias, it now display help of the aliased subcommand +* Improves the default usage string when only a single positional arg is present +* Adds new setting to stop delimiting values with `--` or `AppSettings::TrailingVarArg` +* `App::before_help` and `App::after_help` now correctly wrap +* Fixes bug where positional args are printed out of order when using templates +* Fixes bug where one can't override the auto-generated version or help flags +* Fixes issue where `App::before_help` wasn't printed +* Fixes a failing windows build +* Fixes bug where new color settings couldn't be converted from strings +* Adds missing YAML methods for App and Arg +* Allows printing version to any io::Write object +* Removes extra newline from help and version output + Here's what's new in v.2.5.2 * Removes trailing newlines from help and version output @@ -86,8 +104,6 @@ An example of the optional colored help: Here's the highlights from v2.2.0 -#### Features - * **Help text auto wraps and aligns at term width!** - Long help strings will now properly wrap and align to term width on Linux and OS X (and presumably Unix too). This can be turned off as well. * **Can customize the order of opts, flags, and subcommands in help messages** - Instead of using the default alphabetical order, you can now re-arrange the order of your args and subcommands in help message. This helps to emphasize more popular or important options. * **Can auto-derive the order from declaration order** - Have a bunch of args or subcommmands to re-order? You can now just derive the order from the declaration order! @@ -101,13 +117,8 @@ An example of the help text wrapping at term width: In v2.1.2 -#### New Features - * **Default Values**: Args can now specify default values * **Next Line Help**: Args can have help strings on the line following the argument (useful for long arguments, or those with many values). This can be set command-wide or for individual args - -#### Improvements - * **Documentation Examples**: The examples in the documentation have been vastly improved For full details, see [CHANGELOG.md](https://github.com/kbknapp/clap-rs/blob/master/CHANGELOG.md) diff --git a/src/app/help.rs b/src/app/help.rs index 0831f225..b77e4753 100644 --- a/src/app/help.rs +++ b/src/app/help.rs @@ -80,7 +80,7 @@ pub struct Help<'a> { writer: &'a mut Write, next_line_help: bool, hide_pv: bool, - term_w: Option, + term_w: usize, color: bool, cizer: Colorizer, } @@ -88,13 +88,16 @@ pub struct Help<'a> { // Public Functions impl<'a> Help<'a> { /// Create a new `Help` instance. - pub fn new(w: &'a mut Write, next_line_help: bool, hide_pv: bool, color: bool, cizer: Colorizer) -> Self { + pub fn new(w: &'a mut Write, next_line_help: bool, hide_pv: bool, color: bool, cizer: Colorizer, term_w: Option) -> Self { debugln!("fn=Help::new;"); Help { writer: w, next_line_help: next_line_help, hide_pv: hide_pv, - term_w: term::dimensions().map(|(w, _)| w), + term_w: match term_w { + Some(width) => width, + None => term::dimensions().map(|(w, _)| w).unwrap_or(120), + }, color: color, cizer: cizer, } @@ -132,7 +135,7 @@ impl<'a> Help<'a> { use_stderr: stderr, when: parser.color(), }; - Self::new(w, nlh, hide_v, color, cizer).write_help(parser) + Self::new(w, nlh, hide_v, color, cizer, parser.meta.term_w).write_help(parser) } /// Writes the parser help to the wrapped stream. @@ -324,6 +327,89 @@ impl<'a> Help<'a> { Ok(()) } + fn write_before_after_help<'b, 'c>(&mut self, h: &str) -> io::Result<()> { + debugln!("fn=before_help;"); + let mut help = String::new(); + // determine if our help fits or needs to wrap + let width = self.term_w; + debugln!("Term width...{}", width); + let too_long = str_width(h) >= width; + debugln!("Too long...{:?}", too_long); + + debug!("Too long..."); + if too_long { + sdebugln!("Yes"); + help.push_str(h); + debugln!("help: {}", help); + debugln!("help width: {}", str_width(&*help)); + // Determine how many newlines we need to insert + debugln!("Usable space: {}", width); + let longest_w = { + let mut lw = 0; + for l in help.split(' ').map(|s| str_width(s)) { + if l > lw { + lw = l; + } + } + lw + }; + debugln!("Longest word...{}", longest_w); + debug!("Enough space to wrap..."); + if longest_w < width { + sdebugln!("Yes"); + let mut indices = vec![]; + let mut idx = 0; + loop { + idx += width - 1; + if idx >= help.len() { + break; + } + // 'a' arbitrary non space char + if help.chars().nth(idx).unwrap_or('a') != ' ' { + idx = find_idx_of_space(&*help, idx); + } + debugln!("Adding idx: {}", idx); + debugln!("At {}: {:?}", idx, help.chars().nth(idx)); + indices.push(idx); + if str_width(&help[idx..]) <= width { + break; + } + } + for (i, idx) in indices.iter().enumerate() { + debugln!("iter;i={},idx={}", i, idx); + let j = idx + (2 * i); + debugln!("removing: {}", j); + debugln!("at {}: {:?}", j, help.chars().nth(j)); + help.remove(j); + help.insert(j, '{'); + help.insert(j + 1, 'n'); + help.insert(j + 2, '}'); + } + } else { + sdebugln!("No"); + } + } else { + sdebugln!("No"); + } + let help = if !help.is_empty() { + &*help + } else { + help.push_str(h); + &*help + }; + if help.contains("{n}") { + if let Some(part) = help.split("{n}").next() { + try!(write!(self.writer, "{}", part)); + } + for part in help.split("{n}").skip(1) { + try!(write!(self.writer, "\n{}", part)); + } + } else { + try!(write!(self.writer, "{}", help)); + } + Ok(()) + } + /// Writes argument's help to the wrapped stream. fn help<'b, 'c>(&mut self, arg: &ArgWithDisplay<'b, 'c>, longest: usize) -> io::Result<()> { debugln!("fn=help;"); @@ -336,10 +422,9 @@ impl<'a> Help<'a> { longest + 12 }; // determine if our help fits or needs to wrap - let width = self.term_w.unwrap_or(0); + let width = self.term_w; debugln!("Term width...{}", width); - let too_long = self.term_w.is_some() && - (spcs + str_width(h) + str_width(&*spec_vals) >= width); + let too_long = spcs + str_width(h) + str_width(&*spec_vals) >= width; debugln!("Too long...{:?}", too_long); // Is help on next line, if so newline + 2x tab @@ -603,7 +688,7 @@ impl<'a> Help<'a> { pub fn write_default_help(&mut self, parser: &Parser) -> ClapResult<()> { debugln!("fn=write_default_help;"); if let Some(h) = parser.meta.pre_help { - try!(write!(self.writer, "{}", h)); + try!(self.write_before_after_help(h)); try!(self.writer.write(b"\n\n")); } @@ -638,7 +723,7 @@ impl<'a> Help<'a> { if flags || opts || pos || subcmds { try!(self.writer.write(b"\n\n")); } - try!(write!(self.writer, "{}", h)); + try!(self.write_before_after_help(h)); } self.writer.flush().map_err(Error::from) diff --git a/src/app/meta.rs b/src/app/meta.rs index 4fb63290..b22c09af 100644 --- a/src/app/meta.rs +++ b/src/app/meta.rs @@ -13,6 +13,7 @@ pub struct AppMeta<'b> { pub usage: Option, pub help_str: Option<&'b str>, pub disp_ord: usize, + pub term_w: Option, pub template: Option<&'b str>, } @@ -32,6 +33,7 @@ impl<'b> Default for AppMeta<'b> { disp_ord: 999, template: None, aliases: None, + term_w: None, } } } @@ -61,6 +63,7 @@ impl<'b> Clone for AppMeta<'b> { disp_ord: self.disp_ord, template: self.template, aliases: self.aliases.clone(), + term_w: self.term_w, } } } diff --git a/src/app/mod.rs b/src/app/mod.rs index 03675b4f..f8006adf 100644 --- a/src/app/mod.rs +++ b/src/app/mod.rs @@ -482,6 +482,35 @@ impl<'a, 'b> App<'a, 'b> { self } + /// Sets the terminal width at which to wrap help messages. Defaults to `120`. + /// + /// `clap` automatically tries to determine the terminal width on Unix, Linux, and OSX if the + /// `wrap_help` cargo "feature" has been used while compiling. If the terminal width cannot be + /// determined, `clap` defaults to `120`. + /// + /// **NOTE:** This setting applies globally and *not* on a per-command basis. + /// + /// **NOTE:** This setting must be set **before** any subcommands are added! + /// + /// # Platform Specific + /// + /// Only Unix, Linux, and OSX support automatic determination of terminal width. Even on those + /// platforms, this setting is useful if for any reason the terminal width cannot be + /// determined. + /// + /// # Examples + /// + /// ```no_run + /// # use clap::App; + /// App::new("myprog") + /// .set_term_width(80) + /// # ; + /// ``` + pub fn set_term_width(mut self, width: usize) -> Self { + self.p.meta.term_w = Some(width); + self + } + /// Adds an [argument] to the list of valid possibilties. /// /// # Examples @@ -640,7 +669,7 @@ impl<'a, 'b> App<'a, 'b> { self } - /// Allows adding a [`SubCommand`] alias that functions exactly like those defined with + /// Allows adding a [`SubCommand`] alias that functions exactly like those defined with /// [`App::alias`], except that they are visible inside the help message. /// /// # Examples @@ -664,7 +693,7 @@ impl<'a, 'b> App<'a, 'b> { self } - /// Allows adding multiple [`SubCommand`] aliases that functions exactly like those defined + /// Allows adding multiple [`SubCommand`] aliases that functions exactly like those defined /// with [`App::aliases`], except that they are visible inside the help message. /// /// # Examples diff --git a/src/app/parser.rs b/src/app/parser.rs index bedb90dd..e194b154 100644 --- a/src/app/parser.rs +++ b/src/app/parser.rs @@ -195,6 +195,8 @@ impl<'a, 'b> Parser<'a, 'b> pub fn add_subcommand(&mut self, mut subcmd: App<'a, 'b>) { debugln!("fn=Parser::add_subcommand;"); + debugln!("Term widnth...{:?}", self.p.meta.term_w); + subcmd.p.meta.term_w = self.meta.term_w; debug!("Is help..."); if subcmd.p.meta.name == "help" { sdebugln!("Yes"); @@ -532,14 +534,14 @@ impl<'a, 'b> Parser<'a, 'b> } } else if let Some(c) = sc.subcommands .iter() - .filter(|s| + .filter(|s| if let Some(ref als) = s.p .meta - .aliases { + .aliases { als.iter() .any(|&(a, _)| &a == &&*cmd.to_string_lossy()) - } else { - false + } else { + false } ) .next()