mirror of
https://github.com/clap-rs/clap
synced 2024-12-13 22:32:33 +00:00
commit
afa6b5d66e
11 changed files with 131 additions and 51 deletions
13
CHANGELOG.md
13
CHANGELOG.md
|
@ -1,3 +1,16 @@
|
|||
<a name="2.2.1"></a>
|
||||
### 2.2.1 (2016-03-16)
|
||||
|
||||
|
||||
#### Features
|
||||
|
||||
* **Help Message:** wraps and aligns the help message of subcommands ([813d75d0](https://github.com/kbknapp/clap-rs/commit/813d75d06fbf077c65762608c0fa5e941cfc393c), closes [#452](https://github.com/kbknapp/clap-rs/issues/452))
|
||||
|
||||
#### Bug Fixes
|
||||
|
||||
* **Help Message:** fixes a bug where small terminal sizes causing a loop ([1d73b035](https://github.com/kbknapp/clap-rs/commit/1d73b0355236923aeaf6799abc759762ded7e1d0), closes [#453](https://github.com/kbknapp/clap-rs/issues/453))
|
||||
|
||||
|
||||
<a name="v2.2.0"></a>
|
||||
## v2.2.0 (2016-03-15)
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
[package]
|
||||
|
||||
name = "clap"
|
||||
version = "2.2.0"
|
||||
version = "2.2.1"
|
||||
authors = ["Kevin K. <kbknapp@gmail.com>"]
|
||||
exclude = ["examples/*", "clap-tests/*", "tests/*", "benches/*", "*.png", "clap-perf/*"]
|
||||
description = "A simple to use, efficient, and full featured Command Line Argument Parser"
|
||||
|
|
|
@ -38,6 +38,11 @@ Created by [gh-md-toc](https://github.com/ekalinin/github-markdown-toc)
|
|||
|
||||
## What's New
|
||||
|
||||
Here's the highlights from v2.2.1
|
||||
|
||||
* **Help text auto wraps and aligns at for subcommands too!** - Long help strings of subcommands will now properly wrap and align to term width on Linux and OSX. This can be turned off as well.
|
||||
* Bug fixes
|
||||
|
||||
Here's the highlights from v2.2.0
|
||||
|
||||
#### Features
|
||||
|
|
|
@ -31,12 +31,12 @@ OPTIONS:
|
|||
-o, --option <opt>... tests options
|
||||
|
||||
ARGS:
|
||||
<positional> tests positionals
|
||||
<positional2> tests positionals with exclusions
|
||||
<positional3>... tests positionals with specific values [values: vi, emacs]
|
||||
[positional] tests positionals
|
||||
[positional2] tests positionals with exclusions
|
||||
[positional3]... tests positionals with specific values [values: vi, emacs]
|
||||
|
||||
SUBCOMMANDS:
|
||||
help With no arguments it prints this message, otherwise it prints help information about other subcommands
|
||||
help Prints this message or the help message of the given subcommand(s)
|
||||
subcmd tests subcommands'''
|
||||
|
||||
_version = "claptests v1.4.8"
|
||||
|
@ -152,7 +152,7 @@ OPTIONS:
|
|||
-o, --option <scoption>... tests options
|
||||
|
||||
ARGS:
|
||||
<scpositional> tests positionals'''
|
||||
[scpositional] tests positionals'''
|
||||
|
||||
_scfop = '''flag NOT present
|
||||
option NOT present
|
||||
|
|
|
@ -13,11 +13,15 @@ use std::path::Path;
|
|||
use std::process;
|
||||
use std::ffi::OsString;
|
||||
use std::borrow::Borrow;
|
||||
use std::result::Result as StdResult;
|
||||
use std::rc::Rc;
|
||||
use std::fmt;
|
||||
|
||||
#[cfg(feature = "yaml")]
|
||||
use yaml_rust::Yaml;
|
||||
use vec_map::VecMap;
|
||||
|
||||
use args::{Arg, AnyArg, ArgGroup, ArgMatches, ArgMatcher};
|
||||
use args::{Arg, HelpWriter, ArgSettings, AnyArg, ArgGroup, ArgMatches, ArgMatcher};
|
||||
use app::parser::Parser;
|
||||
use errors::Error;
|
||||
use errors::Result as ClapResult;
|
||||
|
@ -798,6 +802,30 @@ impl<'a, 'b> App<'a, 'b> {
|
|||
|
||||
e.exit()
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub fn write_self_help<W>(&self, w: &mut W, longest: usize, nlh: bool) -> io::Result<()>
|
||||
where W: Write
|
||||
{
|
||||
let hw = HelpWriter::new(self, longest, nlh);
|
||||
hw.write_to(w)
|
||||
|
||||
// try!(write!(w, " {}", self.p.meta.name));
|
||||
// write_spaces!((longest_sc + 4) - (self.p.meta.name.len()), w);
|
||||
// if let Some(a) = self.p.meta.about {
|
||||
// if a.contains("{n}") {
|
||||
// let mut ab = a.split("{n}");
|
||||
// while let Some(part) = ab.next() {
|
||||
// try!(write!(w, "{}\n", part));
|
||||
// write_spaces!(longest_sc + 8, w);
|
||||
// try!(write!(w, "{}", ab.next().unwrap_or("")));
|
||||
// }
|
||||
// } else {
|
||||
// try!(write!(w, "{}", a));
|
||||
// }
|
||||
// }
|
||||
// write!(w, "\n")
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "yaml")]
|
||||
|
@ -883,3 +911,35 @@ impl<'a, 'b> Clone for App<'a, 'b> {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'n, 'e> AnyArg<'n, 'e> for App<'n, 'e> {
|
||||
fn name(&self) -> &'n str {
|
||||
unreachable!("App struct does not support AnyArg::name, this is a bug!")
|
||||
}
|
||||
fn overrides(&self) -> Option<&[&'e str]> { None }
|
||||
fn requires(&self) -> Option<&[&'e str]> { None }
|
||||
fn blacklist(&self) -> Option<&[&'e str]> { None }
|
||||
fn val_names(&self) -> Option<&VecMap<&'e str>> { None }
|
||||
fn is_set(&self, _: ArgSettings) -> bool { false }
|
||||
fn set(&mut self, _: ArgSettings) {
|
||||
unreachable!("App struct does not support AnyArg::set, this is a bug!")
|
||||
}
|
||||
fn has_switch(&self) -> bool { false }
|
||||
fn max_vals(&self) -> Option<u64> { 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> { None }
|
||||
fn long(&self) -> Option<&'e str> { None }
|
||||
fn val_delim(&self) -> Option<char> { None }
|
||||
fn takes_value(&self) -> bool { true }
|
||||
fn help(&self) -> Option<&'e str> { self.p.meta.about }
|
||||
fn default_val(&self) -> Option<&'n str> { None }
|
||||
}
|
||||
|
||||
impl<'n, 'e> fmt::Display for App<'n, 'e> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}", self.p.meta.name)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -814,7 +814,7 @@ impl<'a, 'b> Parser<'a, 'b> where 'a: 'b {
|
|||
.iter()
|
||||
.any(|s| &s.p.meta.name[..] == "help") {
|
||||
debugln!("Building help");
|
||||
self.subcommands.push(App::new("help").about("With no arguments it prints this message, otherwise it prints help information about other subcommands"));
|
||||
self.subcommands.push(App::new("help").about("Prints this message or the help message of the given subcommand(s)"));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1034,7 +1034,7 @@ impl<'a, 'b> Parser<'a, 'b> where 'a: 'b {
|
|||
}
|
||||
|
||||
fn add_single_val_to_arg<A>(&self, arg: &A, v: &OsStr, matcher: &mut ArgMatcher<'a>) -> ClapResult<Option<&'a str>>
|
||||
where A: AnyArg<'a, 'b>
|
||||
where A: AnyArg<'a, 'b> + Display
|
||||
{
|
||||
debugln!("adding val: {:?}", v);
|
||||
matcher.add_val_to(arg.name(), v);
|
||||
|
@ -1052,7 +1052,7 @@ impl<'a, 'b> Parser<'a, 'b> where 'a: 'b {
|
|||
}
|
||||
|
||||
fn validate_value<A>(&self, arg: &A, val: &OsStr, matcher: &ArgMatcher<'a>) -> ClapResult<Option<&'a str>>
|
||||
where A: AnyArg<'a, 'b> {
|
||||
where A: AnyArg<'a, 'b> + Display {
|
||||
debugln!("fn=validate_value; val={:?}", val);
|
||||
if self.is_set(AppSettings::StrictUtf8) && val.to_str().is_none() {
|
||||
return Err(Error::invalid_utf8(&*self.create_current_usage(matcher)));
|
||||
|
@ -1165,7 +1165,7 @@ impl<'a, 'b> Parser<'a, 'b> where 'a: 'b {
|
|||
}
|
||||
|
||||
fn _validate_num_vals<A>(&self, a: &A, ma: &MatchedArg, matcher: &ArgMatcher) -> ClapResult<()>
|
||||
where A: AnyArg<'a, 'b>
|
||||
where A: AnyArg<'a, 'b> + Display
|
||||
{
|
||||
debugln!("fn=_validate_num_vals;");
|
||||
if let Some(num) = a.num_vals() {
|
||||
|
@ -1532,22 +1532,8 @@ impl<'a, 'b> Parser<'a, 'b> where 'a: 'b {
|
|||
btm.insert(sc.p.meta.name.clone(), sc);
|
||||
}
|
||||
for (_, btm) in ord_m.into_iter() {
|
||||
for (name, sc) in btm.into_iter() {
|
||||
try!(write!(w, " {}", name));
|
||||
write_spaces!((longest_sc + 4) - (name.len()), w);
|
||||
if let Some(a) = sc.p.meta.about {
|
||||
if a.contains("{n}") {
|
||||
let mut ab = a.split("{n}");
|
||||
while let Some(part) = ab.next() {
|
||||
try!(write!(w, "{}\n", part));
|
||||
write_spaces!(longest_sc + 8, w);
|
||||
try!(write!(w, "{}", ab.next().unwrap_or("")));
|
||||
}
|
||||
} else {
|
||||
try!(write!(w, "{}", a));
|
||||
}
|
||||
}
|
||||
try!(write!(w, "\n"));
|
||||
for (_, sc) in btm.into_iter() {
|
||||
try!(sc.write_self_help(w, longest_sc, nlh));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,12 +1,11 @@
|
|||
use std::rc::Rc;
|
||||
use std::fmt::Display;
|
||||
|
||||
use vec_map::VecMap;
|
||||
|
||||
use args::settings::ArgSettings;
|
||||
|
||||
#[doc(hidden)]
|
||||
pub trait AnyArg<'n, 'e>: Display {
|
||||
pub trait AnyArg<'n, 'e> {
|
||||
fn name(&self) -> &'n str;
|
||||
fn overrides(&self) -> Option<&[&'e str]>;
|
||||
fn requires(&self) -> Option<&[&'e str]>;
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
use std::io;
|
||||
use std::fmt::Display;
|
||||
|
||||
use args::AnyArg;
|
||||
use args::settings::ArgSettings;
|
||||
|
@ -14,7 +15,7 @@ pub struct HelpWriter<'a, A> where A: 'a {
|
|||
term_w: Option<usize>,
|
||||
}
|
||||
|
||||
impl<'a, 'n, 'e, A> HelpWriter<'a, A> where A: AnyArg<'n, 'e> {
|
||||
impl<'a, 'n, 'e, A> HelpWriter<'a, A> where A: AnyArg<'n, 'e> + Display {
|
||||
pub fn new(a: &'a A, l: usize, nlh: bool) -> Self {
|
||||
HelpWriter {
|
||||
a: a,
|
||||
|
@ -99,7 +100,7 @@ impl<'a, 'n, 'e, A> HelpWriter<'a, A> where A: AnyArg<'n, 'e> {
|
|||
if it.peek().is_some() { try!(write!(w, " ")); }
|
||||
}
|
||||
} else {
|
||||
try!(write!(w, "<{}>{}", self.a.name(), if self.a.is_set(ArgSettings::Multiple) { "..." } else { "" }));
|
||||
try!(write!(w, "{}", self.a));
|
||||
}
|
||||
if self.a.has_switch() {
|
||||
if !(self.nlh || self.a.is_set(ArgSettings::NextLineHelp)) {
|
||||
|
@ -138,7 +139,10 @@ impl<'a, 'n, 'e, A> HelpWriter<'a, A> where A: AnyArg<'n, 'e> {
|
|||
self.l + 12
|
||||
};
|
||||
// determine if our help fits or needs to wrap
|
||||
let too_long = self.term_w.is_some() && (spcs + h.len() + spec_vals.len() >= self.term_w.unwrap_or(0));
|
||||
let width = self.term_w.unwrap_or(0);
|
||||
debugln!("Term width...{}", width);
|
||||
let too_long = self.term_w.is_some() && (spcs + h.len() + spec_vals.len() >= width);
|
||||
debugln!("Too long...{:?}", too_long);
|
||||
|
||||
// Is help on next line, if so newline + 2x tab
|
||||
if self.nlh || self.a.is_set(ArgSettings::NextLineHelp) {
|
||||
|
@ -148,15 +152,26 @@ impl<'a, 'n, 'e, A> HelpWriter<'a, A> where A: AnyArg<'n, 'e> {
|
|||
debug!("Too long...");
|
||||
if too_long {
|
||||
sdebugln!("Yes");
|
||||
if let Some(width) = self.term_w {
|
||||
help.push_str(h);
|
||||
help.push_str(&*spec_vals);
|
||||
debugln!("term width: {}", width);
|
||||
debugln!("help: {}", help);
|
||||
debugln!("help len: {}", help.len());
|
||||
// Determine how many newlines we need to insert
|
||||
let avail_chars = width - spcs;
|
||||
debugln!("Usable space: {}", avail_chars);
|
||||
help.push_str(h);
|
||||
help.push_str(&*spec_vals);
|
||||
debugln!("help: {}", help);
|
||||
debugln!("help len: {}", help.len());
|
||||
// Determine how many newlines we need to insert
|
||||
let avail_chars = width - spcs;
|
||||
debugln!("Usable space: {}", avail_chars);
|
||||
let longest_w = {
|
||||
let mut lw = 0;
|
||||
for l in help.split(' ').map(|s| s.len()) {
|
||||
if l > lw {
|
||||
lw = l;
|
||||
}
|
||||
}
|
||||
lw
|
||||
};
|
||||
debugln!("Longest word...{}", longest_w);
|
||||
debug!("Enough space...");
|
||||
if longest_w < avail_chars {
|
||||
sdebugln!("Yes");
|
||||
let mut indices = vec![];
|
||||
let mut idx = 0;
|
||||
loop {
|
||||
|
@ -183,6 +198,8 @@ impl<'a, 'n, 'e, A> HelpWriter<'a, A> where A: AnyArg<'n, 'e> {
|
|||
help.insert(j + 1 , 'n');
|
||||
help.insert(j + 2, '}');
|
||||
}
|
||||
} else {
|
||||
sdebugln!("No");
|
||||
}
|
||||
} else { sdebugln!("No"); }
|
||||
let help = if !help.is_empty() {
|
||||
|
|
|
@ -355,7 +355,7 @@ impl Error {
|
|||
|
||||
#[doc(hidden)]
|
||||
pub fn argument_conflict<'a, 'b, A, O, U>(arg: &A, other: Option<O>, usage: U) -> Self
|
||||
where A: AnyArg<'a, 'b>,
|
||||
where A: AnyArg<'a, 'b> + Display,
|
||||
O: Into<String>,
|
||||
U: Display
|
||||
{
|
||||
|
@ -383,7 +383,7 @@ impl Error {
|
|||
|
||||
#[doc(hidden)]
|
||||
pub fn empty_value<'a, 'b, A, U>(arg: &A, usage: U) -> Self
|
||||
where A: AnyArg<'a, 'b>,
|
||||
where A: AnyArg<'a, 'b> + Display,
|
||||
U: Display
|
||||
{
|
||||
Error {
|
||||
|
@ -404,7 +404,7 @@ impl Error {
|
|||
pub fn invalid_value<'a, 'b, B, G, A, U>(bad_val: B, good_vals: &[G], arg: &A, usage: U) -> Self
|
||||
where B: AsRef<str>,
|
||||
G: AsRef<str> + Display,
|
||||
A: AnyArg<'a, 'b>,
|
||||
A: AnyArg<'a, 'b> + Display,
|
||||
U: Display
|
||||
{
|
||||
let suffix = suggestions::did_you_mean_suffix(bad_val.as_ref(),
|
||||
|
@ -539,7 +539,7 @@ impl Error {
|
|||
#[doc(hidden)]
|
||||
pub fn too_many_values<'a, 'b, V, A, U>(val: V, arg: &A, usage: U) -> Self
|
||||
where V: AsRef<str> + Display + ToOwned,
|
||||
A: AnyArg<'a, 'b>,
|
||||
A: AnyArg<'a, 'b> + Display,
|
||||
U: Display
|
||||
{
|
||||
let v = val.as_ref();
|
||||
|
@ -560,7 +560,7 @@ impl Error {
|
|||
|
||||
#[doc(hidden)]
|
||||
pub fn too_few_values<'a, 'b, A, U>(arg: &A, min_vals: u64, curr_vals: usize, usage: U) -> Self
|
||||
where A: AnyArg<'a, 'b>,
|
||||
where A: AnyArg<'a, 'b> + Display,
|
||||
U: Display
|
||||
{
|
||||
Error {
|
||||
|
@ -595,7 +595,7 @@ impl Error {
|
|||
|
||||
#[doc(hidden)]
|
||||
pub fn wrong_number_of_values<'a, 'b, A, S, U>(arg: &A, num_vals: u64, curr_vals: usize, suffix: S, usage: U) -> Self
|
||||
where A: AnyArg<'a, 'b>,
|
||||
where A: AnyArg<'a, 'b> + Display,
|
||||
S: Display,
|
||||
U: Display
|
||||
{
|
||||
|
@ -618,7 +618,7 @@ impl Error {
|
|||
|
||||
#[doc(hidden)]
|
||||
pub fn unexpected_multiple_usage<'a, 'b, A, U>(arg: &A, usage: U) -> Self
|
||||
where A: AnyArg<'a, 'b>,
|
||||
where A: AnyArg<'a, 'b> + Display,
|
||||
U: Display
|
||||
{
|
||||
Error {
|
||||
|
|
|
@ -102,7 +102,7 @@ OPTIONS:
|
|||
-V, --version Prints version information
|
||||
|
||||
ARGS:
|
||||
<arg1> some pos arg\n"));
|
||||
[arg1] some pos arg\n"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -135,5 +135,5 @@ OPTIONS:
|
|||
-o, --opt <opt> some option
|
||||
|
||||
ARGS:
|
||||
<arg1> some pos arg\n"));
|
||||
[arg1] some pos arg\n"));
|
||||
}
|
||||
|
|
|
@ -112,5 +112,5 @@ OPTIONS:
|
|||
-o, --opt <opt> some option [values: one, two]
|
||||
|
||||
ARGS:
|
||||
<arg1> some pos arg [values: three, four]\n"));
|
||||
[arg1] some pos arg [values: three, four]\n"));
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue