clap/clap_generate/src/generators/shells/bash.rs

223 lines
6 KiB
Rust
Raw Normal View History

2020-02-05 10:04:59 +00:00
// Std
use std::io::Write;
// Internal
use crate::utils;
2020-02-05 10:04:59 +00:00
use crate::Generator;
use clap::*;
/// Generate bash completion file
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
2020-02-05 10:04:59 +00:00
pub struct Bash;
impl Generator for Bash {
fn file_name(&self, name: &str) -> String {
2020-02-05 10:04:59 +00:00
format!("{}.bash", name)
}
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})
cmd=\"{cmd}\"
;;{subcmds}
2020-02-05 10:04:59 +00:00
*)
;;
esac
done
case \"${{cmd}}\" in
{cmd})
2020-02-05 10:04:59 +00:00
opts=\"{name_opts}\"
if [[ ${{cur}} == -* || ${{COMP_CWORD}} -eq 1 ]] ; then
COMPREPLY=( $(compgen -W \"${{opts}}\" -- \"${{cur}}\") )
2020-02-05 10:04:59 +00:00
return 0
fi
case \"${{prev}}\" in{name_opts_details}
2020-02-05 10:04:59 +00:00
*)
COMPREPLY=()
;;
esac
COMPREPLY=( $(compgen -W \"${{opts}}\" -- \"${{cur}}\") )
2020-02-05 10:04:59 +00:00
return 0
;;{subcmd_details}
2020-02-05 10:04:59 +00:00
esac
}}
complete -F _{name} -o bashdefault -o default {name}
",
name = bin_name,
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
let mut subcmds = vec![String::new()];
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
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("-", "__")
)
}));
2020-02-05 10:04:59 +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
let mut subcmd_dets = vec![String::new()];
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();
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
COMPREPLY=( $(compgen -W \"${{opts}}\" -- \"${{cur}}\") )
2020-02-05 10:04:59 +00:00
return 0
fi
case \"${{prev}}\" in{opts_details}
2020-02-05 10:04:59 +00:00
*)
COMPREPLY=()
;;
esac
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)
)
}));
2020-02-05 10:04:59 +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
let p = utils::find_subcommand_with_path(app, path.split("__").skip(1).collect());
let mut opts = vec![String::new()];
2020-02-05 10:04:59 +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() {
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)
)
}));
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() {
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)
)
}));
2020-02-05 10:04:59 +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() {
format!(
"$(compgen -W \"{}\" -- \"${{cur}}\")",
vals.iter()
.filter_map(ArgValue::get_visible_name)
.collect::<Vec<_>>()
.join(" ")
)
2020-02-05 10:04:59 +00:00
} else {
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
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}",
shorts = utils::shorts_and_visible_aliases(p)
2020-02-06 10:19:03 +00:00
.iter()
.fold(String::new(), |acc, s| format!("{} -{}", acc, s)),
longs = utils::longs_and_visible_aliases(p)
2020-02-06 10:19:03 +00:00
.iter()
.fold(String::new(), |acc, l| format!("{} --{}", acc, l)),
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
}