use std::env;
use std::ffi::OsStr;
use std::io::{self, Write};
use anyhow::{Context, Result, bail};
use clap_complete::Shell;
use clap_complete::engine::CompletionCandidate;
use clap_complete::env::Shells;
use crate::{git, stack};
pub const COMPLETE_VAR: &str = "COMPLETE";
const BASH_GIT_SHIM: &str = r#"
_git_stk() {
local cur="${COMP_WORDS[COMP_CWORD]}"
COMP_WORDS=("git-stk" "${COMP_WORDS[@]:2}")
COMP_CWORD=$((COMP_CWORD - 1))
_clap_complete_git_stk git-stk "$cur" ""
}
"#;
const ZSH_GIT_SHIM: &str = r#"
function _git-stk() {
local -a words=("git-stk" "${words[@]:2}")
local CURRENT=$((CURRENT - 1))
_clap_dynamic_completer_git_stk
}
"#;
pub fn print(shell: Shell) -> Result<()> {
let completer = env::current_exe()
.ok()
.and_then(|path| path.to_str().map(str::to_owned))
.unwrap_or_else(|| "git-stk".to_owned());
write(shell, &completer, &mut io::stdout().lock())
}
pub fn write(shell: Shell, completer: &str, writer: &mut dyn Write) -> Result<()> {
let shells = Shells::builtins();
let name = shell.to_string();
let Some(env_completer) = shells.completer(&name) else {
bail!("no dynamic completion support for {name}");
};
env_completer
.write_registration(COMPLETE_VAR, "git-stk", "git-stk", completer, writer)
.with_context(|| format!("failed to write {name} completion registration"))?;
match shell {
Shell::Bash => write!(writer, "{BASH_GIT_SHIM}")?,
Shell::Zsh => write!(writer, "{ZSH_GIT_SHIM}")?,
_ => {}
}
Ok(())
}
pub fn branch_candidates(current: &OsStr) -> Vec<CompletionCandidate> {
let Some(prefix) = current.to_str() else {
return Vec::new();
};
git::local_branches()
.unwrap_or_default()
.into_iter()
.filter(|branch| branch.starts_with(prefix))
.map(CompletionCandidate::new)
.collect()
}
pub fn child_branch_candidates(current: &OsStr) -> Vec<CompletionCandidate> {
let Some(prefix) = current.to_str() else {
return Vec::new();
};
let Ok(branch) = git::current_branch() else {
return Vec::new();
};
stack::children_for_branch(&branch)
.unwrap_or_default()
.into_iter()
.filter(|child| child.starts_with(prefix))
.map(CompletionCandidate::new)
.collect()
}