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