cc_switch/cli/
completion.rs1use crate::config::ConfigStorage;
2use anyhow::Result;
3use clap::CommandFactory;
4use std::fs;
5use std::io::Write;
6use std::path::PathBuf;
7
8pub fn generate_aliases(shell: &str) -> Result<()> {
16 match shell {
17 "fish" => {
18 println!("alias cs='cc-switch'");
19 println!("alias ccd='claude --dangerously-skip-permissions'");
20 println!("alias cx='cc-switch codex'");
21 }
22 "zsh" => {
23 println!("alias cs='cc-switch'");
24 println!("alias ccd='claude --dangerously-skip-permissions'");
25 println!("alias cx='cc-switch codex'");
26 }
27 "bash" => {
28 println!("alias cs='cc-switch'");
29 println!("alias ccd='claude --dangerously-skip-permissions'");
30 println!("alias cx='cc-switch codex'");
31 }
32 _ => {
33 anyhow::bail!(
34 "Unsupported shell: {}. Supported shells: fish, zsh, bash",
35 shell
36 );
37 }
38 }
39
40 Ok(())
41}
42
43fn completion_install_path(shell: &str) -> Option<PathBuf> {
45 let home = dirs::home_dir()?;
46 match shell {
47 "fish" => Some(home.join(".config/fish/completions/cc-switch.fish")),
48 "zsh" => Some(home.join(".zsh/completions/_cc-switch")),
49 "bash" => Some(home.join(".bash_completion.d/cc-switch")),
50 _ => None,
51 }
52}
53
54pub fn generate_completion(shell: &str) -> Result<()> {
62 use crate::cli::Cli;
63
64 let mut app = Cli::command();
65 let mut buf: Vec<u8> = Vec::new();
66
67 match shell {
68 "fish" => {
69 generate_fish_completion(&mut app, &mut buf);
70 }
71 "zsh" => {
72 clap_complete::generate(clap_complete::shells::Zsh, &mut app, "cc-switch", &mut buf);
73 }
74 "bash" => {
75 clap_complete::generate(clap_complete::shells::Bash, &mut app, "cc-switch", &mut buf);
76 }
77 "elvish" => {
78 clap_complete::generate(
79 clap_complete::shells::Elvish,
80 &mut app,
81 "cc-switch",
82 &mut std::io::stdout(),
83 );
84 return Ok(());
85 }
86 "powershell" => {
87 clap_complete::generate(
88 clap_complete::shells::PowerShell,
89 &mut app,
90 "cc-switch",
91 &mut std::io::stdout(),
92 );
93 return Ok(());
94 }
95 _ => {
96 anyhow::bail!(
97 "Unsupported shell: {}. Supported shells: fish, zsh, bash, elvish, powershell",
98 shell
99 );
100 }
101 }
102
103 if let Some(path) = completion_install_path(shell) {
104 if let Some(parent) = path.parent() {
105 fs::create_dir_all(parent)?;
106 }
107 fs::write(&path, &buf)?;
108 eprintln!("Installed {shell} completion to {}", path.display());
109 } else {
110 std::io::stdout().write_all(&buf)?;
111 }
112
113 Ok(())
114}
115
116pub fn list_aliases_for_completion() -> Result<()> {
125 let storage = ConfigStorage::load()?;
126
127 println!("cc");
129 println!("official");
130
131 if storage.configurations.contains_key("current") {
134 println!("current");
135 }
136
137 let mut aliases: Vec<String> = storage.configurations.keys().cloned().collect();
139 aliases.sort();
140
141 for alias_name in aliases {
142 if alias_name != "current" {
143 println!("{alias_name}");
144 }
145 }
146
147 Ok(())
148}
149
150pub fn list_codex_aliases_for_completion() -> Result<()> {
157 let storage = ConfigStorage::load()?;
158
159 if let Some(ref configs) = storage.codex_configurations {
161 let mut aliases: Vec<String> = configs.keys().cloned().collect();
162 aliases.sort();
163
164 for alias_name in aliases {
165 println!("{alias_name}");
166 }
167 }
168
169 Ok(())
170}
171
172fn generate_fish_completion(app: &mut clap::Command, out: &mut Vec<u8>) {
174 clap_complete::generate(clap_complete::shells::Fish, app, "cc-switch", out);
175
176 let extra = r#"
177# Custom completion for use subcommand with dynamic aliases
178complete -c cc-switch -n '__fish_cc_switch_using_subcommand use' -f -a '(cc-switch --list-aliases)' -d 'Configuration alias name'
179# Custom completion for switch subcommand (alias for use)
180complete -c cc-switch -n '__fish_cc_switch_using_subcommand switch' -f -a '(cc-switch --list-aliases)' -d 'Configuration alias name'
181# Custom completion for remove subcommand with dynamic aliases
182complete -c cc-switch -n '__fish_cc_switch_using_subcommand remove' -f -a '(cc-switch --list-aliases)' -d 'Configuration alias name'
183
184# Completion for 'completion' subcommand with shell types
185complete -c cc-switch -n '__fish_cc_switch_using_subcommand completion' -f -a 'fish zsh bash elvish powershell' -d 'Shell type'
186complete -c cs -n '__fish_seen_subcommand_from completion' -f -a 'fish zsh bash elvish powershell' -d 'Shell type'
187
188# Custom completion for codex subcommand with dynamic aliases
189complete -c cc-switch -n '__fish_seen_subcommand_from codex' -n '__fish_seen_subcommand_from use' -f -a '(cc-switch --list-codex-aliases)' -d 'Codex configuration alias name'
190complete -c cc-switch -n '__fish_seen_subcommand_from codex' -n '__fish_seen_subcommand_from remove' -f -a '(cc-switch --list-codex-aliases)' -d 'Codex configuration alias name'
191
192# Completion for the 'cs' alias
193complete -c cs -w cc-switch
194
195# Completion for 'cs' alias subcommands
196complete -c cs -n '__fish_use_subcommand' -f -a 'add remove list set-default-dir completion alias use switch current codex daemon statusline' -d 'Subcommand'
197
198# Completion for 'daemon' subcommand
199complete -c cc-switch -n '__fish_cc_switch_using_subcommand daemon; and not __fish_seen_subcommand_from start stop status restart' -f -a 'start stop status restart' -d 'Daemon action'
200complete -c cc-switch -n '__fish_cc_switch_using_subcommand daemon; and __fish_seen_subcommand_from start' -l foreground -d 'Run in the foreground'
201complete -c cc-switch -n '__fish_cc_switch_using_subcommand daemon; and __fish_seen_subcommand_from start' -l log-level -d 'Log level (error/warn/info/debug/trace)' -r -f -a 'error warn info debug trace'
202complete -c cc-switch -n '__fish_cc_switch_using_subcommand daemon; and __fish_seen_subcommand_from start' -s v -l verbose -d 'Increase verbosity (-v/-vv/-vvv)'
203complete -c cc-switch -n '__fish_cc_switch_using_subcommand daemon; and __fish_seen_subcommand_from restart' -l foreground -d 'Run in the foreground after restart'
204complete -c cc-switch -n '__fish_cc_switch_using_subcommand daemon; and __fish_seen_subcommand_from restart' -l log-level -d 'Log level (error/warn/info/debug/trace)' -r -f -a 'error warn info debug trace'
205complete -c cc-switch -n '__fish_cc_switch_using_subcommand daemon; and __fish_seen_subcommand_from restart' -s v -l verbose -d 'Increase verbosity (-v/-vv/-vvv)'
206complete -c cc-switch -n '__fish_cc_switch_using_subcommand daemon; and __fish_seen_subcommand_from status' -l json -d 'Output as JSON'
207
208# Completion for 'cs list' subcommand
209complete -c cs -n '__fish_seen_subcommand_from list' -l plain -s p -d 'Plain text output'
210complete -c cs -n '__fish_seen_subcommand_from list' -l name -s n -d 'Show only name and URL'
211
212# Completion for 'cs daemon' subcommand
213complete -c cs -n '__fish_seen_subcommand_from daemon; and not __fish_seen_subcommand_from start stop status restart' -f -a 'start stop status restart' -d 'Daemon action'
214complete -c cs -n '__fish_seen_subcommand_from daemon; and __fish_seen_subcommand_from start' -l foreground -d 'Run in the foreground'
215complete -c cs -n '__fish_seen_subcommand_from daemon; and __fish_seen_subcommand_from start' -l log-level -d 'Log level' -r -f -a 'error warn info debug trace'
216complete -c cs -n '__fish_seen_subcommand_from daemon; and __fish_seen_subcommand_from start' -s v -l verbose -d 'Increase verbosity'
217complete -c cs -n '__fish_seen_subcommand_from daemon; and __fish_seen_subcommand_from restart' -l foreground -d 'Run in the foreground after restart'
218complete -c cs -n '__fish_seen_subcommand_from daemon; and __fish_seen_subcommand_from restart' -l log-level -d 'Log level' -r -f -a 'error warn info debug trace'
219complete -c cs -n '__fish_seen_subcommand_from daemon; and __fish_seen_subcommand_from restart' -s v -l verbose -d 'Increase verbosity'
220complete -c cs -n '__fish_seen_subcommand_from daemon; and __fish_seen_subcommand_from status' -l json -d 'Output as JSON'
221
222# Completion for 'cs statusline' subcommand
223complete -c cs -n '__fish_seen_subcommand_from statusline' -f -a 'install uninstall' -d 'Statusline action'
224
225# Completion for the 'cx' alias (cc-switch codex)
226complete -c cx -f
227complete -c cx -n '__fish_use_subcommand' -f -a 'add use remove list' -d 'Codex subcommand'
228complete -c cx -n '__fish_seen_subcommand_from use' -f -a '(cc-switch --list-codex-aliases)' -d 'Codex configuration alias name'
229complete -c cx -n '__fish_seen_subcommand_from remove' -f -a '(cc-switch --list-codex-aliases)' -d 'Codex configuration alias name'
230complete -c cx -n '__fish_seen_subcommand_from list' -f -l plain -s p -d 'Plain text output'
231complete -c cx -n '__fish_seen_subcommand_from list' -f -l name -s n -d 'Show only name and auth mode'
232complete -c cx -n '__fish_seen_subcommand_from add' -f -l interactive -s i -d 'Interactive mode'
233complete -c cx -n '__fish_seen_subcommand_from add' -f -l from-file -d 'Import from auth.json (defaults to ~/.codex/auth.json if no path)' -r
234"#;
235 out.extend_from_slice(extra.as_bytes());
236
237 generate_cs_completion_file();
238 generate_cx_completion_file();
239}
240
241fn generate_cs_completion_file() {
246 let home = dirs::home_dir().unwrap_or_else(|| std::path::PathBuf::from("~"));
247 let completions_dir = home.join(".config").join("fish").join("completions");
248
249 if !completions_dir.exists()
250 && let Err(e) = fs::create_dir_all(&completions_dir)
251 {
252 eprintln!("Warning: Could not create completions directory: {e}");
253 return;
254 }
255
256 let cs_content = r#"# Completion for 'cs' alias (cc-switch)
257complete -c cs -w cc-switch
258"#;
259
260 let cs_path = completions_dir.join("cs.fish");
261
262 if let Err(e) = fs::write(&cs_path, cs_content) {
263 eprintln!("Warning: Could not write cs.fish: {e}");
264 }
265
266 eprintln!("Created completion file: {}", cs_path.display());
267}
268
269fn generate_cx_completion_file() {
274 let home = dirs::home_dir().unwrap_or_else(|| std::path::PathBuf::from("~"));
276 let completions_dir = home.join(".config").join("fish").join("completions");
277
278 if !completions_dir.exists()
279 && let Err(e) = fs::create_dir_all(&completions_dir)
280 {
281 eprintln!("Warning: Could not create completions directory: {e}");
282 return;
283 }
284
285 let cx_content = r#"# Completion for 'cx' alias (cc-switch codex)
286# cx is a fish function; disable file completion by default
287complete -c cx -f
288complete -c cx -n '__fish_use_subcommand' -f -a 'add use remove list' -d 'Codex subcommand'
289complete -c cx -n '__fish_seen_subcommand_from use' -f -a '(cc-switch --list-codex-aliases)' -d 'Codex configuration alias name'
290complete -c cx -n '__fish_seen_subcommand_from remove' -f -a '(cc-switch --list-codex-aliases)' -d 'Codex configuration alias name'
291complete -c cx -n '__fish_seen_subcommand_from list' -f -l plain -s p -d 'Plain text output'
292complete -c cx -n '__fish_seen_subcommand_from list' -f -l name -s n -d 'Show only name and auth mode'
293complete -c cx -n '__fish_seen_subcommand_from add' -f -l interactive -s i -d 'Interactive mode'
294complete -c cx -n '__fish_seen_subcommand_from add' -f -l from-file -d 'Import from auth.json (defaults to ~/.codex/auth.json if no path)' -r
295"#;
296
297 let cx_path = completions_dir.join("cx.fish");
298
299 if let Err(e) = fs::write(&cx_path, cx_content) {
300 eprintln!("Warning: Could not write cx.fish: {e}");
301 }
302
303 eprintln!("\nCreated completion file: {}", cx_path.display());
304}