garden/cmds/
completion.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
use std::io::Write;

use anyhow::Result;
use clap::{crate_version, value_parser, Arg, Command, CommandFactory, Parser};

use crate::{cli, model};

/// Generate shell completions
#[derive(Parser, Clone, Debug)]
#[command(author, about, long_about)]
pub struct CompletionOptions {
    /// Include completions for custom commands
    #[arg(long, short)]
    commands: bool,
    /// Shell syntax to emit
    #[arg(default_value_t = clap_complete::Shell::Bash, value_parser = value_parser!(clap_complete::Shell))]
    pub shell: clap_complete::Shell,
}

/// Print shell completions for "garden completion"
pub fn main(options: &cli::MainOptions, completion_options: &CompletionOptions) -> Result<()> {
    let mut cmd = cli::MainOptions::command();

    // Register custom commands with the completion system
    if completion_options.commands {
        let app_context = model::ApplicationContext::from_options(options)?;
        let config = app_context.get_root_config();
        for name in config.commands.keys() {
            cmd = cmd.subcommand(
                Command::new(name)
                    .about(format!("Custom {name} command"))
                    .arg(
                        Arg::new("keep_going")
                            .help("Continue to the next tree when errors occur")
                            .short('k')
                            .long("keep-going"),
                    )
                    .arg(
                        Arg::new("no-errexit")
                            .help("Do not pass -e to the shell")
                            .short('n')
                            .long("no-errexit"),
                    )
                    .arg(
                        Arg::new("no-wordsplit")
                            .help("Do not pass -o shwordsplit to zsh")
                            .short('z')
                            .long("no-wordsplit"),
                    )
                    .arg(
                        Arg::new("queries")
                            // NOTE: value_terminator may not be needed in future versions of clap_complete.
                            // https://github.com/clap-rs/clap/pull/4612
                            .value_terminator("--")
                            .help("Tree queries to find trees where commands will be run"),
                    )
                    .arg(
                        Arg::new("arguments")
                            .help("Arguments to forward to custom commands")
                            .last(true),
                    ),
            );
        }
    }

    let comment = format!("\n# Generated by garden v{}.\n", crate_version!());
    // The generated file size (16161) as of garden v1.10.0 is just under 2^14 (16384) bytes.
    // Periodically adjust this as the completions evolve.
    let mut buf = Vec::with_capacity(16384);
    clap_complete::generate(completion_options.shell, &mut cmd, "garden", &mut buf);
    buf.extend_from_slice(comment.as_bytes());
    std::io::stdout().write_all(&buf).unwrap_or(());

    Ok(())
}