Skip to main content

garden/cmds/
completion.rs

1use std::io::Write;
2
3use anyhow::Result;
4use clap::{crate_version, value_parser, Arg, Command, CommandFactory, Parser};
5
6use crate::{cli, model};
7
8/// Generate shell completions
9#[derive(Parser, Clone, Debug)]
10#[command(author, about, long_about)]
11pub struct CompletionOptions {
12    /// Include completions for custom commands
13    #[arg(long, short)]
14    commands: bool,
15    /// Shell syntax to emit
16    #[arg(default_value_t = clap_complete::Shell::Bash, value_parser = value_parser!(clap_complete::Shell))]
17    pub shell: clap_complete::Shell,
18}
19
20/// Print shell completions for "garden completion"
21pub fn main(options: &cli::MainOptions, completion_options: &CompletionOptions) -> Result<()> {
22    let mut cmd = cli::MainOptions::command();
23
24    // Register custom commands with the completion system
25    if completion_options.commands {
26        let app_context = model::ApplicationContext::from_options(options)?;
27        let config = app_context.get_root_config();
28        for name in config.commands.keys() {
29            cmd = cmd.subcommand(
30                Command::new(name)
31                    .about(format!("Custom {name} command"))
32                    .arg(
33                        Arg::new("keep_going")
34                            .help("Continue to the next tree when errors occur")
35                            .short('k')
36                            .long("keep-going"),
37                    )
38                    .arg(
39                        Arg::new("no-errexit")
40                            .help("Do not pass -e to the shell")
41                            .short('n')
42                            .long("no-errexit"),
43                    )
44                    .arg(
45                        Arg::new("no-wordsplit")
46                            .help("Do not pass -o shwordsplit to zsh")
47                            .short('z')
48                            .long("no-wordsplit"),
49                    )
50                    .arg(
51                        Arg::new("queries")
52                            .help("Tree queries to find trees where commands will be run"),
53                    )
54                    .arg(
55                        Arg::new("arguments")
56                            .help("Arguments to forward to custom commands")
57                            .last(true),
58                    ),
59            );
60        }
61    }
62
63    let comment = format!("\n# Generated by garden v{}.\n", crate_version!());
64    // The generated file size (16161) as of garden v1.10.0 is just under 2^14 (16384) bytes.
65    // Periodically adjust this as the completions evolve.
66    let mut buf = Vec::with_capacity(16384);
67    clap_complete::generate(completion_options.shell, &mut cmd, "garden", &mut buf);
68    buf.extend_from_slice(comment.as_bytes());
69    std::io::stdout().write_all(&buf).unwrap_or(());
70
71    Ok(())
72}