2020-02-05 10:04:59 +00:00
|
|
|
// 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})
|
2020-03-01 22:35:49 +00:00
|
|
|
cmd=\"{cmd}\"
|
2020-02-05 10:04:59 +00:00
|
|
|
;;
|
|
|
|
{subcmds}
|
|
|
|
*)
|
|
|
|
;;
|
|
|
|
esac
|
|
|
|
done
|
|
|
|
|
|
|
|
case \"${{cmd}}\" in
|
2020-03-01 22:35:49 +00:00
|
|
|
{cmd})
|
2020-02-05 10:04:59 +00:00
|
|
|
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,
|
2020-03-01 22:35:49 +00:00
|
|
|
cmd = bin_name.replace("-", "__"),
|
2020-02-05 10:04:59 +00:00
|
|
|
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();
|
2020-02-06 10:19:03 +00:00
|
|
|
let mut scs = Bash::all_subcommands(app)
|
|
|
|
.iter()
|
|
|
|
.map(|x| x.0.clone())
|
|
|
|
.collect::<Vec<_>>();
|
|
|
|
|
|
|
|
scs.sort();
|
|
|
|
scs.dedup();
|
2020-02-05 10:04:59 +00:00
|
|
|
|
|
|
|
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();
|
2020-02-06 10:19:03 +00:00
|
|
|
let mut scs = Bash::all_subcommands(app)
|
|
|
|
.iter()
|
|
|
|
.map(|x| x.1.replace(" ", "__"))
|
|
|
|
.collect::<Vec<_>>();
|
2020-02-05 10:04:59 +00:00
|
|
|
|
|
|
|
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),
|
2020-02-06 10:19:03 +00:00
|
|
|
level = sc.split("__").map(|_| 1).sum::<u64>(),
|
2020-02-05 10:04:59 +00:00
|
|
|
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);
|
|
|
|
|
2020-02-06 10:19:03 +00:00
|
|
|
let p = Bash::find_subcommand_with_path(app, path.split("__").skip(1).collect());
|
2020-02-05 10:04:59 +00:00
|
|
|
let mut opts = String::new();
|
|
|
|
|
|
|
|
for o in opts!(p) {
|
2020-04-10 21:15:09 +00:00
|
|
|
if let Some(l) = o.get_long() {
|
2020-02-05 10:04:59 +00:00
|
|
|
opts = format!(
|
|
|
|
"{}
|
|
|
|
--{})
|
|
|
|
COMPREPLY=({})
|
|
|
|
return 0
|
|
|
|
;;",
|
|
|
|
opts,
|
|
|
|
l,
|
|
|
|
vals_for(o)
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2020-04-10 21:15:09 +00:00
|
|
|
if let Some(s) = o.get_short() {
|
2020-02-05 10:04:59 +00:00
|
|
|
opts = format!(
|
|
|
|
"{}
|
|
|
|
-{})
|
|
|
|
COMPREPLY=({})
|
|
|
|
return 0
|
|
|
|
;;",
|
|
|
|
opts,
|
|
|
|
s,
|
|
|
|
vals_for(o)
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
opts
|
|
|
|
}
|
|
|
|
|
|
|
|
fn vals_for(o: &Arg) -> String {
|
2020-04-12 01:39:13 +00:00
|
|
|
debugln!("Bash::vals_for: o={}", o.get_name());
|
2020-02-05 10:04:59 +00:00
|
|
|
|
2020-04-10 21:15:09 +00:00
|
|
|
if let Some(ref vals) = o.get_possible_values() {
|
2020-02-05 10:04:59 +00:00
|
|
|
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);
|
|
|
|
|
2020-02-06 10:19:03 +00:00
|
|
|
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();
|
2020-02-05 10:04:59 +00:00
|
|
|
|
|
|
|
let opts = format!(
|
|
|
|
"{shorts} {longs} {pos} {subcmds}",
|
2020-02-06 10:19:03 +00:00
|
|
|
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)),
|
2020-02-05 10:04:59 +00:00
|
|
|
pos = positionals!(p).fold(String::new(), |acc, p| format!("{} {}", acc, p)),
|
2020-02-06 10:19:03 +00:00
|
|
|
subcmds = scs.join(" "),
|
2020-02-05 10:04:59 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
opts
|
|
|
|
}
|