Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issues 625,626 #628

Merged
merged 7 commits into from
Aug 25, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,20 @@
<a name="v2.10.3"></a>
### 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))



<a name="v2.10.2"></a>
### v2.10.2 (2016-08-22)

Expand Down
7 changes: 4 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]

name = "clap"
version = "2.10.2"
version = "2.10.3"
authors = ["Kevin K. <kbknapp@gmail.com>"]
exclude = ["examples/*", "clap-test/*", "tests/*", "benches/*", "*.png", "clap-perf/*", "*.dot"]
description = "A simple to use, efficient, and full featured Command Line Argument Parser"
Expand All @@ -19,7 +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-width = "~0.1.3"
unicode-segmentation = "~0.1.2"
term_size = { version = "~0.1.0", optional = true }

[dev-dependencies]
Expand All @@ -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", "term_size"]
lints = ["clippy", "nightly"]
nightly = [] # for building with nightly and unstable features
unstable = [] # for building with unstable features on stable Rust
Expand Down
14 changes: 10 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -59,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`


Expand Down Expand Up @@ -547,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`)
Expand Down
51 changes: 22 additions & 29 deletions src/app/help.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@ 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;
use unicode_segmentation::UnicodeSegmentation;

use errors::{Error, Result as ClapResult};

Expand All @@ -21,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)
}
Expand Down Expand Up @@ -102,7 +97,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,
Expand Down Expand Up @@ -458,33 +457,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;
}
// 'a' arbitrary non space char
if help.chars().nth(idx).unwrap_or('a') != ' ' {
idx = find_idx_of_space(&*help, idx);
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;
}
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");
Expand Down
7 changes: 4 additions & 3 deletions src/app/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -409,13 +409,13 @@ 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;
extern crate unicode_segmentation;

#[cfg(feature = "yaml")]
pub use yaml_rust::YamlLoader;
Expand Down
85 changes: 84 additions & 1 deletion tests/help.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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. <kbknapp@gmail.com>
Expand Down Expand Up @@ -81,6 +81,45 @@ FLAGS:
OPTIONS:
-o, --option <scoption>... 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 <FILE> 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 <FILE> 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")
Expand Down Expand Up @@ -189,6 +228,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();
Expand All @@ -199,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);
}