• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Std
2 use std::io::Write;
3 
4 // Internal
5 use app::parser::Parser;
6 use INTERNAL_ERROR_MSG;
7 
8 pub struct ElvishGen<'a, 'b>
9 where
10     'a: 'b,
11 {
12     p: &'b Parser<'a, 'b>,
13 }
14 
15 impl<'a, 'b> ElvishGen<'a, 'b> {
new(p: &'b Parser<'a, 'b>) -> Self16     pub fn new(p: &'b Parser<'a, 'b>) -> Self {
17         ElvishGen { p: p }
18     }
19 
generate_to<W: Write>(&self, buf: &mut W)20     pub fn generate_to<W: Write>(&self, buf: &mut W) {
21         let bin_name = self.p.meta.bin_name.as_ref().unwrap();
22 
23         let mut names = vec![];
24         let subcommands_cases = generate_inner(self.p, "", &mut names);
25 
26         let result = format!(
27             r#"
28 edit:completion:arg-completer[{bin_name}] = [@words]{{
29     fn spaces [n]{{
30         repeat $n ' ' | joins ''
31     }}
32     fn cand [text desc]{{
33         edit:complex-candidate $text &display-suffix=' '(spaces (- 14 (wcswidth $text)))$desc
34     }}
35     command = '{bin_name}'
36     for word $words[1:-1] {{
37         if (has-prefix $word '-') {{
38             break
39         }}
40         command = $command';'$word
41     }}
42     completions = [{subcommands_cases}
43     ]
44     $completions[$command]
45 }}
46 "#,
47             bin_name = bin_name,
48             subcommands_cases = subcommands_cases
49         );
50 
51         w!(buf, result.as_bytes());
52     }
53 }
54 
55 // Escape string inside single quotes
escape_string(string: &str) -> String56 fn escape_string(string: &str) -> String {
57     string.replace("'", "''")
58 }
59 
get_tooltip<T: ToString>(help: Option<&str>, data: T) -> String60 fn get_tooltip<T: ToString>(help: Option<&str>, data: T) -> String {
61     match help {
62         Some(help) => escape_string(help),
63         _ => data.to_string(),
64     }
65 }
66 
generate_inner<'a, 'b, 'p>( p: &'p Parser<'a, 'b>, previous_command_name: &str, names: &mut Vec<&'p str>, ) -> String67 fn generate_inner<'a, 'b, 'p>(
68     p: &'p Parser<'a, 'b>,
69     previous_command_name: &str,
70     names: &mut Vec<&'p str>,
71 ) -> String {
72     debugln!("ElvishGen::generate_inner;");
73     let command_name = if previous_command_name.is_empty() {
74         p.meta.bin_name.as_ref().expect(INTERNAL_ERROR_MSG).clone()
75     } else {
76         format!("{};{}", previous_command_name, &p.meta.name)
77     };
78 
79     let mut completions = String::new();
80     let preamble = String::from("\n            cand ");
81 
82     for option in p.opts() {
83         if let Some(data) = option.s.short {
84             let tooltip = get_tooltip(option.b.help, data);
85             completions.push_str(&preamble);
86             completions.push_str(format!("-{} '{}'", data, tooltip).as_str());
87         }
88         if let Some(data) = option.s.long {
89             let tooltip = get_tooltip(option.b.help, data);
90             completions.push_str(&preamble);
91             completions.push_str(format!("--{} '{}'", data, tooltip).as_str());
92         }
93     }
94 
95     for flag in p.flags() {
96         if let Some(data) = flag.s.short {
97             let tooltip = get_tooltip(flag.b.help, data);
98             completions.push_str(&preamble);
99             completions.push_str(format!("-{} '{}'", data, tooltip).as_str());
100         }
101         if let Some(data) = flag.s.long {
102             let tooltip = get_tooltip(flag.b.help, data);
103             completions.push_str(&preamble);
104             completions.push_str(format!("--{} '{}'", data, tooltip).as_str());
105         }
106     }
107 
108     for subcommand in &p.subcommands {
109         let data = &subcommand.p.meta.name;
110         let tooltip = get_tooltip(subcommand.p.meta.about, data);
111         completions.push_str(&preamble);
112         completions.push_str(format!("{} '{}'", data, tooltip).as_str());
113     }
114 
115     let mut subcommands_cases = format!(
116         r"
117         &'{}'= {{{}
118         }}",
119         &command_name, completions
120     );
121 
122     for subcommand in &p.subcommands {
123         let subcommand_subcommands_cases = generate_inner(&subcommand.p, &command_name, names);
124         subcommands_cases.push_str(&subcommand_subcommands_cases);
125     }
126 
127     subcommands_cases
128 }
129