use std::io::Write;
use clap::*;
use crate::generator::{utils, Generator};
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub struct Fish;
impl Generator for Fish {
fn file_name(&self, name: &str) -> String {
format!("{name}.fish")
}
fn generate(&self, cmd: &Command, buf: &mut dyn Write) {
let bin_name = cmd
.get_bin_name()
.expect("crate::generate should have set the bin_name");
let mut buffer = String::new();
gen_fish_inner(bin_name, &[], cmd, &mut buffer);
w!(buf, buffer.as_bytes());
}
}
fn escape_string(string: &str, escape_comma: bool) -> String {
let string = string.replace('\\', "\\\\").replace('\'', "\\'");
if escape_comma {
string.replace(',', "\\,")
} else {
string
}
}
fn gen_fish_inner(
root_command: &str,
parent_commands: &[&str],
cmd: &Command,
buffer: &mut String,
) {
debug!("gen_fish_inner");
let mut basic_template = format!("complete -c {root_command}");
if parent_commands.is_empty() {
if cmd.has_subcommands() {
basic_template.push_str(" -n \"__fish_use_subcommand\"");
}
} else {
basic_template.push_str(
format!(
" -n \"{}\"",
parent_commands
.iter()
.map(|command| format!("__fish_seen_subcommand_from {command}"))
.chain(
cmd.get_subcommands()
.map(|command| format!("not __fish_seen_subcommand_from {command}"))
)
.collect::<Vec<_>>()
.join("; and ")
)
.as_str(),
);
}
debug!("gen_fish_inner: parent_commands={parent_commands:?}");
for option in cmd.get_opts() {
let mut template = basic_template.clone();
if let Some(shorts) = option.get_short_and_visible_aliases() {
for short in shorts {
template.push_str(format!(" -s {short}").as_str());
}
}
if let Some(longs) = option.get_long_and_visible_aliases() {
for long in longs {
template.push_str(format!(" -l {}", escape_string(long, false)).as_str());
}
}
if let Some(data) = option.get_help() {
template
.push_str(format!(" -d '{}'", escape_string(&data.to_string(), false)).as_str());
}
template.push_str(value_completion(option).as_str());
buffer.push_str(template.as_str());
buffer.push('\n');
}
for flag in utils::flags(cmd) {
let mut template = basic_template.clone();
if let Some(shorts) = flag.get_short_and_visible_aliases() {
for short in shorts {
template.push_str(format!(" -s {short}").as_str());
}
}
if let Some(longs) = flag.get_long_and_visible_aliases() {
for long in longs {
template.push_str(format!(" -l {}", escape_string(long, false)).as_str());
}
}
if let Some(data) = flag.get_help() {
template
.push_str(format!(" -d '{}'", escape_string(&data.to_string(), false)).as_str());
}
buffer.push_str(template.as_str());
buffer.push('\n');
}
for subcommand in cmd.get_subcommands() {
let mut template = basic_template.clone();
template.push_str(" -f");
template.push_str(format!(" -a \"{}\"", &subcommand.get_name()).as_str());
if let Some(data) = subcommand.get_about() {
template.push_str(format!(" -d '{}'", escape_string(&data.to_string(), false)).as_str())
}
buffer.push_str(template.as_str());
buffer.push('\n');
}
for subcommand in cmd.get_subcommands() {
let mut parent_commands: Vec<_> = parent_commands.into();
parent_commands.push(subcommand.get_name());
gen_fish_inner(root_command, &parent_commands, subcommand, buffer);
}
}
fn value_completion(option: &Arg) -> String {
if !option.get_num_args().expect("built").takes_values() {
return "".to_string();
}
if let Some(data) = crate::generator::utils::possible_values(option) {
format!(
" -r -f -a \"{{{}}}\"",
data.iter()
.filter_map(|value| if value.is_hide_set() {
None
} else {
Some(format!(
"{}\t{}",
escape_string(value.get_name(), true).as_str(),
escape_string(&value.get_help().unwrap_or_default().to_string(), true)
))
})
.collect::<Vec<_>>()
.join(",")
)
} else {
match option.get_value_hint() {
ValueHint::Unknown => " -r",
ValueHint::AnyPath | ValueHint::FilePath | ValueHint::ExecutablePath => " -r -F",
ValueHint::DirPath => " -r -f -a \"(__fish_complete_directories)\"",
ValueHint::CommandString | ValueHint::CommandName => {
" -r -f -a \"(__fish_complete_command)\""
}
ValueHint::Username => " -r -f -a \"(__fish_complete_users)\"",
ValueHint::Hostname => " -r -f -a \"(__fish_print_hostnames)\"",
_ => " -r -f",
}
.to_string()
}
}