1use std::env;
2use std::ffi::OsStr;
3use std::io::{self, Write};
4
5use anyhow::{Context, Result, bail};
6use clap_complete::Shell;
7use clap_complete::engine::CompletionCandidate;
8use clap_complete::env::Shells;
9
10use crate::{git, stack};
11
12pub const COMPLETE_VAR: &str = "COMPLETE";
14
15const BASH_GIT_SHIM: &str = r#"
19_git_stk() {
20 local cur="${COMP_WORDS[COMP_CWORD]}"
21 COMP_WORDS=("git-stk" "${COMP_WORDS[@]:2}")
22 COMP_CWORD=$((COMP_CWORD - 1))
23 _clap_complete_git_stk git-stk "$cur" ""
24}
25"#;
26
27const ZSH_GIT_SHIM: &str = r#"
31function _git-stk() {
32 local -a words=("git-stk" "${words[@]:2}")
33 local CURRENT=$((CURRENT - 1))
34 _clap_dynamic_completer_git_stk
35}
36"#;
37
38pub fn print(shell: Shell) -> Result<()> {
39 let completer = env::current_exe()
43 .ok()
44 .and_then(|path| path.to_str().map(str::to_owned))
45 .unwrap_or_else(|| "git-stk".to_owned());
46
47 write(shell, &completer, &mut io::stdout().lock())
48}
49
50pub fn write(shell: Shell, completer: &str, writer: &mut dyn Write) -> Result<()> {
54 let shells = Shells::builtins();
55 let name = shell.to_string();
56 let Some(env_completer) = shells.completer(&name) else {
57 bail!("no dynamic completion support for {name}");
58 };
59
60 env_completer
61 .write_registration(COMPLETE_VAR, "git-stk", "git-stk", completer, writer)
62 .with_context(|| format!("failed to write {name} completion registration"))?;
63
64 match shell {
65 Shell::Bash => write!(writer, "{BASH_GIT_SHIM}")?,
66 Shell::Zsh => write!(writer, "{ZSH_GIT_SHIM}")?,
67 _ => {}
68 }
69
70 Ok(())
71}
72
73pub fn branch_candidates(current: &OsStr) -> Vec<CompletionCandidate> {
75 let Some(prefix) = current.to_str() else {
76 return Vec::new();
77 };
78
79 git::local_branches()
80 .unwrap_or_default()
81 .into_iter()
82 .filter(|branch| branch.starts_with(prefix))
83 .map(CompletionCandidate::new)
84 .collect()
85}
86
87pub fn child_branch_candidates(current: &OsStr) -> Vec<CompletionCandidate> {
89 let Some(prefix) = current.to_str() else {
90 return Vec::new();
91 };
92 let Ok(branch) = git::current_branch() else {
93 return Vec::new();
94 };
95
96 stack::children_for_branch(&branch)
97 .unwrap_or_default()
98 .into_iter()
99 .filter(|child| child.starts_with(prefix))
100 .map(CompletionCandidate::new)
101 .collect()
102}