Skip to main content

ralph_workflow/cli/
completions.rs

1//! Shell completion generation handlers.
2//!
3//! This module handles the `--generate-completion` flag for generating
4//! shell completion scripts for bash, zsh, fish, elvish, and powershell.
5
6use crate::cli::args::Shell;
7use clap::CommandFactory;
8
9trait StdIoWriteCompat {
10    fn write_fmt(&mut self, args: std::fmt::Arguments<'_>) -> std::io::Result<()>;
11}
12
13impl<T: std::io::Write> StdIoWriteCompat for T {
14    fn write_fmt(&mut self, args: std::fmt::Arguments<'_>) -> std::io::Result<()> {
15        std::io::Write::write_fmt(self, args)
16    }
17}
18
19/// Handle the `--generate-completion` flag.
20///
21/// Generates a shell completion script for the specified shell and writes it to stdout.
22///
23/// # Arguments
24///
25/// * `shell` - The shell type to generate completions for
26///
27/// # Returns
28///
29/// Returns `true` if the flag was handled (program should exit after).
30#[must_use]
31pub fn handle_generate_completion(shell: Shell) -> bool {
32    let shell_name = shell.name();
33
34    // Generate completion to stdout using a scope for the mutable references
35    let shell_type = match shell {
36        Shell::Bash => clap_complete::Shell::Bash,
37        Shell::Zsh => clap_complete::Shell::Zsh,
38        Shell::Fish => clap_complete::Shell::Fish,
39        Shell::Elvish => clap_complete::Shell::Elvish,
40        Shell::Pwsh => clap_complete::Shell::PowerShell,
41    };
42
43    clap_complete::generate(
44        shell_type,
45        &mut crate::cli::Args::command(),
46        "ralph",
47        &mut std::io::stdout(),
48    );
49
50    // Print installation instructions
51    let _ = writeln!(std::io::stderr());
52    let _ = writeln!(
53        std::io::stderr(),
54        "=== Shell completion installation for {shell_name} ==="
55    );
56    let _ = writeln!(std::io::stderr());
57    let _ = writeln!(
58        std::io::stderr(),
59        "To enable completions, add the following to your shell config:"
60    );
61    let _ = writeln!(std::io::stderr());
62
63    match shell {
64        Shell::Bash => {
65            let _ = writeln!(
66                std::io::stderr(),
67                "  # Add to ~/.bashrc or ~/.bash_profile:"
68            );
69            let _ = writeln!(
70                std::io::stderr(),
71                "  source <(ralph --generate-completion=bash)"
72            );
73            let _ = writeln!(std::io::stderr());
74            let _ = writeln!(std::io::stderr(), "  # Or save to a file:");
75            let _ = writeln!(std::io::stderr(), "  ralph --generate-completion=bash > ~/.local/share/bash-completion/completions/ralph");
76        }
77        Shell::Zsh => {
78            let _ = writeln!(std::io::stderr(), "  # Add to ~/.zshrc:");
79            let _ = writeln!(
80                std::io::stderr(),
81                "  source <(ralph --generate-completion=zsh)"
82            );
83            let _ = writeln!(std::io::stderr());
84            let _ = writeln!(std::io::stderr(), "  # Or save to a file:");
85            let _ = writeln!(
86                std::io::stderr(),
87                "  ralph --generate-completion=zsh > ~/.zsh/completion/_ralph"
88            );
89            let _ = writeln!(std::io::stderr(), "  # Then add to ~/.zshrc:");
90            let _ = writeln!(std::io::stderr(), "  fpath=(~/.zsh/completion $fpath)");
91            let _ = writeln!(std::io::stderr(), "  autoload -U compinit && compinit");
92        }
93        Shell::Fish => {
94            let _ = writeln!(
95                std::io::stderr(),
96                "  # Add to ~/.config/fish/completions/ralph.fish:"
97            );
98            let _ = writeln!(
99                std::io::stderr(),
100                "  ralph --generate-completion=fish > ~/.config/fish/completions/ralph.fish"
101            );
102        }
103        Shell::Elvish => {
104            let _ = writeln!(std::io::stderr(), "  # Add to ~/.elvish/rc.elv:");
105            let _ = writeln!(
106                std::io::stderr(),
107                "  ralph --generate-completion=elvish > ~/.config/elvish/lib/ralph.elv"
108            );
109            let _ = writeln!(std::io::stderr(), "  # Then add to ~/.elvish/rc.elv:");
110            let _ = writeln!(
111                std::io::stderr(),
112                "  put ~/.config/elvish/lib/ralph.elv | slurp"
113            );
114        }
115        Shell::Pwsh => {
116            let _ = writeln!(
117                std::io::stderr(),
118                "  # Add to your PowerShell profile ($PROFILE):"
119            );
120            let _ = writeln!(
121                std::io::stderr(),
122                "  ralph --generate-completion=pwsh > ralph-completion.ps1"
123            );
124            let _ = writeln!(std::io::stderr(), "  # Then add to $PROFILE:");
125            let _ = writeln!(std::io::stderr(), "  . ralph-completion.ps1");
126        }
127    }
128
129    let _ = writeln!(std::io::stderr());
130    let _ = writeln!(
131        std::io::stderr(),
132        "Restart your shell or source your config file to apply changes."
133    );
134
135    true
136}
137
138impl Shell {
139    /// Returns the name of the shell as a string.
140    pub const fn name(self) -> &'static str {
141        match self {
142            Self::Bash => "bash",
143            Self::Zsh => "zsh",
144            Self::Fish => "fish",
145            Self::Elvish => "elvish",
146            Self::Pwsh => "powershell",
147        }
148    }
149}