Skip to main content

theater_cli/commands/
completion.rs

1use clap::{CommandFactory, Parser};
2use clap_complete::{generate, Shell};
3use std::io;
4use tracing::debug;
5
6use crate::error::{CliError, CliResult};
7use crate::CommandContext;
8
9#[derive(Debug, Parser)]
10#[command(
11    long_about = "Generate shell completion scripts.\n\nYou typically only need to re-run this after updating the theater CLI or changing the completion file location."
12)]
13pub struct CompletionArgs {
14    /// Shell to generate completions for
15    #[arg(value_enum)]
16    pub shell: Shell,
17
18    /// Output file (defaults to stdout)
19    #[arg(short, long)]
20    pub output: Option<std::path::PathBuf>,
21}
22
23/// Generate shell completion scripts
24pub async fn execute_async(args: &CompletionArgs, ctx: &CommandContext) -> CliResult<()> {
25    debug!("Generating shell completion for: {:?}", args.shell);
26
27    let mut app = crate::Cli::command();
28    let app_name = app.get_name().to_string();
29
30    match &args.output {
31        Some(output_path) => {
32            debug!("Writing completion to file: {:?}", output_path);
33
34            let mut file = std::fs::File::create(output_path).map_err(|e| {
35                CliError::file_operation_failed(
36                    "create completion file",
37                    output_path.display().to_string(),
38                    e,
39                )
40            })?;
41
42            generate(args.shell, &mut app, &app_name, &mut file);
43
44            ctx.output.success(&format!(
45                "Shell completion for {} written to: {}",
46                args.shell,
47                output_path.display()
48            ))?;
49        }
50        None => {
51            debug!("Writing completion to stdout");
52            generate(args.shell, &mut app, &app_name, &mut io::stdout());
53        }
54    }
55
56    // Only show installation instructions when writing to a file
57    // Don't show them when output goes to stdout (for eval)
58    if args.output.is_some() && !ctx.json {
59        show_installation_instructions(args.shell, ctx)?;
60    }
61
62    Ok(())
63}
64
65/// Show installation instructions for the generated completion script
66fn show_installation_instructions(shell: Shell, ctx: &CommandContext) -> CliResult<()> {
67    let instructions = match shell {
68        Shell::Bash => {
69            r#"
70To install bash completions:
71
721. Save the completion script:
73   theater completion bash > ~/.local/share/bash-completion/completions/theater
74
752. Or add to your ~/.bashrc:
76   eval "$(theater completion bash)"
77
783. Restart your shell or run:
79   source ~/.bashrc
80"#
81        }
82        Shell::Zsh => {
83            r#"
84To install zsh completions:
85
861. Save the completion script to a directory in your $fpath:
87   theater completion zsh > ~/.local/share/zsh/site-functions/_theater
88
892. Or add to your ~/.zshrc:
90   eval "$(theater completion zsh)"
91
923. Restart your shell or run:
93   source ~/.zshrc
94"#
95        }
96        Shell::Fish => {
97            r#"
98To install fish completions:
99
1001. Save the completion script:
101   theater completion fish > ~/.config/fish/completions/theater.fish
102
1032. Or add to your fish config:
104   theater completion fish | source
105
1063. Restart your shell
107"#
108        }
109        Shell::PowerShell => {
110            r#"
111To install PowerShell completions:
112
1131. Add to your PowerShell profile:
114   theater completion powershell | Out-String | Invoke-Expression
115
1162. Or save to a file and dot-source it in your profile:
117   theater completion powershell > theater_completion.ps1
118   . .\theater_completion.ps1
119"#
120        }
121        Shell::Elvish => {
122            r#"
123To install Elvish completions:
124
1251. Add to your ~/.config/elvish/rc.elv:
126   eval (theater completion elvish | slurp)
127"#
128        }
129        _ => "Please refer to your shell's documentation for completion installation.",
130    };
131
132    ctx.output.info(instructions)?;
133    Ok(())
134}