mirror of
https://github.com/clap-rs/clap
synced 2024-11-10 14:54:15 +00:00
Merge #1678
1678: Refactor clap_generate r=CreepySkeleton a=pksunkara I have copied the code from [clap_generate]( https://github.com/clap-rs/clap_generate) and refactored the structure a bit. This new structure will allow people to write their own generators using our `Generator` trait which will contain some helpers (Still working on polishing them). Co-authored-by: Ole Martin Ruud <barskern@outlook.com> Co-authored-by: Pavan Kumar Sunkara <pavan.sss1991@gmail.com>
This commit is contained in:
commit
ad5606b5a0
20 changed files with 2724 additions and 17 deletions
|
@ -84,7 +84,10 @@ features = ["doc"]
|
|||
[workspace]
|
||||
members = [
|
||||
"clap_derive",
|
||||
"clap_generate",
|
||||
]
|
||||
default-members = [
|
||||
".", "clap_derive",
|
||||
".",
|
||||
"clap_derive",
|
||||
"clap_generate",
|
||||
]
|
||||
|
|
|
@ -46,6 +46,7 @@ proc-macro-error = "0.4.3"
|
|||
clap = { path = "../", version = "3.0.0-beta.1" }
|
||||
trybuild = "1.0.5"
|
||||
rustversion = "1"
|
||||
version-sync = "0.8"
|
||||
|
||||
[features]
|
||||
default = []
|
||||
|
|
|
@ -1,9 +1,4 @@
|
|||
# Work in Progress
|
||||
|
||||
This crate is currently a work in progress and not meant to be used. Please use [`structopt`](https://github.com/TeXitoi/structopt)
|
||||
while this crate is being built.
|
||||
|
||||
# clap_derive[![Build status](https://travis-ci.org/clap-rs/clap_derive.svg?branch=master)](https://travis-ci.org/clap-rs/clap_derive) [![](https://img.shields.io/crates/v/clap_derive.svg)](https://crates.io/crates/clap_derive) [![](https://docs.rs/clap_derive/badge.svg)](https://docs.rs/clap_derive)
|
||||
# clap_derive
|
||||
|
||||
Parse command line argument by defining a struct. It combines [structopt](https://github.com/TeXitoi/structopt) and [clap](https://crates.io/crates/clap) into a single experience. This crate is used by clap, and not meant to be used directly by
|
||||
consumers.
|
||||
|
|
50
clap_generate/Cargo.toml
Normal file
50
clap_generate/Cargo.toml
Normal file
|
@ -0,0 +1,50 @@
|
|||
[package]
|
||||
name = "clap_generate"
|
||||
version = "3.0.0-beta.1"
|
||||
edition = "2018"
|
||||
authors = [
|
||||
"Kevin K. <kbknapp@gmail.com>",
|
||||
"Pavan Kumar Sunkara <pavan.sss1991@gmail.com>",
|
||||
]
|
||||
include = [
|
||||
"src/**/*",
|
||||
"Cargo.toml",
|
||||
"README.md"
|
||||
]
|
||||
description = "A generator library used with clap for shell completion scripts, manpage, etc."
|
||||
repository = "https://github.com/clap-rs/clap/tree/master/clap_generate"
|
||||
documentation = "https://docs.rs/clap_generate"
|
||||
homepage = "https://clap.rs/"
|
||||
keywords = [
|
||||
"clap",
|
||||
"cli",
|
||||
"generate",
|
||||
"completion",
|
||||
"manpage",
|
||||
"parse"
|
||||
]
|
||||
categories = ["command-line-interface"]
|
||||
license = "MIT OR Apache-2.0"
|
||||
readme = "README.md"
|
||||
|
||||
[badges]
|
||||
is-it-maintained-issue-resolution = { repository = "clap-rs/clap" }
|
||||
is-it-maintained-open-issues = { repository = "clap-rs/clap" }
|
||||
maintenance = {status = "actively-developed"}
|
||||
|
||||
[dependencies]
|
||||
clap = { path = "../", version = "3.0.0-beta.1" }
|
||||
|
||||
[dev-dependencies]
|
||||
pretty_assertions = "0.6"
|
||||
version-sync = "0.8"
|
||||
|
||||
[features]
|
||||
default = []
|
||||
unstable = ["clap/unstable"]
|
||||
nightly = ["clap/nightly"]
|
||||
debug = ["clap/debug"]
|
||||
doc = []
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
features = ["doc"]
|
1
clap_generate/README.md
Normal file
1
clap_generate/README.md
Normal file
|
@ -0,0 +1 @@
|
|||
# clap_generate
|
11
clap_generate/examples/bash_completion.rs
Normal file
11
clap_generate/examples/bash_completion.rs
Normal file
|
@ -0,0 +1,11 @@
|
|||
use clap::App;
|
||||
use clap_generate::{generate, generators::Bash};
|
||||
use std::io;
|
||||
|
||||
fn main() {
|
||||
let mut app = App::new("myapp")
|
||||
.subcommand(App::new("test").subcommand(App::new("config")))
|
||||
.subcommand(App::new("hello"));
|
||||
|
||||
generate::<Bash, _>(&mut app, "myapp", &mut io::stdout());
|
||||
}
|
323
clap_generate/src/generators/mod.rs
Normal file
323
clap_generate/src/generators/mod.rs
Normal file
|
@ -0,0 +1,323 @@
|
|||
mod shells;
|
||||
|
||||
// Std
|
||||
use std::io::Write;
|
||||
|
||||
// Internal
|
||||
use clap::*;
|
||||
pub use shells::*;
|
||||
|
||||
/// Generator trait which can be used to write generators
|
||||
pub trait Generator {
|
||||
/// Returns the file name that is created when this generator is called during compile time.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use std::io::Write;
|
||||
/// # use clap::App;
|
||||
/// use clap_generate::Generator;
|
||||
///
|
||||
/// pub struct Fish;
|
||||
///
|
||||
/// impl Generator for Fish {
|
||||
/// # fn generate(app: &App, buf: &mut dyn Write) {}
|
||||
/// fn file_name(name: &str) -> String {
|
||||
/// format!("{}.fish", name)
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
fn file_name(name: &str) -> String;
|
||||
|
||||
/// Generates output out of [`clap::App`](../clap/struct.App.html).
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// The following example generator displays the [`clap::App`](../clap/struct.App.html)
|
||||
/// as if it is printed using [`std::println`](https://doc.rust-lang.org/std/macro.println.html).
|
||||
///
|
||||
/// ```
|
||||
/// use std::{io::Write, fmt::write};
|
||||
/// use clap::App;
|
||||
/// use clap_generate::Generator;
|
||||
///
|
||||
/// pub struct ClapDebug;
|
||||
///
|
||||
/// impl Generator for ClapDebug {
|
||||
/// fn generate(app: &App, buf: &mut dyn Write) {
|
||||
/// write!(buf, "{}", app).unwrap();
|
||||
/// }
|
||||
/// # fn file_name(name: &str) -> String {
|
||||
/// # name.into()
|
||||
/// # }
|
||||
/// }
|
||||
/// ```
|
||||
fn generate(app: &App, buf: &mut dyn Write);
|
||||
|
||||
/// Gets all subcommands including child subcommands in the form of `("name", "bin_name")`.
|
||||
///
|
||||
/// Subcommand `rustup toolchain install` would be converted to
|
||||
/// `("install", "rustup toolchain install")`.
|
||||
fn all_subcommands(app: &App) -> Vec<(String, String)> {
|
||||
let mut subcmds: Vec<_> = Self::subcommands(app);
|
||||
|
||||
for sc_v in subcommands!(app).map(|s| Self::all_subcommands(&s)) {
|
||||
subcmds.extend(sc_v);
|
||||
}
|
||||
|
||||
subcmds
|
||||
}
|
||||
|
||||
/// Finds the subcommand [`clap::App`][clap] from the given [`clap::App`][clap] with the given path.
|
||||
///
|
||||
/// **NOTE:** `path` should not contain the root `bin_name`.
|
||||
///
|
||||
/// [clap]: ../clap/struct.App.html
|
||||
fn find_subcommand_with_path<'b>(p: &'b App<'b>, path: Vec<&str>) -> &'b App<'b> {
|
||||
let mut app = p;
|
||||
|
||||
for sc in path {
|
||||
app = find_subcmd!(app, sc).unwrap();
|
||||
}
|
||||
|
||||
app
|
||||
}
|
||||
|
||||
/// Gets subcommands of [`clap::App`](../clap/struct.App.html) in the form of `("name", "bin_name")`.
|
||||
///
|
||||
/// Subcommand `rustup toolchain install` would be converted to
|
||||
/// `("install", "rustup toolchain install")`.
|
||||
fn subcommands(p: &App) -> Vec<(String, String)> {
|
||||
debugln!("subcommands: name={}", p.name);
|
||||
debugln!("subcommands: Has subcommands...{:?}", p.has_subcommands());
|
||||
|
||||
let mut subcmds = vec![];
|
||||
|
||||
if !p.has_subcommands() {
|
||||
return subcmds;
|
||||
}
|
||||
|
||||
for sc in &p.subcommands {
|
||||
let sc_bin_name = sc.get_bin_name().unwrap();
|
||||
|
||||
debugln!(
|
||||
"subcommands:iter: name={}, bin_name={}",
|
||||
sc.name,
|
||||
sc_bin_name
|
||||
);
|
||||
|
||||
subcmds.push((sc.name.clone(), sc_bin_name.to_string()));
|
||||
}
|
||||
|
||||
subcmds
|
||||
}
|
||||
|
||||
/// Gets all the short options and flags of a [`clap::App`](../clap/struct.App.html).
|
||||
/// Includes `h` and `V` depending on the [`clap::AppSettings`](../clap/enum.AppSettings.html).
|
||||
fn shorts<'b>(p: &'b App<'b>) -> Vec<char> {
|
||||
debugln!("shorts: name={}", p.name);
|
||||
|
||||
let mut shorts: Vec<char> = p
|
||||
.args
|
||||
.args
|
||||
.iter()
|
||||
.filter_map(|a| {
|
||||
if a.index.is_none() && a.short.is_some() {
|
||||
Some(a.short.unwrap())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
if shorts.iter().find(|x| **x == 'h').is_none() {
|
||||
shorts.push('h');
|
||||
}
|
||||
|
||||
if !p.is_set(AppSettings::DisableVersion) && shorts.iter().find(|x| **x == 'V').is_none() {
|
||||
shorts.push('V');
|
||||
}
|
||||
|
||||
shorts
|
||||
}
|
||||
|
||||
/// Gets all the long options and flags of a [`clap::App`](../clap/struct.App.html).
|
||||
/// Includes `help` and `version` depending on the [`clap::AppSettings`](../clap/enum.AppSettings.html).
|
||||
fn longs<'b>(p: &'b App<'b>) -> Vec<String> {
|
||||
debugln!("longs: name={}", p.name);
|
||||
|
||||
let mut longs: Vec<String> = p
|
||||
.args
|
||||
.args
|
||||
.iter()
|
||||
.filter_map(|a| {
|
||||
if a.index.is_none() && a.long.is_some() {
|
||||
Some(a.long.unwrap().to_string())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
if longs.iter().find(|x| **x == "help").is_none() {
|
||||
longs.push(String::from("help"));
|
||||
}
|
||||
|
||||
if !p.is_set(AppSettings::DisableVersion)
|
||||
&& longs.iter().find(|x| **x == "version").is_none()
|
||||
{
|
||||
longs.push(String::from("version"));
|
||||
}
|
||||
|
||||
longs
|
||||
}
|
||||
|
||||
/// Gets all the flags of a [`clap::App`](../clap/struct.App.html).
|
||||
/// Includes `help` and `version` depending on the [`clap::AppSettings`](../clap/enum.AppSettings.html).
|
||||
fn flags<'b>(p: &'b App<'b>) -> Vec<Arg> {
|
||||
debugln!("flags: name={}", p.name);
|
||||
|
||||
let mut flags: Vec<_> = flags!(p).cloned().collect();
|
||||
|
||||
if flags.iter().find(|x| x.name == "help").is_none() {
|
||||
flags.push(
|
||||
Arg::with_name("help")
|
||||
.short('h')
|
||||
.long("help")
|
||||
.help("Prints help information"),
|
||||
);
|
||||
}
|
||||
|
||||
if !p.is_set(AppSettings::DisableVersion)
|
||||
&& flags.iter().find(|x| x.name == "version").is_none()
|
||||
{
|
||||
flags.push(
|
||||
Arg::with_name("version")
|
||||
.short('V')
|
||||
.long("version")
|
||||
.help("Prints version information"),
|
||||
);
|
||||
}
|
||||
|
||||
flags
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use pretty_assertions::assert_eq;
|
||||
|
||||
struct Foo;
|
||||
|
||||
impl Generator for Foo {
|
||||
fn generate(_: &App, _: &mut dyn Write) {}
|
||||
|
||||
fn file_name(name: &str) -> String {
|
||||
name.to_string()
|
||||
}
|
||||
}
|
||||
|
||||
fn common() -> App<'static> {
|
||||
let mut app = App::new("myapp")
|
||||
.subcommand(
|
||||
App::new("test")
|
||||
.subcommand(App::new("config"))
|
||||
.arg(Arg::with_name("file").short('f').long("file")),
|
||||
)
|
||||
.subcommand(App::new("hello"))
|
||||
.bin_name("my-app");
|
||||
|
||||
app._build();
|
||||
app._build_bin_names();
|
||||
app
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_subcommands() {
|
||||
let app = common();
|
||||
|
||||
assert_eq!(
|
||||
Foo::subcommands(&app),
|
||||
vec![
|
||||
("test".to_string(), "my-app test".to_string()),
|
||||
("hello".to_string(), "my-app hello".to_string()),
|
||||
("help".to_string(), "my-app help".to_string()),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_all_subcommands() {
|
||||
let app = common();
|
||||
|
||||
assert_eq!(
|
||||
Foo::all_subcommands(&app),
|
||||
vec![
|
||||
("test".to_string(), "my-app test".to_string()),
|
||||
("hello".to_string(), "my-app hello".to_string()),
|
||||
("help".to_string(), "my-app help".to_string()),
|
||||
("config".to_string(), "my-app test config".to_string()),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_find_subcommand_with_path() {
|
||||
let app = common();
|
||||
let sc_app = Foo::find_subcommand_with_path(&app, "test config".split(" ").collect());
|
||||
|
||||
assert_eq!(sc_app.name, "config");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_flags() {
|
||||
let app = common();
|
||||
let flags = Foo::flags(&app);
|
||||
|
||||
assert_eq!(flags.len(), 2);
|
||||
assert_eq!(flags[0].long, Some("help"));
|
||||
assert_eq!(flags[1].long, Some("version"));
|
||||
|
||||
let sc_flags = Foo::flags(Foo::find_subcommand_with_path(&app, vec!["test"]));
|
||||
|
||||
assert_eq!(sc_flags.len(), 3);
|
||||
assert_eq!(sc_flags[0].long, Some("file"));
|
||||
assert_eq!(sc_flags[1].long, Some("help"));
|
||||
assert_eq!(sc_flags[2].long, Some("version"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_shorts() {
|
||||
let app = common();
|
||||
let shorts = Foo::shorts(&app);
|
||||
|
||||
assert_eq!(shorts.len(), 2);
|
||||
assert_eq!(shorts[0], 'h');
|
||||
assert_eq!(shorts[1], 'V');
|
||||
|
||||
let sc_shorts = Foo::shorts(Foo::find_subcommand_with_path(&app, vec!["test"]));
|
||||
|
||||
assert_eq!(sc_shorts.len(), 3);
|
||||
assert_eq!(sc_shorts[0], 'f');
|
||||
assert_eq!(sc_shorts[1], 'h');
|
||||
assert_eq!(sc_shorts[2], 'V');
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_longs() {
|
||||
let app = common();
|
||||
let longs = Foo::longs(&app);
|
||||
|
||||
assert_eq!(longs.len(), 2);
|
||||
assert_eq!(longs[0], "help");
|
||||
assert_eq!(longs[1], "version");
|
||||
|
||||
let sc_longs = Foo::longs(Foo::find_subcommand_with_path(&app, vec!["test"]));
|
||||
|
||||
assert_eq!(sc_longs.len(), 3);
|
||||
assert_eq!(sc_longs[0], "file");
|
||||
assert_eq!(sc_longs[1], "help");
|
||||
assert_eq!(sc_longs[2], "version");
|
||||
}
|
||||
}
|
208
clap_generate/src/generators/shells/bash.rs
Normal file
208
clap_generate/src/generators/shells/bash.rs
Normal file
|
@ -0,0 +1,208 @@
|
|||
// Std
|
||||
use std::io::Write;
|
||||
|
||||
// Internal
|
||||
use crate::Generator;
|
||||
use clap::*;
|
||||
|
||||
/// Generate bash completion file
|
||||
pub struct Bash;
|
||||
|
||||
impl Generator for Bash {
|
||||
fn file_name(name: &str) -> String {
|
||||
format!("{}.bash", name)
|
||||
}
|
||||
|
||||
fn generate(app: &App, buf: &mut dyn Write) {
|
||||
let bin_name = app.get_bin_name().unwrap();
|
||||
|
||||
w!(
|
||||
buf,
|
||||
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
|
||||
COMPREPLY=( $(compgen -W \"${{opts}}\" -- ${{cur}}) )
|
||||
return 0
|
||||
;;
|
||||
{subcmd_details}
|
||||
esac
|
||||
}}
|
||||
|
||||
complete -F _{name} -o bashdefault -o default {name}
|
||||
",
|
||||
name = bin_name,
|
||||
name_opts = all_options_for_path(app, bin_name),
|
||||
name_opts_details = option_details_for_path(app, bin_name),
|
||||
subcmds = all_subcommands(app),
|
||||
subcmd_details = subcommand_details(app)
|
||||
)
|
||||
.as_bytes()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn all_subcommands(app: &App) -> String {
|
||||
debugln!("Bash::all_subcommands;");
|
||||
|
||||
let mut subcmds = String::new();
|
||||
let mut scs = Bash::all_subcommands(app)
|
||||
.iter()
|
||||
.map(|x| x.0.clone())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
scs.sort();
|
||||
scs.dedup();
|
||||
|
||||
for sc in &scs {
|
||||
subcmds = format!(
|
||||
"{}
|
||||
{name})
|
||||
cmd+=\"__{fn_name}\"
|
||||
;;",
|
||||
subcmds,
|
||||
name = sc,
|
||||
fn_name = sc.replace("-", "__")
|
||||
);
|
||||
}
|
||||
|
||||
subcmds
|
||||
}
|
||||
|
||||
fn subcommand_details(app: &App) -> String {
|
||||
debugln!("Bash::subcommand_details;");
|
||||
|
||||
let mut subcmd_dets = String::new();
|
||||
let mut scs = Bash::all_subcommands(app)
|
||||
.iter()
|
||||
.map(|x| x.1.replace(" ", "__"))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
scs.sort();
|
||||
|
||||
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
|
||||
COMPREPLY=( $(compgen -W \"${{opts}}\" -- ${{cur}}) )
|
||||
return 0
|
||||
;;",
|
||||
subcmd_dets,
|
||||
subcmd = sc.replace("-", "__"),
|
||||
sc_opts = all_options_for_path(app, &*sc),
|
||||
level = sc.split("__").map(|_| 1).sum::<u64>(),
|
||||
opts_details = option_details_for_path(app, &*sc)
|
||||
);
|
||||
}
|
||||
|
||||
subcmd_dets
|
||||
}
|
||||
|
||||
fn option_details_for_path(app: &App, path: &str) -> String {
|
||||
debugln!("Bash::option_details_for_path: path={}", path);
|
||||
|
||||
let p = Bash::find_subcommand_with_path(app, path.split("__").skip(1).collect());
|
||||
let mut opts = String::new();
|
||||
|
||||
for o in opts!(p) {
|
||||
if let Some(l) = o.long {
|
||||
opts = format!(
|
||||
"{}
|
||||
--{})
|
||||
COMPREPLY=({})
|
||||
return 0
|
||||
;;",
|
||||
opts,
|
||||
l,
|
||||
vals_for(o)
|
||||
);
|
||||
}
|
||||
|
||||
if let Some(s) = o.short {
|
||||
opts = format!(
|
||||
"{}
|
||||
-{})
|
||||
COMPREPLY=({})
|
||||
return 0
|
||||
;;",
|
||||
opts,
|
||||
s,
|
||||
vals_for(o)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
opts
|
||||
}
|
||||
|
||||
fn vals_for(o: &Arg) -> String {
|
||||
debugln!("Bash::vals_for: o={}", o.name);
|
||||
|
||||
if let Some(ref vals) = o.possible_vals {
|
||||
format!("$(compgen -W \"{}\" -- ${{cur}})", vals.join(" "))
|
||||
} else {
|
||||
String::from("$(compgen -f ${cur})")
|
||||
}
|
||||
}
|
||||
|
||||
fn all_options_for_path(app: &App, path: &str) -> String {
|
||||
debugln!("Bash::all_options_for_path: path={}", path);
|
||||
|
||||
let p = Bash::find_subcommand_with_path(app, path.split("__").skip(1).collect());
|
||||
let scs: Vec<_> = Bash::subcommands(p).iter().map(|x| x.0.clone()).collect();
|
||||
|
||||
let opts = format!(
|
||||
"{shorts} {longs} {pos} {subcmds}",
|
||||
shorts = Bash::shorts(p)
|
||||
.iter()
|
||||
.fold(String::new(), |acc, s| format!("{} -{}", acc, s)),
|
||||
longs = Bash::longs(p)
|
||||
.iter()
|
||||
.fold(String::new(), |acc, l| format!("{} --{}", acc, l)),
|
||||
pos = positionals!(p).fold(String::new(), |acc, p| format!("{} {}", acc, p)),
|
||||
subcmds = scs.join(" "),
|
||||
);
|
||||
|
||||
opts
|
||||
}
|
133
clap_generate/src/generators/shells/elvish.rs
Normal file
133
clap_generate/src/generators/shells/elvish.rs
Normal file
|
@ -0,0 +1,133 @@
|
|||
// Std
|
||||
use std::io::Write;
|
||||
|
||||
// Internal
|
||||
use crate::Generator;
|
||||
use crate::INTERNAL_ERROR_MSG;
|
||||
use clap::*;
|
||||
|
||||
/// Generate elvish completion file
|
||||
pub struct Elvish;
|
||||
|
||||
impl Generator for Elvish {
|
||||
fn file_name(name: &str) -> String {
|
||||
format!("{}.elv", name)
|
||||
}
|
||||
|
||||
fn generate(app: &App, buf: &mut dyn Write) {
|
||||
let bin_name = app.get_bin_name().unwrap();
|
||||
|
||||
let mut names = vec![];
|
||||
let subcommands_cases = generate_inner(app, "", &mut names);
|
||||
|
||||
let result = format!(
|
||||
r#"
|
||||
edit:completion:arg-completer[{bin_name}] = [@words]{{
|
||||
fn spaces [n]{{
|
||||
repeat $n ' ' | joins ''
|
||||
}}
|
||||
fn cand [text desc]{{
|
||||
edit:complex-candidate $text &display-suffix=' '(spaces (- 14 (wcswidth $text)))$desc
|
||||
}}
|
||||
command = '{bin_name}'
|
||||
for word $words[1:-1] {{
|
||||
if (has-prefix $word '-') {{
|
||||
break
|
||||
}}
|
||||
command = $command';'$word
|
||||
}}
|
||||
completions = [{subcommands_cases}
|
||||
]
|
||||
$completions[$command]
|
||||
}}
|
||||
"#,
|
||||
bin_name = bin_name,
|
||||
subcommands_cases = subcommands_cases
|
||||
);
|
||||
|
||||
w!(buf, result.as_bytes());
|
||||
}
|
||||
}
|
||||
|
||||
// Escape string inside single quotes
|
||||
fn escape_string(string: &str) -> String {
|
||||
string.replace("'", "''")
|
||||
}
|
||||
|
||||
fn get_tooltip<T: ToString>(help: Option<&str>, data: T) -> String {
|
||||
match help {
|
||||
Some(help) => escape_string(help),
|
||||
_ => data.to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
fn generate_inner<'b>(
|
||||
p: &'b App<'b>,
|
||||
previous_command_name: &str,
|
||||
names: &mut Vec<&'b str>,
|
||||
) -> String {
|
||||
debugln!("Elvish::generate_inner;");
|
||||
|
||||
let command_name = if previous_command_name.is_empty() {
|
||||
p.get_bin_name().expect(INTERNAL_ERROR_MSG).to_string()
|
||||
} else {
|
||||
format!("{};{}", previous_command_name, &p.name)
|
||||
};
|
||||
|
||||
let mut completions = String::new();
|
||||
let preamble = String::from("\n cand ");
|
||||
|
||||
for option in opts!(p) {
|
||||
if let Some(data) = option.short {
|
||||
let tooltip = get_tooltip(option.help, data);
|
||||
|
||||
completions.push_str(&preamble);
|
||||
completions.push_str(format!("-{} '{}'", data, tooltip).as_str());
|
||||
}
|
||||
|
||||
if let Some(data) = option.long {
|
||||
let tooltip = get_tooltip(option.help, data);
|
||||
|
||||
completions.push_str(&preamble);
|
||||
completions.push_str(format!("--{} '{}'", data, tooltip).as_str());
|
||||
}
|
||||
}
|
||||
|
||||
for flag in Elvish::flags(p) {
|
||||
if let Some(data) = flag.short {
|
||||
let tooltip = get_tooltip(flag.help, data);
|
||||
|
||||
completions.push_str(&preamble);
|
||||
completions.push_str(format!("-{} '{}'", data, tooltip).as_str());
|
||||
}
|
||||
|
||||
if let Some(data) = flag.long {
|
||||
let tooltip = get_tooltip(flag.help, data);
|
||||
|
||||
completions.push_str(&preamble);
|
||||
completions.push_str(format!("--{} '{}'", data, tooltip).as_str());
|
||||
}
|
||||
}
|
||||
|
||||
for subcommand in &p.subcommands {
|
||||
let data = &subcommand.name;
|
||||
let tooltip = get_tooltip(subcommand.about, data);
|
||||
|
||||
completions.push_str(&preamble);
|
||||
completions.push_str(format!("{} '{}'", data, tooltip).as_str());
|
||||
}
|
||||
|
||||
let mut subcommands_cases = format!(
|
||||
r"
|
||||
&'{}'= {{{}
|
||||
}}",
|
||||
&command_name, completions
|
||||
);
|
||||
|
||||
for subcommand in &p.subcommands {
|
||||
let subcommand_subcommands_cases = generate_inner(&subcommand, &command_name, names);
|
||||
subcommands_cases.push_str(&subcommand_subcommands_cases);
|
||||
}
|
||||
|
||||
subcommands_cases
|
||||
}
|
117
clap_generate/src/generators/shells/fish.rs
Normal file
117
clap_generate/src/generators/shells/fish.rs
Normal file
|
@ -0,0 +1,117 @@
|
|||
// Std
|
||||
use std::io::Write;
|
||||
|
||||
// Internal
|
||||
use crate::Generator;
|
||||
use clap::*;
|
||||
|
||||
/// Generate fish completion file
|
||||
pub struct Fish;
|
||||
|
||||
impl Generator for Fish {
|
||||
fn file_name(name: &str) -> String {
|
||||
format!("{}.fish", name)
|
||||
}
|
||||
|
||||
fn generate(app: &App, buf: &mut dyn Write) {
|
||||
let command = app.get_bin_name().unwrap();
|
||||
let mut buffer = String::new();
|
||||
|
||||
gen_fish_inner(command, app, &mut buffer);
|
||||
w!(buf, buffer.as_bytes());
|
||||
}
|
||||
}
|
||||
|
||||
// Escape string inside single quotes
|
||||
fn escape_string(string: &str) -> String {
|
||||
string.replace("\\", "\\\\").replace("'", "\\'")
|
||||
}
|
||||
|
||||
fn gen_fish_inner(root_command: &str, app: &App, buffer: &mut String) {
|
||||
debugln!("Fish::gen_fish_inner;");
|
||||
// example :
|
||||
//
|
||||
// complete
|
||||
// -c {command}
|
||||
// -d "{description}"
|
||||
// -s {short}
|
||||
// -l {long}
|
||||
// -a "{possible_arguments}"
|
||||
// -r # if require parameter
|
||||
// -f # don't use file completion
|
||||
// -n "__fish_use_subcommand" # complete for command "myprog"
|
||||
// -n "__fish_seen_subcommand_from subcmd1" # complete for command "myprog subcmd1"
|
||||
|
||||
let mut basic_template = format!("complete -c {} -n ", root_command);
|
||||
let mut bin_name = app.get_bin_name().unwrap();
|
||||
|
||||
if root_command == bin_name {
|
||||
basic_template.push_str("\"__fish_use_subcommand\"");
|
||||
} else {
|
||||
bin_name = &app.name;
|
||||
basic_template.push_str(format!("\"__fish_seen_subcommand_from {}\"", bin_name).as_str());
|
||||
}
|
||||
|
||||
debugln!("Fish::gen_fish_inner; bin_name={}", bin_name);
|
||||
|
||||
for option in opts!(app) {
|
||||
let mut template = basic_template.clone();
|
||||
|
||||
if let Some(data) = option.short {
|
||||
template.push_str(format!(" -s {}", data).as_str());
|
||||
}
|
||||
|
||||
if let Some(data) = option.long {
|
||||
template.push_str(format!(" -l {}", data).as_str());
|
||||
}
|
||||
|
||||
if let Some(data) = option.help {
|
||||
template.push_str(format!(" -d '{}'", escape_string(data)).as_str());
|
||||
}
|
||||
|
||||
if let Some(ref data) = option.possible_vals {
|
||||
template.push_str(format!(" -r -f -a \"{}\"", data.join(" ")).as_str());
|
||||
}
|
||||
|
||||
buffer.push_str(template.as_str());
|
||||
buffer.push_str("\n");
|
||||
}
|
||||
|
||||
for flag in Fish::flags(app) {
|
||||
let mut template = basic_template.clone();
|
||||
|
||||
if let Some(data) = flag.short {
|
||||
template.push_str(format!(" -s {}", data).as_str());
|
||||
}
|
||||
|
||||
if let Some(data) = flag.long {
|
||||
template.push_str(format!(" -l {}", data).as_str());
|
||||
}
|
||||
|
||||
if let Some(data) = flag.help {
|
||||
template.push_str(format!(" -d '{}'", escape_string(data)).as_str());
|
||||
}
|
||||
|
||||
buffer.push_str(template.as_str());
|
||||
buffer.push_str("\n");
|
||||
}
|
||||
|
||||
for subcommand in &app.subcommands {
|
||||
let mut template = basic_template.clone();
|
||||
|
||||
template.push_str(" -f");
|
||||
template.push_str(format!(" -a \"{}\"", &subcommand.name).as_str());
|
||||
|
||||
if let Some(data) = subcommand.about {
|
||||
template.push_str(format!(" -d '{}'", escape_string(data)).as_str())
|
||||
}
|
||||
|
||||
buffer.push_str(template.as_str());
|
||||
buffer.push_str("\n");
|
||||
}
|
||||
|
||||
// generate options of subcommands
|
||||
for subcommand in &app.subcommands {
|
||||
gen_fish_inner(root_command, subcommand, buffer);
|
||||
}
|
||||
}
|
11
clap_generate/src/generators/shells/mod.rs
Normal file
11
clap_generate/src/generators/shells/mod.rs
Normal file
|
@ -0,0 +1,11 @@
|
|||
mod bash;
|
||||
mod elvish;
|
||||
mod fish;
|
||||
mod powershell;
|
||||
mod zsh;
|
||||
|
||||
pub use bash::Bash;
|
||||
pub use elvish::Elvish;
|
||||
pub use fish::Fish;
|
||||
pub use powershell::PowerShell;
|
||||
pub use zsh::Zsh;
|
171
clap_generate/src/generators/shells/powershell.rs
Normal file
171
clap_generate/src/generators/shells/powershell.rs
Normal file
|
@ -0,0 +1,171 @@
|
|||
// Std
|
||||
use std::io::Write;
|
||||
|
||||
// Internal
|
||||
use crate::Generator;
|
||||
use crate::INTERNAL_ERROR_MSG;
|
||||
use clap::*;
|
||||
|
||||
/// Generate powershell completion file
|
||||
pub struct PowerShell;
|
||||
|
||||
impl Generator for PowerShell {
|
||||
fn file_name(name: &str) -> String {
|
||||
format!("_{}.ps1", name)
|
||||
}
|
||||
|
||||
fn generate(app: &App, buf: &mut dyn Write) {
|
||||
let bin_name = app.get_bin_name().unwrap();
|
||||
|
||||
let mut names = vec![];
|
||||
let subcommands_cases = generate_inner(app, "", &mut names);
|
||||
|
||||
let result = format!(
|
||||
r#"
|
||||
using namespace System.Management.Automation
|
||||
using namespace System.Management.Automation.Language
|
||||
|
||||
Register-ArgumentCompleter -Native -CommandName '{bin_name}' -ScriptBlock {{
|
||||
param($wordToComplete, $commandAst, $cursorPosition)
|
||||
|
||||
$commandElements = $commandAst.CommandElements
|
||||
$command = @(
|
||||
'{bin_name}'
|
||||
for ($i = 1; $i -lt $commandElements.Count; $i++) {{
|
||||
$element = $commandElements[$i]
|
||||
if ($element -isnot [StringConstantExpressionAst] -or
|
||||
$element.StringConstantType -ne [StringConstantType]::BareWord -or
|
||||
$element.Value.StartsWith('-')) {{
|
||||
break
|
||||
}}
|
||||
$element.Value
|
||||
}}) -join ';'
|
||||
|
||||
$completions = @(switch ($command) {{{subcommands_cases}
|
||||
}})
|
||||
|
||||
$completions.Where{{ $_.CompletionText -like "$wordToComplete*" }} |
|
||||
Sort-Object -Property ListItemText
|
||||
}}
|
||||
"#,
|
||||
bin_name = bin_name,
|
||||
subcommands_cases = subcommands_cases
|
||||
);
|
||||
|
||||
w!(buf, result.as_bytes());
|
||||
}
|
||||
}
|
||||
|
||||
// Escape string inside single quotes
|
||||
fn escape_string(string: &str) -> String {
|
||||
string.replace("'", "''")
|
||||
}
|
||||
|
||||
fn get_tooltip<T: ToString>(help: Option<&str>, data: T) -> String {
|
||||
match help {
|
||||
Some(help) => escape_string(&help),
|
||||
_ => data.to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
fn generate_inner<'b>(
|
||||
p: &'b App<'b>,
|
||||
previous_command_name: &str,
|
||||
names: &mut Vec<&'b str>,
|
||||
) -> String {
|
||||
debugln!("PowerShell::generate_inner;");
|
||||
|
||||
let command_name = if previous_command_name.is_empty() {
|
||||
p.get_bin_name().expect(INTERNAL_ERROR_MSG).to_string()
|
||||
} else {
|
||||
format!("{};{}", previous_command_name, &p.name)
|
||||
};
|
||||
|
||||
let mut completions = String::new();
|
||||
let preamble = String::from("\n [CompletionResult]::new(");
|
||||
|
||||
for option in opts!(p) {
|
||||
if let Some(data) = option.short {
|
||||
let tooltip = get_tooltip(option.help, data);
|
||||
|
||||
completions.push_str(&preamble);
|
||||
completions.push_str(
|
||||
format!(
|
||||
"'-{}', '{}', {}, '{}')",
|
||||
data, data, "[CompletionResultType]::ParameterName", tooltip
|
||||
)
|
||||
.as_str(),
|
||||
);
|
||||
}
|
||||
|
||||
if let Some(data) = option.long {
|
||||
let tooltip = get_tooltip(option.help, data);
|
||||
|
||||
completions.push_str(&preamble);
|
||||
completions.push_str(
|
||||
format!(
|
||||
"'--{}', '{}', {}, '{}')",
|
||||
data, data, "[CompletionResultType]::ParameterName", tooltip
|
||||
)
|
||||
.as_str(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
for flag in PowerShell::flags(p) {
|
||||
if let Some(data) = flag.short {
|
||||
let tooltip = get_tooltip(flag.help, data);
|
||||
|
||||
completions.push_str(&preamble);
|
||||
completions.push_str(
|
||||
format!(
|
||||
"'-{}', '{}', {}, '{}')",
|
||||
data, data, "[CompletionResultType]::ParameterName", tooltip
|
||||
)
|
||||
.as_str(),
|
||||
);
|
||||
}
|
||||
|
||||
if let Some(data) = flag.long {
|
||||
let tooltip = get_tooltip(flag.help, data);
|
||||
|
||||
completions.push_str(&preamble);
|
||||
completions.push_str(
|
||||
format!(
|
||||
"'--{}', '{}', {}, '{}')",
|
||||
data, data, "[CompletionResultType]::ParameterName", tooltip
|
||||
)
|
||||
.as_str(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
for subcommand in subcommands!(p) {
|
||||
let data = &subcommand.name;
|
||||
let tooltip = get_tooltip(subcommand.about, data);
|
||||
|
||||
completions.push_str(&preamble);
|
||||
completions.push_str(
|
||||
format!(
|
||||
"'{}', '{}', {}, '{}')",
|
||||
data, data, "[CompletionResultType]::ParameterValue", tooltip
|
||||
)
|
||||
.as_str(),
|
||||
);
|
||||
}
|
||||
|
||||
let mut subcommands_cases = format!(
|
||||
r"
|
||||
'{}' {{{}
|
||||
break
|
||||
}}",
|
||||
&command_name, completions
|
||||
);
|
||||
|
||||
for subcommand in &p.subcommands {
|
||||
let subcommand_subcommands_cases = generate_inner(&subcommand, &command_name, names);
|
||||
subcommands_cases.push_str(&subcommand_subcommands_cases);
|
||||
}
|
||||
|
||||
subcommands_cases
|
||||
}
|
527
clap_generate/src/generators/shells/zsh.rs
Normal file
527
clap_generate/src/generators/shells/zsh.rs
Normal file
|
@ -0,0 +1,527 @@
|
|||
// Std
|
||||
use std::io::Write;
|
||||
|
||||
// Internal
|
||||
use crate::Generator;
|
||||
use crate::INTERNAL_ERROR_MSG;
|
||||
use clap::*;
|
||||
|
||||
/// Generate zsh completion file
|
||||
pub struct Zsh;
|
||||
|
||||
impl Generator for Zsh {
|
||||
fn file_name(name: &str) -> String {
|
||||
format!("_{}", name)
|
||||
}
|
||||
|
||||
fn generate(app: &App, buf: &mut dyn Write) {
|
||||
w!(
|
||||
buf,
|
||||
format!(
|
||||
"\
|
||||
#compdef {name}
|
||||
|
||||
autoload -U is-at-least
|
||||
|
||||
_{name}() {{
|
||||
typeset -A opt_args
|
||||
typeset -a _arguments_options
|
||||
local ret=1
|
||||
|
||||
if is-at-least 5.2; then
|
||||
_arguments_options=(-s -S -C)
|
||||
else
|
||||
_arguments_options=(-s -C)
|
||||
fi
|
||||
|
||||
local context curcontext=\"$curcontext\" state line
|
||||
{initial_args}
|
||||
{subcommands}
|
||||
}}
|
||||
|
||||
{subcommand_details}
|
||||
|
||||
_{name} \"$@\"",
|
||||
name = app.get_bin_name().unwrap(),
|
||||
initial_args = get_args_of(app),
|
||||
subcommands = get_subcommands_of(app),
|
||||
subcommand_details = subcommand_details(app)
|
||||
)
|
||||
.as_bytes()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Displays the commands of a subcommand
|
||||
// (( $+functions[_[bin_name_underscore]_commands] )) ||
|
||||
// _[bin_name_underscore]_commands() {
|
||||
// local commands; commands=(
|
||||
// '[arg_name]:[arg_help]'
|
||||
// )
|
||||
// _describe -t commands '[bin_name] commands' commands "$@"
|
||||
//
|
||||
// Where the following variables are present:
|
||||
// [bin_name_underscore]: The full space deliniated bin_name, where spaces have been replaced by
|
||||
// underscore characters
|
||||
// [arg_name]: The name of the subcommand
|
||||
// [arg_help]: The help message of the subcommand
|
||||
// [bin_name]: The full space deliniated bin_name
|
||||
//
|
||||
// Here's a snippet from rustup:
|
||||
//
|
||||
// (( $+functions[_rustup_commands] )) ||
|
||||
// _rustup_commands() {
|
||||
// local commands; commands=(
|
||||
// 'show:Show the active and installed toolchains'
|
||||
// 'update:Update Rust toolchains'
|
||||
// # ... snip for brevity
|
||||
// 'help:Prints this message or the help of the given subcommand(s)'
|
||||
// )
|
||||
// _describe -t commands 'rustup commands' commands "$@"
|
||||
//
|
||||
fn subcommand_details(p: &App) -> String {
|
||||
debugln!("ZshGen::subcommand_details;");
|
||||
|
||||
let name = p.get_bin_name().unwrap();
|
||||
|
||||
// First we do ourself
|
||||
let mut ret = vec![format!(
|
||||
"\
|
||||
(( $+functions[_{bin_name_underscore}_commands] )) ||
|
||||
_{bin_name_underscore}_commands() {{
|
||||
local commands; commands=(
|
||||
{subcommands_and_args}
|
||||
)
|
||||
_describe -t commands '{bin_name} commands' commands \"$@\"
|
||||
}}",
|
||||
bin_name_underscore = name.replace(" ", "__"),
|
||||
bin_name = name,
|
||||
subcommands_and_args = subcommands_of(p)
|
||||
)];
|
||||
|
||||
// Next we start looping through all the children, grandchildren, etc.
|
||||
let mut all_subcommands = Zsh::all_subcommands(p);
|
||||
|
||||
all_subcommands.sort();
|
||||
all_subcommands.dedup();
|
||||
|
||||
for &(_, ref bin_name) in &all_subcommands {
|
||||
debugln!("Zsh::subcommand_details:iter: bin_name={}", bin_name);
|
||||
|
||||
ret.push(format!(
|
||||
"\
|
||||
(( $+functions[_{bin_name_underscore}_commands] )) ||
|
||||
_{bin_name_underscore}_commands() {{
|
||||
local commands; commands=(
|
||||
{subcommands_and_args}
|
||||
)
|
||||
_describe -t commands '{bin_name} commands' commands \"$@\"
|
||||
}}",
|
||||
bin_name_underscore = bin_name.replace(" ", "__"),
|
||||
bin_name = bin_name,
|
||||
subcommands_and_args = subcommands_of(parser_of(p, bin_name))
|
||||
));
|
||||
}
|
||||
|
||||
ret.join("\n")
|
||||
}
|
||||
|
||||
// Generates subcommand completions in form of
|
||||
//
|
||||
// '[arg_name]:[arg_help]'
|
||||
//
|
||||
// Where:
|
||||
// [arg_name]: the subcommand's name
|
||||
// [arg_help]: the help message of the subcommand
|
||||
//
|
||||
// A snippet from rustup:
|
||||
// 'show:Show the active and installed toolchains'
|
||||
// 'update:Update Rust toolchains'
|
||||
fn subcommands_of(p: &App) -> String {
|
||||
debugln!("Zsh::subcommands_of;");
|
||||
|
||||
let mut ret = vec![];
|
||||
|
||||
fn add_sc(sc: &App, n: &str, ret: &mut Vec<String>) {
|
||||
debugln!("Zsh::add_sc;");
|
||||
|
||||
let s = format!(
|
||||
"\"{name}:{help}\" \\",
|
||||
name = n,
|
||||
help = sc
|
||||
.about
|
||||
.unwrap_or("")
|
||||
.replace("[", "\\[")
|
||||
.replace("]", "\\]")
|
||||
);
|
||||
|
||||
if !s.is_empty() {
|
||||
ret.push(s);
|
||||
}
|
||||
}
|
||||
|
||||
// The subcommands
|
||||
for sc in subcommands!(p) {
|
||||
debugln!("Zsh::subcommands_of:iter: subcommand={}", sc.name);
|
||||
|
||||
add_sc(sc, &sc.name, &mut ret);
|
||||
|
||||
if let Some(ref v) = sc.aliases {
|
||||
for alias in v.iter().filter(|&&(_, vis)| vis).map(|&(n, _)| n) {
|
||||
add_sc(sc, alias, &mut ret);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ret.join("\n")
|
||||
}
|
||||
|
||||
// Get's the subcommand section of a completion file
|
||||
// This looks roughly like:
|
||||
//
|
||||
// case $state in
|
||||
// ([bin_name]_args)
|
||||
// curcontext=\"${curcontext%:*:*}:[name_hyphen]-command-$words[1]:\"
|
||||
// case $line[1] in
|
||||
//
|
||||
// ([name])
|
||||
// _arguments -C -s -S \
|
||||
// [subcommand_args]
|
||||
// && ret=0
|
||||
//
|
||||
// [RECURSIVE_CALLS]
|
||||
//
|
||||
// ;;",
|
||||
//
|
||||
// [repeat]
|
||||
//
|
||||
// esac
|
||||
// ;;
|
||||
// esac",
|
||||
//
|
||||
// Where the following variables are present:
|
||||
// [name] = The subcommand name in the form of "install" for "rustup toolchain install"
|
||||
// [bin_name] = The full space deliniated bin_name such as "rustup toolchain install"
|
||||
// [name_hyphen] = The full space deliniated bin_name, but replace spaces with hyphens
|
||||
// [repeat] = From the same recursive calls, but for all subcommands
|
||||
// [subcommand_args] = The same as zsh::get_args_of
|
||||
fn get_subcommands_of(p: &App) -> String {
|
||||
debugln!("Zsh::get_subcommands_of;");
|
||||
debugln!(
|
||||
"Zsh::get_subcommands_of: Has subcommands...{:?}",
|
||||
p.has_subcommands()
|
||||
);
|
||||
|
||||
if !p.has_subcommands() {
|
||||
return String::new();
|
||||
}
|
||||
|
||||
let sc_names = Zsh::subcommands(p);
|
||||
let mut subcmds = vec![];
|
||||
|
||||
for &(ref name, ref bin_name) in &sc_names {
|
||||
let mut v = vec![format!("({})", name)];
|
||||
let subcommand_args = get_args_of(parser_of(p, &*bin_name));
|
||||
|
||||
if !subcommand_args.is_empty() {
|
||||
v.push(subcommand_args);
|
||||
}
|
||||
|
||||
let subcommands = get_subcommands_of(parser_of(p, &*bin_name));
|
||||
|
||||
if !subcommands.is_empty() {
|
||||
v.push(subcommands);
|
||||
}
|
||||
|
||||
v.push(String::from(";;"));
|
||||
subcmds.push(v.join("\n"));
|
||||
}
|
||||
|
||||
format!(
|
||||
"case $state in
|
||||
({name})
|
||||
words=($line[{pos}] \"${{words[@]}}\")
|
||||
(( CURRENT += 1 ))
|
||||
curcontext=\"${{curcontext%:*:*}}:{name_hyphen}-command-$line[{pos}]:\"
|
||||
case $line[{pos}] in
|
||||
{subcommands}
|
||||
esac
|
||||
;;
|
||||
esac",
|
||||
name = p.name,
|
||||
name_hyphen = p.get_bin_name().unwrap().replace(" ", "-"),
|
||||
subcommands = subcmds.join("\n"),
|
||||
pos = positionals!(p).count() + 1
|
||||
)
|
||||
}
|
||||
|
||||
fn parser_of<'b>(p: &'b App<'b>, mut sc: &str) -> &'b App<'b> {
|
||||
debugln!("Zsh::parser_of: sc={}", sc);
|
||||
|
||||
if sc == p.get_bin_name().unwrap_or(&String::new()) {
|
||||
return p;
|
||||
}
|
||||
|
||||
sc = sc.split(' ').last().unwrap();
|
||||
find_subcmd!(p, sc).expect(INTERNAL_ERROR_MSG)
|
||||
}
|
||||
|
||||
// Writes out the args section, which ends up being the flags, opts and postionals, and a jump to
|
||||
// another ZSH function if there are subcommands.
|
||||
// The structer works like this:
|
||||
// ([conflicting_args]) [multiple] arg [takes_value] [[help]] [: :(possible_values)]
|
||||
// ^-- list '-v -h' ^--'*' ^--'+' ^-- list 'one two three'
|
||||
//
|
||||
// An example from the rustup command:
|
||||
//
|
||||
// _arguments -C -s -S \
|
||||
// '(-h --help --verbose)-v[Enable verbose output]' \
|
||||
// '(-V -v --version --verbose --help)-h[Prints help information]' \
|
||||
// # ... snip for brevity
|
||||
// ':: :_rustup_commands' \ # <-- displays subcommands
|
||||
// '*::: :->rustup' \ # <-- displays subcommand args and child subcommands
|
||||
// && ret=0
|
||||
//
|
||||
// The args used for _arguments are as follows:
|
||||
// -C: modify the $context internal variable
|
||||
// -s: Allow stacking of short args (i.e. -a -b -c => -abc)
|
||||
// -S: Do not complete anything after '--' and treat those as argument values
|
||||
fn get_args_of(p: &App) -> String {
|
||||
debugln!("Zsh::get_args_of;");
|
||||
|
||||
let mut ret = vec![String::from("_arguments \"${_arguments_options[@]}\" \\")];
|
||||
let opts = write_opts_of(p);
|
||||
let flags = write_flags_of(p);
|
||||
let positionals = write_positionals_of(p);
|
||||
|
||||
let sc_or_a = if p.has_subcommands() {
|
||||
format!(
|
||||
"\":: :_{name}_commands\" \\",
|
||||
name = p.bin_name.as_ref().unwrap().replace(" ", "__")
|
||||
)
|
||||
} else {
|
||||
String::new()
|
||||
};
|
||||
|
||||
let sc = if p.has_subcommands() {
|
||||
format!("\"*::: :->{name}\" \\", name = p.name)
|
||||
} else {
|
||||
String::new()
|
||||
};
|
||||
|
||||
if !opts.is_empty() {
|
||||
ret.push(opts);
|
||||
}
|
||||
|
||||
if !flags.is_empty() {
|
||||
ret.push(flags);
|
||||
}
|
||||
|
||||
if !positionals.is_empty() {
|
||||
ret.push(positionals);
|
||||
}
|
||||
|
||||
if !sc_or_a.is_empty() {
|
||||
ret.push(sc_or_a);
|
||||
}
|
||||
|
||||
if !sc.is_empty() {
|
||||
ret.push(sc);
|
||||
}
|
||||
|
||||
ret.push(String::from("&& ret=0"));
|
||||
ret.join("\n")
|
||||
}
|
||||
|
||||
// Escape help string inside single quotes and brackets
|
||||
fn escape_help(string: &str) -> String {
|
||||
string
|
||||
.replace("\\", "\\\\")
|
||||
.replace("'", "'\\''")
|
||||
.replace("[", "\\[")
|
||||
.replace("]", "\\]")
|
||||
}
|
||||
|
||||
// Escape value string inside single quotes and parentheses
|
||||
fn escape_value(string: &str) -> String {
|
||||
string
|
||||
.replace("\\", "\\\\")
|
||||
.replace("'", "'\\''")
|
||||
.replace("(", "\\(")
|
||||
.replace(")", "\\)")
|
||||
.replace(" ", "\\ ")
|
||||
}
|
||||
|
||||
fn write_opts_of(p: &App) -> String {
|
||||
debugln!("Zsh::write_opts_of;");
|
||||
|
||||
let mut ret = vec![];
|
||||
|
||||
for o in opts!(p) {
|
||||
debugln!("Zsh::write_opts_of:iter: o={}", o.name);
|
||||
|
||||
let help = o.help.map_or(String::new(), escape_help);
|
||||
let mut conflicts = get_zsh_arg_conflicts!(p, o, INTERNAL_ERROR_MSG);
|
||||
|
||||
conflicts = if conflicts.is_empty() {
|
||||
String::new()
|
||||
} else {
|
||||
format!("({})", conflicts)
|
||||
};
|
||||
|
||||
// @TODO @soundness should probably be either multiple occurrences or multiple values and
|
||||
// not both
|
||||
let multiple = if o.is_set(ArgSettings::MultipleOccurrences)
|
||||
|| o.is_set(ArgSettings::MultipleValues)
|
||||
{
|
||||
"*"
|
||||
} else {
|
||||
""
|
||||
};
|
||||
|
||||
let pv = if let Some(ref pv_vec) = o.possible_vals {
|
||||
format!(
|
||||
": :({})",
|
||||
pv_vec
|
||||
.iter()
|
||||
.map(|v| escape_value(*v))
|
||||
.collect::<Vec<String>>()
|
||||
.join(" ")
|
||||
)
|
||||
} else {
|
||||
String::new()
|
||||
};
|
||||
|
||||
if let Some(short) = o.short {
|
||||
let s = format!(
|
||||
"'{conflicts}{multiple}-{arg}+[{help}]{possible_values}' \\",
|
||||
conflicts = conflicts,
|
||||
multiple = multiple,
|
||||
arg = short,
|
||||
possible_values = pv,
|
||||
help = help
|
||||
);
|
||||
|
||||
debugln!("write_opts_of:iter: Wrote...{}", &*s);
|
||||
ret.push(s);
|
||||
}
|
||||
|
||||
if let Some(long) = o.long {
|
||||
let l = format!(
|
||||
"'{conflicts}{multiple}--{arg}=[{help}]{possible_values}' \\",
|
||||
conflicts = conflicts,
|
||||
multiple = multiple,
|
||||
arg = long,
|
||||
possible_values = pv,
|
||||
help = help
|
||||
);
|
||||
|
||||
debugln!("write_opts_of:iter: Wrote...{}", &*l);
|
||||
ret.push(l);
|
||||
}
|
||||
}
|
||||
|
||||
ret.join("\n")
|
||||
}
|
||||
|
||||
fn write_flags_of(p: &App) -> String {
|
||||
debugln!("Zsh::write_flags_of;");
|
||||
|
||||
let mut ret = vec![];
|
||||
|
||||
for f in Zsh::flags(p) {
|
||||
debugln!("Zsh::write_flags_of:iter: f={}", f.name);
|
||||
|
||||
let help = f.help.map_or(String::new(), escape_help);
|
||||
let mut conflicts = get_zsh_arg_conflicts!(p, f, INTERNAL_ERROR_MSG);
|
||||
|
||||
conflicts = if conflicts.is_empty() {
|
||||
String::new()
|
||||
} else {
|
||||
format!("({})", conflicts)
|
||||
};
|
||||
|
||||
let multiple = if f.is_set(ArgSettings::MultipleOccurrences) {
|
||||
"*"
|
||||
} else {
|
||||
""
|
||||
};
|
||||
|
||||
if let Some(short) = f.short {
|
||||
let s = format!(
|
||||
"'{conflicts}{multiple}-{arg}[{help}]' \\",
|
||||
multiple = multiple,
|
||||
conflicts = conflicts,
|
||||
arg = short,
|
||||
help = help
|
||||
);
|
||||
|
||||
debugln!("Zsh::write_flags_of:iter: Wrote...{}", &*s);
|
||||
|
||||
ret.push(s);
|
||||
}
|
||||
|
||||
if let Some(long) = f.long {
|
||||
let l = format!(
|
||||
"'{conflicts}{multiple}--{arg}[{help}]' \\",
|
||||
conflicts = conflicts,
|
||||
multiple = multiple,
|
||||
arg = long,
|
||||
help = help
|
||||
);
|
||||
|
||||
debugln!("Zsh::write_flags_of:iter: Wrote...{}", &*l);
|
||||
|
||||
ret.push(l);
|
||||
}
|
||||
}
|
||||
|
||||
ret.join("\n")
|
||||
}
|
||||
|
||||
fn write_positionals_of(p: &App) -> String {
|
||||
debugln!("Zsh::write_positionals_of;");
|
||||
|
||||
let mut ret = vec![];
|
||||
|
||||
for arg in positionals!(p) {
|
||||
debugln!("Zsh::write_positionals_of:iter: arg={}", arg.name);
|
||||
|
||||
let optional = if !arg.is_set(ArgSettings::Required) {
|
||||
":"
|
||||
} else {
|
||||
""
|
||||
};
|
||||
|
||||
let a = format!(
|
||||
"'{optional}:{name}{help}:{action}' \\",
|
||||
optional = optional,
|
||||
name = arg.name,
|
||||
help = arg
|
||||
.help
|
||||
.map_or("".to_owned(), |v| " -- ".to_owned() + v)
|
||||
.replace("[", "\\[")
|
||||
.replace("]", "\\]")
|
||||
.replace(":", "\\:"),
|
||||
action = arg
|
||||
.possible_vals
|
||||
.as_ref()
|
||||
.map_or("_files".to_owned(), |values| {
|
||||
format!(
|
||||
"({})",
|
||||
values
|
||||
.iter()
|
||||
.map(|v| escape_value(*v))
|
||||
.collect::<Vec<String>>()
|
||||
.join(" ")
|
||||
)
|
||||
})
|
||||
);
|
||||
|
||||
debugln!("Zsh::write_positionals_of:iter: Wrote...{}", a);
|
||||
|
||||
ret.push(a);
|
||||
}
|
||||
|
||||
ret.join("\n")
|
||||
}
|
189
clap_generate/src/lib.rs
Normal file
189
clap_generate/src/lib.rs
Normal file
|
@ -0,0 +1,189 @@
|
|||
// Copyright ⓒ 2015-2018 Kevin B. Knapp
|
||||
//
|
||||
// `clap_generate` is distributed under the terms of both the MIT license and the Apache License
|
||||
// (Version 2.0).
|
||||
// See the [LICENSE-APACHE](LICENSE-APACHE) and [LICENSE-MIT](LICENSE-MIT) files in this repository
|
||||
// for more information.
|
||||
|
||||
//! Generates stuff for [`clap`](https://github.com/clap-rs/clap) based CLIs
|
||||
|
||||
#![doc(html_root_url = "https://docs.rs/clap_generate/3.0.0-beta.1")]
|
||||
#![deny(
|
||||
missing_docs,
|
||||
trivial_casts,
|
||||
unused_import_braces,
|
||||
unused_allocation,
|
||||
trivial_numeric_casts
|
||||
)]
|
||||
#![allow(clippy::needless_doctest_main)]
|
||||
|
||||
const INTERNAL_ERROR_MSG: &str = "Fatal internal error. Please consider filing a bug \
|
||||
report at https://github.com/clap-rs/clap/issues";
|
||||
|
||||
#[macro_use]
|
||||
#[allow(missing_docs)]
|
||||
mod macros;
|
||||
|
||||
/// Contains some popular generators
|
||||
pub mod generators;
|
||||
|
||||
use std::ffi::OsString;
|
||||
use std::fs::File;
|
||||
use std::io::Write;
|
||||
use std::path::PathBuf;
|
||||
|
||||
#[doc(inline)]
|
||||
pub use generators::Generator;
|
||||
|
||||
/// Generate a file for a specified generator at compile time.
|
||||
///
|
||||
/// **NOTE:** to generate the 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
|
||||
/// potentially hundreds of arguments.
|
||||
///
|
||||
/// First, it helps if we separate out our `App` definition into a separate file. Whether you
|
||||
/// do this as a function, or bare App definition is a matter of personal preference.
|
||||
///
|
||||
/// ```
|
||||
/// // src/cli.rs
|
||||
///
|
||||
/// use clap::{App, Arg};
|
||||
///
|
||||
/// pub fn build_cli() -> App<'static> {
|
||||
/// App::new("compl")
|
||||
/// .about("Tests completions")
|
||||
/// .arg(Arg::with_name("file")
|
||||
/// .help("some input file"))
|
||||
/// .subcommand(App::new("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_matches()`, or any of the other normal methods directly after. For example:
|
||||
///
|
||||
/// ```ignore
|
||||
/// // src/main.rs
|
||||
///
|
||||
/// mod 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.
|
||||
///
|
||||
/// ```toml
|
||||
/// # Cargo.toml
|
||||
/// build = "build.rs"
|
||||
///
|
||||
/// [build-dependencies]
|
||||
/// clap = "*"
|
||||
/// ```
|
||||
///
|
||||
/// Next, we place a `build.rs` in our project root.
|
||||
///
|
||||
/// ```ignore
|
||||
/// use clap_generate::{generate_to, generators::Bash};
|
||||
///
|
||||
/// include!("src/cli.rs");
|
||||
///
|
||||
/// fn main() {
|
||||
/// let outdir = match env::var_os("OUT_DIR") {
|
||||
/// None => return,
|
||||
/// Some(outdir) => outdir,
|
||||
/// };
|
||||
///
|
||||
/// let mut app = build_cli();
|
||||
/// generate_to::<Bash, _>(&mut app, // We need to specify what generator to use
|
||||
/// "myapp", // We need to specify the bin name manually
|
||||
/// outdir); // We need to specify where to write to
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Now, once we compile there will be a `{bin_name}.bash` file in the directory.
|
||||
/// Assuming we compiled with debug mode, it would be somewhere similar to
|
||||
/// `<project>/target/debug/build/myapp-<hash>/out/myapp.bash`.
|
||||
///
|
||||
/// **NOTE:** Please look at the individual [generators](./generators/index.html)
|
||||
/// to see the name of the files generated.
|
||||
pub fn generate_to<G, S, T>(app: &mut clap::App, bin_name: S, out_dir: T)
|
||||
where
|
||||
G: Generator,
|
||||
S: Into<String>,
|
||||
T: Into<OsString>,
|
||||
{
|
||||
use std::error::Error;
|
||||
|
||||
let out_dir = PathBuf::from(out_dir.into());
|
||||
let file_name = G::file_name(app.get_bin_name().unwrap());
|
||||
|
||||
let mut file = match File::create(out_dir.join(file_name)) {
|
||||
Err(why) => panic!("couldn't create completion file: {}", why.description()),
|
||||
Ok(file) => file,
|
||||
};
|
||||
|
||||
generate::<G, S>(app, bin_name, &mut file)
|
||||
}
|
||||
|
||||
/// Generate a completions file for a specified shell at runtime.
|
||||
///
|
||||
/// Until `cargo install` can install extra files like a completion script, this may be
|
||||
/// used e.g. in a command that outputs the contents of the completion script, to be
|
||||
/// redirected into a file by the user.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Assuming a separate `cli.rs` like the [example above](./fn.generate_to.html),
|
||||
/// we can let users generate a completion script using a command:
|
||||
///
|
||||
/// ```ignore
|
||||
/// // src/main.rs
|
||||
///
|
||||
/// mod cli;
|
||||
/// use std::io;
|
||||
/// use clap_generate::{generate, generators::Bash};
|
||||
///
|
||||
/// fn main() {
|
||||
/// let matches = cli::build_cli().get_matches();
|
||||
///
|
||||
/// if matches.is_present("generate-bash-completions") {
|
||||
/// generate::<Bash, _>(&mut cli::build_cli(), "myapp", &mut io::stdout());
|
||||
/// }
|
||||
///
|
||||
/// // normal logic continues...
|
||||
/// }
|
||||
///
|
||||
/// ```
|
||||
///
|
||||
/// Usage:
|
||||
///
|
||||
/// ```shell
|
||||
/// $ myapp generate-bash-completions > /usr/share/bash-completion/completions/myapp.bash
|
||||
/// ```
|
||||
pub fn generate<G, S>(app: &mut clap::App, bin_name: S, buf: &mut dyn Write)
|
||||
where
|
||||
G: Generator,
|
||||
S: Into<String>,
|
||||
{
|
||||
app.bin_name = Some(bin_name.into());
|
||||
|
||||
if !app.is_set(clap::AppSettings::Built) {
|
||||
app._build();
|
||||
app._build_bin_names();
|
||||
}
|
||||
|
||||
G::generate(app, buf)
|
||||
}
|
87
clap_generate/src/macros.rs
Normal file
87
clap_generate/src/macros.rs
Normal file
|
@ -0,0 +1,87 @@
|
|||
macro_rules! w {
|
||||
($buf:expr, $to_w:expr) => {
|
||||
match $buf.write_all($to_w) {
|
||||
Ok(..) => (),
|
||||
Err(..) => panic!("Failed to write to generated file"),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! get_zsh_arg_conflicts {
|
||||
($app:expr, $arg:ident, $msg:ident) => {
|
||||
if let Some(ref conf_vec) = $arg.blacklist {
|
||||
let mut v = vec![];
|
||||
|
||||
for arg_name in conf_vec {
|
||||
let arg = find!($app, arg_name).expect($msg);
|
||||
|
||||
if let Some(s) = arg.short {
|
||||
v.push(format!("-{}", s));
|
||||
}
|
||||
|
||||
if let Some(l) = arg.long {
|
||||
v.push(format!("--{}", l));
|
||||
}
|
||||
}
|
||||
|
||||
v.join(" ")
|
||||
} else {
|
||||
String::new()
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(feature = "debug")]
|
||||
#[cfg_attr(feature = "debug", macro_use)]
|
||||
#[cfg_attr(feature = "debug", allow(unused_macros))]
|
||||
mod debug_macros {
|
||||
macro_rules! debugln {
|
||||
($fmt:expr) => (println!(concat!("DEBUG:clap_generate:", $fmt)));
|
||||
($fmt:expr, $($arg:tt)*) => (println!(concat!("DEBUG:clap_generate:",$fmt), $($arg)*));
|
||||
}
|
||||
macro_rules! sdebugln {
|
||||
($fmt:expr) => (println!($fmt));
|
||||
($fmt:expr, $($arg:tt)*) => (println!($fmt, $($arg)*));
|
||||
}
|
||||
macro_rules! debug {
|
||||
($fmt:expr) => (print!(concat!("DEBUG:clap_generate:", $fmt)));
|
||||
($fmt:expr, $($arg:tt)*) => (print!(concat!("DEBUG:clap_generate:",$fmt), $($arg)*));
|
||||
}
|
||||
macro_rules! sdebug {
|
||||
($fmt:expr) => (print!($fmt));
|
||||
($fmt:expr, $($arg:tt)*) => (print!($fmt, $($arg)*));
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "debug"))]
|
||||
#[cfg_attr(not(feature = "debug"), macro_use)]
|
||||
#[cfg_attr(not(feature = "debug"), allow(unused_macros))]
|
||||
mod debug_macros {
|
||||
macro_rules! debugln {
|
||||
($fmt:expr) => {};
|
||||
($fmt:expr, $($arg:tt)*) => {};
|
||||
}
|
||||
macro_rules! sdebugln {
|
||||
($fmt:expr) => {};
|
||||
($fmt:expr, $($arg:tt)*) => {};
|
||||
}
|
||||
macro_rules! debug {
|
||||
($fmt:expr) => {};
|
||||
($fmt:expr, $($arg:tt)*) => {};
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! find {
|
||||
($app:expr, $name:expr, $what:ident) => {
|
||||
$what!($app).find(|a| &a.name == $name)
|
||||
};
|
||||
($app:expr, $name:expr) => {
|
||||
$app.args.args.iter().find(|a| {
|
||||
if let Some(v) = a.index {
|
||||
&v == $name
|
||||
} else {
|
||||
false
|
||||
}
|
||||
})
|
||||
};
|
||||
}
|
858
clap_generate/tests/completions.rs
Normal file
858
clap_generate/tests/completions.rs
Normal file
|
@ -0,0 +1,858 @@
|
|||
use clap::{App, Arg};
|
||||
use clap_generate::{generate, generators::*};
|
||||
use std::fmt;
|
||||
|
||||
#[derive(PartialEq, Eq)]
|
||||
pub struct PrettyString<'a>(pub &'a str);
|
||||
|
||||
impl<'a> fmt::Debug for PrettyString<'a> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.write_str(self.0)
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! assert_eq {
|
||||
($left:expr, $right:expr) => {
|
||||
pretty_assertions::assert_eq!(PrettyString($left), PrettyString($right));
|
||||
};
|
||||
}
|
||||
|
||||
static BASH: &'static str = r#"_myapp() {
|
||||
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
|
||||
myapp)
|
||||
cmd="myapp"
|
||||
;;
|
||||
|
||||
help)
|
||||
cmd+="__help"
|
||||
;;
|
||||
test)
|
||||
cmd+="__test"
|
||||
;;
|
||||
*)
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
case "${cmd}" in
|
||||
myapp)
|
||||
opts=" -h -V --help --version <file> test help"
|
||||
if [[ ${cur} == -* || ${COMP_CWORD} -eq 1 ]] ; then
|
||||
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
|
||||
return 0
|
||||
fi
|
||||
case "${prev}" in
|
||||
|
||||
*)
|
||||
COMPREPLY=()
|
||||
;;
|
||||
esac
|
||||
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
|
||||
return 0
|
||||
;;
|
||||
|
||||
myapp__help)
|
||||
opts=" -h -V --help --version "
|
||||
if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then
|
||||
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
|
||||
return 0
|
||||
fi
|
||||
case "${prev}" in
|
||||
|
||||
*)
|
||||
COMPREPLY=()
|
||||
;;
|
||||
esac
|
||||
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
|
||||
return 0
|
||||
;;
|
||||
myapp__test)
|
||||
opts=" -h -V --case --help --version "
|
||||
if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then
|
||||
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
|
||||
return 0
|
||||
fi
|
||||
case "${prev}" in
|
||||
|
||||
--case)
|
||||
COMPREPLY=($(compgen -f ${cur}))
|
||||
return 0
|
||||
;;
|
||||
*)
|
||||
COMPREPLY=()
|
||||
;;
|
||||
esac
|
||||
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
|
||||
return 0
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
complete -F _myapp -o bashdefault -o default myapp
|
||||
"#;
|
||||
|
||||
static ZSH: &'static str = r#"#compdef myapp
|
||||
|
||||
autoload -U is-at-least
|
||||
|
||||
_myapp() {
|
||||
typeset -A opt_args
|
||||
typeset -a _arguments_options
|
||||
local ret=1
|
||||
|
||||
if is-at-least 5.2; then
|
||||
_arguments_options=(-s -S -C)
|
||||
else
|
||||
_arguments_options=(-s -C)
|
||||
fi
|
||||
|
||||
local context curcontext="$curcontext" state line
|
||||
_arguments "${_arguments_options[@]}" \
|
||||
'-h[Prints help information]' \
|
||||
'--help[Prints help information]' \
|
||||
'-V[Prints version information]' \
|
||||
'--version[Prints version information]' \
|
||||
'::file -- some input file:_files' \
|
||||
":: :_myapp_commands" \
|
||||
"*::: :->myapp" \
|
||||
&& ret=0
|
||||
case $state in
|
||||
(myapp)
|
||||
words=($line[2] "${words[@]}")
|
||||
(( CURRENT += 1 ))
|
||||
curcontext="${curcontext%:*:*}:myapp-command-$line[2]:"
|
||||
case $line[2] in
|
||||
(test)
|
||||
_arguments "${_arguments_options[@]}" \
|
||||
'--case=[the case to test]' \
|
||||
'-h[Prints help information]' \
|
||||
'--help[Prints help information]' \
|
||||
'-V[Prints version information]' \
|
||||
'--version[Prints version information]' \
|
||||
&& ret=0
|
||||
;;
|
||||
(help)
|
||||
_arguments "${_arguments_options[@]}" \
|
||||
'-h[Prints help information]' \
|
||||
'--help[Prints help information]' \
|
||||
'-V[Prints version information]' \
|
||||
'--version[Prints version information]' \
|
||||
&& ret=0
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
(( $+functions[_myapp_commands] )) ||
|
||||
_myapp_commands() {
|
||||
local commands; commands=(
|
||||
"test:tests things" \
|
||||
"help:Prints this message or the help of the given subcommand(s)" \
|
||||
)
|
||||
_describe -t commands 'myapp commands' commands "$@"
|
||||
}
|
||||
(( $+functions[_myapp__help_commands] )) ||
|
||||
_myapp__help_commands() {
|
||||
local commands; commands=(
|
||||
|
||||
)
|
||||
_describe -t commands 'myapp help commands' commands "$@"
|
||||
}
|
||||
(( $+functions[_myapp__test_commands] )) ||
|
||||
_myapp__test_commands() {
|
||||
local commands; commands=(
|
||||
|
||||
)
|
||||
_describe -t commands 'myapp test commands' commands "$@"
|
||||
}
|
||||
|
||||
_myapp "$@""#;
|
||||
|
||||
static FISH: &'static str = r#"complete -c myapp -n "__fish_use_subcommand" -s h -l help -d 'Prints help information'
|
||||
complete -c myapp -n "__fish_use_subcommand" -s V -l version -d 'Prints version information'
|
||||
complete -c myapp -n "__fish_use_subcommand" -f -a "test" -d 'tests things'
|
||||
complete -c myapp -n "__fish_use_subcommand" -f -a "help" -d 'Prints this message or the help of the given subcommand(s)'
|
||||
complete -c myapp -n "__fish_seen_subcommand_from test" -l case -d 'the case to test'
|
||||
complete -c myapp -n "__fish_seen_subcommand_from test" -s h -l help -d 'Prints help information'
|
||||
complete -c myapp -n "__fish_seen_subcommand_from test" -s V -l version -d 'Prints version information'
|
||||
complete -c myapp -n "__fish_seen_subcommand_from help" -s h -l help -d 'Prints help information'
|
||||
complete -c myapp -n "__fish_seen_subcommand_from help" -s V -l version -d 'Prints version information'
|
||||
"#;
|
||||
|
||||
static POWERSHELL: &'static str = r#"
|
||||
using namespace System.Management.Automation
|
||||
using namespace System.Management.Automation.Language
|
||||
|
||||
Register-ArgumentCompleter -Native -CommandName 'my_app' -ScriptBlock {
|
||||
param($wordToComplete, $commandAst, $cursorPosition)
|
||||
|
||||
$commandElements = $commandAst.CommandElements
|
||||
$command = @(
|
||||
'my_app'
|
||||
for ($i = 1; $i -lt $commandElements.Count; $i++) {
|
||||
$element = $commandElements[$i]
|
||||
if ($element -isnot [StringConstantExpressionAst] -or
|
||||
$element.StringConstantType -ne [StringConstantType]::BareWord -or
|
||||
$element.Value.StartsWith('-')) {
|
||||
break
|
||||
}
|
||||
$element.Value
|
||||
}) -join ';'
|
||||
|
||||
$completions = @(switch ($command) {
|
||||
'my_app' {
|
||||
[CompletionResult]::new('-h', 'h', [CompletionResultType]::ParameterName, 'Prints help information')
|
||||
[CompletionResult]::new('--help', 'help', [CompletionResultType]::ParameterName, 'Prints help information')
|
||||
[CompletionResult]::new('-V', 'V', [CompletionResultType]::ParameterName, 'Prints version information')
|
||||
[CompletionResult]::new('--version', 'version', [CompletionResultType]::ParameterName, 'Prints version information')
|
||||
[CompletionResult]::new('test', 'test', [CompletionResultType]::ParameterValue, 'tests things')
|
||||
[CompletionResult]::new('help', 'help', [CompletionResultType]::ParameterValue, 'Prints this message or the help of the given subcommand(s)')
|
||||
break
|
||||
}
|
||||
'my_app;test' {
|
||||
[CompletionResult]::new('--case', 'case', [CompletionResultType]::ParameterName, 'the case to test')
|
||||
[CompletionResult]::new('-h', 'h', [CompletionResultType]::ParameterName, 'Prints help information')
|
||||
[CompletionResult]::new('--help', 'help', [CompletionResultType]::ParameterName, 'Prints help information')
|
||||
[CompletionResult]::new('-V', 'V', [CompletionResultType]::ParameterName, 'Prints version information')
|
||||
[CompletionResult]::new('--version', 'version', [CompletionResultType]::ParameterName, 'Prints version information')
|
||||
break
|
||||
}
|
||||
'my_app;help' {
|
||||
[CompletionResult]::new('-h', 'h', [CompletionResultType]::ParameterName, 'Prints help information')
|
||||
[CompletionResult]::new('--help', 'help', [CompletionResultType]::ParameterName, 'Prints help information')
|
||||
[CompletionResult]::new('-V', 'V', [CompletionResultType]::ParameterName, 'Prints version information')
|
||||
[CompletionResult]::new('--version', 'version', [CompletionResultType]::ParameterName, 'Prints version information')
|
||||
break
|
||||
}
|
||||
})
|
||||
|
||||
$completions.Where{ $_.CompletionText -like "$wordToComplete*" } |
|
||||
Sort-Object -Property ListItemText
|
||||
}
|
||||
"#;
|
||||
|
||||
static ELVISH: &'static str = r#"
|
||||
edit:completion:arg-completer[my_app] = [@words]{
|
||||
fn spaces [n]{
|
||||
repeat $n ' ' | joins ''
|
||||
}
|
||||
fn cand [text desc]{
|
||||
edit:complex-candidate $text &display-suffix=' '(spaces (- 14 (wcswidth $text)))$desc
|
||||
}
|
||||
command = 'my_app'
|
||||
for word $words[1:-1] {
|
||||
if (has-prefix $word '-') {
|
||||
break
|
||||
}
|
||||
command = $command';'$word
|
||||
}
|
||||
completions = [
|
||||
&'my_app'= {
|
||||
cand -h 'Prints help information'
|
||||
cand --help 'Prints help information'
|
||||
cand -V 'Prints version information'
|
||||
cand --version 'Prints version information'
|
||||
cand test 'tests things'
|
||||
cand help 'Prints this message or the help of the given subcommand(s)'
|
||||
}
|
||||
&'my_app;test'= {
|
||||
cand --case 'the case to test'
|
||||
cand -h 'Prints help information'
|
||||
cand --help 'Prints help information'
|
||||
cand -V 'Prints version information'
|
||||
cand --version 'Prints version information'
|
||||
}
|
||||
&'my_app;help'= {
|
||||
cand -h 'Prints help information'
|
||||
cand --help 'Prints help information'
|
||||
cand -V 'Prints version information'
|
||||
cand --version 'Prints version information'
|
||||
}
|
||||
]
|
||||
$completions[$command]
|
||||
}
|
||||
"#;
|
||||
|
||||
static ELVISH_SPECIAL_CMDS: &'static str = r#"
|
||||
edit:completion:arg-completer[my_app] = [@words]{
|
||||
fn spaces [n]{
|
||||
repeat $n ' ' | joins ''
|
||||
}
|
||||
fn cand [text desc]{
|
||||
edit:complex-candidate $text &display-suffix=' '(spaces (- 14 (wcswidth $text)))$desc
|
||||
}
|
||||
command = 'my_app'
|
||||
for word $words[1:-1] {
|
||||
if (has-prefix $word '-') {
|
||||
break
|
||||
}
|
||||
command = $command';'$word
|
||||
}
|
||||
completions = [
|
||||
&'my_app'= {
|
||||
cand -h 'Prints help information'
|
||||
cand --help 'Prints help information'
|
||||
cand -V 'Prints version information'
|
||||
cand --version 'Prints version information'
|
||||
cand test 'tests things'
|
||||
cand some_cmd 'tests other things'
|
||||
cand some-cmd-with-hypens 'some-cmd-with-hypens'
|
||||
cand help 'Prints this message or the help of the given subcommand(s)'
|
||||
}
|
||||
&'my_app;test'= {
|
||||
cand --case 'the case to test'
|
||||
cand -h 'Prints help information'
|
||||
cand --help 'Prints help information'
|
||||
cand -V 'Prints version information'
|
||||
cand --version 'Prints version information'
|
||||
}
|
||||
&'my_app;some_cmd'= {
|
||||
cand --config 'the other case to test'
|
||||
cand -h 'Prints help information'
|
||||
cand --help 'Prints help information'
|
||||
cand -V 'Prints version information'
|
||||
cand --version 'Prints version information'
|
||||
}
|
||||
&'my_app;some-cmd-with-hypens'= {
|
||||
cand -h 'Prints help information'
|
||||
cand --help 'Prints help information'
|
||||
cand -V 'Prints version information'
|
||||
cand --version 'Prints version information'
|
||||
}
|
||||
&'my_app;help'= {
|
||||
cand -h 'Prints help information'
|
||||
cand --help 'Prints help information'
|
||||
cand -V 'Prints version information'
|
||||
cand --version 'Prints version information'
|
||||
}
|
||||
]
|
||||
$completions[$command]
|
||||
}
|
||||
"#;
|
||||
|
||||
static POWERSHELL_SPECIAL_CMDS: &'static str = r#"
|
||||
using namespace System.Management.Automation
|
||||
using namespace System.Management.Automation.Language
|
||||
|
||||
Register-ArgumentCompleter -Native -CommandName 'my_app' -ScriptBlock {
|
||||
param($wordToComplete, $commandAst, $cursorPosition)
|
||||
|
||||
$commandElements = $commandAst.CommandElements
|
||||
$command = @(
|
||||
'my_app'
|
||||
for ($i = 1; $i -lt $commandElements.Count; $i++) {
|
||||
$element = $commandElements[$i]
|
||||
if ($element -isnot [StringConstantExpressionAst] -or
|
||||
$element.StringConstantType -ne [StringConstantType]::BareWord -or
|
||||
$element.Value.StartsWith('-')) {
|
||||
break
|
||||
}
|
||||
$element.Value
|
||||
}) -join ';'
|
||||
|
||||
$completions = @(switch ($command) {
|
||||
'my_app' {
|
||||
[CompletionResult]::new('-h', 'h', [CompletionResultType]::ParameterName, 'Prints help information')
|
||||
[CompletionResult]::new('--help', 'help', [CompletionResultType]::ParameterName, 'Prints help information')
|
||||
[CompletionResult]::new('-V', 'V', [CompletionResultType]::ParameterName, 'Prints version information')
|
||||
[CompletionResult]::new('--version', 'version', [CompletionResultType]::ParameterName, 'Prints version information')
|
||||
[CompletionResult]::new('test', 'test', [CompletionResultType]::ParameterValue, 'tests things')
|
||||
[CompletionResult]::new('some_cmd', 'some_cmd', [CompletionResultType]::ParameterValue, 'tests other things')
|
||||
[CompletionResult]::new('some-cmd-with-hypens', 'some-cmd-with-hypens', [CompletionResultType]::ParameterValue, 'some-cmd-with-hypens')
|
||||
[CompletionResult]::new('help', 'help', [CompletionResultType]::ParameterValue, 'Prints this message or the help of the given subcommand(s)')
|
||||
break
|
||||
}
|
||||
'my_app;test' {
|
||||
[CompletionResult]::new('--case', 'case', [CompletionResultType]::ParameterName, 'the case to test')
|
||||
[CompletionResult]::new('-h', 'h', [CompletionResultType]::ParameterName, 'Prints help information')
|
||||
[CompletionResult]::new('--help', 'help', [CompletionResultType]::ParameterName, 'Prints help information')
|
||||
[CompletionResult]::new('-V', 'V', [CompletionResultType]::ParameterName, 'Prints version information')
|
||||
[CompletionResult]::new('--version', 'version', [CompletionResultType]::ParameterName, 'Prints version information')
|
||||
break
|
||||
}
|
||||
'my_app;some_cmd' {
|
||||
[CompletionResult]::new('--config', 'config', [CompletionResultType]::ParameterName, 'the other case to test')
|
||||
[CompletionResult]::new('-h', 'h', [CompletionResultType]::ParameterName, 'Prints help information')
|
||||
[CompletionResult]::new('--help', 'help', [CompletionResultType]::ParameterName, 'Prints help information')
|
||||
[CompletionResult]::new('-V', 'V', [CompletionResultType]::ParameterName, 'Prints version information')
|
||||
[CompletionResult]::new('--version', 'version', [CompletionResultType]::ParameterName, 'Prints version information')
|
||||
break
|
||||
}
|
||||
'my_app;some-cmd-with-hypens' {
|
||||
[CompletionResult]::new('-h', 'h', [CompletionResultType]::ParameterName, 'Prints help information')
|
||||
[CompletionResult]::new('--help', 'help', [CompletionResultType]::ParameterName, 'Prints help information')
|
||||
[CompletionResult]::new('-V', 'V', [CompletionResultType]::ParameterName, 'Prints version information')
|
||||
[CompletionResult]::new('--version', 'version', [CompletionResultType]::ParameterName, 'Prints version information')
|
||||
break
|
||||
}
|
||||
'my_app;help' {
|
||||
[CompletionResult]::new('-h', 'h', [CompletionResultType]::ParameterName, 'Prints help information')
|
||||
[CompletionResult]::new('--help', 'help', [CompletionResultType]::ParameterName, 'Prints help information')
|
||||
[CompletionResult]::new('-V', 'V', [CompletionResultType]::ParameterName, 'Prints version information')
|
||||
[CompletionResult]::new('--version', 'version', [CompletionResultType]::ParameterName, 'Prints version information')
|
||||
break
|
||||
}
|
||||
})
|
||||
|
||||
$completions.Where{ $_.CompletionText -like "$wordToComplete*" } |
|
||||
Sort-Object -Property ListItemText
|
||||
}
|
||||
"#;
|
||||
|
||||
static ZSH_SPECIAL_CMDS: &'static str = r#"#compdef my_app
|
||||
|
||||
autoload -U is-at-least
|
||||
|
||||
_my_app() {
|
||||
typeset -A opt_args
|
||||
typeset -a _arguments_options
|
||||
local ret=1
|
||||
|
||||
if is-at-least 5.2; then
|
||||
_arguments_options=(-s -S -C)
|
||||
else
|
||||
_arguments_options=(-s -C)
|
||||
fi
|
||||
|
||||
local context curcontext="$curcontext" state line
|
||||
_arguments "${_arguments_options[@]}" \
|
||||
'-h[Prints help information]' \
|
||||
'--help[Prints help information]' \
|
||||
'-V[Prints version information]' \
|
||||
'--version[Prints version information]' \
|
||||
'::file -- some input file:_files' \
|
||||
":: :_my_app_commands" \
|
||||
"*::: :->my_app" \
|
||||
&& ret=0
|
||||
case $state in
|
||||
(my_app)
|
||||
words=($line[2] "${words[@]}")
|
||||
(( CURRENT += 1 ))
|
||||
curcontext="${curcontext%:*:*}:my_app-command-$line[2]:"
|
||||
case $line[2] in
|
||||
(test)
|
||||
_arguments "${_arguments_options[@]}" \
|
||||
'--case=[the case to test]' \
|
||||
'-h[Prints help information]' \
|
||||
'--help[Prints help information]' \
|
||||
'-V[Prints version information]' \
|
||||
'--version[Prints version information]' \
|
||||
&& ret=0
|
||||
;;
|
||||
(some_cmd)
|
||||
_arguments "${_arguments_options[@]}" \
|
||||
'--config=[the other case to test]' \
|
||||
'-h[Prints help information]' \
|
||||
'--help[Prints help information]' \
|
||||
'-V[Prints version information]' \
|
||||
'--version[Prints version information]' \
|
||||
&& ret=0
|
||||
;;
|
||||
(some-cmd-with-hypens)
|
||||
_arguments "${_arguments_options[@]}" \
|
||||
'-h[Prints help information]' \
|
||||
'--help[Prints help information]' \
|
||||
'-V[Prints version information]' \
|
||||
'--version[Prints version information]' \
|
||||
&& ret=0
|
||||
;;
|
||||
(help)
|
||||
_arguments "${_arguments_options[@]}" \
|
||||
'-h[Prints help information]' \
|
||||
'--help[Prints help information]' \
|
||||
'-V[Prints version information]' \
|
||||
'--version[Prints version information]' \
|
||||
&& ret=0
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
(( $+functions[_my_app_commands] )) ||
|
||||
_my_app_commands() {
|
||||
local commands; commands=(
|
||||
"test:tests things" \
|
||||
"some_cmd:tests other things" \
|
||||
"some-cmd-with-hypens:" \
|
||||
"help:Prints this message or the help of the given subcommand(s)" \
|
||||
)
|
||||
_describe -t commands 'my_app commands' commands "$@"
|
||||
}
|
||||
(( $+functions[_my_app__help_commands] )) ||
|
||||
_my_app__help_commands() {
|
||||
local commands; commands=(
|
||||
|
||||
)
|
||||
_describe -t commands 'my_app help commands' commands "$@"
|
||||
}
|
||||
(( $+functions[_my_app__some-cmd-with-hypens_commands] )) ||
|
||||
_my_app__some-cmd-with-hypens_commands() {
|
||||
local commands; commands=(
|
||||
|
||||
)
|
||||
_describe -t commands 'my_app some-cmd-with-hypens commands' commands "$@"
|
||||
}
|
||||
(( $+functions[_my_app__some_cmd_commands] )) ||
|
||||
_my_app__some_cmd_commands() {
|
||||
local commands; commands=(
|
||||
|
||||
)
|
||||
_describe -t commands 'my_app some_cmd commands' commands "$@"
|
||||
}
|
||||
(( $+functions[_my_app__test_commands] )) ||
|
||||
_my_app__test_commands() {
|
||||
local commands; commands=(
|
||||
|
||||
)
|
||||
_describe -t commands 'my_app test commands' commands "$@"
|
||||
}
|
||||
|
||||
_my_app "$@""#;
|
||||
|
||||
static FISH_SPECIAL_CMDS: &'static str = r#"complete -c my_app -n "__fish_use_subcommand" -s h -l help -d 'Prints help information'
|
||||
complete -c my_app -n "__fish_use_subcommand" -s V -l version -d 'Prints version information'
|
||||
complete -c my_app -n "__fish_use_subcommand" -f -a "test" -d 'tests things'
|
||||
complete -c my_app -n "__fish_use_subcommand" -f -a "some_cmd" -d 'tests other things'
|
||||
complete -c my_app -n "__fish_use_subcommand" -f -a "some-cmd-with-hypens"
|
||||
complete -c my_app -n "__fish_use_subcommand" -f -a "help" -d 'Prints this message or the help of the given subcommand(s)'
|
||||
complete -c my_app -n "__fish_seen_subcommand_from test" -l case -d 'the case to test'
|
||||
complete -c my_app -n "__fish_seen_subcommand_from test" -s h -l help -d 'Prints help information'
|
||||
complete -c my_app -n "__fish_seen_subcommand_from test" -s V -l version -d 'Prints version information'
|
||||
complete -c my_app -n "__fish_seen_subcommand_from some_cmd" -l config -d 'the other case to test'
|
||||
complete -c my_app -n "__fish_seen_subcommand_from some_cmd" -s h -l help -d 'Prints help information'
|
||||
complete -c my_app -n "__fish_seen_subcommand_from some_cmd" -s V -l version -d 'Prints version information'
|
||||
complete -c my_app -n "__fish_seen_subcommand_from some-cmd-with-hypens" -s h -l help -d 'Prints help information'
|
||||
complete -c my_app -n "__fish_seen_subcommand_from some-cmd-with-hypens" -s V -l version -d 'Prints version information'
|
||||
complete -c my_app -n "__fish_seen_subcommand_from help" -s h -l help -d 'Prints help information'
|
||||
complete -c my_app -n "__fish_seen_subcommand_from help" -s V -l version -d 'Prints version information'
|
||||
"#;
|
||||
|
||||
static BASH_SPECIAL_CMDS: &'static str = r#"_my_app() {
|
||||
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
|
||||
my_app)
|
||||
cmd="my_app"
|
||||
;;
|
||||
|
||||
help)
|
||||
cmd+="__help"
|
||||
;;
|
||||
some-cmd-with-hypens)
|
||||
cmd+="__some__cmd__with__hypens"
|
||||
;;
|
||||
some_cmd)
|
||||
cmd+="__some_cmd"
|
||||
;;
|
||||
test)
|
||||
cmd+="__test"
|
||||
;;
|
||||
*)
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
case "${cmd}" in
|
||||
my_app)
|
||||
opts=" -h -V --help --version <file> test some_cmd some-cmd-with-hypens help"
|
||||
if [[ ${cur} == -* || ${COMP_CWORD} -eq 1 ]] ; then
|
||||
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
|
||||
return 0
|
||||
fi
|
||||
case "${prev}" in
|
||||
|
||||
*)
|
||||
COMPREPLY=()
|
||||
;;
|
||||
esac
|
||||
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
|
||||
return 0
|
||||
;;
|
||||
|
||||
my_app__help)
|
||||
opts=" -h -V --help --version "
|
||||
if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then
|
||||
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
|
||||
return 0
|
||||
fi
|
||||
case "${prev}" in
|
||||
|
||||
*)
|
||||
COMPREPLY=()
|
||||
;;
|
||||
esac
|
||||
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
|
||||
return 0
|
||||
;;
|
||||
my_app__some__cmd__with__hypens)
|
||||
opts=" -h -V --help --version "
|
||||
if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then
|
||||
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
|
||||
return 0
|
||||
fi
|
||||
case "${prev}" in
|
||||
|
||||
*)
|
||||
COMPREPLY=()
|
||||
;;
|
||||
esac
|
||||
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
|
||||
return 0
|
||||
;;
|
||||
my_app__some_cmd)
|
||||
opts=" -h -V --config --help --version "
|
||||
if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then
|
||||
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
|
||||
return 0
|
||||
fi
|
||||
case "${prev}" in
|
||||
|
||||
--config)
|
||||
COMPREPLY=($(compgen -f ${cur}))
|
||||
return 0
|
||||
;;
|
||||
*)
|
||||
COMPREPLY=()
|
||||
;;
|
||||
esac
|
||||
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
|
||||
return 0
|
||||
;;
|
||||
my_app__test)
|
||||
opts=" -h -V --case --help --version "
|
||||
if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then
|
||||
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
|
||||
return 0
|
||||
fi
|
||||
case "${prev}" in
|
||||
|
||||
--case)
|
||||
COMPREPLY=($(compgen -f ${cur}))
|
||||
return 0
|
||||
;;
|
||||
*)
|
||||
COMPREPLY=()
|
||||
;;
|
||||
esac
|
||||
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
|
||||
return 0
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
complete -F _my_app -o bashdefault -o default my_app
|
||||
"#;
|
||||
|
||||
static FISH_SPECIAL_HELP: &'static str = r#"complete -c my_app -n "__fish_use_subcommand" -l single-quotes -d 'Can be \'always\', \'auto\', or \'never\''
|
||||
complete -c my_app -n "__fish_use_subcommand" -l double-quotes -d 'Can be "always", "auto", or "never"'
|
||||
complete -c my_app -n "__fish_use_subcommand" -l backticks -d 'For more information see `echo test`'
|
||||
complete -c my_app -n "__fish_use_subcommand" -l backslash -d 'Avoid \'\\n\''
|
||||
complete -c my_app -n "__fish_use_subcommand" -l brackets -d 'List packages [filter]'
|
||||
complete -c my_app -n "__fish_use_subcommand" -l expansions -d 'Execute the shell command with $SHELL'
|
||||
complete -c my_app -n "__fish_use_subcommand" -s h -l help -d 'Prints help information'
|
||||
complete -c my_app -n "__fish_use_subcommand" -s V -l version -d 'Prints version information'
|
||||
"#;
|
||||
|
||||
static ZSH_SPECIAL_HELP: &'static str = r#"#compdef my_app
|
||||
|
||||
autoload -U is-at-least
|
||||
|
||||
_my_app() {
|
||||
typeset -A opt_args
|
||||
typeset -a _arguments_options
|
||||
local ret=1
|
||||
|
||||
if is-at-least 5.2; then
|
||||
_arguments_options=(-s -S -C)
|
||||
else
|
||||
_arguments_options=(-s -C)
|
||||
fi
|
||||
|
||||
local context curcontext="$curcontext" state line
|
||||
_arguments "${_arguments_options[@]}" \
|
||||
'--single-quotes[Can be '\''always'\'', '\''auto'\'', or '\''never'\'']' \
|
||||
'--double-quotes[Can be "always", "auto", or "never"]' \
|
||||
'--backticks[For more information see `echo test`]' \
|
||||
'--backslash[Avoid '\''\\n'\'']' \
|
||||
'--brackets[List packages \[filter\]]' \
|
||||
'--expansions[Execute the shell command with $SHELL]' \
|
||||
'-h[Prints help information]' \
|
||||
'--help[Prints help information]' \
|
||||
'-V[Prints version information]' \
|
||||
'--version[Prints version information]' \
|
||||
&& ret=0
|
||||
|
||||
}
|
||||
|
||||
(( $+functions[_my_app_commands] )) ||
|
||||
_my_app_commands() {
|
||||
local commands; commands=(
|
||||
|
||||
)
|
||||
_describe -t commands 'my_app commands' commands "$@"
|
||||
}
|
||||
|
||||
_my_app "$@""#;
|
||||
|
||||
fn build_app() -> App<'static> {
|
||||
build_app_with_name("myapp")
|
||||
}
|
||||
|
||||
fn build_app_with_name(s: &'static str) -> App<'static> {
|
||||
App::new(s)
|
||||
.about("Tests completions")
|
||||
.arg(Arg::with_name("file").help("some input file"))
|
||||
.subcommand(
|
||||
App::new("test").about("tests things").arg(
|
||||
Arg::with_name("case")
|
||||
.long("case")
|
||||
.takes_value(true)
|
||||
.help("the case to test"),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
fn build_app_special_commands() -> App<'static> {
|
||||
build_app_with_name("my_app")
|
||||
.subcommand(
|
||||
App::new("some_cmd").about("tests other things").arg(
|
||||
Arg::with_name("config")
|
||||
.long("--config")
|
||||
.takes_value(true)
|
||||
.help("the other case to test"),
|
||||
),
|
||||
)
|
||||
.subcommand(App::new("some-cmd-with-hypens").alias("hyphen"))
|
||||
}
|
||||
|
||||
fn build_app_special_help() -> App<'static> {
|
||||
App::new("my_app")
|
||||
.arg(
|
||||
Arg::with_name("single-quotes")
|
||||
.long("single-quotes")
|
||||
.help("Can be 'always', 'auto', or 'never'"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("double-quotes")
|
||||
.long("double-quotes")
|
||||
.help("Can be \"always\", \"auto\", or \"never\""),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("backticks")
|
||||
.long("backticks")
|
||||
.help("For more information see `echo test`"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("backslash")
|
||||
.long("backslash")
|
||||
.help("Avoid '\\n'"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("brackets")
|
||||
.long("brackets")
|
||||
.help("List packages [filter]"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("expansions")
|
||||
.long("expansions")
|
||||
.help("Execute the shell command with $SHELL"),
|
||||
)
|
||||
}
|
||||
|
||||
fn common<G: Generator>(app: &mut App, name: &str, fixture: &str) {
|
||||
let mut buf = vec![];
|
||||
generate::<G, _>(app, name, &mut buf);
|
||||
let string = String::from_utf8(buf).unwrap();
|
||||
|
||||
assert_eq!(&string, fixture);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bash() {
|
||||
let mut app = build_app();
|
||||
common::<Bash>(&mut app, "myapp", BASH);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn zsh() {
|
||||
let mut app = build_app();
|
||||
common::<Zsh>(&mut app, "myapp", ZSH);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fish() {
|
||||
let mut app = build_app();
|
||||
common::<Fish>(&mut app, "myapp", FISH);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn powershell() {
|
||||
let mut app = build_app();
|
||||
common::<PowerShell>(&mut app, "my_app", POWERSHELL);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn elvish() {
|
||||
let mut app = build_app();
|
||||
common::<Elvish>(&mut app, "my_app", ELVISH);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn elvish_with_special_commands() {
|
||||
let mut app = build_app_special_commands();
|
||||
common::<Elvish>(&mut app, "my_app", ELVISH_SPECIAL_CMDS);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn powershell_with_special_commands() {
|
||||
let mut app = build_app_special_commands();
|
||||
common::<PowerShell>(&mut app, "my_app", POWERSHELL_SPECIAL_CMDS);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bash_with_special_commands() {
|
||||
let mut app = build_app_special_commands();
|
||||
common::<Bash>(&mut app, "my_app", BASH_SPECIAL_CMDS);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fish_with_special_commands() {
|
||||
let mut app = build_app_special_commands();
|
||||
common::<Fish>(&mut app, "my_app", FISH_SPECIAL_CMDS);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn zsh_with_special_commands() {
|
||||
let mut app = build_app_special_commands();
|
||||
common::<Zsh>(&mut app, "my_app", ZSH_SPECIAL_CMDS);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fish_with_special_help() {
|
||||
let mut app = build_app_special_help();
|
||||
common::<Fish>(&mut app, "my_app", FISH_SPECIAL_HELP);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn zsh_with_special_help() {
|
||||
let mut app = build_app_special_help();
|
||||
common::<Zsh>(&mut app, "my_app", ZSH_SPECIAL_HELP);
|
||||
}
|
6
clap_generate/tests/version-numbers.rs
Normal file
6
clap_generate/tests/version-numbers.rs
Normal file
|
@ -0,0 +1,6 @@
|
|||
use version_sync::assert_html_root_url_updated;
|
||||
|
||||
#[test]
|
||||
fn test_html_root_url() {
|
||||
assert_html_root_url_updated!("src/lib.rs");
|
||||
}
|
|
@ -474,8 +474,11 @@ mod output;
|
|||
mod parse;
|
||||
mod util;
|
||||
|
||||
#[doc(hidden)]
|
||||
pub use mkeymap::KeyType;
|
||||
|
||||
const INTERNAL_ERROR_MSG: &str = "Fatal internal error. Please consider filing a bug \
|
||||
report at https://github.com/kbknapp/clap-rs/issues";
|
||||
report at https://github.com/clap-rs/clap/issues";
|
||||
const INVALID_UTF8: &str = "unexpected invalid UTF-8 code point";
|
||||
|
||||
/// @TODO @release @docs
|
||||
|
|
|
@ -919,18 +919,18 @@ macro_rules! write_nspaces {
|
|||
}};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
macro_rules! flags {
|
||||
($app:expr, $how:ident) => {{
|
||||
$app.args
|
||||
.args
|
||||
.$how()
|
||||
.filter(|a| {
|
||||
!a.settings.is_set(crate::build::ArgSettings::TakesValue) && a.index.is_none()
|
||||
})
|
||||
.filter(|a| !a.settings.is_set($crate::ArgSettings::TakesValue) && a.index.is_none())
|
||||
.filter(|a| !a.help_heading.is_some())
|
||||
}};
|
||||
($app:expr) => {
|
||||
flags!($app, iter)
|
||||
$crate::flags!($app, iter)
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -941,14 +941,14 @@ macro_rules! flags_mut {
|
|||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
macro_rules! opts {
|
||||
($app:expr, $how:ident) => {{
|
||||
$app.args
|
||||
.args
|
||||
.$how()
|
||||
.filter(|a| {
|
||||
a.settings.is_set(crate::build::ArgSettings::TakesValue) && a.index.is_none()
|
||||
})
|
||||
.filter(|a| a.settings.is_set($crate::ArgSettings::TakesValue) && a.index.is_none())
|
||||
.filter(|a| !a.help_heading.is_some())
|
||||
}};
|
||||
($app:expr) => {
|
||||
|
@ -963,6 +963,8 @@ macro_rules! opts_mut {
|
|||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
macro_rules! positionals {
|
||||
($app:expr) => {
|
||||
$app.args
|
||||
|
@ -997,6 +999,8 @@ macro_rules! subcommands_cloned {
|
|||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
macro_rules! subcommands {
|
||||
($app:expr, $how:ident) => {
|
||||
$app.subcommands.$how()
|
||||
|
@ -1028,6 +1032,8 @@ macro_rules! find_subcmd_cloned {
|
|||
}};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
macro_rules! find_subcmd {
|
||||
($app:expr, $sc:expr) => {{
|
||||
subcommands!($app).find(|a| match_alias!(a, $sc, &*a.name))
|
||||
|
@ -1047,7 +1053,9 @@ macro_rules! longs {
|
|||
}};
|
||||
}
|
||||
|
||||
macro_rules! _names {
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
macro_rules! names {
|
||||
(@args $app:expr) => {{
|
||||
$app.args.args.iter().map(|a| &*a.name)
|
||||
}};
|
||||
|
@ -1061,12 +1069,16 @@ macro_rules! _names {
|
|||
}};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
macro_rules! sc_names {
|
||||
($app:expr) => {{
|
||||
_names!(@sc $app)
|
||||
names!(@sc $app)
|
||||
}};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
macro_rules! match_alias {
|
||||
($a:expr, $to:expr, $what:expr) => {{
|
||||
$what == $to
|
||||
|
|
|
@ -16,6 +16,7 @@ pub struct MKeyMap<'b> {
|
|||
built: bool, // mutation isn't possible after being built
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[derive(Debug, PartialEq, Eq, Hash, Clone)]
|
||||
pub enum KeyType {
|
||||
Short(char),
|
||||
|
|
Loading…
Reference in a new issue