Auto merge of #628 - kbknapp:issues-625,626, r=kbknapp

Issues 625,626
This commit is contained in:
Homu 2016-08-25 11:23:26 +09:00
commit bd704e0d4f
7 changed files with 142 additions and 41 deletions

View file

@ -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)

View file

@ -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"
@ -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]
@ -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

View file

@ -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
@ -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`)

View file

@ -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};
@ -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)
}
@ -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,
@ -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;
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");

View file

@ -525,7 +525,8 @@ impl<'a, 'b> App<'a, 'b> {
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

View file

@ -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;

View file

@ -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>
@ -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")
@ -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();
@ -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);
}