clap_complete/aot/shells/
elvish.rs
1use std::io::Write;
2
3use clap::builder::StyledStr;
4use clap::Command;
5
6use crate::generator::{utils, Generator};
7use crate::INTERNAL_ERROR_MSG;
8
9#[derive(Copy, Clone, PartialEq, Eq, Debug)]
11pub struct Elvish;
12
13impl Generator for Elvish {
14 fn file_name(&self, name: &str) -> String {
15 format!("{name}.elv")
16 }
17
18 fn generate(&self, cmd: &Command, buf: &mut dyn Write) {
19 let bin_name = cmd
20 .get_bin_name()
21 .expect("crate::generate should have set the bin_name");
22
23 let subcommands_cases = generate_inner(cmd, "");
24
25 write!(
26 buf,
27 r#"
28use builtin;
29use str;
30
31set edit:completion:arg-completer[{bin_name}] = {{|@words|
32 fn spaces {{|n|
33 builtin:repeat $n ' ' | str:join ''
34 }}
35 fn cand {{|text desc|
36 edit:complex-candidate $text &display=$text' '(spaces (- 14 (wcswidth $text)))$desc
37 }}
38 var command = '{bin_name}'
39 for word $words[1..-1] {{
40 if (str:has-prefix $word '-') {{
41 break
42 }}
43 set command = $command';'$word
44 }}
45 var completions = [{subcommands_cases}
46 ]
47 $completions[$command]
48}}
49"#,
50 )
51 .expect("failed to write completion file");
52 }
53}
54
55fn escape_string(string: &str) -> String {
57 string.replace('\'', "''")
58}
59
60fn escape_help<T: ToString>(help: Option<&StyledStr>, data: T) -> String {
61 match help {
62 Some(help) => escape_string(&help.to_string().replace('\n', " ")),
63 _ => data.to_string(),
64 }
65}
66
67fn generate_inner(p: &Command, previous_command_name: &str) -> String {
68 debug!("generate_inner");
69
70 let command_names = if previous_command_name.is_empty() {
71 vec![p.get_bin_name().expect(INTERNAL_ERROR_MSG).to_string()]
72 } else {
73 p.get_name_and_visible_aliases()
74 .into_iter()
75 .map(|name| format!("{previous_command_name};{name}"))
76 .collect()
77 };
78
79 let mut completions = String::new();
80 let preamble = String::from("\n cand ");
81
82 for option in p.get_opts() {
83 if let Some(shorts) = option.get_short_and_visible_aliases() {
84 let tooltip = escape_help(option.get_help(), shorts[0]);
85 for short in shorts {
86 completions.push_str(&preamble);
87 completions.push_str(format!("-{short} '{tooltip}'").as_str());
88 }
89 }
90
91 if let Some(longs) = option.get_long_and_visible_aliases() {
92 let tooltip = escape_help(option.get_help(), longs[0]);
93 for long in longs {
94 completions.push_str(&preamble);
95 completions.push_str(format!("--{long} '{tooltip}'").as_str());
96 }
97 }
98 }
99
100 for flag in utils::flags(p) {
101 if let Some(shorts) = flag.get_short_and_visible_aliases() {
102 let tooltip = escape_help(flag.get_help(), shorts[0]);
103 for short in shorts {
104 completions.push_str(&preamble);
105 completions.push_str(format!("-{short} '{tooltip}'").as_str());
106 }
107 }
108
109 if let Some(longs) = flag.get_long_and_visible_aliases() {
110 let tooltip = escape_help(flag.get_help(), longs[0]);
111 for long in longs {
112 completions.push_str(&preamble);
113 completions.push_str(format!("--{long} '{tooltip}'").as_str());
114 }
115 }
116 }
117
118 for subcommand in p.get_subcommands() {
119 for name in subcommand.get_name_and_visible_aliases() {
120 let tooltip = escape_help(subcommand.get_about(), name);
121
122 completions.push_str(&preamble);
123 completions.push_str(format!("{name} '{tooltip}'").as_str());
124 }
125 }
126
127 let mut subcommands_cases = String::new();
128 for command_name in &command_names {
129 subcommands_cases.push_str(&format!(
130 r"
131 &'{}'= {{{}
132 }}",
133 &command_name, completions
134 ));
135 }
136
137 for subcommand in p.get_subcommands() {
138 for command_name in &command_names {
139 let subcommand_subcommands_cases = generate_inner(subcommand, command_name);
140 subcommands_cases.push_str(&subcommand_subcommands_cases);
141 }
142 }
143
144 subcommands_cases
145}