Skip to content

Commit 08119fb

Browse files
committed
Enable completions in the shell
1 parent 2d4004f commit 08119fb

File tree

2 files changed

+123
-1
lines changed

2 files changed

+123
-1
lines changed

README.md

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,47 @@ terris --list
3838
terris --delete feature-a
3939
```
4040

41+
## Shell completion
42+
43+
Generate a completion script and source it in your shell:
44+
45+
```bash
46+
# Bash
47+
terris --completions bash > /tmp/terris.bash
48+
source /tmp/terris.bash
49+
50+
# Zsh
51+
terris --completions zsh > /tmp/_terris
52+
fpath=(/tmp $fpath)
53+
autoload -U compinit && compinit
54+
55+
# Fish
56+
terris --completions fish > /tmp/terris.fish
57+
source /tmp/terris.fish
58+
```
59+
60+
Install permanently (recommended):
61+
62+
```bash
63+
# Bash (user-level)
64+
mkdir -p ~/.local/share/bash-completion/completions
65+
terris --completions bash > ~/.local/share/bash-completion/completions/terris
66+
67+
# Bash (system-wide)
68+
sudo mkdir -p /etc/bash_completion.d
69+
sudo terris --completions bash > /etc/bash_completion.d/terris
70+
71+
# Zsh
72+
mkdir -p ~/.zsh/completions
73+
terris --completions zsh > ~/.zsh/completions/_terris
74+
fpath=(~/.zsh/completions $fpath)
75+
autoload -U compinit && compinit
76+
77+
# Fish
78+
mkdir -p ~/.config/fish/completions
79+
terris --completions fish > ~/.config/fish/completions/terris.fish
80+
```
81+
4182
## How it works
4283
- `terris <branch>` creates the worktree if missing and prints the path every time.
4384
- If the branch exists, it is used directly.

src/main.rs

Lines changed: 82 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,15 @@ use std::path::{Path, PathBuf};
33
use std::process::Command;
44

55
use anyhow::{Context, Result, bail};
6-
use clap::{CommandFactory, Parser};
6+
use clap::{CommandFactory, Parser, ValueEnum};
77
use rand::Rng;
88

99
#[derive(Parser)]
1010
#[command(name = "terris", version, about = "Git worktree manager")]
1111
struct Cli {
12+
/// Print shell completion script (bash or zsh)
13+
#[arg(long, value_enum, conflicts_with_all = ["list", "delete", "branch"])]
14+
completions: Option<CompletionShell>,
1215
/// List worktrees for the current repository
1316
#[arg(long, conflicts_with_all = ["delete", "branch"])]
1417
list: bool,
@@ -20,6 +23,13 @@ struct Cli {
2023
branch: Option<String>,
2124
}
2225

26+
#[derive(Clone, Copy, Debug, ValueEnum)]
27+
enum CompletionShell {
28+
Bash,
29+
Zsh,
30+
Fish,
31+
}
32+
2333
#[derive(Debug, Default)]
2434
struct Worktree {
2535
path: PathBuf,
@@ -32,6 +42,10 @@ struct Worktree {
3242

3343
fn main() -> Result<()> {
3444
let cli = Cli::parse();
45+
if let Some(shell) = cli.completions {
46+
print_completions(shell);
47+
return Ok(());
48+
}
3549
if cli.list {
3650
return cmd_list();
3751
}
@@ -46,6 +60,73 @@ fn main() -> Result<()> {
4660
Ok(())
4761
}
4862

63+
fn print_completions(shell: CompletionShell) {
64+
match shell {
65+
CompletionShell::Bash => {
66+
println!(
67+
r#"_terris_branches() {{
68+
git for-each-ref --format='%(refname:short)' refs/heads 2>/dev/null
69+
}}
70+
71+
_terris_complete() {{
72+
local cur prev
73+
cur="${{COMP_WORDS[COMP_CWORD]}}"
74+
prev="${{COMP_WORDS[COMP_CWORD-1]}}"
75+
76+
if [[ "$cur" == -* ]]; then
77+
COMPREPLY=($(compgen -W "--list --delete" -- "$cur"))
78+
return 0
79+
fi
80+
81+
if [[ $COMP_CWORD -eq 1 || "$prev" == "--delete" ]]; then
82+
COMPREPLY=($(compgen -W "$(_terris_branches)" -- "$cur"))
83+
return 0
84+
fi
85+
86+
COMPREPLY=()
87+
}}
88+
89+
complete -F _terris_complete terris
90+
"#
91+
);
92+
}
93+
CompletionShell::Zsh => {
94+
println!(
95+
r#"#compdef terris
96+
97+
_terris_branches() {{
98+
git for-each-ref --format='%(refname:short)' refs/heads 2>/dev/null
99+
}}
100+
101+
_arguments -s \
102+
'--list[List worktrees]' \
103+
'--delete[Remove a worktree by branch name]:branch:->branches' \
104+
'1:branch:->branches' \
105+
'*: :->args'
106+
107+
case $state in
108+
branches)
109+
_values 'branches' $(_terris_branches)
110+
;;
111+
esac
112+
"#
113+
);
114+
}
115+
CompletionShell::Fish => {
116+
println!(
117+
r#"function __terris_branches
118+
command git for-each-ref --format='%(refname:short)' refs/heads 2>/dev/null
119+
end
120+
121+
complete -c terris -l list -d 'List worktrees'
122+
complete -c terris -l delete -d 'Remove a worktree by branch name' -a "(__terris_branches)"
123+
complete -c terris -f -a "(__terris_branches)"
124+
"#
125+
);
126+
}
127+
}
128+
}
129+
49130
fn cmd_list() -> Result<()> {
50131
let root = git_root()?;
51132
let worktrees = list_worktrees(&root)?;

0 commit comments

Comments
 (0)