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

212 lines
5.3 KiB
Rust
Raw Normal View History

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})
cmd=\"{cmd}\"
2020-02-05 10:04:59 +00:00
;;
{subcmds}
*)
;;
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}
*)
COMPREPLY=()
;;
esac
COMPREPLY=( $(compgen -W \"${{opts}}\" -- \"${{cur}}\") )
2020-02-05 10:04:59 +00:00
return 0
;;
{subcmd_details}
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 = 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 {
2020-04-22 18:14:47 +00:00
debug!("subcommand_details");
2020-02-05 10:04:59 +00:00
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}}\") )
2020-02-05 10:04:59 +00:00
return 0
fi
case \"${{prev}}\" in
{opts_details}
*)
COMPREPLY=()
;;
esac
COMPREPLY=( $(compgen -W \"${{opts}}\" -- \"${{cur}}\") )
2020-02-05 10:04:59 +00:00
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 {
2020-04-22 18:14:47 +00:00
debug!("option_details_for_path: path={}", path);
2020-02-05 10:04:59 +00:00
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 p.get_opts() {
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)
);
}
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-22 18:14:47 +00:00
debug!("vals_for: o={}", o.get_name());
2020-02-05 10:04:59 +00:00
if let Some(ref vals) = o.get_possible_values() {
format!("$(compgen -W \"{}\" -- \"${{cur}}\")", vals.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
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}",
shorts = Bash::shorts_and_visible_aliases(p)
2020-02-06 10:19:03 +00:00
.iter()
.fold(String::new(), |acc, s| format!("{} -{}", acc, s)),
longs = Bash::longs_and_visible_aliases(p)
2020-02-06 10:19:03 +00:00
.iter()
.fold(String::new(), |acc, l| format!("{} --{}", acc, l)),
2020-06-18 20:16:46 +00:00
pos = p
.get_positionals()
.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
}