mirror of
https://github.com/clap-rs/clap
synced 2024-12-13 22:32:33 +00:00
commit
70fa5f780a
7 changed files with 427 additions and 40 deletions
13
CHANGELOG.md
13
CHANGELOG.md
|
@ -1,3 +1,16 @@
|
||||||
|
<a name="v2.9.0"></a>
|
||||||
|
## v2.9.0 (2016-07-01)
|
||||||
|
|
||||||
|
|
||||||
|
#### Documentation
|
||||||
|
|
||||||
|
* **Completions:** adds documentation for completion scripts ([c6c519e4](https://github.com/kbknapp/clap-rs/commit/c6c519e40efd6c4533a9ef5efe8e74fd150391b7))
|
||||||
|
|
||||||
|
#### Features
|
||||||
|
|
||||||
|
* **Completions:** one can now generate a bash completions script at compile time! ([e75b6c7b](https://github.com/kbknapp/clap-rs/commit/e75b6c7b75f729afb9eb1d2a2faf61dca7674634), closes [#376](https://github.com/kbknapp/clap-rs/issues/376))
|
||||||
|
|
||||||
|
|
||||||
<a name="v2.8.0"></a>
|
<a name="v2.8.0"></a>
|
||||||
## v2.8.0 (2016-06-30)
|
## v2.8.0 (2016-06-30)
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
[package]
|
[package]
|
||||||
|
|
||||||
name = "clap"
|
name = "clap"
|
||||||
version = "2.8.0"
|
version = "2.9.0"
|
||||||
authors = ["Kevin K. <kbknapp@gmail.com>"]
|
authors = ["Kevin K. <kbknapp@gmail.com>"]
|
||||||
exclude = ["examples/*", "clap-test/*", "tests/*", "benches/*", "*.png", "clap-perf/*", "*.dot"]
|
exclude = ["examples/*", "clap-test/*", "tests/*", "benches/*", "*.png", "clap-perf/*", "*.dot"]
|
||||||
description = "A simple to use, efficient, and full featured Command Line Argument Parser"
|
description = "A simple to use, efficient, and full featured Command Line Argument Parser"
|
||||||
|
|
40
README.md
40
README.md
|
@ -1,4 +1,5 @@
|
||||||
# clap
|
clap
|
||||||
|
====
|
||||||
|
|
||||||
[![Crates.io](https://img.shields.io/crates/v/clap.svg)](https://crates.io/crates/clap) [![Crates.io](https://img.shields.io/crates/d/clap.svg)](https://crates.io/crates/clap) [![license](http://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/kbknapp/clap-rs/blob/master/LICENSE-MIT) [![Coverage Status](https://coveralls.io/repos/kbknapp/clap-rs/badge.svg?branch=master&service=github)](https://coveralls.io/github/kbknapp/clap-rs?branch=master) [![Join the chat at https://gitter.im/kbknapp/clap-rs](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/kbknapp/clap-rs?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
[![Crates.io](https://img.shields.io/crates/v/clap.svg)](https://crates.io/crates/clap) [![Crates.io](https://img.shields.io/crates/d/clap.svg)](https://crates.io/crates/clap) [![license](http://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/kbknapp/clap-rs/blob/master/LICENSE-MIT) [![Coverage Status](https://coveralls.io/repos/kbknapp/clap-rs/badge.svg?branch=master&service=github)](https://coveralls.io/github/kbknapp/clap-rs?branch=master) [![Join the chat at https://gitter.im/kbknapp/clap-rs](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/kbknapp/clap-rs?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||||
|
|
||||||
|
@ -38,6 +39,10 @@ Created by [gh-md-toc](https://github.com/ekalinin/github-markdown-toc)
|
||||||
|
|
||||||
## What's New
|
## What's New
|
||||||
|
|
||||||
|
Here's the highlights for v2.9.0
|
||||||
|
|
||||||
|
* **Completions:** one can now generate a bash completions script at compile time!
|
||||||
|
|
||||||
Here's the highlights for v2.8.0
|
Here's the highlights for v2.8.0
|
||||||
|
|
||||||
* **Arg:** adds new optional setting `Arg::require_delimiter` which requires val delimiter to parse multiple values
|
* **Arg:** adds new optional setting `Arg::require_delimiter` which requires val delimiter to parse multiple values
|
||||||
|
@ -192,6 +197,8 @@ Below are a few of the features which `clap` supports, full descriptions and usa
|
||||||
|
|
||||||
* **Auto-generated Help, Version, and Usage information**
|
* **Auto-generated Help, Version, and Usage information**
|
||||||
- Can optionally be fully, or partially overridden if you want a custom help, version, or usage
|
- Can optionally be fully, or partially overridden if you want a custom help, version, or usage
|
||||||
|
* **Auto-generated bash completion scripts at compile time**
|
||||||
|
- Even works through many multiple levels of subcommands
|
||||||
* **Flags / Switches** (i.e. bool fields)
|
* **Flags / Switches** (i.e. bool fields)
|
||||||
- Both short and long versions supported (i.e. `-f` and `--flag` respectively)
|
- Both short and long versions supported (i.e. `-f` and `--flag` respectively)
|
||||||
- Supports combining short versions (i.e. `-fBgoZ` is the same as `-f -B -g -o -Z`)
|
- Supports combining short versions (i.e. `-fBgoZ` is the same as `-f -B -g -o -Z`)
|
||||||
|
@ -340,37 +347,6 @@ fn main() {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
The following combines the previous two examples by using the less verbose `from_usage` methods and the performance of the Builder Pattern.
|
|
||||||
|
|
||||||
```rust
|
|
||||||
// (Full example with detailed comments in examples/01c_quick_example.rs)
|
|
||||||
// Must be compiled with `--features unstable`
|
|
||||||
//
|
|
||||||
// This example demonstrates clap's "usage strings" method of creating arguments which is less
|
|
||||||
// less verbose
|
|
||||||
#[macro_use]
|
|
||||||
extern crate clap;
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
let matches = clap_app!(myapp =>
|
|
||||||
(version: "1.0")
|
|
||||||
(author: "Kevin K. <kbknapp@gmail.com>")
|
|
||||||
(about: "Does awesome things")
|
|
||||||
(@arg config: -c --config +takes_value "Sets a custom config file")
|
|
||||||
(@arg INPUT: +required "Sets the input file to use")
|
|
||||||
(@arg verbose: -v ... "Sets the level of verbosity")
|
|
||||||
(@subcommand test =>
|
|
||||||
(about: "controls testing features")
|
|
||||||
(version: "1.3")
|
|
||||||
(author: "Someone E. <someone_else@other.com>")
|
|
||||||
(@arg verbose: -d --debug "Print debug information")
|
|
||||||
)
|
|
||||||
).get_matches();
|
|
||||||
|
|
||||||
// Same as previous examples...
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
This final method shows how you can use a YAML file to build your CLI and keep your Rust source tidy or support multiple localized translations by having different YAML files for each localization. First, create the `cli.yml` file to hold your CLI options, but it could be called anything we like (we'll use the same both examples above to keep it functionally equivalent):
|
This final method shows how you can use a YAML file to build your CLI and keep your Rust source tidy or support multiple localized translations by having different YAML files for each localization. First, create the `cli.yml` file to hold your CLI options, but it could be called anything we like (we'll use the same both examples above to keep it functionally equivalent):
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
mod settings;
|
mod settings;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
mod macros;
|
mod macros;
|
||||||
mod parser;
|
pub mod parser;
|
||||||
mod meta;
|
mod meta;
|
||||||
mod help;
|
mod help;
|
||||||
|
|
||||||
|
@ -27,6 +27,7 @@ use app::parser::Parser;
|
||||||
use app::help::Help;
|
use app::help::Help;
|
||||||
use errors::Error;
|
use errors::Error;
|
||||||
use errors::Result as ClapResult;
|
use errors::Result as ClapResult;
|
||||||
|
use shell::Shell;
|
||||||
|
|
||||||
/// Used to create a representation of a command line program and all possible command line
|
/// Used to create a representation of a command line program and all possible command line
|
||||||
/// arguments. Application settings are set using the "builder pattern" with the
|
/// arguments. Application settings are set using the "builder pattern" with the
|
||||||
|
@ -939,6 +940,87 @@ impl<'a, 'b> App<'a, 'b> {
|
||||||
self.p.write_version(w).map_err(From::from)
|
self.p.write_version(w).map_err(From::from)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Generate a completions file for a specified shell at compile time.
|
||||||
|
///
|
||||||
|
/// **NOTE:** to generate the this file at compile time you must use a `build.rs` "Build Script"
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// The following example generates a bash completion script via a `build.rs` script. In this
|
||||||
|
/// simple example, we'll demo a very small application with only a single subcommand and two
|
||||||
|
/// args. Real applications could be many multiple levels deep in subcommands, and have tens or
|
||||||
|
/// potentiall hundreds of arguments.
|
||||||
|
///
|
||||||
|
/// First, it helps if we separate out our `App` definition into a seperate file. Whether you
|
||||||
|
/// do this as a function, or bare App definition is a matter of personal preference.
|
||||||
|
///
|
||||||
|
/// ```ignore
|
||||||
|
/// // src/cli.rs
|
||||||
|
///
|
||||||
|
/// use clap::{App, Arg, SubCommand};
|
||||||
|
///
|
||||||
|
/// fn build_cli() -> App<'static, 'static> {
|
||||||
|
/// App::new("compl")
|
||||||
|
/// .about("Tests completions")
|
||||||
|
/// .arg(Arg::with_name("file")
|
||||||
|
/// .help("some input file"))
|
||||||
|
/// .subcommand(SubCommand::with_name("test")
|
||||||
|
/// .about("tests things")
|
||||||
|
/// .arg(Arg::with_name("case")
|
||||||
|
/// .long("case")
|
||||||
|
/// .takes_value(true)
|
||||||
|
/// .help("the case to test")))
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// In our regular code, we can simply call this `build_cli()` function, then call
|
||||||
|
/// `get_mathces()`, or any of the other normal methods directly after. For example:
|
||||||
|
///
|
||||||
|
/// ```ignore
|
||||||
|
/// src/main.rs
|
||||||
|
///
|
||||||
|
/// use cli;
|
||||||
|
///
|
||||||
|
/// fn main() {
|
||||||
|
/// let m = cli::build_cli().get_matches();
|
||||||
|
///
|
||||||
|
/// // normal logic continues...
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
/// Next, we set up our `Cargo.toml` to use a `build.rs` build script.
|
||||||
|
/// ```ignore
|
||||||
|
/// # Cargo.toml
|
||||||
|
/// build = "build.rs"
|
||||||
|
///
|
||||||
|
/// [build-dependencies]
|
||||||
|
/// clap = "2.9"
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// Next, we place a `build.rs` in our project root.
|
||||||
|
///
|
||||||
|
/// ```ignore
|
||||||
|
/// extern crate clap;
|
||||||
|
///
|
||||||
|
/// use clap::Shell;
|
||||||
|
///
|
||||||
|
/// include!("src/cli.rs");
|
||||||
|
///
|
||||||
|
/// fn main() {
|
||||||
|
/// let mut app = build_cli();
|
||||||
|
/// app.gen_completions("myapp", // We need to specify the bin name manually
|
||||||
|
/// Shell::Bash, // Then say which shell to build completions for
|
||||||
|
/// env!("OUT_DIR")); // Then say where write the completions to
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
/// Now, once we combile there will be a `bash.sh` file in the directory. Assuming we compiled
|
||||||
|
/// with debug mode, it would be somewhere similar to
|
||||||
|
/// `<project>/target/debug/build/myapp-<hash>/out/bash.sh`
|
||||||
|
pub fn gen_completions<T: Into<OsString>, S: Into<String>>(&mut self, bin_name: S, for_shell: Shell, out_dir: T) {
|
||||||
|
self.p.meta.bin_name = Some(bin_name.into());
|
||||||
|
self.p.gen_completions(for_shell, out_dir.into());
|
||||||
|
}
|
||||||
|
|
||||||
/// Starts the parsing process, upon a failed parse an error will be displayed to the user and
|
/// Starts the parsing process, upon a failed parse an error will be displayed to the user and
|
||||||
/// the process will exit with the appropriate error code. By default this method gets all user
|
/// the process will exit with the appropriate error code. By default this method gets all user
|
||||||
/// provided arguments from [`env::args_os`] in order to allow for invalid UTF-8 code points,
|
/// provided arguments from [`env::args_os`] in order to allow for invalid UTF-8 code points,
|
||||||
|
|
|
@ -24,6 +24,8 @@ use fmt::{Format, ColorWhen};
|
||||||
use osstringext::OsStrExt2;
|
use osstringext::OsStrExt2;
|
||||||
use app::meta::AppMeta;
|
use app::meta::AppMeta;
|
||||||
use args::MatchedArg;
|
use args::MatchedArg;
|
||||||
|
use shell::Shell;
|
||||||
|
use completions::ComplGen;
|
||||||
|
|
||||||
#[allow(missing_debug_implementations)]
|
#[allow(missing_debug_implementations)]
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
|
@ -31,15 +33,15 @@ pub struct Parser<'a, 'b>
|
||||||
where 'a: 'b
|
where 'a: 'b
|
||||||
{
|
{
|
||||||
required: Vec<&'b str>,
|
required: Vec<&'b str>,
|
||||||
short_list: Vec<char>,
|
pub short_list: Vec<char>,
|
||||||
long_list: Vec<&'b str>,
|
pub long_list: Vec<&'b str>,
|
||||||
blacklist: Vec<&'b str>,
|
blacklist: Vec<&'b str>,
|
||||||
// A list of possible flags
|
// A list of possible flags
|
||||||
flags: Vec<FlagBuilder<'a, 'b>>,
|
flags: Vec<FlagBuilder<'a, 'b>>,
|
||||||
// A list of possible options
|
// A list of possible options
|
||||||
opts: Vec<OptBuilder<'a, 'b>>,
|
pub opts: Vec<OptBuilder<'a, 'b>>,
|
||||||
// A list of positional arguments
|
// A list of positional arguments
|
||||||
positionals: VecMap<PosBuilder<'a, 'b>>,
|
pub positionals: VecMap<PosBuilder<'a, 'b>>,
|
||||||
// A list of subcommands
|
// A list of subcommands
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub subcommands: Vec<App<'a, 'b>>,
|
pub subcommands: Vec<App<'a, 'b>>,
|
||||||
|
@ -97,6 +99,12 @@ impl<'a, 'b> Parser<'a, 'b>
|
||||||
.nth(0);
|
.nth(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn gen_completions(&mut self, for_shell: Shell, od: OsString) {
|
||||||
|
self.propogate_help_version();
|
||||||
|
self.build_bin_names();
|
||||||
|
ComplGen::new(self, od).generate(for_shell)
|
||||||
|
}
|
||||||
|
|
||||||
// actually adds the arguments
|
// actually adds the arguments
|
||||||
pub fn add_arg(&mut self, a: &Arg<'a, 'b>) {
|
pub fn add_arg(&mut self, a: &Arg<'a, 'b>) {
|
||||||
debug_assert!(!(self.flags.iter().any(|f| &f.name == &a.name) ||
|
debug_assert!(!(self.flags.iter().any(|f| &f.name == &a.name) ||
|
||||||
|
@ -236,6 +244,7 @@ impl<'a, 'b> Parser<'a, 'b>
|
||||||
self.required.iter()
|
self.required.iter()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[cfg_attr(feature = "lints", allow(for_kv_map))]
|
#[cfg_attr(feature = "lints", allow(for_kv_map))]
|
||||||
pub fn get_required_from(&self,
|
pub fn get_required_from(&self,
|
||||||
reqs: &[&'a str],
|
reqs: &[&'a str],
|
||||||
|
@ -652,7 +661,7 @@ impl<'a, 'b> Parser<'a, 'b>
|
||||||
self.meta
|
self.meta
|
||||||
.bin_name
|
.bin_name
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.unwrap_or(&String::new()),
|
.unwrap_or(&self.meta.name.clone()),
|
||||||
if self.meta.bin_name.is_some() {
|
if self.meta.bin_name.is_some() {
|
||||||
" "
|
" "
|
||||||
} else {
|
} else {
|
||||||
|
@ -788,6 +797,41 @@ impl<'a, 'b> Parser<'a, 'b>
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn propogate_help_version(&mut self) {
|
||||||
|
debugln!("exec=propogate_help_version;");
|
||||||
|
self.create_help_and_version();
|
||||||
|
for sc in self.subcommands.iter_mut() {
|
||||||
|
sc.p.propogate_help_version();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build_bin_names(&mut self) {
|
||||||
|
debugln!("exec=build_bin_names;");
|
||||||
|
for sc in self.subcommands.iter_mut() {
|
||||||
|
debug!("bin_name set...");
|
||||||
|
if sc.p.meta.bin_name.is_none() {
|
||||||
|
sdebugln!("No");
|
||||||
|
let bin_name = format!("{}{}{}",
|
||||||
|
self.meta
|
||||||
|
.bin_name
|
||||||
|
.as_ref()
|
||||||
|
.unwrap_or(&self.meta.name.clone()),
|
||||||
|
if self.meta.bin_name.is_some() {
|
||||||
|
" "
|
||||||
|
} else {
|
||||||
|
""
|
||||||
|
},
|
||||||
|
&*sc.p.meta.name);
|
||||||
|
debugln!("Setting bin_name of {} to {}", self.meta.name, bin_name);
|
||||||
|
sc.p.meta.bin_name = Some(bin_name);
|
||||||
|
} else {
|
||||||
|
sdebugln!("yes ({:?})", sc.p.meta.bin_name);
|
||||||
|
}
|
||||||
|
debugln!("Calling build_bin_names from...{}", sc.p.meta.name);
|
||||||
|
sc.p.build_bin_names();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn parse_subcommand<I, T>(&mut self,
|
fn parse_subcommand<I, T>(&mut self,
|
||||||
sc_name: String,
|
sc_name: String,
|
||||||
matcher: &mut ArgMatcher<'a>,
|
matcher: &mut ArgMatcher<'a>,
|
||||||
|
|
261
src/completions.rs
Normal file
261
src/completions.rs
Normal file
|
@ -0,0 +1,261 @@
|
||||||
|
use std::path::PathBuf;
|
||||||
|
use std::fs::File;
|
||||||
|
use std::ffi::OsString;
|
||||||
|
use std::io::Write;
|
||||||
|
|
||||||
|
use app::parser::Parser;
|
||||||
|
use shell::Shell;
|
||||||
|
use args::{ArgSettings, OptBuilder};
|
||||||
|
|
||||||
|
macro_rules! w {
|
||||||
|
($_self:ident, $f:ident, $to_w:expr) => {
|
||||||
|
match $f.write_all($to_w) {
|
||||||
|
Ok(..) => (),
|
||||||
|
Err(..) => panic!(format!("Failed to write to file completions file")),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ComplGen<'a, 'b> where 'a: 'b {
|
||||||
|
p: &'b Parser<'a, 'b>,
|
||||||
|
out_dir: OsString,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, 'b> ComplGen<'a, 'b> {
|
||||||
|
pub fn new(p: &'b Parser<'a, 'b>, od: OsString) -> Self {
|
||||||
|
ComplGen {
|
||||||
|
p: p,
|
||||||
|
out_dir: od,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn generate(&self, for_shell: Shell) {
|
||||||
|
match for_shell {
|
||||||
|
Shell::Bash => self.gen_bash(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn gen_bash(&self) {
|
||||||
|
use std::error::Error;
|
||||||
|
let out_dir = PathBuf::from(&self.out_dir);
|
||||||
|
|
||||||
|
let mut file = match File::create(out_dir.join("bash.sh")) {
|
||||||
|
Err(why) => panic!("couldn't create bash completion file: {}",
|
||||||
|
why.description()),
|
||||||
|
Ok(file) => file,
|
||||||
|
};
|
||||||
|
w!(self, file, format!(
|
||||||
|
"_{name}() {{
|
||||||
|
local i cur prev opts cmds
|
||||||
|
COMPREPLY=()
|
||||||
|
cur=\"${{COMP_WORDS[COMP_CWORD]}}\"
|
||||||
|
prev=\"${{COMP_WORDS[COMP_CWORD-1]}}\"
|
||||||
|
cmd=\"\"
|
||||||
|
opts=\"\"
|
||||||
|
|
||||||
|
for i in ${{COMP_WORDS[@]}}
|
||||||
|
do
|
||||||
|
case \"${{i}}\" in
|
||||||
|
{name})
|
||||||
|
cmd=\"{name}\"
|
||||||
|
;;
|
||||||
|
{subcmds}
|
||||||
|
*)
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
case \"${{cmd}}\" in
|
||||||
|
{name})
|
||||||
|
opts=\"{name_opts}\"
|
||||||
|
if [[ ${{cur}} == -* || ${{COMP_CWORD}} -eq 1 ]] ; then
|
||||||
|
COMPREPLY=( $(compgen -W \"${{opts}}\" -- ${{cur}}) )
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
case \"${{prev}}\" in
|
||||||
|
{name_opts_details}
|
||||||
|
*)
|
||||||
|
COMPREPLY=()
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
;;
|
||||||
|
{subcmd_details}
|
||||||
|
esac
|
||||||
|
}}
|
||||||
|
|
||||||
|
complete -F _{name} {name}
|
||||||
|
",
|
||||||
|
name=self.p.meta.bin_name.as_ref().unwrap(),
|
||||||
|
name_opts=self.all_options_for_path(self.p.meta.bin_name.as_ref().unwrap()),
|
||||||
|
name_opts_details=self.option_details_for_path(self.p.meta.bin_name.as_ref().unwrap()),
|
||||||
|
subcmds=self.all_subcommands(),
|
||||||
|
subcmd_details=self.subcommand_details()
|
||||||
|
).as_bytes());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn all_subcommands(&self) -> String {
|
||||||
|
let mut subcmds = String::new();
|
||||||
|
let mut scs = get_all_subcommands(self.p);
|
||||||
|
scs.sort();
|
||||||
|
scs.dedup();
|
||||||
|
|
||||||
|
for sc in &scs {
|
||||||
|
subcmds = format!(
|
||||||
|
"{}
|
||||||
|
{name})
|
||||||
|
cmd+=\"_{name}\"
|
||||||
|
;;",
|
||||||
|
subcmds,
|
||||||
|
name=sc.replace("-", "_"));
|
||||||
|
}
|
||||||
|
|
||||||
|
subcmds
|
||||||
|
}
|
||||||
|
|
||||||
|
fn subcommand_details(&self) -> String {
|
||||||
|
let mut subcmd_dets = String::new();
|
||||||
|
let mut scs = get_all_subcommand_paths(self.p, true);
|
||||||
|
scs.sort();
|
||||||
|
scs.dedup();
|
||||||
|
|
||||||
|
for sc in &scs {
|
||||||
|
subcmd_dets = format!(
|
||||||
|
"{}
|
||||||
|
{subcmd})
|
||||||
|
opts=\"{sc_opts}\"
|
||||||
|
if [[ ${{cur}} == -* || ${{COMP_CWORD}} -eq {level} ]] ; then
|
||||||
|
COMPREPLY=( $(compgen -W \"${{opts}}\" -- ${{cur}}) )
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
case \"${{prev}}\" in
|
||||||
|
{opts_details}
|
||||||
|
*)
|
||||||
|
COMPREPLY=()
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
;;",
|
||||||
|
subcmd_dets,
|
||||||
|
subcmd=sc.replace("-", "_"),
|
||||||
|
sc_opts=self.all_options_for_path(&*sc),
|
||||||
|
level=sc.split("_").map(|_|1).fold(0, |acc, n| acc + n),
|
||||||
|
opts_details=self.option_details_for_path(&*sc)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
subcmd_dets
|
||||||
|
}
|
||||||
|
|
||||||
|
fn all_options_for_path(&self, path: &str) -> String {
|
||||||
|
let mut p = self.p;
|
||||||
|
for sc in path.split("_").skip(1) {
|
||||||
|
debugln!("iter;sc={}", sc);
|
||||||
|
p = &p.subcommands.iter().filter(|s| s.p.meta.name == sc).next().unwrap().p;
|
||||||
|
}
|
||||||
|
let mut opts = p.short_list.iter().fold(String::new(), |acc, s| format!("{} -{}", acc, s));
|
||||||
|
opts = format!("{} {}", opts, p.long_list.iter()
|
||||||
|
.fold(String::new(), |acc, l| format!("{} --{}", acc, l)));
|
||||||
|
opts = format!("{} {}", opts, p.positionals.values()
|
||||||
|
.fold(String::new(), |acc, p| format!("{} {}", acc, p)));
|
||||||
|
opts = format!("{} {}", opts, p.subcommands.iter()
|
||||||
|
.fold(String::new(), |acc, s| format!("{} {}", acc, s.p.meta.name)));
|
||||||
|
opts
|
||||||
|
}
|
||||||
|
|
||||||
|
fn option_details_for_path(&self, path: &str) -> String {
|
||||||
|
let mut p = self.p;
|
||||||
|
for sc in path.split("_").skip(1) {
|
||||||
|
debugln!("iter;sc={}", sc);
|
||||||
|
p = &p.subcommands.iter().filter(|s| s.p.meta.name == sc).next().unwrap().p;
|
||||||
|
}
|
||||||
|
let mut opts = String::new();
|
||||||
|
for o in &p.opts {
|
||||||
|
if let Some(l) = o.long {
|
||||||
|
opts = format!("{}
|
||||||
|
--{})
|
||||||
|
COMPREPLY=(\"{}\")
|
||||||
|
;;", opts, l, vals_for(o));
|
||||||
|
}
|
||||||
|
if let Some(s) = o.short {
|
||||||
|
opts = format!("{}
|
||||||
|
-{})
|
||||||
|
COMPREPLY=(\"{}\")
|
||||||
|
;;", opts, s, vals_for(o));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
opts
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_all_subcommands(p: &Parser) -> Vec<String> {
|
||||||
|
let mut subcmds = vec![];
|
||||||
|
if !p.has_subcommands() {
|
||||||
|
return vec![p.meta.name.clone()]
|
||||||
|
}
|
||||||
|
for sc in p.subcommands.iter().map(|ref s| s.p.meta.name.clone()) {
|
||||||
|
subcmds.push(sc);
|
||||||
|
}
|
||||||
|
for sc_v in p.subcommands.iter().map(|ref s| get_all_subcommands(&s.p)) {
|
||||||
|
subcmds.extend(sc_v);
|
||||||
|
}
|
||||||
|
subcmds
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_all_subcommand_paths(p: &Parser, first: bool) -> Vec<String> {
|
||||||
|
let mut subcmds = vec![];
|
||||||
|
if !p.has_subcommands() {
|
||||||
|
if !first {
|
||||||
|
return vec![p.meta.bin_name.as_ref().unwrap().clone().replace(" ", "_")]
|
||||||
|
}
|
||||||
|
return vec![];
|
||||||
|
}
|
||||||
|
for sc in p.subcommands.iter()
|
||||||
|
.map(|ref s| s.p.meta.bin_name.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.clone()
|
||||||
|
.replace(" ", "_")) {
|
||||||
|
subcmds.push(sc);
|
||||||
|
}
|
||||||
|
for sc_v in p.subcommands.iter().map(|ref s| get_all_subcommand_paths(&s.p, false)) {
|
||||||
|
subcmds.extend(sc_v);
|
||||||
|
}
|
||||||
|
subcmds
|
||||||
|
}
|
||||||
|
|
||||||
|
fn vals_for(o: &OptBuilder) -> String {
|
||||||
|
use args::AnyArg;
|
||||||
|
let mut ret = String::new();
|
||||||
|
if let Some(ref vec) = o.val_names() {
|
||||||
|
let mut it = vec.iter().peekable();
|
||||||
|
while let Some((_, val)) = it.next() {
|
||||||
|
ret = format!("{}<{}>{}", ret, val,
|
||||||
|
if it.peek().is_some() {
|
||||||
|
" "
|
||||||
|
} else {
|
||||||
|
""
|
||||||
|
});
|
||||||
|
}
|
||||||
|
let num = vec.len();
|
||||||
|
if o.is_set(ArgSettings::Multiple) && num == 1 {
|
||||||
|
ret = format!("{}...", ret);
|
||||||
|
}
|
||||||
|
} else if let Some(num) = o.num_vals() {
|
||||||
|
let mut it = (0..num).peekable();
|
||||||
|
while let Some(_) = it.next() {
|
||||||
|
ret = format!("{}<{}>{}", ret, o.name(),
|
||||||
|
if it.peek().is_some() {
|
||||||
|
" "
|
||||||
|
} else {
|
||||||
|
""
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if o.is_set(ArgSettings::Multiple) && num == 1 {
|
||||||
|
ret = format!("{}...", ret);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ret = format!("<{}>", o.name());
|
||||||
|
if o.is_set(ArgSettings::Multiple) {
|
||||||
|
ret = format!("{}...", ret);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ret
|
||||||
|
}
|
11
src/lib.rs
11
src/lib.rs
|
@ -423,6 +423,7 @@ pub use args::{Arg, ArgGroup, ArgMatches, ArgSettings, SubCommand, Values, OsVal
|
||||||
pub use app::{App, AppSettings};
|
pub use app::{App, AppSettings};
|
||||||
pub use fmt::Format;
|
pub use fmt::Format;
|
||||||
pub use errors::{Error, ErrorKind, Result};
|
pub use errors::{Error, ErrorKind, Result};
|
||||||
|
pub use shell::Shell;
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
mod macros;
|
mod macros;
|
||||||
|
@ -434,7 +435,17 @@ mod suggestions;
|
||||||
mod errors;
|
mod errors;
|
||||||
mod osstringext;
|
mod osstringext;
|
||||||
mod strext;
|
mod strext;
|
||||||
|
mod completions;
|
||||||
|
|
||||||
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";
|
||||||
const INVALID_UTF8: &'static str = "unexpected invalid UTF-8 code point";
|
const INVALID_UTF8: &'static str = "unexpected invalid UTF-8 code point";
|
||||||
|
|
||||||
|
mod shell {
|
||||||
|
/// Describes which shell to produce a completions file for
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
pub enum Shell {
|
||||||
|
/// Generates a .sh completion file for the Bourne Again SHell (BASH)
|
||||||
|
Bash
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue