use std::io::Write; use clap::builder::StyledStr; use clap::*; use crate::generator::{utils, Generator}; use crate::INTERNAL_ERROR_MSG; /// Generate elvish completion file #[derive(Copy, Clone, PartialEq, Eq, Debug)] pub struct Elvish; impl Generator for Elvish { fn file_name(&self, name: &str) -> String { format!("{}.elv", name) } 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 subcommands_cases = generate_inner(cmd, ""); let result = format!( r#" use builtin; use str; set edit:completion:arg-completer[{bin_name}] = {{|@words| fn spaces {{|n| builtin:repeat $n ' ' | str:join '' }} fn cand {{|text desc| edit:complex-candidate $text &display=$text' '(spaces (- 14 (wcswidth $text)))$desc }} var command = '{bin_name}' for word $words[1..-1] {{ if (str:has-prefix $word '-') {{ break }} set command = $command';'$word }} var completions = [{subcommands_cases} ] $completions[$command] }} "#, bin_name = bin_name, subcommands_cases = subcommands_cases ); w!(buf, result.as_bytes()); } } // Escape string inside single quotes fn escape_string(string: &str) -> String { string.replace('\'', "''") } fn get_tooltip(help: Option<&StyledStr>, data: T) -> String { match help { Some(help) => escape_string(&help.to_string()), _ => data.to_string(), } } fn generate_inner(p: &Command, previous_command_name: &str) -> String { debug!("generate_inner"); let command_name = if previous_command_name.is_empty() { p.get_bin_name().expect(INTERNAL_ERROR_MSG).to_string() } else { format!("{};{}", previous_command_name, &p.get_name()) }; let mut completions = String::new(); let preamble = String::from("\n cand "); for option in p.get_opts() { if let Some(shorts) = option.get_short_and_visible_aliases() { let tooltip = get_tooltip(option.get_help(), shorts[0]); for short in shorts { completions.push_str(&preamble); completions.push_str(format!("-{} '{}'", short, tooltip).as_str()); } } if let Some(longs) = option.get_long_and_visible_aliases() { let tooltip = get_tooltip(option.get_help(), longs[0]); for long in longs { completions.push_str(&preamble); completions.push_str(format!("--{} '{}'", long, tooltip).as_str()); } } } for flag in utils::flags(p) { if let Some(shorts) = flag.get_short_and_visible_aliases() { let tooltip = get_tooltip(flag.get_help(), shorts[0]); for short in shorts { completions.push_str(&preamble); completions.push_str(format!("-{} '{}'", short, tooltip).as_str()); } } if let Some(longs) = flag.get_long_and_visible_aliases() { let tooltip = get_tooltip(flag.get_help(), longs[0]); for long in longs { completions.push_str(&preamble); completions.push_str(format!("--{} '{}'", long, tooltip).as_str()); } } } for subcommand in p.get_subcommands() { let data = &subcommand.get_name(); let tooltip = get_tooltip(subcommand.get_about(), data); completions.push_str(&preamble); completions.push_str(format!("{} '{}'", data, tooltip).as_str()); } let mut subcommands_cases = format!( r" &'{}'= {{{} }}", &command_name, completions ); for subcommand in p.get_subcommands() { let subcommand_subcommands_cases = generate_inner(subcommand, &command_name); subcommands_cases.push_str(&subcommand_subcommands_cases); } subcommands_cases }