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