mirror of
https://github.com/clap-rs/clap
synced 2024-12-13 22:32:33 +00:00
Auto merge of #449 - kbknapp:issue-428-redux, r=kbknapp
Issue 428 redux
This commit is contained in:
commit
f6d1685000
20 changed files with 486 additions and 211 deletions
21
CHANGELOG.md
21
CHANGELOG.md
|
@ -1,3 +1,24 @@
|
|||
<a name="v2.2.0"></a>
|
||||
## v2.2.0 (2016-03-14)
|
||||
|
||||
|
||||
#### Documentation
|
||||
|
||||
* **Groups:** explains required ArgGroups better ([4ff0205b](https://github.com/kbknapp/clap-rs/commit/4ff0205b85a45151b59bbaf090a89df13438380f), closes [#439](https://github.com/kbknapp/clap-rs/issues/439))
|
||||
|
||||
#### Features
|
||||
|
||||
* **Help Message:** can auto wrap and aligning help text to term width ([e36af026](https://github.com/kbknapp/clap-rs/commit/e36af0266635f23e85e951b9088d561e9a5d1bf6), closes [#428](https://github.com/kbknapp/clap-rs/issues/428))
|
||||
* **Opts and Flags:** adds support for custom ordering in help messages ([9803b51e](https://github.com/kbknapp/clap-rs/commit/9803b51e799904c0befaac457418ee766ccc1ab9))
|
||||
* **Settings:** adds support for automatically deriving custom display order of args ([ad86e433](https://github.com/kbknapp/clap-rs/commit/ad86e43334c4f70e86909689a088fb87e26ff95a), closes [#444](https://github.com/kbknapp/clap-rs/issues/444))
|
||||
* **Subcommands:** adds support for custom ordering in help messages ([7d2a2ed4](https://github.com/kbknapp/clap-rs/commit/7d2a2ed413f5517d45988eef0765cdcd663b6372), closes [#442](https://github.com/kbknapp/clap-rs/issues/442))
|
||||
|
||||
#### Bug Fixes
|
||||
|
||||
* **From Usage:** fixes a bug where adding empty lines werent ignored ([c5c58c86](https://github.com/kbknapp/clap-rs/commit/c5c58c86b9c503d8de19da356a5a5cffb59fbe84))
|
||||
|
||||
|
||||
|
||||
<a name="v2.1.2"></a>
|
||||
### v2.1.2 (2016-02-24)
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
[package]
|
||||
|
||||
name = "clap"
|
||||
version = "2.1.2"
|
||||
version = "2.2.0"
|
||||
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"
|
||||
|
@ -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
|
||||
|
|
31
README.md
31
README.md
|
@ -38,7 +38,20 @@ Created by [gh-md-toc](https://github.com/ekalinin/github-markdown-toc)
|
|||
|
||||
## What's New
|
||||
|
||||
In v2.1.1
|
||||
Here's the highlights from v2.2.0
|
||||
|
||||
#### Features
|
||||
|
||||
* **Help text auto wraps and aligns at term width!** - Long help strings will now properly wrap and align to term width on Linux and OSX (and resumably Unix too). This can be turned off as well.
|
||||
* **Can customize the order of opts, flags, and subcommands in help messages** - Instead of using the default alphabetical order, you can now re-arange the order of your args and subcommands in help message. This helps to emphasize more popular or important options.
|
||||
* **Can auto-derive the order from declaration order** - Have a bunch of args or subcommmands to re-order? You can now just derive the order from the declaration order!
|
||||
* Other minor bug fixes
|
||||
|
||||
An example of the help text wrapping at term width:
|
||||
|
||||
![screenshot](http://i.imgur.com/PAJzJJG.png)
|
||||
|
||||
In v2.1.2
|
||||
|
||||
#### New Features
|
||||
|
||||
|
@ -47,7 +60,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)
|
||||
|
||||
|
@ -433,14 +446,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
|
||||
|
@ -472,7 +485,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
|
||||
|
||||
|
@ -553,6 +566,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,7 +13,7 @@ Kevin K. <kbknapp@gmail.com>
|
|||
tests clap library
|
||||
|
||||
USAGE:
|
||||
\tclaptests [FLAGS] [OPTIONS] [ARGS] [SUBCOMMAND]
|
||||
claptests [FLAGS] [OPTIONS] [ARGS] [SUBCOMMAND]
|
||||
|
||||
FLAGS:
|
||||
-f, --flag tests flags
|
||||
|
@ -47,7 +47,7 @@ _sc_dym_usage = '''error: The subcommand 'subcm' wasn't recognized
|
|||
If you believe you received this message in error, try re-running with 'claptests -- subcm'
|
||||
|
||||
USAGE:
|
||||
\tclaptests [FLAGS] [OPTIONS] [ARGS] [SUBCOMMAND]
|
||||
claptests [FLAGS] [OPTIONS] [ARGS] [SUBCOMMAND]
|
||||
|
||||
For more information try --help'''
|
||||
|
||||
|
@ -55,7 +55,7 @@ _arg_dym_usage = '''error: Found argument '--optio' which wasn't expected, or is
|
|||
\tDid you mean --option ?
|
||||
|
||||
USAGE:
|
||||
\tclaptests --option <opt>...
|
||||
claptests --option <opt>...
|
||||
|
||||
For more information try --help'''
|
||||
|
||||
|
@ -65,30 +65,30 @@ _pv_dym_usage = '''error: 'slo' isn't a valid value for '--Option <option3>'
|
|||
Did you mean 'slow' ?
|
||||
|
||||
USAGE:
|
||||
\tclaptests --Option <option3>
|
||||
claptests --Option <option3>
|
||||
|
||||
For more information try --help'''
|
||||
|
||||
_excluded = '''error: The argument '--flag' cannot be used with '-F'
|
||||
|
||||
USAGE:
|
||||
\tclaptests [positional2] -F --long-option-2 <option2>
|
||||
claptests [positional2] -F --long-option-2 <option2>
|
||||
|
||||
For more information try --help'''
|
||||
|
||||
_excluded_l = '''error: The argument '-f' cannot be used with '-F'
|
||||
|
||||
USAGE:
|
||||
\tclaptests [positional2] -F --long-option-2 <option2>
|
||||
claptests [positional2] -F --long-option-2 <option2>
|
||||
|
||||
For more information try --help'''
|
||||
|
||||
_required = '''error: The following required arguments were not provided:
|
||||
\t[positional2]
|
||||
\t--long-option-2 <option2>
|
||||
[positional2]
|
||||
--long-option-2 <option2>
|
||||
|
||||
USAGE:
|
||||
\tclaptests [positional2] -F --long-option-2 <option2>
|
||||
claptests [positional2] -F --long-option-2 <option2>
|
||||
|
||||
For more information try --help'''
|
||||
|
||||
|
@ -141,7 +141,7 @@ Kevin K. <kbknapp@gmail.com>
|
|||
tests subcommands
|
||||
|
||||
USAGE:
|
||||
\tclaptests subcmd [FLAGS] [OPTIONS] [--] [ARGS]
|
||||
claptests subcmd [FLAGS] [OPTIONS] [--] [ARGS]
|
||||
|
||||
FLAGS:
|
||||
-f, --flag tests flags
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 39 KiB After Width: | Height: | Size: 45 KiB |
|
@ -1235,7 +1235,7 @@ impl<'a, 'b> Parser<'a, 'b> where 'a: 'b {
|
|||
&*self.get_required_from(&*self.required.iter().map(|&r| &*r).collect::<Vec<_>>(), Some(matcher))
|
||||
.iter()
|
||||
.fold(String::new(),
|
||||
|acc, s| acc + &format!("\n\t{}", Format::Error(s))[..]),
|
||||
|acc, s| acc + &format!("\n {}", Format::Error(s))[..]),
|
||||
&*self.create_current_usage(matcher))
|
||||
};
|
||||
return Err(err);
|
||||
|
@ -1290,7 +1290,7 @@ impl<'a, 'b> Parser<'a, 'b> where 'a: 'b {
|
|||
fn create_usage(&self, used: &[&str]) -> String {
|
||||
debugln!("fn=create_usage;");
|
||||
let mut usage = String::with_capacity(75);
|
||||
usage.push_str("USAGE:\n\t");
|
||||
usage.push_str("USAGE:\n ");
|
||||
if let Some(u) = self.meta.usage_str {
|
||||
usage.push_str(&*u);
|
||||
} else if used.is_empty() {
|
||||
|
@ -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 {
|
||||
|
@ -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() {
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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}") {
|
||||
|
|
|
@ -476,3 +476,29 @@ impl FromStr for AppSettings {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::AppSettings;
|
||||
|
||||
#[test]
|
||||
fn app_settings_fromstr() {
|
||||
assert_eq!("subcommandsnegatereqs".parse::<AppSettings>().unwrap(), AppSettings::SubcommandsNegateReqs);
|
||||
assert_eq!("subcommandsrequired".parse::<AppSettings>().unwrap(), AppSettings::SubcommandRequired);
|
||||
assert_eq!("argrequiredelsehelp".parse::<AppSettings>().unwrap(), AppSettings::ArgRequiredElseHelp);
|
||||
assert_eq!("globalversion".parse::<AppSettings>().unwrap(), AppSettings::GlobalVersion);
|
||||
assert_eq!("versionlesssubcommands".parse::<AppSettings>().unwrap(), AppSettings::VersionlessSubcommands);
|
||||
assert_eq!("unifiedhelpmessage".parse::<AppSettings>().unwrap(), AppSettings::UnifiedHelpMessage);
|
||||
assert_eq!("waitonerror".parse::<AppSettings>().unwrap(), AppSettings::WaitOnError);
|
||||
assert_eq!("subcommandrequiredelsehelp".parse::<AppSettings>().unwrap(), AppSettings::SubcommandRequiredElseHelp);
|
||||
assert_eq!("allowexternalsubcommands".parse::<AppSettings>().unwrap(), AppSettings::AllowExternalSubcommands);
|
||||
assert_eq!("trailingvararg".parse::<AppSettings>().unwrap(), AppSettings::TrailingVarArg);
|
||||
assert_eq!("nobinaryname".parse::<AppSettings>().unwrap(), AppSettings::NoBinaryName);
|
||||
assert_eq!("strictutf8".parse::<AppSettings>().unwrap(), AppSettings::StrictUtf8);
|
||||
assert_eq!("allowinvalidutf8".parse::<AppSettings>().unwrap(), AppSettings::AllowInvalidUtf8);
|
||||
assert_eq!("allowleadinghyphen".parse::<AppSettings>().unwrap(), AppSettings::AllowLeadingHyphen);
|
||||
assert_eq!("hidepossiblevaluesinhelp".parse::<AppSettings>().unwrap(), AppSettings::HidePossibleValuesInHelp);
|
||||
assert_eq!("hidden".parse::<AppSettings>().unwrap(), AppSettings::Hidden);
|
||||
assert!("hahahaha".parse::<AppSettings>().is_err());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
use std::rc::Rc;
|
||||
use std::fmt::Display;
|
||||
|
||||
use vec_map::VecMap;
|
||||
|
||||
use args::settings::ArgSettings;
|
||||
|
||||
#[doc(hidden)]
|
||||
|
@ -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>;
|
||||
}
|
||||
|
|
|
@ -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)]
|
||||
|
@ -47,9 +49,9 @@ 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)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -98,8 +100,10 @@ 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 }
|
||||
|
@ -107,6 +111,8 @@ impl<'n, 'e> AnyArg<'n, 'e> for FlagBuilder<'n, 'e> {
|
|||
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)]
|
||||
|
|
|
@ -1,126 +0,0 @@
|
|||
macro_rules! write_arg_help {
|
||||
(@opt $_self:ident, $w:ident, $tab:ident, $longest:ident, $skip_pv:ident, $nlh:ident) => {
|
||||
write_arg_help!(@short $_self, $w, $tab);
|
||||
write_arg_help!(@opt_long $_self, $w, $nlh, $longest);
|
||||
write_arg_help!(@val $_self, $w);
|
||||
if !($nlh || $_self.settings.is_set(ArgSettings::NextLineHelp)) {
|
||||
write_spaces!(if $_self.long.is_some() { $longest + 4 } else { $longest + 8 } - ($_self.to_string().len()), $w);
|
||||
}
|
||||
if let Some(h) = $_self.help {
|
||||
write_arg_help!(@help $_self, $w, h, $tab, $longest, $nlh);
|
||||
write_arg_help!(@spec_vals $_self, $w, $skip_pv);
|
||||
}
|
||||
};
|
||||
(@flag $_self:ident, $w:ident, $tab:ident, $longest:ident, $nlh:ident) => {
|
||||
write_arg_help!(@short $_self, $w, $tab);
|
||||
write_arg_help!(@flag_long $_self, $w, $longest, $nlh);
|
||||
if let Some(h) = $_self.help {
|
||||
write_arg_help!(@help $_self, $w, h, $tab, $longest, $nlh);
|
||||
}
|
||||
};
|
||||
(@pos $_self:ident, $w:ident, $tab:ident, $longest:ident, $skip_pv:ident, $nlh:ident) => {
|
||||
try!(write!($w, "{}", $tab));
|
||||
write_arg_help!(@val $_self, $w);
|
||||
if !($nlh || $_self.settings.is_set(ArgSettings::NextLineHelp)) {
|
||||
write_spaces!($longest + 4 - ($_self.to_string().len()), $w);
|
||||
}
|
||||
if let Some(h) = $_self.help {
|
||||
write_arg_help!(@help $_self, $w, h, $tab, $longest, $nlh);
|
||||
write_arg_help!(@spec_vals $_self, $w, $skip_pv);
|
||||
}
|
||||
};
|
||||
(@short $_self:ident, $w:ident, $tab:ident) => {
|
||||
try!(write!($w, "{}", $tab));
|
||||
if let Some(s) = $_self.short {
|
||||
try!(write!($w, "-{}", s));
|
||||
} else {
|
||||
try!(write!($w, "{}", $tab));
|
||||
}
|
||||
};
|
||||
(@flag_long $_self:ident, $w:ident, $longest:ident, $nlh:ident) => {
|
||||
if let Some(l) = $_self.long {
|
||||
write_arg_help!(@long $_self, $w, l);
|
||||
if !$nlh || !$_self.settings.is_set(ArgSettings::NextLineHelp) {
|
||||
write_spaces!(($longest + 4) - (l.len() + 2), $w);
|
||||
}
|
||||
} else {
|
||||
if !$nlh || !$_self.settings.is_set(ArgSettings::NextLineHelp) {
|
||||
// 6 is tab (4) + -- (2)
|
||||
write_spaces!(($longest + 6), $w);
|
||||
}
|
||||
}
|
||||
};
|
||||
(@opt_long $_self:ident, $w:ident, $nlh:ident, $longest:ident) => {
|
||||
if let Some(l) = $_self.long {
|
||||
write_arg_help!(@long $_self, $w, l);
|
||||
}
|
||||
try!(write!($w, " "));
|
||||
};
|
||||
(@long $_self:ident, $w:ident, $l:ident) => {
|
||||
try!(write!($w,
|
||||
"{}--{}",
|
||||
if $_self.short.is_some() {
|
||||
", "
|
||||
} else {
|
||||
""
|
||||
},
|
||||
$l));
|
||||
};
|
||||
(@val $_self:ident, $w:ident) => {
|
||||
if let Some(ref vec) = $_self.val_names {
|
||||
let mut it = vec.iter().peekable();
|
||||
while let Some((_, val)) = it.next() {
|
||||
try!(write!($w, "<{}>", val));
|
||||
if it.peek().is_some() { try!(write!($w, " ")); }
|
||||
}
|
||||
let num = vec.len();
|
||||
if $_self.settings.is_set(ArgSettings::Multiple) && num == 1 {
|
||||
try!(write!($w, "..."));
|
||||
}
|
||||
} else if let Some(num) = $_self.num_vals {
|
||||
for _ in 0..num {
|
||||
try!(write!($w, "<{}>", $_self.name));
|
||||
}
|
||||
} else {
|
||||
try!(write!($w,
|
||||
"<{}>{}",
|
||||
$_self.name,
|
||||
if $_self.settings.is_set(ArgSettings::Multiple) {
|
||||
"..."
|
||||
} else {
|
||||
""
|
||||
}));
|
||||
}
|
||||
};
|
||||
(@spec_vals $_self:ident, $w:ident, $skip_pv:ident) => {
|
||||
if let Some(ref pv) = $_self.default_val {
|
||||
try!(write!($w, " [default: {}]", pv));
|
||||
}
|
||||
if !$skip_pv {
|
||||
if let Some(ref pv) = $_self.possible_vals {
|
||||
try!(write!($w, " [values: {}]", pv.join(", ")));
|
||||
}
|
||||
}
|
||||
};
|
||||
(@help $_self:ident, $w:ident, $h:ident, $tab:ident, $longest:expr, $nlh:ident) => {
|
||||
if $nlh || $_self.settings.is_set(ArgSettings::NextLineHelp) {
|
||||
try!(write!($w, "\n{}{}", $tab, $tab));
|
||||
}
|
||||
if $h.contains("{n}") {
|
||||
if let Some(part) = $h.split("{n}").next() {
|
||||
try!(write!($w, "{}", part));
|
||||
}
|
||||
for part in $h.split("{n}").skip(1) {
|
||||
try!(write!($w, "\n"));
|
||||
if $nlh || $_self.settings.is_set(ArgSettings::NextLineHelp) {
|
||||
try!(write!($w, "{}{}", $tab, $tab));
|
||||
} else {
|
||||
write_spaces!($longest + 12, $w);
|
||||
}
|
||||
try!(write!($w, "{}", part));
|
||||
}
|
||||
} else {
|
||||
try!(write!($w, "{}", $h));
|
||||
}
|
||||
};
|
||||
}
|
|
@ -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)]
|
||||
|
|
|
@ -5,7 +5,7 @@ use std::io;
|
|||
|
||||
use vec_map::VecMap;
|
||||
|
||||
use args::{AnyArg, Arg};
|
||||
use args::{AnyArg, Arg, HelpWriter};
|
||||
use args::settings::{ArgFlags, ArgSettings};
|
||||
|
||||
#[allow(missing_debug_implementations)]
|
||||
|
@ -104,10 +104,10 @@ impl<'n, 'e> OptBuilder<'n, 'e> {
|
|||
ob
|
||||
}
|
||||
|
||||
pub fn write_help<W: io::Write>(&self, w: &mut W, tab: &str, longest: usize, skip_pv: bool, nlh: bool) -> io::Result<()> {
|
||||
debugln!("fn=write_help");
|
||||
write_arg_help!(@opt self, w, tab, longest, skip_pv, nlh);
|
||||
write!(w, "\n")
|
||||
pub fn write_help<W: io::Write>(&self, w: &mut W, longest: usize, skip_pv: bool, nlh: bool) -> io::Result<()> {
|
||||
let mut hw = HelpWriter::new(self, longest, nlh);
|
||||
hw.skip_pv = skip_pv;
|
||||
hw.write_to(w)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -116,29 +116,30 @@ impl<'n, 'e> Display for OptBuilder<'n, 'e> {
|
|||
debugln!("fn=fmt");
|
||||
// Write the name such --long or -l
|
||||
if let Some(l) = self.long {
|
||||
try!(write!(f, "--{}", l));
|
||||
try!(write!(f, "--{} ", l));
|
||||
} else {
|
||||
try!(write!(f, "-{}", self.short.unwrap()));
|
||||
try!(write!(f, "-{} ", self.short.unwrap()));
|
||||
}
|
||||
|
||||
// Write the values such as <name1> <name2>
|
||||
if let Some(ref vec) = self.val_names {
|
||||
for (_, n) in vec {
|
||||
debugln!("writing val_name: {}", n);
|
||||
try!(write!(f, " <{}>", n));
|
||||
let mut it = vec.iter().peekable();
|
||||
while let Some((_, val)) = it.next() {
|
||||
try!(write!(f, "<{}>", val));
|
||||
if it.peek().is_some() { try!(write!(f, " ")); }
|
||||
}
|
||||
let num = vec.len();
|
||||
if self.settings.is_set(ArgSettings::Multiple) && num == 1 {
|
||||
if self.is_set(ArgSettings::Multiple) && num == 1 {
|
||||
try!(write!(f, "..."));
|
||||
}
|
||||
} else if let Some(num) = self.num_vals {
|
||||
let mut it = (0..num).peekable();
|
||||
while let Some(_) = it.next() {
|
||||
try!(write!(f, "<{}>", self.name));
|
||||
if it.peek().is_some() { try!(write!(f, " ")); }
|
||||
}
|
||||
} else {
|
||||
let num = self.num_vals.unwrap_or(1);
|
||||
for _ in 0..num {
|
||||
try!(write!(f, " <{}>", self.name));
|
||||
}
|
||||
if self.settings.is_set(ArgSettings::Multiple) && num == 1 {
|
||||
try!(write!(f, "..."));
|
||||
}
|
||||
try!(write!(f, "<{}>{}", self.name, if self.is_set(ArgSettings::Multiple) { "..." } else { "" }));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
@ -150,6 +151,7 @@ impl<'n, 'e> AnyArg<'n, 'e> for OptBuilder<'n, 'e> {
|
|||
fn overrides(&self) -> Option<&[&'e str]> { self.overrides.as_ref().map(|o| &o[..]) }
|
||||
fn requires(&self) -> Option<&[&'e str]> { self.requires.as_ref().map(|o| &o[..]) }
|
||||
fn blacklist(&self) -> Option<&[&'e str]> { self.blacklist.as_ref().map(|o| &o[..]) }
|
||||
fn val_names(&self) -> Option<&VecMap<&'e str>> { self.val_names.as_ref().map(|o| o) }
|
||||
fn is_set(&self, s: ArgSettings) -> bool { self.settings.is_set(s) }
|
||||
fn has_switch(&self) -> bool { true }
|
||||
fn set(&mut self, s: ArgSettings) { self.settings.set(s) }
|
||||
|
@ -163,6 +165,9 @@ impl<'n, 'e> AnyArg<'n, 'e> for OptBuilder<'n, 'e> {
|
|||
fn short(&self) -> Option<char> { self.short }
|
||||
fn long(&self) -> Option<&'e str> { self.long }
|
||||
fn val_delim(&self) -> Option<char> { self.val_delim }
|
||||
fn takes_value(&self) -> bool { true }
|
||||
fn help(&self) -> Option<&'e str> { self.help }
|
||||
fn default_val(&self) -> Option<&'n str> { self.default_val }
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
|
@ -6,7 +6,7 @@ use std::io;
|
|||
use vec_map::VecMap;
|
||||
|
||||
use Arg;
|
||||
use args::AnyArg;
|
||||
use args::{AnyArg, HelpWriter};
|
||||
use args::settings::{ArgFlags, ArgSettings};
|
||||
|
||||
#[allow(missing_debug_implementations)]
|
||||
|
@ -63,7 +63,7 @@ impl<'n, 'e> PosBuilder<'n, 'e> {
|
|||
}
|
||||
|
||||
pub fn from_arg(a: &Arg<'n, 'e>, idx: u64, reqs: &mut Vec<&'e str>) -> Self {
|
||||
assert!(a.short.is_none() || a.long.is_none(),
|
||||
debug_assert!(a.short.is_none() || a.long.is_none(),
|
||||
format!("Argument \"{}\" has conflicting requirements, both index() and short(), \
|
||||
or long(), were supplied", a.name));
|
||||
|
||||
|
@ -105,9 +105,10 @@ impl<'n, 'e> PosBuilder<'n, 'e> {
|
|||
pb
|
||||
}
|
||||
|
||||
pub fn write_help<W: io::Write>(&self, w: &mut W, tab: &str, longest: usize, skip_pv: bool, nlh: bool) -> io::Result<()> {
|
||||
write_arg_help!(@pos self, w, tab, longest, skip_pv, nlh);
|
||||
write!(w, "\n")
|
||||
pub fn write_help<W: io::Write>(&self, w: &mut W, longest: usize, skip_pv: bool, nlh: bool) -> io::Result<()> {
|
||||
let mut hw = HelpWriter::new(self, longest, nlh);
|
||||
hw.skip_pv = skip_pv;
|
||||
hw.write_to(w)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -139,6 +140,7 @@ impl<'n, 'e> AnyArg<'n, 'e> for PosBuilder<'n, 'e> {
|
|||
fn overrides(&self) -> Option<&[&'e str]> { self.overrides.as_ref().map(|o| &o[..]) }
|
||||
fn requires(&self) -> Option<&[&'e str]> { self.requires.as_ref().map(|o| &o[..]) }
|
||||
fn blacklist(&self) -> Option<&[&'e str]> { self.blacklist.as_ref().map(|o| &o[..]) }
|
||||
fn val_names(&self) -> Option<&VecMap<&'e str>> { self.val_names.as_ref() }
|
||||
fn is_set(&self, s: ArgSettings) -> bool { self.settings.is_set(s) }
|
||||
fn set(&mut self, s: ArgSettings) { self.settings.set(s) }
|
||||
fn has_switch(&self) -> bool { false }
|
||||
|
@ -152,6 +154,9 @@ impl<'n, 'e> AnyArg<'n, 'e> for PosBuilder<'n, 'e> {
|
|||
fn short(&self) -> Option<char> { None }
|
||||
fn long(&self) -> Option<&'e str> { None }
|
||||
fn val_delim(&self) -> Option<char> { self.val_delim }
|
||||
fn takes_value(&self) -> bool { true }
|
||||
fn help(&self) -> Option<&'e str> { self.help }
|
||||
fn default_val(&self) -> Option<&'n str> { self.default_val }
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
254
src/args/help_writer.rs
Normal file
254
src/args/help_writer.rs
Normal file
|
@ -0,0 +1,254 @@
|
|||
use std::io;
|
||||
|
||||
use args::AnyArg;
|
||||
use args::settings::ArgSettings;
|
||||
use term;
|
||||
|
||||
const TAB: &'static str = " ";
|
||||
|
||||
pub struct HelpWriter<'a, A> where A: 'a {
|
||||
a: &'a A,
|
||||
l: usize,
|
||||
nlh: bool,
|
||||
pub skip_pv: bool,
|
||||
term_w: Option<usize>,
|
||||
}
|
||||
|
||||
impl<'a, 'n, 'e, A> HelpWriter<'a, A> where A: AnyArg<'n, 'e> {
|
||||
pub fn new(a: &'a A, l: usize, nlh: bool) -> Self {
|
||||
HelpWriter {
|
||||
a: a,
|
||||
l: l,
|
||||
nlh: nlh,
|
||||
skip_pv: false,
|
||||
term_w: term::dimensions().map(|(w, _)| w),
|
||||
}
|
||||
}
|
||||
pub fn write_to<W: io::Write>(&self, w: &mut W) -> io::Result<()> {
|
||||
debugln!("fn=write_to;");
|
||||
try!(self.short(w));
|
||||
try!(self.long(w));
|
||||
try!(self.val(w));
|
||||
try!(self.help(w));
|
||||
write!(w, "\n")
|
||||
}
|
||||
|
||||
fn short<W>(&self, w: &mut W) -> io::Result<()>
|
||||
where W: io::Write
|
||||
{
|
||||
debugln!("fn=short;");
|
||||
try!(write!(w, "{}", TAB));
|
||||
if let Some(s) = self.a.short() {
|
||||
write!(w, "-{}", s)
|
||||
} else if self.a.has_switch() {
|
||||
write!(w, "{}", TAB)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn long<W>(&self, w: &mut W) -> io::Result<()>
|
||||
where W: io::Write
|
||||
{
|
||||
debugln!("fn=long;");
|
||||
if !self.a.has_switch() {
|
||||
return Ok(());
|
||||
}
|
||||
if self.a.takes_value() {
|
||||
if let Some(l) = self.a.long() {
|
||||
try!(write!(w, "{}--{}", if self.a.short().is_some() { ", " } else { "" }, l));
|
||||
}
|
||||
try!(write!(w, " "));
|
||||
} else {
|
||||
if let Some(l) = self.a.long() {
|
||||
try!(write!(w, "{}--{}", if self.a.short().is_some() { ", " } else { "" }, l));
|
||||
if !self.nlh || !self.a.is_set(ArgSettings::NextLineHelp) {
|
||||
write_spaces!((self.l + 4) - (l.len() + 2), w);
|
||||
}
|
||||
} else {
|
||||
if !self.nlh || !self.a.is_set(ArgSettings::NextLineHelp) {
|
||||
// 6 is tab (4) + -- (2)
|
||||
write_spaces!((self.l + 6), w);
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn val<W>(&self, w: &mut W) -> io::Result<()>
|
||||
where W: io::Write
|
||||
{
|
||||
debugln!("fn=val;");
|
||||
if !self.a.takes_value() {
|
||||
return Ok(());
|
||||
}
|
||||
if let Some(ref vec) = self.a.val_names() {
|
||||
let mut it = vec.iter().peekable();
|
||||
while let Some((_, val)) = it.next() {
|
||||
try!(write!(w, "<{}>", val));
|
||||
if it.peek().is_some() { try!(write!(w, " ")); }
|
||||
}
|
||||
let num = vec.len();
|
||||
if self.a.is_set(ArgSettings::Multiple) && num == 1 {
|
||||
try!(write!(w, "..."));
|
||||
}
|
||||
} else if let Some(num) = self.a.num_vals() {
|
||||
let mut it = (0..num).peekable();
|
||||
while let Some(_) = it.next() {
|
||||
try!(write!(w, "<{}>", self.a.name()));
|
||||
if it.peek().is_some() { try!(write!(w, " ")); }
|
||||
}
|
||||
} else {
|
||||
try!(write!(w, "<{}>{}", self.a.name(), if self.a.is_set(ArgSettings::Multiple) { "..." } else { "" }));
|
||||
}
|
||||
if self.a.has_switch() {
|
||||
if !(self.nlh || self.a.is_set(ArgSettings::NextLineHelp)) {
|
||||
let self_len = self.a.to_string().len();
|
||||
// subtract ourself
|
||||
let mut spcs = self.l - self_len;
|
||||
// Since we're writing spaces from the tab point we first need to know if we
|
||||
// had a long and short, or just short
|
||||
if self.a.long().is_some() {
|
||||
// Only account 4 after the val
|
||||
spcs += 4;
|
||||
} else {
|
||||
// Only account for ', --' + 4 after the val
|
||||
spcs += 8;
|
||||
}
|
||||
write_spaces!(spcs, w);
|
||||
}
|
||||
} else {
|
||||
if !(self.nlh || self.a.is_set(ArgSettings::NextLineHelp)) {
|
||||
write_spaces!(self.l + 4 - (self.a.to_string().len()), w);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn help<W>(&self, w: &mut W) -> io::Result<()>
|
||||
where W: io::Write
|
||||
{
|
||||
debugln!("fn=help;");
|
||||
let spec_vals = self.spec_vals();
|
||||
let mut help = String::new();
|
||||
let h = self.a.help().unwrap_or("");
|
||||
let spcs = if self.nlh || self.a.is_set(ArgSettings::NextLineHelp) {
|
||||
8 // "tab" + "tab"
|
||||
} else {
|
||||
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));
|
||||
|
||||
// Is help on next line, if so newline + 2x tab
|
||||
if self.nlh || self.a.is_set(ArgSettings::NextLineHelp) {
|
||||
try!(write!(w, "\n{}{}", TAB, TAB));
|
||||
}
|
||||
|
||||
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);
|
||||
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);
|
||||
}
|
||||
debugln!("Adding idx: {}", idx);
|
||||
debugln!("At {}: {:?}", idx, help.chars().nth(idx));
|
||||
indices.push(idx);
|
||||
if &help[idx..].len() <= &avail_chars {
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (i, idx) in indices.iter().enumerate() {
|
||||
debugln!("iter;i={},idx={}", i, idx);
|
||||
let j = idx+(2*i);
|
||||
debugln!("removing: {}", j);
|
||||
debugln!("at {}: {:?}", j, help.chars().nth(j));
|
||||
help.remove(j);
|
||||
help.insert(j, '{');
|
||||
help.insert(j + 1 , 'n');
|
||||
help.insert(j + 2, '}');
|
||||
}
|
||||
}
|
||||
} else { sdebugln!("No"); }
|
||||
let help = if !help.is_empty() {
|
||||
&*help
|
||||
} else if !spec_vals.is_empty() {
|
||||
help.push_str(h);
|
||||
help.push_str(&*spec_vals);
|
||||
&*help
|
||||
} else {
|
||||
h
|
||||
};
|
||||
if help.contains("{n}") {
|
||||
if let Some(part) = help.split("{n}").next() {
|
||||
try!(write!(w, "{}", part));
|
||||
}
|
||||
for part in help.split("{n}").skip(1) {
|
||||
try!(write!(w, "\n"));
|
||||
if self.nlh || self.a.is_set(ArgSettings::NextLineHelp) {
|
||||
try!(write!(w, "{}{}", TAB, TAB));
|
||||
} else {
|
||||
if self.a.has_switch() {
|
||||
write_spaces!(self.l + 12, w);
|
||||
} else {
|
||||
write_spaces!(self.l + 8, w);
|
||||
}
|
||||
}
|
||||
try!(write!(w, "{}", part));
|
||||
}
|
||||
} else {
|
||||
try!(write!(w, "{}", help));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn spec_vals(&self) -> String {
|
||||
debugln!("fn=spec_vals;");
|
||||
if let Some(ref pv) = self.a.default_val() {
|
||||
debugln!("Writing defaults");
|
||||
return format!(" [default: {}] {}", pv,
|
||||
if !self.skip_pv {
|
||||
if let Some(ref pv) = self.a.possible_vals() {
|
||||
format!(" [values: {}]", pv.join(", "))
|
||||
} else { "".into() }
|
||||
} else { "".into() }
|
||||
);
|
||||
} else if !self.skip_pv {
|
||||
debugln!("Writing values");
|
||||
if let Some(ref pv) = self.a.possible_vals() {
|
||||
debugln!("Possible vals...{:?}", pv);
|
||||
return format!(" [values: {}]", pv.join(", "));
|
||||
}
|
||||
}
|
||||
String::new()
|
||||
}
|
||||
}
|
||||
|
||||
fn find_idx_of_space(full: &str, start: usize) -> usize {
|
||||
debugln!("fn=find_idx_of_space;");
|
||||
let haystack = &full[..start];
|
||||
debugln!("haystack: {}", haystack);
|
||||
for (i, c) in haystack.chars().rev().enumerate() {
|
||||
debugln!("iter;c={},i={}", c, i);
|
||||
if c == ' ' {
|
||||
debugln!("Found space returning start-i...{}", start - (i+1));
|
||||
return start - (i+1);
|
||||
}
|
||||
}
|
||||
0
|
||||
}
|
|
@ -7,6 +7,7 @@ pub use self::matched_arg::MatchedArg;
|
|||
pub use self::group::ArgGroup;
|
||||
pub use self::any_arg::AnyArg;
|
||||
pub use self::settings::ArgSettings;
|
||||
pub use self::help_writer::HelpWriter;
|
||||
|
||||
mod arg;
|
||||
pub mod any_arg;
|
||||
|
@ -18,3 +19,4 @@ mod matched_arg;
|
|||
mod group;
|
||||
#[allow(dead_code)]
|
||||
pub mod settings;
|
||||
mod help_writer;
|
||||
|
|
|
@ -405,6 +405,8 @@ extern crate strsim;
|
|||
extern crate ansi_term;
|
||||
#[cfg(feature = "yaml")]
|
||||
extern crate yaml_rust;
|
||||
#[cfg(feature = "wrap_help")]
|
||||
extern crate libc;
|
||||
#[macro_use]
|
||||
extern crate bitflags;
|
||||
extern crate vec_map;
|
||||
|
@ -425,6 +427,7 @@ mod fmt;
|
|||
mod suggestions;
|
||||
mod errors;
|
||||
mod osstringext;
|
||||
mod term;
|
||||
|
||||
const INTERNAL_ERROR_MSG: &'static str = "Fatal internal error. Please consider filing a bug \
|
||||
report at https://github.com/kbknapp/clap-rs/issues";
|
||||
|
|
82
src/term.rs
Normal file
82
src/term.rs
Normal file
|
@ -0,0 +1,82 @@
|
|||
// The following was taken and adapated from exa source
|
||||
// repo: https://github.com/ogham/exa
|
||||
// commit: b9eb364823d0d4f9085eb220233c704a13d0f611
|
||||
// license: MIT - Copyright (c) 2014 Benjamin Sago
|
||||
|
||||
//! System calls for getting the terminal size.
|
||||
//!
|
||||
//! Getting the terminal size is performed using an ioctl command that takes
|
||||
//! the file handle to the terminal -- which in this case, is stdout -- and
|
||||
//! populates a structure containing the values.
|
||||
//!
|
||||
//! The size is needed when the user wants the output formatted into columns:
|
||||
//! the default grid view, or the hybrid grid-details view.
|
||||
|
||||
#[cfg(all(feature = "wrap_help", not(target_os = "windows")))]
|
||||
use std::mem::zeroed;
|
||||
#[cfg(all(feature = "wrap_help", not(target_os = "windows")))]
|
||||
use libc::{c_int, c_ushort, c_ulong, STDOUT_FILENO};
|
||||
|
||||
|
||||
/// The number of rows and columns of a terminal.
|
||||
#[cfg(all(feature = "wrap_help", not(target_os = "windows")))]
|
||||
struct Winsize {
|
||||
ws_row: c_ushort,
|
||||
ws_col: c_ushort,
|
||||
}
|
||||
|
||||
// Unfortunately the actual command is not standardised...
|
||||
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
#[cfg(feature = "wrap_help")]
|
||||
static TIOCGWINSZ: c_ulong = 0x5413;
|
||||
|
||||
#[cfg(any(target_os = "macos",
|
||||
target_os = "ios",
|
||||
target_os = "bitrig",
|
||||
target_os = "dragonfly",
|
||||
target_os = "freebsd",
|
||||
target_os = "netbsd",
|
||||
target_os = "openbsd"))]
|
||||
#[cfg(feature = "wrap_help")]
|
||||
static TIOCGWINSZ: c_ulong = 0x40087468;
|
||||
|
||||
extern {
|
||||
#[cfg(all(feature = "wrap_help", not(target_os = "windows")))]
|
||||
pub fn ioctl(fd: c_int, request: c_ulong, ...) -> c_int;
|
||||
}
|
||||
|
||||
/// Runs the ioctl command. Returns (0, 0) if output is not to a terminal, or
|
||||
/// there is an error. (0, 0) is an invalid size to have anyway, which is why
|
||||
/// it can be used as a nil value.
|
||||
#[cfg(all(feature = "wrap_help", not(target_os = "windows")))]
|
||||
unsafe fn get_dimensions() -> Winsize {
|
||||
let mut window: Winsize = zeroed();
|
||||
let result = ioctl(STDOUT_FILENO, TIOCGWINSZ, &mut window);
|
||||
|
||||
if result == -1 {
|
||||
zeroed()
|
||||
}
|
||||
else {
|
||||
window
|
||||
}
|
||||
}
|
||||
|
||||
/// Query the current processes's output, returning its width and height as a
|
||||
/// number of characters. Returns `None` if the output isn't to a terminal.
|
||||
#[cfg(all(feature = "wrap_help", not(target_os = "windows")))]
|
||||
pub fn dimensions() -> Option<(usize, usize)> {
|
||||
let w = unsafe { get_dimensions() };
|
||||
|
||||
if w.ws_col == 0 || w.ws_row == 0 {
|
||||
None
|
||||
}
|
||||
else {
|
||||
Some((w.ws_col as usize, w.ws_row as usize))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(not(feature = "wrap_help"), target_os = "windows"))]
|
||||
pub fn dimensions() -> Option<(usize, usize)> {
|
||||
None
|
||||
}
|
|
@ -93,7 +93,7 @@ Kevin K.
|
|||
tests stuff
|
||||
|
||||
USAGE:
|
||||
\ttest [OPTIONS] [ARGS]
|
||||
test [OPTIONS] [ARGS]
|
||||
|
||||
OPTIONS:
|
||||
-f, --flag some flag
|
||||
|
@ -125,7 +125,7 @@ Kevin K.
|
|||
tests stuff
|
||||
|
||||
USAGE:
|
||||
\ttest [FLAGS] [OPTIONS] [ARGS]
|
||||
test [FLAGS] [OPTIONS] [ARGS]
|
||||
|
||||
FLAGS:
|
||||
-h, --help Prints help information
|
||||
|
@ -137,24 +137,3 @@ OPTIONS:
|
|||
ARGS:
|
||||
<arg1> some pos arg\n"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn app_settings_fromstr() {
|
||||
assert_eq!("subcommandsnegatereqs".parse::<AppSettings>().unwrap(), AppSettings::SubcommandsNegateReqs);
|
||||
assert_eq!("subcommandsrequired".parse::<AppSettings>().unwrap(), AppSettings::SubcommandRequired);
|
||||
assert_eq!("argrequiredelsehelp".parse::<AppSettings>().unwrap(), AppSettings::ArgRequiredElseHelp);
|
||||
assert_eq!("globalversion".parse::<AppSettings>().unwrap(), AppSettings::GlobalVersion);
|
||||
assert_eq!("versionlesssubcommands".parse::<AppSettings>().unwrap(), AppSettings::VersionlessSubcommands);
|
||||
assert_eq!("unifiedhelpmessage".parse::<AppSettings>().unwrap(), AppSettings::UnifiedHelpMessage);
|
||||
assert_eq!("waitonerror".parse::<AppSettings>().unwrap(), AppSettings::WaitOnError);
|
||||
assert_eq!("subcommandrequiredelsehelp".parse::<AppSettings>().unwrap(), AppSettings::SubcommandRequiredElseHelp);
|
||||
assert_eq!("allowexternalsubcommands".parse::<AppSettings>().unwrap(), AppSettings::AllowExternalSubcommands);
|
||||
assert_eq!("trailingvararg".parse::<AppSettings>().unwrap(), AppSettings::TrailingVarArg);
|
||||
assert_eq!("nobinaryname".parse::<AppSettings>().unwrap(), AppSettings::NoBinaryName);
|
||||
assert_eq!("strictutf8".parse::<AppSettings>().unwrap(), AppSettings::StrictUtf8);
|
||||
assert_eq!("allowinvalidutf8".parse::<AppSettings>().unwrap(), AppSettings::AllowInvalidUtf8);
|
||||
assert_eq!("allowleadinghyphen".parse::<AppSettings>().unwrap(), AppSettings::AllowLeadingHyphen);
|
||||
assert_eq!("hidepossiblevaluesinhelp".parse::<AppSettings>().unwrap(), AppSettings::HidePossibleValuesInHelp);
|
||||
assert_eq!("hidden".parse::<AppSettings>().unwrap(), AppSettings::Hidden);
|
||||
assert!("hahahaha".parse::<AppSettings>().is_err());
|
||||
}
|
||||
|
|
|
@ -72,7 +72,7 @@ Kevin K.
|
|||
tests stuff
|
||||
|
||||
USAGE:
|
||||
\ttest [FLAGS] [OPTIONS]
|
||||
test [FLAGS] [OPTIONS]
|
||||
|
||||
FLAGS:
|
||||
-f, --flag some flag
|
||||
|
@ -102,7 +102,7 @@ Kevin K.
|
|||
tests stuff
|
||||
|
||||
USAGE:
|
||||
\ttest [FLAGS] [OPTIONS] [ARGS]
|
||||
test [FLAGS] [OPTIONS] [ARGS]
|
||||
|
||||
FLAGS:
|
||||
-h, --help Prints help information
|
||||
|
|
|
@ -22,7 +22,7 @@ Kevin K.
|
|||
tests stuff
|
||||
|
||||
USAGE:
|
||||
\ttest [FLAGS] [OPTIONS]
|
||||
test [FLAGS] [OPTIONS]
|
||||
|
||||
FLAGS:
|
||||
-F, --flag2 some other flag
|
||||
|
|
Loading…
Reference in a new issue