use anyhow::Context;
use clap_complete::env::{
Bash as EnvBash, EnvCompleter, Fish as EnvFish, Powershell as EnvPowershell, Zsh as EnvZsh,
};
use std::io::{self, Write};
use worktrunk::shell;
use worktrunk::styling::println;
pub fn handle_init(shell: shell::Shell, cmd: String) -> Result<(), String> {
let init = shell::ShellInit::with_prefix(shell, cmd);
let integration_output = init
.generate()
.map_err(|e| format!("Failed to generate shell code: {}", e))?;
println!("{}", integration_output);
Ok(())
}
pub fn handle_completions(shell: shell::Shell) -> anyhow::Result<()> {
let cmd_name = crate::binary_name();
let mut stdout = io::stdout();
match shell {
shell::Shell::Bash => {
EnvBash
.write_registration("COMPLETE", &cmd_name, &cmd_name, &cmd_name, &mut stdout)
.context("failed to write bash completion registration")?;
}
shell::Shell::Zsh => {
let mut buf = Vec::new();
EnvZsh
.write_registration("COMPLETE", &cmd_name, &cmd_name, &cmd_name, &mut buf)
.context("failed to write zsh completion registration")?;
let script = String::from_utf8(buf)
.context("zsh completion registration was not valid UTF-8")?;
let script = make_zsh_autoload_safe(&script, &cmd_name);
write!(stdout, "{}", script).context("failed to write to stdout")?;
}
shell::Shell::Fish => {
EnvFish
.write_registration("COMPLETE", &cmd_name, &cmd_name, &cmd_name, &mut stdout)
.context("failed to write fish completion registration")?;
}
shell::Shell::Nushell => {
let init = shell::ShellInit::with_prefix(shell, cmd_name.clone());
let code = init
.generate()
.context("failed to generate nushell integration")?;
write!(stdout, "{}", code).context("failed to write to stdout")?;
}
shell::Shell::PowerShell => {
EnvPowershell
.write_registration("COMPLETE", &cmd_name, &cmd_name, &cmd_name, &mut stdout)
.context("failed to write powershell completion registration")?;
}
}
Ok(())
}
fn make_zsh_autoload_safe(script: &str, cmd_name: &str) -> String {
let func = format!("_clap_dynamic_completer_{}", cmd_name.replace('-', "_"));
let trailing = format!("compdef {func} {cmd_name}");
let replacement = format!(
r#"# Single-column display keeps descriptions visually associated with each branch.
zstyle ':completion:*:{cmd_name}:*' list-max 1
# Prevent grouping branches with identical descriptions (same timestamp) on one line.
zstyle ':completion:*:*:{cmd_name}:*' list-grouped false
if [ "$funcstack[1]" = "_{cmd_name}" ]; then
{func} "$@"
else
compdef {func} {cmd_name}
fi"#
);
script.replace(&trailing, &replacement)
}