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