mirror of
https://github.com/clap-rs/clap
synced 2024-12-13 14:22:34 +00:00
fix(Help Message): fixes bug with wrapping in the middle of a unicode sequence
Closes #456
This commit is contained in:
parent
6933b8491c
commit
05365ddcc2
5 changed files with 50 additions and 13 deletions
|
@ -19,13 +19,14 @@ ansi_term = { version = "~0.7.2", optional = true }
|
||||||
strsim = { version = "~0.4.0", optional = true }
|
strsim = { version = "~0.4.0", optional = true }
|
||||||
yaml-rust = { version = "~0.3", optional = true }
|
yaml-rust = { version = "~0.3", optional = true }
|
||||||
clippy = { version = "=0.0.55", optional = true }
|
clippy = { version = "=0.0.55", optional = true }
|
||||||
|
unicode-width = { version = "~0.1.3", optional = true }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["suggestions", "color", "wrap_help"]
|
default = ["suggestions", "color", "wrap_help"]
|
||||||
suggestions = ["strsim"]
|
suggestions = ["strsim"]
|
||||||
color = ["ansi_term"]
|
color = ["ansi_term"]
|
||||||
yaml = ["yaml-rust"]
|
yaml = ["yaml-rust"]
|
||||||
wrap_help = ["libc"]
|
wrap_help = ["libc", "unicode-width"]
|
||||||
lints = ["clippy", "nightly"]
|
lints = ["clippy", "nightly"]
|
||||||
nightly = [] # for building with nightly and unstable features
|
nightly = [] # for building with nightly and unstable features
|
||||||
unstable = [] # for building with unstable features on stable Rust
|
unstable = [] # for building with unstable features on stable Rust
|
||||||
|
|
|
@ -457,7 +457,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`)
|
* **"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`)
|
* **"color"**: Turns on colored error messages. This feature only works on non-Windows OSs. (builds dependency `ansi-term`)
|
||||||
* **"wrap_help"**: Automatically detects terminal width and wraps long help text lines with proper indentation alignment (builds dependency `libc`)
|
* **"wrap_help"**: Automatically detects terminal width and wraps long help text lines with proper indentation alignment (builds dependency `libc` and 'unicode-width')
|
||||||
* **"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`)
|
* **"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.
|
* **"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`)
|
* **"yaml"**: This is **not** included by default. Enables building CLIs from YAML documents. (builds dependency `yaml-rust`)
|
||||||
|
|
|
@ -1,9 +1,23 @@
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
|
|
||||||
|
#[cfg(all(feature = "wrap_help", not(target_os = "windows")))]
|
||||||
|
use unicode_width::UnicodeWidthStr;
|
||||||
|
|
||||||
use args::AnyArg;
|
use args::AnyArg;
|
||||||
use args::settings::ArgSettings;
|
use args::settings::ArgSettings;
|
||||||
use term;
|
use term;
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
const TAB: &'static str = " ";
|
const TAB: &'static str = " ";
|
||||||
|
|
||||||
|
@ -139,7 +153,7 @@ impl<'a, 'n, 'e, A> HelpWriter<'a, A> where A: AnyArg<'n, 'e> + Display {
|
||||||
// determine if our help fits or needs to wrap
|
// determine if our help fits or needs to wrap
|
||||||
let width = self.term_w.unwrap_or(0);
|
let width = self.term_w.unwrap_or(0);
|
||||||
debugln!("Term width...{}", width);
|
debugln!("Term width...{}", width);
|
||||||
let too_long = self.term_w.is_some() && (spcs + h.len() + spec_vals.len() >= width);
|
let too_long = self.term_w.is_some() && (spcs + str_width(h) + str_width(&*spec_vals) >= width);
|
||||||
debugln!("Too long...{:?}", too_long);
|
debugln!("Too long...{:?}", too_long);
|
||||||
|
|
||||||
// Is help on next line, if so newline + 2x tab
|
// Is help on next line, if so newline + 2x tab
|
||||||
|
@ -153,13 +167,13 @@ impl<'a, 'n, 'e, A> HelpWriter<'a, A> where A: AnyArg<'n, 'e> + Display {
|
||||||
help.push_str(h);
|
help.push_str(h);
|
||||||
help.push_str(&*spec_vals);
|
help.push_str(&*spec_vals);
|
||||||
debugln!("help: {}", help);
|
debugln!("help: {}", help);
|
||||||
debugln!("help len: {}", help.len());
|
debugln!("help width: {}", str_width(help));
|
||||||
// Determine how many newlines we need to insert
|
// Determine how many newlines we need to insert
|
||||||
let avail_chars = width - spcs;
|
let avail_chars = width - spcs;
|
||||||
debugln!("Usable space: {}", avail_chars);
|
debugln!("Usable space: {}", avail_chars);
|
||||||
let longest_w = {
|
let longest_w = {
|
||||||
let mut lw = 0;
|
let mut lw = 0;
|
||||||
for l in help.split(' ').map(|s| s.len()) {
|
for l in help.split(' ').map(|s| str_width(s)) {
|
||||||
if l > lw {
|
if l > lw {
|
||||||
lw = l;
|
lw = l;
|
||||||
}
|
}
|
||||||
|
@ -167,7 +181,7 @@ impl<'a, 'n, 'e, A> HelpWriter<'a, A> where A: AnyArg<'n, 'e> + Display {
|
||||||
lw
|
lw
|
||||||
};
|
};
|
||||||
debugln!("Longest word...{}", longest_w);
|
debugln!("Longest word...{}", longest_w);
|
||||||
debug!("Enough space...");
|
debug!("Enough space to wrap...");
|
||||||
if longest_w < avail_chars {
|
if longest_w < avail_chars {
|
||||||
sdebugln!("Yes");
|
sdebugln!("Yes");
|
||||||
let mut indices = vec![];
|
let mut indices = vec![];
|
||||||
|
@ -182,13 +196,13 @@ impl<'a, 'n, 'e, A> HelpWriter<'a, A> where A: AnyArg<'n, 'e> + Display {
|
||||||
debugln!("Adding idx: {}", idx);
|
debugln!("Adding idx: {}", idx);
|
||||||
debugln!("At {}: {:?}", idx, help.chars().nth(idx));
|
debugln!("At {}: {:?}", idx, help.chars().nth(idx));
|
||||||
indices.push(idx);
|
indices.push(idx);
|
||||||
if &help[idx..].len() <= &avail_chars {
|
if str_width(&help[idx..]) <= avail_chars {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (i, idx) in indices.iter().enumerate() {
|
for (i, idx) in indices.iter().enumerate() {
|
||||||
debugln!("iter;i={},idx={}", i, idx);
|
debugln!("iter;i={},idx={}", i, idx);
|
||||||
let j = idx+(2*i);
|
let j = idx + (2 * i);
|
||||||
debugln!("removing: {}", j);
|
debugln!("removing: {}", j);
|
||||||
debugln!("at {}: {:?}", j, help.chars().nth(j));
|
debugln!("at {}: {:?}", j, help.chars().nth(j));
|
||||||
help.remove(j);
|
help.remove(j);
|
||||||
|
@ -252,15 +266,20 @@ impl<'a, 'n, 'e, A> HelpWriter<'a, A> where A: AnyArg<'n, 'e> + Display {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn find_idx_of_space(full: &str, start: usize) -> usize {
|
fn find_idx_of_space(full: &str, mut start: usize) -> usize {
|
||||||
debugln!("fn=find_idx_of_space;");
|
debugln!("fn=find_idx_of_space;");
|
||||||
let haystack = &full[..start];
|
let haystack = if full._is_char_boundary(start) {
|
||||||
|
&full[..start]
|
||||||
|
} else {
|
||||||
|
while !full._is_char_boundary(start) { start -= 1; }
|
||||||
|
&full[..start]
|
||||||
|
};
|
||||||
debugln!("haystack: {}", haystack);
|
debugln!("haystack: {}", haystack);
|
||||||
for (i, c) in haystack.chars().rev().enumerate() {
|
for (i, c) in haystack.chars().rev().enumerate() {
|
||||||
debugln!("iter;c={},i={}", c, i);
|
debugln!("iter;c={},i={}", c, i);
|
||||||
if c == ' ' {
|
if c == ' ' {
|
||||||
debugln!("Found space returning start-i...{}", start - (i+1));
|
debugln!("Found space returning start-i...{}", start - (i + 1));
|
||||||
return start - (i+1);
|
return start - (i + 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
0
|
0
|
||||||
|
|
|
@ -406,8 +406,10 @@ extern crate strsim;
|
||||||
extern crate ansi_term;
|
extern crate ansi_term;
|
||||||
#[cfg(feature = "yaml")]
|
#[cfg(feature = "yaml")]
|
||||||
extern crate yaml_rust;
|
extern crate yaml_rust;
|
||||||
#[cfg(feature = "wrap_help")]
|
#[cfg(all(feature = "wrap_help", not(target_os = "windows")))]
|
||||||
extern crate libc;
|
extern crate libc;
|
||||||
|
#[cfg(all(feature = "wrap_help", not(target_os = "windows")))]
|
||||||
|
extern crate unicode_width;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate bitflags;
|
extern crate bitflags;
|
||||||
extern crate vec_map;
|
extern crate vec_map;
|
||||||
|
@ -429,6 +431,7 @@ mod suggestions;
|
||||||
mod errors;
|
mod errors;
|
||||||
mod osstringext;
|
mod osstringext;
|
||||||
mod term;
|
mod term;
|
||||||
|
mod strext;
|
||||||
|
|
||||||
const INTERNAL_ERROR_MSG: &'static str = "Fatal internal error. Please consider filing a bug \
|
const INTERNAL_ERROR_MSG: &'static str = "Fatal internal error. Please consider filing a bug \
|
||||||
report at https://github.com/kbknapp/clap-rs/issues";
|
report at https://github.com/kbknapp/clap-rs/issues";
|
||||||
|
|
14
src/strext.rs
Normal file
14
src/strext.rs
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
pub trait _StrExt {
|
||||||
|
fn _is_char_boundary(&self, index: usize) -> bool;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl _StrExt for str {
|
||||||
|
#[inline]
|
||||||
|
fn _is_char_boundary(&self, index: usize) -> bool {
|
||||||
|
if index == self.len() { return true; }
|
||||||
|
match self.as_bytes().get(index) {
|
||||||
|
None => false,
|
||||||
|
Some(&b) => b < 128 || b >= 192,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue