• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 use std::io::Write;
2 
3 use clap::builder::StyledStr;
4 use clap::*;
5 
6 use crate::generator::{utils, Generator};
7 use crate::INTERNAL_ERROR_MSG;
8 
9 /// Generate elvish completion file
10 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
11 pub struct Elvish;
12 
13 impl Generator for Elvish {
file_name(&self, name: &str) -> String14     fn file_name(&self, name: &str) -> String {
15         format!("{}.elv", name)
16     }
17 
generate(&self, cmd: &Command, buf: &mut dyn Write)18     fn generate(&self, cmd: &Command, buf: &mut dyn Write) {
19         let bin_name = cmd
20             .get_bin_name()
21             .expect("crate::generate should have set the bin_name");
22 
23         let subcommands_cases = generate_inner(cmd, "");
24 
25         let result = format!(
26             r#"
27 use builtin;
28 use str;
29 
30 set edit:completion:arg-completer[{bin_name}] = {{|@words|
31     fn spaces {{|n|
32         builtin:repeat $n ' ' | str:join ''
33     }}
34     fn cand {{|text desc|
35         edit:complex-candidate $text &display=$text' '(spaces (- 14 (wcswidth $text)))$desc
36     }}
37     var command = '{bin_name}'
38     for word $words[1..-1] {{
39         if (str:has-prefix $word '-') {{
40             break
41         }}
42         set command = $command';'$word
43     }}
44     var completions = [{subcommands_cases}
45     ]
46     $completions[$command]
47 }}
48 "#,
49             bin_name = bin_name,
50             subcommands_cases = subcommands_cases
51         );
52 
53         w!(buf, result.as_bytes());
54     }
55 }
56 
57 // Escape string inside single quotes
escape_string(string: &str) -> String58 fn escape_string(string: &str) -> String {
59     string.replace('\'', "''")
60 }
61 
get_tooltip<T: ToString>(help: Option<&StyledStr>, data: T) -> String62 fn get_tooltip<T: ToString>(help: Option<&StyledStr>, data: T) -> String {
63     match help {
64         Some(help) => escape_string(&help.to_string()),
65         _ => data.to_string(),
66     }
67 }
68 
generate_inner(p: &Command, previous_command_name: &str) -> String69 fn generate_inner(p: &Command, previous_command_name: &str) -> String {
70     debug!("generate_inner");
71 
72     let command_name = if previous_command_name.is_empty() {
73         p.get_bin_name().expect(INTERNAL_ERROR_MSG).to_string()
74     } else {
75         format!("{};{}", previous_command_name, &p.get_name())
76     };
77 
78     let mut completions = String::new();
79     let preamble = String::from("\n            cand ");
80 
81     for option in p.get_opts() {
82         if let Some(shorts) = option.get_short_and_visible_aliases() {
83             let tooltip = get_tooltip(option.get_help(), shorts[0]);
84             for short in shorts {
85                 completions.push_str(&preamble);
86                 completions.push_str(format!("-{} '{}'", short, tooltip).as_str());
87             }
88         }
89 
90         if let Some(longs) = option.get_long_and_visible_aliases() {
91             let tooltip = get_tooltip(option.get_help(), longs[0]);
92             for long in longs {
93                 completions.push_str(&preamble);
94                 completions.push_str(format!("--{} '{}'", long, tooltip).as_str());
95             }
96         }
97     }
98 
99     for flag in utils::flags(p) {
100         if let Some(shorts) = flag.get_short_and_visible_aliases() {
101             let tooltip = get_tooltip(flag.get_help(), shorts[0]);
102             for short in shorts {
103                 completions.push_str(&preamble);
104                 completions.push_str(format!("-{} '{}'", short, tooltip).as_str());
105             }
106         }
107 
108         if let Some(longs) = flag.get_long_and_visible_aliases() {
109             let tooltip = get_tooltip(flag.get_help(), longs[0]);
110             for long in longs {
111                 completions.push_str(&preamble);
112                 completions.push_str(format!("--{} '{}'", long, tooltip).as_str());
113             }
114         }
115     }
116 
117     for subcommand in p.get_subcommands() {
118         let data = &subcommand.get_name();
119         let tooltip = get_tooltip(subcommand.get_about(), data);
120 
121         completions.push_str(&preamble);
122         completions.push_str(format!("{} '{}'", data, tooltip).as_str());
123     }
124 
125     let mut subcommands_cases = format!(
126         r"
127         &'{}'= {{{}
128         }}",
129         &command_name, completions
130     );
131 
132     for subcommand in p.get_subcommands() {
133         let subcommand_subcommands_cases = generate_inner(subcommand, &command_name);
134         subcommands_cases.push_str(&subcommand_subcommands_cases);
135     }
136 
137     subcommands_cases
138 }
139