clap/clap_complete/src/shells/bash.rs

218 lines
5.8 KiB
Rust
Raw Normal View History

2021-10-14 21:51:40 +00:00
use std::{fmt::Write as _, io::Write};
2020-02-05 10:04:59 +00:00
use clap::*;
2021-12-31 19:20:55 +00:00
use crate::generator::{utils, Generator};
2020-02-05 10:04:59 +00:00
/// 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) {
let bin_name = app
.get_bin_name()
.expect("crate::generate should have set the bin_name");
2020-02-05 10:04:59 +00:00
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
\"$1\")
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(' ', "__"))
2020-02-06 10:19:03 +00:00
.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('-', "__"),
2020-02-05 10:04:59 +00:00
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(PossibleValue::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());
2021-10-14 21:51:40 +00:00
let mut opts = String::new();
for short in utils::shorts_and_visible_aliases(p) {
write!(&mut opts, "-{} ", short).unwrap();
}
for long in utils::longs_and_visible_aliases(p) {
write!(&mut opts, "--{} ", long).unwrap();
}
for pos in p.get_positionals() {
if let Some(vals) = pos.get_possible_values() {
for value in vals {
write!(&mut opts, "{} ", value.get_name()).unwrap();
}
2021-10-14 21:51:40 +00:00
} else {
write!(&mut opts, "{} ", pos).unwrap();
}
}
for (sc, _) in utils::subcommands(p) {
write!(&mut opts, "{} ", sc).unwrap();
}
opts.pop();
2020-02-05 10:04:59 +00:00
opts
}