1use std::io::Write;
3#[allow(deprecated, unused_imports)]
4use std::ascii::AsciiExt;
5
6use app::App;
8use app::parser::Parser;
9use args::{AnyArg, ArgSettings};
10use completions;
11use INTERNAL_ERROR_MSG;
12
13pub struct ZshGen<'a, 'b>
14where
15 'a: 'b,
16{
17 p: &'b Parser<'a, 'b>,
18}
19
20impl<'a, 'b> ZshGen<'a, 'b> {
21 pub fn new(p: &'b Parser<'a, 'b>) -> Self {
22 debugln!("ZshGen::new;");
23 ZshGen { p: p }
24 }
25
26 pub fn generate_to<W: Write>(&self, buf: &mut W) {
27 debugln!("ZshGen::generate_to;");
28 w!(
29 buf,
30 format!(
31 "\
32#compdef {name}
33
34autoload -U is-at-least
35
36_{name}() {{
37 typeset -A opt_args
38 typeset -a _arguments_options
39 local ret=1
40
41 if is-at-least 5.2; then
42 _arguments_options=(-s -S -C)
43 else
44 _arguments_options=(-s -C)
45 fi
46
47 local context curcontext=\"$curcontext\" state line
48 {initial_args}
49 {subcommands}
50}}
51
52{subcommand_details}
53
54_{name} \"$@\"",
55 name = self.p.meta.bin_name.as_ref().unwrap(),
56 initial_args = get_args_of(self.p),
57 subcommands = get_subcommands_of(self.p),
58 subcommand_details = subcommand_details(self.p)
59 ).as_bytes()
60 );
61 }
62}
63
64fn subcommand_details(p: &Parser) -> String {
92 debugln!("ZshGen::subcommand_details;");
93 let mut ret = vec![
95 format!(
96 "\
97(( $+functions[_{bin_name_underscore}_commands] )) ||
98_{bin_name_underscore}_commands() {{
99 local commands; commands=(
100 {subcommands_and_args}
101 )
102 _describe -t commands '{bin_name} commands' commands \"$@\"
103}}",
104 bin_name_underscore = p.meta.bin_name.as_ref().unwrap().replace(" ", "__"),
105 bin_name = p.meta.bin_name.as_ref().unwrap(),
106 subcommands_and_args = subcommands_of(p)
107 ),
108 ];
109
110 let mut all_subcommands = completions::all_subcommands(p);
112 all_subcommands.sort();
113 all_subcommands.dedup();
114 for &(_, ref bin_name) in &all_subcommands {
115 debugln!("ZshGen::subcommand_details:iter: bin_name={}", bin_name);
116 ret.push(format!(
117 "\
118(( $+functions[_{bin_name_underscore}_commands] )) ||
119_{bin_name_underscore}_commands() {{
120 local commands; commands=(
121 {subcommands_and_args}
122 )
123 _describe -t commands '{bin_name} commands' commands \"$@\"
124}}",
125 bin_name_underscore = bin_name.replace(" ", "__"),
126 bin_name = bin_name,
127 subcommands_and_args = subcommands_of(parser_of(p, bin_name))
128 ));
129 }
130
131 ret.join("\n")
132}
133
134fn subcommands_of(p: &Parser) -> String {
146 debugln!("ZshGen::subcommands_of;");
147 let mut ret = vec![];
148 fn add_sc(sc: &App, n: &str, ret: &mut Vec<String>) {
149 debugln!("ZshGen::add_sc;");
150 let s = format!(
151 "\"{name}:{help}\" \\",
152 name = n,
153 help = sc.p
154 .meta
155 .about
156 .unwrap_or("")
157 .replace("[", "\\[")
158 .replace("]", "\\]")
159 );
160 if !s.is_empty() {
161 ret.push(s);
162 }
163 }
164
165 for sc in p.subcommands() {
167 debugln!(
168 "ZshGen::subcommands_of:iter: subcommand={}",
169 sc.p.meta.name
170 );
171 add_sc(sc, &sc.p.meta.name, &mut ret);
172 if let Some(ref v) = sc.p.meta.aliases {
173 for alias in v.iter().filter(|&&(_, vis)| vis).map(|&(n, _)| n) {
174 add_sc(sc, alias, &mut ret);
175 }
176 }
177 }
178
179 ret.join("\n")
180}
181
182fn get_subcommands_of(p: &Parser) -> String {
212 debugln!("get_subcommands_of;");
213
214 debugln!(
215 "get_subcommands_of: Has subcommands...{:?}",
216 p.has_subcommands()
217 );
218 if !p.has_subcommands() {
219 return String::new();
220 }
221
222 let sc_names = completions::subcommands_of(p);
223
224 let mut subcmds = vec![];
225 for &(ref name, ref bin_name) in &sc_names {
226 let mut v = vec![format!("({})", name)];
227 let subcommand_args = get_args_of(parser_of(p, &*bin_name));
228 if !subcommand_args.is_empty() {
229 v.push(subcommand_args);
230 }
231 let subcommands = get_subcommands_of(parser_of(p, &*bin_name));
232 if !subcommands.is_empty() {
233 v.push(subcommands);
234 }
235 v.push(String::from(";;"));
236 subcmds.push(v.join("\n"));
237 }
238
239 format!(
240 "case $state in
241 ({name})
242 words=($line[{pos}] \"${{words[@]}}\")
243 (( CURRENT += 1 ))
244 curcontext=\"${{curcontext%:*:*}}:{name_hyphen}-command-$line[{pos}]:\"
245 case $line[{pos}] in
246 {subcommands}
247 esac
248 ;;
249esac",
250 name = p.meta.name,
251 name_hyphen = p.meta.bin_name.as_ref().unwrap().replace(" ", "-"),
252 subcommands = subcmds.join("\n"),
253 pos = p.positionals().len() + 1
254 )
255}
256
257fn parser_of<'a, 'b>(p: &'b Parser<'a, 'b>, sc: &str) -> &'b Parser<'a, 'b> {
258 debugln!("parser_of: sc={}", sc);
259 if sc == p.meta.bin_name.as_ref().unwrap_or(&String::new()) {
260 return p;
261 }
262 &p.find_subcommand(sc).expect(INTERNAL_ERROR_MSG).p
263}
264
265fn get_args_of(p: &Parser) -> String {
286 debugln!("get_args_of;");
287 let mut ret = vec![String::from("_arguments \"${_arguments_options[@]}\" \\")];
288 let opts = write_opts_of(p);
289 let flags = write_flags_of(p);
290 let positionals = write_positionals_of(p);
291 let sc_or_a = if p.has_subcommands() {
292 format!(
293 "\":: :_{name}_commands\" \\",
294 name = p.meta.bin_name.as_ref().unwrap().replace(" ", "__")
295 )
296 } else {
297 String::new()
298 };
299 let sc = if p.has_subcommands() {
300 format!("\"*::: :->{name}\" \\", name = p.meta.name)
301 } else {
302 String::new()
303 };
304
305 if !opts.is_empty() {
306 ret.push(opts);
307 }
308 if !flags.is_empty() {
309 ret.push(flags);
310 }
311 if !positionals.is_empty() {
312 ret.push(positionals);
313 }
314 if !sc_or_a.is_empty() {
315 ret.push(sc_or_a);
316 }
317 if !sc.is_empty() {
318 ret.push(sc);
319 }
320 ret.push(String::from("&& ret=0"));
321
322 ret.join("\n")
323}
324
325fn escape_help(string: &str) -> String {
327 string
328 .replace("\\", "\\\\")
329 .replace("'", "'\\''")
330 .replace("[", "\\[")
331 .replace("]", "\\]")
332}
333
334fn escape_value(string: &str) -> String {
336 string
337 .replace("\\", "\\\\")
338 .replace("'", "'\\''")
339 .replace("(", "\\(")
340 .replace(")", "\\)")
341 .replace(" ", "\\ ")
342}
343
344fn write_opts_of(p: &Parser) -> String {
345 debugln!("write_opts_of;");
346 let mut ret = vec![];
347 for o in p.opts() {
348 debugln!("write_opts_of:iter: o={}", o.name());
349 let help = o.help().map_or(String::new(), escape_help);
350 let mut conflicts = get_zsh_arg_conflicts!(p, o, INTERNAL_ERROR_MSG);
351 conflicts = if conflicts.is_empty() {
352 String::new()
353 } else {
354 format!("({})", conflicts)
355 };
356
357 let multiple = if o.is_set(ArgSettings::Multiple) {
358 "*"
359 } else {
360 ""
361 };
362 let pv = if let Some(pv_vec) = o.possible_vals() {
363 format!(": :({})", pv_vec.iter().map(
364 |v| escape_value(*v)).collect::<Vec<String>>().join(" "))
365 } else {
366 String::new()
367 };
368 if let Some(short) = o.short() {
369 let s = format!(
370 "'{conflicts}{multiple}-{arg}+[{help}]{possible_values}' \\",
371 conflicts = conflicts,
372 multiple = multiple,
373 arg = short,
374 possible_values = pv,
375 help = help
376 );
377
378 debugln!("write_opts_of:iter: Wrote...{}", &*s);
379 ret.push(s);
380 }
381 if let Some(long) = o.long() {
382 let l = format!(
383 "'{conflicts}{multiple}--{arg}=[{help}]{possible_values}' \\",
384 conflicts = conflicts,
385 multiple = multiple,
386 arg = long,
387 possible_values = pv,
388 help = help
389 );
390
391 debugln!("write_opts_of:iter: Wrote...{}", &*l);
392 ret.push(l);
393 }
394 }
395
396 ret.join("\n")
397}
398
399fn write_flags_of(p: &Parser) -> String {
400 debugln!("write_flags_of;");
401 let mut ret = vec![];
402 for f in p.flags() {
403 debugln!("write_flags_of:iter: f={}", f.name());
404 let help = f.help().map_or(String::new(), escape_help);
405 let mut conflicts = get_zsh_arg_conflicts!(p, f, INTERNAL_ERROR_MSG);
406 conflicts = if conflicts.is_empty() {
407 String::new()
408 } else {
409 format!("({})", conflicts)
410 };
411
412 let multiple = if f.is_set(ArgSettings::Multiple) {
413 "*"
414 } else {
415 ""
416 };
417 if let Some(short) = f.short() {
418 let s = format!(
419 "'{conflicts}{multiple}-{arg}[{help}]' \\",
420 multiple = multiple,
421 conflicts = conflicts,
422 arg = short,
423 help = help
424 );
425
426 debugln!("write_flags_of:iter: Wrote...{}", &*s);
427 ret.push(s);
428 }
429
430 if let Some(long) = f.long() {
431 let l = format!(
432 "'{conflicts}{multiple}--{arg}[{help}]' \\",
433 conflicts = conflicts,
434 multiple = multiple,
435 arg = long,
436 help = help
437 );
438
439 debugln!("write_flags_of:iter: Wrote...{}", &*l);
440 ret.push(l);
441 }
442 }
443
444 ret.join("\n")
445}
446
447fn write_positionals_of(p: &Parser) -> String {
448 debugln!("write_positionals_of;");
449 let mut ret = vec![];
450 for arg in p.positionals() {
451 debugln!("write_positionals_of:iter: arg={}", arg.b.name);
452 let a = format!(
453 "'{optional}:{name}{help}:{action}' \\",
454 optional = if !arg.b.is_set(ArgSettings::Required) { ":" } else { "" },
455 name = arg.b.name,
456 help = arg.b
457 .help
458 .map_or("".to_owned(), |v| " -- ".to_owned() + v)
459 .replace("[", "\\[")
460 .replace("]", "\\]"),
461 action = arg.possible_vals().map_or("_files".to_owned(), |values| {
462 format!("({})",
463 values.iter().map(|v| escape_value(*v)).collect::<Vec<String>>().join(" "))
464 })
465 );
466
467 debugln!("write_positionals_of:iter: Wrote...{}", a);
468 ret.push(a);
469 }
470
471 ret.join("\n")
472}