From 7dfdaf200ebb5c431351a045b48f5e0f0d3f31db Mon Sep 17 00:00:00 2001 From: Kevin K Date: Wed, 24 Aug 2016 19:32:10 -0400 Subject: [PATCH 1/7] feat(Help): adds new short hand way to use source formatting and ignore term width in help messages Prior to this commit if one wished to use source formatting and ignore term width they could do `App::set_term_width(usize::MAX)` now one can also use `App::set_term_width(0)` which does the same thing. Closes #625 --- src/app/help.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/app/help.rs b/src/app/help.rs index 9b9fe82a..66d85065 100644 --- a/src/app/help.rs +++ b/src/app/help.rs @@ -2,6 +2,7 @@ use std::io::{self, Cursor, Read, Write}; use std::collections::BTreeMap; use std::fmt::Display; use std::cmp; +use std::usize; use vec_map::VecMap; @@ -102,7 +103,11 @@ impl<'a> Help<'a> { next_line_help: next_line_help, hide_pv: hide_pv, term_w: match term_w { - Some(width) => width, + Some(width) => if width == 0 { + usize::MAX + } else { + width + }, None => term_size::dimensions().map_or(120, |(w, _)| w), }, color: color, From 7558261c884e3b893c0b2c0b7ac36621c415660d Mon Sep 17 00:00:00 2001 From: Kevin K Date: Wed, 24 Aug 2016 19:35:16 -0400 Subject: [PATCH 2/7] tests: adds tests for ignoring term width with set_term_width(0) --- tests/help.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/help.rs b/tests/help.rs index 85b8a3d7..97e4abd6 100644 --- a/tests/help.rs +++ b/tests/help.rs @@ -189,6 +189,14 @@ fn multi_level_sc_help() { test::check_err_output(app, "ctest help subcmd multi", MULTI_SC_HELP, false); } +#[test] +fn no_wrap_help() { + let app = App::new("ctest") + .set_term_width(0) + .help(MULTI_SC_HELP); + test::check_err_output(app, "ctest --help", MULTI_SC_HELP, false); +} + #[test] fn complex_subcommand_help_output() { let mut a = test::complex_app(); From 00b8205d22639d1b54b9c453c55c785aace52cb2 Mon Sep 17 00:00:00 2001 From: Kevin K Date: Wed, 24 Aug 2016 19:37:35 -0400 Subject: [PATCH 3/7] docs(Term Width): adds details about set_term_width(0) --- src/app/mod.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/app/mod.rs b/src/app/mod.rs index 4458d72a..f567130c 100644 --- a/src/app/mod.rs +++ b/src/app/mod.rs @@ -485,7 +485,7 @@ impl<'a, 'b> App<'a, 'b> { } /// Disables a single command, or [`SubCommand`], level setting. - /// + /// /// See [`AppSettings`] for a full list of possibilities and examples. /// /// # Examples @@ -522,10 +522,11 @@ impl<'a, 'b> App<'a, 'b> { for s in settings { self.p.unset(*s); } - self + self } - /// Sets the terminal width at which to wrap help messages. Defaults to `120`. + /// Sets the terminal width at which to wrap help messages. Defaults to `120`. Using `0` will + /// ignore terminal widths and use source formatting. /// /// `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 From 881a647ee946918187d4b03b702ed81db12dc9e4 Mon Sep 17 00:00:00 2001 From: Kevin K Date: Wed, 24 Aug 2016 21:50:20 -0400 Subject: [PATCH 4/7] tests: adds tests to prevent issue 626 --- tests/help.rs | 77 ++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 76 insertions(+), 1 deletion(-) diff --git a/tests/help.rs b/tests/help.rs index 97e4abd6..9077dc97 100644 --- a/tests/help.rs +++ b/tests/help.rs @@ -3,7 +3,7 @@ extern crate regex; include!("../clap-test.rs"); -use clap::{App, SubCommand, ErrorKind}; +use clap::{App, SubCommand, ErrorKind, Arg}; static HELP: &'static str = "clap-test v1.4.8 Kevin K. @@ -81,6 +81,45 @@ FLAGS: OPTIONS: -o, --option ... tests options"; +static ISSUE_626_CUTOFF: &'static str = "ctest 0.1 + +USAGE: + ctest [OPTIONS] + +FLAGS: + -h, --help Prints help information + -V, --version Prints version information + +OPTIONS: + -c, --cafe A coffeehouse, coffee shop, or café is an + establishment which primarily serves hot + coffee, related coffee beverages (e.g., café + latte, cappuccino, espresso), tea, and other + hot beverages. Some coffeehouses also serve cold + beverages such as iced coffee and iced tea. Many + cafés also serve some type of food, such as light + snacks, muffins, or pastries."; + +static ISSUE_626_PANIC: &'static str = "ctest 0.1 + +USAGE: + ctest [OPTIONS] + +FLAGS: + -h, --help Prints help information + -V, --version Prints version information + +OPTIONS: + -c, --cafe La culture du café est très + développée dans de + nombreux pays à climat chaud + d'Amérique, d'Afrique et + d'Asie, dans des plantations qui + sont cultivées pour les marchés + d'exportation. Le café est souvent + une contribution majeure aux + exportations des régions productrices."; + #[test] fn help_short() { let m = App::new("test") @@ -207,3 +246,39 @@ fn complex_subcommand_help_output() { sc.write_help(&mut help).ok().expect("failed to print help"); assert_eq!(&*String::from_utf8(help).unwrap(), SC_HELP); } + + +#[test] +fn issue_626_unicode_cutoff() { + let app = App::new("ctest") + .version("0.1") + .set_term_width(70) + .arg(Arg::with_name("cafe") + .short("c") + .long("cafe") + .value_name("FILE") + .help("A coffeehouse, coffee shop, or café is an establishment \ + which primarily serves hot coffee, related coffee beverages \ + (e.g., café latte, cappuccino, espresso), tea, and other hot \ + beverages. Some coffeehouses also serve cold beverages such as \ + iced coffee and iced tea. Many cafés also serve some type of \ + food, such as light snacks, muffins, or pastries.") + .takes_value(true)); + test::check_err_output(app, "ctest --help", ISSUE_626_CUTOFF, false); +} + +#[test] +fn issue_626_panic() { + let app = App::new("ctest") + .version("0.1") + .set_term_width(53) + .arg(Arg::with_name("cafe") + .short("c") + .long("cafe") + .value_name("FILE") + .help("La culture du café est très développée dans de nombreux pays à climat chaud d'Amérique, \ + d'Afrique et d'Asie, dans des plantations qui sont cultivées pour les marchés d'exportation. \ + Le café est souvent une contribution majeure aux exportations des régions productrices.") + .takes_value(true)); + test::check_err_output(app, "ctest --help", ISSUE_626_PANIC, false); +} From 763a5c920e23efc74d190af0cb8b5dd714b2d67a Mon Sep 17 00:00:00 2001 From: Kevin K Date: Wed, 24 Aug 2016 21:50:27 -0400 Subject: [PATCH 5/7] fix(Unicode): fixes two bugs where non-English characters were stripped or caused a panic with help wrapping Closes #626 --- Cargo.toml | 3 ++- src/app/help.rs | 37 ++++++++++++++++--------------------- src/lib.rs | 2 ++ 3 files changed, 20 insertions(+), 22 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 8381bcd3..43671d2f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,6 +20,7 @@ strsim = { version = "~0.5.1", optional = true } yaml-rust = { version = "~0.3.2", optional = true } clippy = { version = "~0.0.79", optional = true } unicode-width = { version = "~0.1.3", optional = true } +unicode-segmentation = { version = "~0.1.2", optional = true } term_size = { version = "~0.1.0", optional = true } [dev-dependencies] @@ -30,7 +31,7 @@ default = ["suggestions", "color", "wrap_help"] suggestions = ["strsim"] color = ["ansi_term", "libc"] yaml = ["yaml-rust"] -wrap_help = ["libc", "unicode-width", "term_size"] +wrap_help = ["libc", "unicode-width", "term_size", "unicode-segmentation"] lints = ["clippy", "nightly"] nightly = [] # for building with nightly and unstable features unstable = [] # for building with unstable features on stable Rust diff --git a/src/app/help.rs b/src/app/help.rs index 66d85065..f303cad5 100644 --- a/src/app/help.rs +++ b/src/app/help.rs @@ -5,6 +5,7 @@ use std::cmp; use std::usize; use vec_map::VecMap; +use unicode_segmentation::UnicodeSegmentation; use errors::{Error, Result as ClapResult}; @@ -463,33 +464,27 @@ impl<'a> Help<'a> { debug!("Enough space to wrap..."); if longest_w < avail_chars { sdebugln!("Yes"); - let mut indices = vec![]; - let mut idx = 0; - loop { - idx += avail_chars - 1; - if idx >= help.len() { - break; + let mut prev_space = 0; + let mut j = 0; + let mut i = 0; + for (idx, g) in (&*help.clone()).grapheme_indices(true) { + debugln!("iter;idx={},g={}", idx, g); + if g != " " { continue; } + if str_width(&help[j..idx]) < avail_chars { + debugln!("Still enough space..."); + prev_space = idx; + continue; } - // '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..]) <= avail_chars { - break; - } - } - for (i, idx) in indices.iter().enumerate() { - debugln!("iter;i={},idx={}", i, idx); - let j = idx + (2 * i); + debugln!("Adding Newline..."); + j = prev_space + (2 * i); + debugln!("i={},prev_space={},j={}", i, prev_space, j); debugln!("removing: {}", j); - debugln!("at {}: {:?}", j, help.chars().nth(j)); + debugln!("char at {}: {}", j, &help[j..j]); help.remove(j); help.insert(j, '{'); help.insert(j + 1, 'n'); help.insert(j + 2, '}'); + i += 1; } } else { sdebugln!("No"); diff --git a/src/lib.rs b/src/lib.rs index 5b64b6ed..20ad117c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -416,6 +416,8 @@ extern crate bitflags; extern crate vec_map; #[cfg(feature = "wrap_help")] extern crate term_size; +#[cfg(feature = "wrap_help")] +extern crate unicode_segmentation; #[cfg(feature = "yaml")] pub use yaml_rust::YamlLoader; From 84ad746ee556af0ae4e5055c4f55c77e2ed478b5 Mon Sep 17 00:00:00 2001 From: Kevin K Date: Wed, 24 Aug 2016 21:55:01 -0400 Subject: [PATCH 6/7] chore: increase version --- CHANGELOG.md | 17 +++++++++++++++++ Cargo.toml | 2 +- README.md | 6 ++++++ 3 files changed, 24 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 463bba4b..7e2c98d6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,20 @@ + +### v2.10.3 (2016-08-25) + +#### Features + +* **Help:** adds new short hand way to use source formatting and ignore term width in help messages ([7dfdaf20](https://github.com/kbknapp/clap-rs/commit/7dfdaf200ebb5c431351a045b48f5e0f0d3f31db), closes [#625](https://github.com/kbknapp/clap-rs/issues/625)) + +#### Documentation + +* **Term Width:** adds details about set_term_width(0) ([00b8205d](https://github.com/kbknapp/clap-rs/commit/00b8205d22639d1b54b9c453c55c785aace52cb2)) + +#### Bug Fixes + +* **Unicode:** fixes two bugs where non-English characters were stripped or caused a panic with help wrapping ([763a5c92](https://github.com/kbknapp/clap-rs/commit/763a5c920e23efc74d190af0cb8b5dd714b2d67a), closes [#626](https://github.com/kbknapp/clap-rs/issues/626)) + + + ### v2.10.2 (2016-08-22) diff --git a/Cargo.toml b/Cargo.toml index 43671d2f..9335b8af 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "clap" -version = "2.10.2" +version = "2.10.3" authors = ["Kevin K. "] exclude = ["examples/*", "clap-test/*", "tests/*", "benches/*", "*.png", "clap-perf/*", "*.dot"] description = "A simple to use, efficient, and full featured Command Line Argument Parser" diff --git a/README.md b/README.md index 5b39f638..29403cce 100644 --- a/README.md +++ b/README.md @@ -39,6 +39,12 @@ Created by [gh-md-toc](https://github.com/ekalinin/github-markdown-toc) ## What's New +Here's the highlights for v2.10.3 + +* Fixes a bug with non-English characters in help text wrapping, where the character is stripped or causes a panic +* Fixes an issue with `strsim` which caused a panic in some scenarios +* Adds a shorthand way to ignore help text wrapping and use source formatting (i.e. `App::set_term_width(0)`) + Here's the highlights for v2.10.2 * Fixes a critical bug where the help message is printed twice From 57690b2af1f55ceaa3d49393b46f626e77cc66b1 Mon Sep 17 00:00:00 2001 From: Kevin K Date: Wed, 24 Aug 2016 22:04:19 -0400 Subject: [PATCH 7/7] chore: allows wrapping help on windows --- Cargo.toml | 6 +++--- README.md | 8 ++++---- src/app/help.rs | 7 ------- src/lib.rs | 2 -- 4 files changed, 7 insertions(+), 16 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 9335b8af..9dfa66ec 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,8 +19,8 @@ ansi_term = { version = "~0.8.0", optional = true } strsim = { version = "~0.5.1", optional = true } yaml-rust = { version = "~0.3.2", optional = true } clippy = { version = "~0.0.79", optional = true } -unicode-width = { version = "~0.1.3", optional = true } -unicode-segmentation = { version = "~0.1.2", optional = true } +unicode-width = "~0.1.3" +unicode-segmentation = "~0.1.2" term_size = { version = "~0.1.0", optional = true } [dev-dependencies] @@ -31,7 +31,7 @@ default = ["suggestions", "color", "wrap_help"] suggestions = ["strsim"] color = ["ansi_term", "libc"] yaml = ["yaml-rust"] -wrap_help = ["libc", "unicode-width", "term_size", "unicode-segmentation"] +wrap_help = ["libc", "term_size"] lints = ["clippy", "nightly"] nightly = [] # for building with nightly and unstable features unstable = [] # for building with unstable features on stable Rust diff --git a/README.md b/README.md index 29403cce..ac315bbb 100644 --- a/README.md +++ b/README.md @@ -65,9 +65,9 @@ Here's the highlights for v2.10.0 Here's the highlights for v2.9.3 -* Adds the ability to generate completions to an `io::Write` object -* Adds an `App::unset_setting` and `App::unset_settings` -* Fixes bug where only first arg in list of `required_unless_one` is recognized +* Adds the ability to generate completions to an `io::Write` object +* Adds an `App::unset_setting` and `App::unset_settings` +* Fixes bug where only first arg in list of `required_unless_one` is recognized * Fixes a typo bug `SubcommandsRequired`->`SubcommandRequired` @@ -553,7 +553,7 @@ The following is a list of optional `clap` features: * **"suggestions"**: Turns on the `Did you mean '--myoption'?` feature for when users make typos. (builds dependency `strsim`) * **"color"**: Turns on colored error messages. This feature only works on non-Windows OSs. (builds dependency `ansi-term` and `libc`) -* **"wrap_help"**: Automatically detects terminal width and wraps long help text lines with proper indentation alignment (builds dependency `libc` and 'unicode-width') +* **"wrap_help"**: Automatically detects terminal width and wraps long help text lines with proper indentation alignment (builds dependency `libc`, and `term_size`) * **"lints"**: This is **not** included by default and should only be used while developing to run basic lints against changes. This can only be used on Rust nightly. (builds dependency `clippy`) * **"debug"**: This is **not** included by default and should only be used while developing to display debugging information. * **"yaml"**: This is **not** included by default. Enables building CLIs from YAML documents. (builds dependency `yaml-rust`) diff --git a/src/app/help.rs b/src/app/help.rs index f303cad5..f5858d8e 100644 --- a/src/app/help.rs +++ b/src/app/help.rs @@ -23,17 +23,10 @@ mod term_size { } } -#[cfg(all(feature = "wrap_help", not(target_os = "windows")))] use unicode_width::UnicodeWidthStr; use strext::_StrExt; -#[cfg(any(not(feature = "wrap_help"), target_os = "windows"))] -fn str_width(s: &str) -> usize { - s.len() -} - -#[cfg(all(feature = "wrap_help", not(target_os = "windows")))] fn str_width(s: &str) -> usize { UnicodeWidthStr::width(s) } diff --git a/src/lib.rs b/src/lib.rs index 20ad117c..5b243794 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -409,14 +409,12 @@ extern crate ansi_term; extern crate yaml_rust; #[cfg(any(feature = "wrap_help", feature = "color"))] extern crate libc; -#[cfg(all(feature = "wrap_help", not(target_os = "windows")))] extern crate unicode_width; #[macro_use] extern crate bitflags; extern crate vec_map; #[cfg(feature = "wrap_help")] extern crate term_size; -#[cfg(feature = "wrap_help")] extern crate unicode_segmentation; #[cfg(feature = "yaml")]