2020-02-05 10:04:59 +00:00
|
|
|
// Std
|
|
|
|
use std::io::Write;
|
|
|
|
|
|
|
|
// Internal
|
2021-10-12 13:27:24 +00:00
|
|
|
use crate::utils;
|
2020-02-05 10:04:59 +00:00
|
|
|
use crate::Generator;
|
|
|
|
use clap::*;
|
|
|
|
|
|
|
|
/// Generate bash completion file
|
2021-08-18 18:25:43 +00:00
|
|
|
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
2020-02-05 10:04:59 +00:00
|
|
|
pub struct Bash;
|
|
|
|
|
|
|
|
impl Generator for Bash {
|
2021-08-18 18:25:43 +00:00
|
|
|
fn file_name(&self, name: &str) -> String {
|
2020-02-05 10:04:59 +00:00
|
|
|
format!("{}.bash", name)
|
|
|
|
}
|
|
|
|
|
2021-08-18 18:25:43 +00:00
|
|
|
fn generate(&self, app: &App, buf: &mut dyn Write) {
|
2020-02-05 10:04:59 +00:00
|
|
|
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}\"
|
2021-10-11 23:17:09 +00:00
|
|
|
;;{subcmds}
|
2020-02-05 10:04:59 +00:00
|
|
|
*)
|
|
|
|
;;
|
|
|
|
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
|
2019-03-31 16:16:04 +00:00
|
|
|
COMPREPLY=( $(compgen -W \"${{opts}}\" -- \"${{cur}}\") )
|
2020-02-05 10:04:59 +00:00
|
|
|
return 0
|
|
|
|
fi
|
2021-10-11 23:17:09 +00:00
|
|
|
case \"${{prev}}\" in{name_opts_details}
|
2020-02-05 10:04:59 +00:00
|
|
|
*)
|
|
|
|
COMPREPLY=()
|
|
|
|
;;
|
|
|
|
esac
|
2019-03-31 16:16:04 +00:00
|
|
|
COMPREPLY=( $(compgen -W \"${{opts}}\" -- \"${{cur}}\") )
|
2020-02-05 10:04:59 +00:00
|
|
|
return 0
|
2021-10-11 23:17:09 +00:00
|
|
|
;;{subcmd_details}
|
2020-02-05 10:04:59 +00:00
|
|
|
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 {
|
2020-04-22 18:14:47 +00:00
|
|
|
debug!("all_subcommands");
|
2020-02-05 10:04:59 +00:00
|
|
|
|
2021-10-11 23:17:09 +00:00
|
|
|
let mut subcmds = vec![String::new()];
|
2021-10-12 13:27:24 +00:00
|
|
|
let mut scs = utils::all_subcommands(app)
|
2020-02-06 10:19:03 +00:00
|
|
|
.iter()
|
|
|
|
.map(|x| x.0.clone())
|
|
|
|
.collect::<Vec<_>>();
|
|
|
|
|
|
|
|
scs.sort();
|
|
|
|
scs.dedup();
|
2020-02-05 10:04:59 +00:00
|
|
|
|
2021-10-11 23:17:09 +00:00
|
|
|
subcmds.extend(scs.iter().map(|sc| {
|
|
|
|
format!(
|
|
|
|
"{name})
|
2020-02-05 10:04:59 +00:00
|
|
|
cmd+=\"__{fn_name}\"
|
|
|
|
;;",
|
|
|
|
name = sc,
|
|
|
|
fn_name = sc.replace("-", "__")
|
2021-10-11 23:17:09 +00:00
|
|
|
)
|
|
|
|
}));
|
2020-02-05 10:04:59 +00:00
|
|
|
|
2021-10-11 23:17:09 +00:00
|
|
|
subcmds.join("\n ")
|
2020-02-05 10:04:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn subcommand_details(app: &App) -> String {
|
2020-04-22 18:14:47 +00:00
|
|
|
debug!("subcommand_details");
|
2020-02-05 10:04:59 +00:00
|
|
|
|
2021-10-11 23:17:09 +00:00
|
|
|
let mut subcmd_dets = vec![String::new()];
|
2021-10-12 13:27:24 +00:00
|
|
|
let mut scs = utils::all_subcommands(app)
|
2020-02-06 10:19:03 +00:00
|
|
|
.iter()
|
|
|
|
.map(|x| x.1.replace(" ", "__"))
|
|
|
|
.collect::<Vec<_>>();
|
2020-02-05 10:04:59 +00:00
|
|
|
|
|
|
|
scs.sort();
|
|
|
|
|
2021-10-11 23:17:09 +00:00
|
|
|
subcmd_dets.extend(scs.iter().map(|sc| {
|
|
|
|
format!(
|
|
|
|
"{subcmd})
|
2020-02-05 10:04:59 +00:00
|
|
|
opts=\"{sc_opts}\"
|
|
|
|
if [[ ${{cur}} == -* || ${{COMP_CWORD}} -eq {level} ]] ; then
|
2019-03-31 16:16:04 +00:00
|
|
|
COMPREPLY=( $(compgen -W \"${{opts}}\" -- \"${{cur}}\") )
|
2020-02-05 10:04:59 +00:00
|
|
|
return 0
|
|
|
|
fi
|
2021-10-11 23:17:09 +00:00
|
|
|
case \"${{prev}}\" in{opts_details}
|
2020-02-05 10:04:59 +00:00
|
|
|
*)
|
|
|
|
COMPREPLY=()
|
|
|
|
;;
|
|
|
|
esac
|
2019-03-31 16:16:04 +00:00
|
|
|
COMPREPLY=( $(compgen -W \"${{opts}}\" -- \"${{cur}}\") )
|
2020-02-05 10:04:59 +00:00
|
|
|
return 0
|
|
|
|
;;",
|
|
|
|
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)
|
2021-10-11 23:17:09 +00:00
|
|
|
)
|
|
|
|
}));
|
2020-02-05 10:04:59 +00:00
|
|
|
|
2021-10-11 23:17:09 +00:00
|
|
|
subcmd_dets.join("\n ")
|
2020-02-05 10:04:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn option_details_for_path(app: &App, path: &str) -> String {
|
2020-04-22 18:14:47 +00:00
|
|
|
debug!("option_details_for_path: path={}", path);
|
2020-02-05 10:04:59 +00:00
|
|
|
|
2021-10-12 13:27:24 +00:00
|
|
|
let p = utils::find_subcommand_with_path(app, path.split("__").skip(1).collect());
|
2021-10-11 23:17:09 +00:00
|
|
|
let mut opts = vec![String::new()];
|
2020-02-05 10:04:59 +00:00
|
|
|
|
2020-10-19 15:55:07 +00:00
|
|
|
for o in p.get_opts() {
|
2021-02-22 08:34:18 +00:00
|
|
|
if let Some(longs) = o.get_long_and_visible_aliases() {
|
2021-10-11 23:17:09 +00:00
|
|
|
opts.extend(longs.iter().map(|long| {
|
|
|
|
format!(
|
|
|
|
"--{})
|
2020-02-05 10:04:59 +00:00
|
|
|
COMPREPLY=({})
|
|
|
|
return 0
|
|
|
|
;;",
|
2021-02-22 08:34:18 +00:00
|
|
|
long,
|
|
|
|
vals_for(o)
|
2021-10-11 23:17:09 +00:00
|
|
|
)
|
|
|
|
}));
|
2020-02-05 10:04:59 +00:00
|
|
|
}
|
|
|
|
|
2021-02-22 08:34:18 +00:00
|
|
|
if let Some(shorts) = o.get_short_and_visible_aliases() {
|
2021-10-11 23:17:09 +00:00
|
|
|
opts.extend(shorts.iter().map(|short| {
|
|
|
|
format!(
|
|
|
|
"-{})
|
2020-02-05 10:04:59 +00:00
|
|
|
COMPREPLY=({})
|
|
|
|
return 0
|
|
|
|
;;",
|
2021-02-22 08:34:18 +00:00
|
|
|
short,
|
|
|
|
vals_for(o)
|
2021-10-11 23:17:09 +00:00
|
|
|
)
|
|
|
|
}));
|
2020-02-05 10:04:59 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-10-11 23:17:09 +00:00
|
|
|
opts.join("\n ")
|
2020-02-05 10:04:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn vals_for(o: &Arg) -> String {
|
2020-04-22 18:14:47 +00:00
|
|
|
debug!("vals_for: o={}", o.get_name());
|
2020-02-05 10:04:59 +00:00
|
|
|
|
2021-06-17 03:25:13 +00:00
|
|
|
if let Some(vals) = o.get_possible_values() {
|
2021-09-19 10:29:09 +00:00
|
|
|
format!(
|
|
|
|
"$(compgen -W \"{}\" -- \"${{cur}}\")",
|
|
|
|
vals.iter()
|
|
|
|
.filter_map(ArgValue::get_visible_name)
|
|
|
|
.collect::<Vec<_>>()
|
|
|
|
.join(" ")
|
|
|
|
)
|
2020-02-05 10:04:59 +00:00
|
|
|
} else {
|
2019-03-31 16:16:04 +00:00
|
|
|
String::from("$(compgen -f \"${cur}\")")
|
2020-02-05 10:04:59 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn all_options_for_path(app: &App, path: &str) -> String {
|
2020-04-22 18:14:47 +00:00
|
|
|
debug!("all_options_for_path: path={}", path);
|
2020-02-05 10:04:59 +00:00
|
|
|
|
2021-10-12 13:27:24 +00:00
|
|
|
let p = utils::find_subcommand_with_path(app, path.split("__").skip(1).collect());
|
|
|
|
let scs: Vec<_> = utils::subcommands(p).iter().map(|x| x.0.clone()).collect();
|
2020-02-05 10:04:59 +00:00
|
|
|
|
|
|
|
let opts = format!(
|
|
|
|
"{shorts} {longs} {pos} {subcmds}",
|
2021-10-12 13:27:24 +00:00
|
|
|
shorts = utils::shorts_and_visible_aliases(p)
|
2020-02-06 10:19:03 +00:00
|
|
|
.iter()
|
|
|
|
.fold(String::new(), |acc, s| format!("{} -{}", acc, s)),
|
2021-10-12 13:27:24 +00:00
|
|
|
longs = utils::longs_and_visible_aliases(p)
|
2020-02-06 10:19:03 +00:00
|
|
|
.iter()
|
|
|
|
.fold(String::new(), |acc, l| format!("{} --{}", acc, l)),
|
2021-10-14 18:41:11 +00:00
|
|
|
pos = p.get_positionals().fold(String::new(), |acc, p| {
|
|
|
|
if let Some(vals) = p.get_possible_values() {
|
|
|
|
format!(
|
|
|
|
"{} {}",
|
|
|
|
acc,
|
|
|
|
vals.iter()
|
|
|
|
.map(|x| x.get_name())
|
|
|
|
.collect::<Vec<_>>()
|
|
|
|
.join(" ")
|
|
|
|
)
|
|
|
|
} else {
|
|
|
|
format!("{} {}", acc, p)
|
|
|
|
}
|
|
|
|
}),
|
2020-02-06 10:19:03 +00:00
|
|
|
subcmds = scs.join(" "),
|
2020-02-05 10:04:59 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
opts
|
|
|
|
}
|