clap/completions/
elvish.rs

1// Std
2use std::io::Write;
3
4// Internal
5use 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
53// Escape string inside single quotes
54fn 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}