diff options
Diffstat (limited to 'vendor/clap_complete/src/shells/zsh.rs')
-rw-r--r-- | vendor/clap_complete/src/shells/zsh.rs | 153 |
1 files changed, 92 insertions, 61 deletions
diff --git a/vendor/clap_complete/src/shells/zsh.rs b/vendor/clap_complete/src/shells/zsh.rs index 580de77a2..71c586d8e 100644 --- a/vendor/clap_complete/src/shells/zsh.rs +++ b/vendor/clap_complete/src/shells/zsh.rs @@ -11,7 +11,7 @@ pub struct Zsh; impl Generator for Zsh { fn file_name(&self, name: &str) -> String { - format!("_{}", name) + format!("_{name}") } fn generate(&self, cmd: &Command, buf: &mut dyn Write) { @@ -43,7 +43,11 @@ _{name}() {{ {subcommand_details} -_{name} \"$@\" +if [ \"$funcstack[1]\" = \"_{name}\" ]; then + _{name} \"$@\" +else + compdef _{name} {name} +fi ", name = bin_name, initial_args = get_args_of(cmd, None), @@ -111,8 +115,8 @@ _{bin_name_underscore}_commands() {{ all_subcommands.sort(); all_subcommands.dedup(); - for &(_, ref bin_name) in &all_subcommands { - debug!("subcommand_details:iter: bin_name={}", bin_name); + for (_, ref bin_name) in &all_subcommands { + debug!("subcommand_details:iter: bin_name={bin_name}"); ret.push(format!( "\ @@ -223,14 +227,12 @@ fn get_subcommands_of(parent: &Command) -> String { let subcommand_names = utils::subcommands(parent); let mut all_subcommands = vec![]; - for &(ref name, ref bin_name) in &subcommand_names { + for (ref name, ref bin_name) in &subcommand_names { debug!( - "get_subcommands_of:iter: parent={}, name={}, bin_name={}", + "get_subcommands_of:iter: parent={}, name={name}, bin_name={bin_name}", parent.get_name(), - name, - bin_name, ); - let mut segments = vec![format!("({})", name)]; + let mut segments = vec![format!("({name})")]; let subcommand_args = get_args_of( parser_of(parent, bin_name).expect(INTERNAL_ERROR_MSG), Some(parent), @@ -424,6 +426,9 @@ fn escape_help(string: &str) -> String { .replace('\'', "'\\''") .replace('[', "\\[") .replace(']', "\\]") + .replace(':', "\\:") + .replace('$', "\\$") + .replace('`', "\\`") } /// Escape value string inside single quotes and parentheses @@ -431,6 +436,11 @@ fn escape_value(string: &str) -> String { string .replace('\\', "\\\\") .replace('\'', "'\\''") + .replace('[', "\\[") + .replace(']', "\\]") + .replace(':', "\\:") + .replace('$', "\\$") + .replace('`', "\\`") .replace('(', "\\(") .replace(')', "\\)") .replace(' ', "\\ ") @@ -458,21 +468,14 @@ fn write_opts_of(p: &Command, p_global: Option<&Command>) -> String { Some(val) => val[0].to_string(), }; let vc = match value_completion(o) { - Some(val) => format!(":{}:{}", vn, val), - None => format!(":{}: ", vn), + Some(val) => format!(":{vn}:{val}"), + None => format!(":{vn}: "), }; let vc = vc.repeat(o.get_num_args().expect("built").min_values()); if let Some(shorts) = o.get_short_and_visible_aliases() { for short in shorts { - let s = format!( - "'{conflicts}{multiple}-{arg}+[{help}]{value_completion}' \\", - conflicts = conflicts, - multiple = multiple, - arg = short, - value_completion = vc, - help = help - ); + let s = format!("'{conflicts}{multiple}-{short}+[{help}]{vc}' \\"); debug!("write_opts_of:iter: Wrote...{}", &*s); ret.push(s); @@ -480,14 +483,7 @@ fn write_opts_of(p: &Command, p_global: Option<&Command>) -> String { } if let Some(longs) = o.get_long_and_visible_aliases() { for long in longs { - let l = format!( - "'{conflicts}{multiple}--{arg}=[{help}]{value_completion}' \\", - conflicts = conflicts, - multiple = multiple, - arg = long, - value_completion = vc, - help = help - ); + let l = format!("'{conflicts}{multiple}--{long}=[{help}]{vc}' \\"); debug!("write_opts_of:iter: Wrote...{}", &*l); ret.push(l); @@ -502,11 +498,11 @@ fn arg_conflicts(cmd: &Command, arg: &Arg, app_global: Option<&Command>) -> Stri fn push_conflicts(conflicts: &[&Arg], res: &mut Vec<String>) { for conflict in conflicts { if let Some(s) = conflict.get_short() { - res.push(format!("-{}", s)); + res.push(format!("-{s}")); } if let Some(l) = conflict.get_long() { - res.push(format!("--{}", l)); + res.push(format!("--{l}")); } } } @@ -554,13 +550,7 @@ fn write_flags_of(p: &Command, p_global: Option<&Command>) -> String { }; if let Some(short) = f.get_short() { - let s = format!( - "'{conflicts}{multiple}-{arg}[{help}]' \\", - multiple = multiple, - conflicts = conflicts, - arg = short, - help = help - ); + let s = format!("'{conflicts}{multiple}-{short}[{help}]' \\"); debug!("write_flags_of:iter: Wrote...{}", &*s); @@ -568,13 +558,7 @@ fn write_flags_of(p: &Command, p_global: Option<&Command>) -> String { if let Some(short_aliases) = f.get_visible_short_aliases() { for alias in short_aliases { - let s = format!( - "'{conflicts}{multiple}-{arg}[{help}]' \\", - multiple = multiple, - conflicts = conflicts, - arg = alias, - help = help - ); + let s = format!("'{conflicts}{multiple}-{alias}[{help}]' \\",); debug!("write_flags_of:iter: Wrote...{}", &*s); @@ -584,13 +568,7 @@ fn write_flags_of(p: &Command, p_global: Option<&Command>) -> String { } if let Some(long) = f.get_long() { - let l = format!( - "'{conflicts}{multiple}--{arg}[{help}]' \\", - conflicts = conflicts, - multiple = multiple, - arg = long, - help = help - ); + let l = format!("'{conflicts}{multiple}--{long}[{help}]' \\"); debug!("write_flags_of:iter: Wrote...{}", &*l); @@ -598,13 +576,7 @@ fn write_flags_of(p: &Command, p_global: Option<&Command>) -> String { if let Some(aliases) = f.get_visible_aliases() { for alias in aliases { - let l = format!( - "'{conflicts}{multiple}--{arg}[{help}]' \\", - conflicts = conflicts, - multiple = multiple, - arg = alias, - help = help - ); + let l = format!("'{conflicts}{multiple}--{alias}[{help}]' \\"); debug!("write_flags_of:iter: Wrote...{}", &*l); @@ -622,12 +594,47 @@ fn write_positionals_of(p: &Command) -> String { let mut ret = vec![]; + // Completions for commands that end with two Vec arguments require special care. + // - You can have two Vec args separated with a custom value terminator. + // - You can have two Vec args with the second one set to last (raw sets last) + // which will require a '--' separator to be used before the second argument + // on the command-line. + // + // We use the '-S' _arguments option to disable completion after '--'. Thus, the + // completion for the second argument in scenario (B) does not need to be emitted + // because it is implicitly handled by the '-S' option. + // We only need to emit the first catch-all. + // + // Have we already emitted a catch-all multi-valued positional argument + // without a custom value terminator? + let mut catch_all_emitted = false; + for arg in p.get_positionals() { debug!("write_positionals_of:iter: arg={}", arg.get_id()); let num_args = arg.get_num_args().expect("built"); - let cardinality = if num_args.max_values() > 1 { - "*:" + let is_multi_valued = num_args.max_values() > 1; + + if catch_all_emitted && (arg.is_last_set() || is_multi_valued) { + // This is the final argument and it also takes multiple arguments. + // We've already emitted a catch-all positional argument so we don't need + // to emit anything for this argument because it is implicitly handled by + // the use of the '-S' _arguments option. + continue; + } + + let cardinality_value; + let cardinality = if is_multi_valued { + match arg.get_value_terminator() { + Some(terminator) => { + cardinality_value = format!("*{}:", escape_value(terminator)); + cardinality_value.as_str() + } + None => { + catch_all_emitted = true; + "*:" + } + } } else if !arg.is_required_set() { ":" } else { @@ -641,7 +648,8 @@ fn write_positionals_of(p: &Command) -> String { help = arg .get_help() .map(|s| s.to_string()) - .map_or("".to_owned(), |v| " -- ".to_owned() + &v) + .map(|v| " -- ".to_owned() + &v) + .unwrap_or_else(|| "".to_owned()) .replace('[', "\\[") .replace(']', "\\]") .replace('\'', "'\\''") @@ -649,10 +657,33 @@ fn write_positionals_of(p: &Command) -> String { value_completion = value_completion(arg).unwrap_or_default() ); - debug!("write_positionals_of:iter: Wrote...{}", a); + debug!("write_positionals_of:iter: Wrote...{a}"); ret.push(a); } ret.join("\n") } + +#[cfg(test)] +mod tests { + use crate::shells::zsh::{escape_help, escape_value}; + + #[test] + fn test_escape_value() { + let raw_string = "\\ [foo]() `bar https://$PATH"; + assert_eq!( + escape_value(raw_string), + "\\\\\\ \\[foo\\]\\(\\)\\ \\`bar\\ https\\://\\$PATH" + ) + } + + #[test] + fn test_escape_help() { + let raw_string = "\\ [foo]() `bar https://$PATH"; + assert_eq!( + escape_help(raw_string), + "\\\\ \\[foo\\]() \\`bar https\\://\\$PATH" + ) + } +} |