Skip to content

Commit

Permalink
feat(Help Message): can auto wrap and aligning help text to term width
Browse files Browse the repository at this point in the history
By default `clap` now automatically wraps and aligns help strings to the
term width. i.e.

```
    -o, --option <opt>    some really long help
text that should be auto aligned but isn't righ
t now
```

Now looks like this:

```
    -o, --option <opt>    some really long help
                          text that should be
                          auto aligned but isn't
                          right now
```

The wrapping also respects words, and wraps at spaces so as to not cut
words in the middle.

This requires the `libc` dep which is enabled (by default) with the
`wrap_help` cargo feature flag.

Closes #428
  • Loading branch information
kbknapp committed Mar 14, 2016
1 parent df38346 commit e36af02
Show file tree
Hide file tree
Showing 13 changed files with 413 additions and 171 deletions.
4 changes: 3 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,18 @@ keywords = ["argument", "command", "arg", "parser", "parse"]
[dependencies]
bitflags = "~0.4"
vec_map = "~0.6"
libc = { version = "~0.2.8", optional = true }
ansi_term = { version = "~0.7.2", optional = true }
strsim = { version = "~0.4.0", optional = true }
yaml-rust = { version = "~0.3", optional = true }
clippy = { version = "~0.0.48", optional = true }

[features]
default = ["suggestions", "color"]
default = ["suggestions", "color", "wrap_help"]
suggestions = ["strsim"]
color = ["ansi_term"]
yaml = ["yaml-rust"]
wrap_help = ["libc"]
lints = ["clippy", "nightly"]
nightly = [] # for building with nightly and unstable features
unstable = [] # for building with unstable features on stable Rust
Expand Down
16 changes: 8 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ In v2.1.1

#### Improvements

* **Documenation Examples**: The examples in the documentation have been vastly improved
* **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)

Expand Down Expand Up @@ -433,14 +433,14 @@ default-features = false
# Cherry-pick the features you'd like to use
features = [ "suggestions", "color" ]
```

The following is a list of optional `clap` features:

* **"suggestions"**: Turns on the `Did you mean '--myoption' ?` feature for when users make typos.
* **"color"**: Turns on colored error messages. This feature only works on non-Windows OSs.
* **"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.
* **"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`)
* **"wrap_help"**: Automatically detects terminal width and wraps long help text lines with proper indentation alignment (builds dependency `libc`)
* **"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.
* **"yaml"**: This is **not** included by default. Enables building CLIs from YAML documents. (builds dependency `yaml-rust`)
* **"unstable"**: This is **not** included by default. Enables unstable features, unstable refers to whether or not they may change, not performance stability.

### Dependencies Tree
Expand Down Expand Up @@ -472,7 +472,7 @@ Contributions are always welcome! And there is a multitude of ways in which you

Another really great way to help is if you find an interesting, or helpful way in which to use `clap`. You can either add it to the [examples/](examples) directory, or file an issue and tell me. I'm all about giving credit where credit is due :)

Please read [CONTRIBUTING.md](CONTRIBUTING.md) before you start contributing.
Please read [CONTRIBUTING.md](.github/CONTRIBUTING.md) before you start contributing.

### Running the tests

Expand Down Expand Up @@ -553,6 +553,6 @@ As of 2.0.0 (From 1.x)

Old method names will be left around for several minor version bumps, or one major version bump.

As of 2.1.1:
As of 2.2.0:

* None!
13 changes: 6 additions & 7 deletions src/app/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1448,7 +1448,6 @@ impl<'a, 'b> Parser<'a, 'b> where 'a: 'b {
try!(write!(w, "\n"));
}

let tab = " ";
let longest = if !unified_help || longest_opt == 0 {
longest_flag
} else {
Expand All @@ -1461,13 +1460,13 @@ impl<'a, 'b> Parser<'a, 'b> where 'a: 'b {
for f in self.flags.iter().filter(|f| !f.settings.is_set(ArgSettings::Hidden)) {
let btm = ord_m.entry(f.disp_ord).or_insert(BTreeMap::new());
let mut v = vec![];
try!(f.write_help(&mut v, tab, longest, nlh));
try!(f.write_help(&mut v, longest, nlh));
btm.insert(f.name, v);
}
for o in self.opts.iter().filter(|o| !o.settings.is_set(ArgSettings::Hidden)) {
let btm = ord_m.entry(o.disp_ord).or_insert(BTreeMap::new());
let mut v = vec![];
try!(o.write_help(&mut v, tab, longest, self.is_set(AppSettings::HidePossibleValuesInHelp), nlh));
try!(o.write_help(&mut v, longest, self.is_set(AppSettings::HidePossibleValuesInHelp), nlh));
btm.insert(o.name, v);
}
for (_, btm) in ord_m.into_iter() {
Expand All @@ -1486,7 +1485,7 @@ impl<'a, 'b> Parser<'a, 'b> where 'a: 'b {
}
for (_, btm) in ord_m.into_iter() {
for (_, f) in btm.into_iter() {
try!(f.write_help(w, tab, longest, nlh));
try!(f.write_help(w, longest, nlh));
}
}
}
Expand All @@ -1499,7 +1498,7 @@ impl<'a, 'b> Parser<'a, 'b> where 'a: 'b {
}
for (_, btm) in ord_m.into_iter() {
for (_, o) in btm.into_iter() {
try!(o.write_help(w, tab, longest_opt, self.is_set(AppSettings::HidePossibleValuesInHelp), nlh));
try!(o.write_help(w, longest_opt, self.is_set(AppSettings::HidePossibleValuesInHelp), nlh));
}
}
}
Expand All @@ -1508,7 +1507,7 @@ impl<'a, 'b> Parser<'a, 'b> where 'a: 'b {
try!(write!(w, "\nARGS:\n"));
for v in self.positionals.values()
.filter(|p| !p.settings.is_set(ArgSettings::Hidden)) {
try!(v.write_help(w, tab, longest_pos, self.is_set(AppSettings::HidePossibleValuesInHelp), nlh));
try!(v.write_help(w, longest_pos, self.is_set(AppSettings::HidePossibleValuesInHelp), nlh));
}
}
if subcmds {
Expand All @@ -1520,7 +1519,7 @@ impl<'a, 'b> Parser<'a, 'b> where 'a: 'b {
}
for (_, btm) in ord_m.into_iter() {
for (name, sc) in btm.into_iter() {
try!(write!(w, "{}{}", tab, name));
try!(write!(w, " {}", name));
write_spaces!((longest_sc + 4) - (name.len()), w);
if let Some(a) = sc.p.meta.about {
if a.contains("{n}") {
Expand Down
6 changes: 6 additions & 0 deletions src/args/any_arg.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use std::rc::Rc;
use std::fmt::Display;

use vec_map::VecMap;

use args::settings::ArgSettings;

#[doc(hidden)]
Expand All @@ -20,4 +22,8 @@ pub trait AnyArg<'n, 'e>: Display {
fn short(&self) -> Option<char>;
fn long(&self) -> Option<&'e str>;
fn val_delim(&self) -> Option<char>;
fn takes_value(&self) -> bool;
fn val_names(&self) -> Option<&VecMap<&'e str>>;
fn help(&self) -> Option<&'e str>;
fn default_val(&self) -> Option<&'n str>;
}
18 changes: 14 additions & 4 deletions src/args/arg_builder/flag.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@ use std::io;
use std::rc::Rc;
use std::result::Result as StdResult;

use vec_map::VecMap;

use Arg;
use args::AnyArg;
use args::{AnyArg, HelpWriter};
use args::settings::{ArgFlags, ArgSettings};

#[derive(Debug)]
Expand Down Expand Up @@ -47,9 +49,13 @@ impl<'n, 'e> FlagBuilder<'n, 'e> {
}
}

pub fn write_help<W: io::Write>(&self, w: &mut W, tab: &str, longest: usize, nlh: bool) -> io::Result<()> {
write_arg_help!(@flag self, w, tab, longest, nlh);
write!(w, "\n")
pub fn write_help<W: io::Write>(&self, w: &mut W, longest: usize, nlh: bool) -> io::Result<()> {
let hw = HelpWriter::new(
self,
longest,
nlh,
);
hw.write_to(w)
}
}

Expand Down Expand Up @@ -98,15 +104,19 @@ impl<'n, 'e> AnyArg<'n, 'e> for FlagBuilder<'n, 'e> {
fn blacklist(&self) -> Option<&[&'e str]> { self.blacklist.as_ref().map(|o| &o[..]) }
fn is_set(&self, s: ArgSettings) -> bool { self.settings.is_set(s) }
fn has_switch(&self) -> bool { true }
fn takes_value(&self) -> bool { false }
fn set(&mut self, s: ArgSettings) { self.settings.set(s) }
fn max_vals(&self) -> Option<u64> { None }
fn val_names(&self) -> Option<&VecMap<&'e str>> { None }
fn num_vals(&self) -> Option<u64> { None }
fn possible_vals(&self) -> Option<&[&'e str]> { None }
fn validator(&self) -> Option<&Rc<Fn(String) -> StdResult<(), String>>> { None }
fn min_vals(&self) -> Option<u64> { None }
fn short(&self) -> Option<char> { self.short }
fn long(&self) -> Option<&'e str> { self.long }
fn val_delim(&self) -> Option<char> { None }
fn help(&self) -> Option<&'e str> { self.help }
fn default_val(&self) -> Option<&'n str> { None }
}

#[cfg(test)]
Expand Down
126 changes: 0 additions & 126 deletions src/args/arg_builder/macros.rs

This file was deleted.

2 changes: 0 additions & 2 deletions src/args/arg_builder/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@ pub use self::flag::FlagBuilder;
pub use self::option::OptBuilder;
pub use self::positional::PosBuilder;

#[macro_use]
mod macros;
#[allow(dead_code)]
mod flag;
#[allow(dead_code)]
Expand Down
Loading

0 comments on commit e36af02

Please sign in to comment.