1use anyhow::{Result, bail};
2
3const ZSH_INIT: &str = r#"# tmx shell integration (zsh)
4twt() {
5 emulate -L zsh
6 local sub="${1:-}"
7 case "$sub" in
8 ""|list)
9 # `list --plain` is a text dump → pass through and never cd, even
10 # when only one entry exists (command substitution would strip the
11 # trailing newline, defeating a multi-line heuristic).
12 local arg
13 for arg in "${@:2}"; do
14 if [[ "$arg" == "--plain" ]]; then
15 command tmx worktree list "${@:2}"
16 return $?
17 fi
18 done
19 # Picker (or `list <target>`) → cd into the printed path.
20 local out
21 out=$(command tmx worktree list "${@:2}")
22 local rc=$?
23 (( rc != 0 )) && return $rc
24 [[ -z "$out" ]] && return 0
25 cd "$out"
26 ;;
27 rm|create|help|-h|--help)
28 command tmx worktree "$@"
29 ;;
30 *)
31 # Implicit `create <branch>` shorthand. Default flow now spawns
32 # and switches to a tmux session inside tmx itself, so the wrapper
33 # only needs to capture stdout when --keep-current (or -p) is set.
34 local print_only=0 keep_current=0 args=() a
35 for a in "$@"; do
36 case "$a" in
37 -p|--print)
38 print_only=1
39 keep_current=1
40 ;;
41 --keep-current)
42 keep_current=1
43 args+=("$a")
44 ;;
45 *) args+=("$a") ;;
46 esac
47 done
48 if (( keep_current )); then
49 # Ensure --keep-current is passed exactly once even if user
50 # supplied only -p.
51 local has_kc=0 a2
52 for a2 in "${args[@]}"; do
53 [[ "$a2" == "--keep-current" ]] && has_kc=1
54 done
55 (( has_kc )) || args+=("--keep-current")
56 local out
57 out=$(command tmx worktree create "${args[@]}")
58 local rc=$?
59 (( rc != 0 )) && return $rc
60 [[ -z "$out" ]] && return 0
61 if (( print_only )); then
62 printf '%s\n' "$out"
63 return 0
64 fi
65 cd "$out"
66 return 0
67 fi
68 # Default: tmx creates the worktree and switches into a new tmux
69 # session itself; we don't need to cd.
70 command tmx worktree create "${args[@]}"
71 ;;
72 esac
73}
74"#;
75
76pub fn emit(shell: &str) -> Result<&'static str> {
77 match shell {
78 "zsh" => Ok(ZSH_INIT),
79 other => bail!("unsupported shell: {other} (supported: zsh)"),
80 }
81}
82
83#[cfg(test)]
84mod tests {
85 use super::*;
86
87 #[test]
88 fn emit_zsh_contains_function_definition() {
89 let out = emit("zsh").unwrap();
90 assert!(out.contains("twt()"));
91 assert!(out.contains("tmx worktree create"));
92 assert!(out.contains("tmx worktree list"));
93 assert!(out.contains("rm|create|help"));
95 assert!(out.contains("--plain"));
97 assert!(out.contains("--keep-current"));
99 assert!(out.contains("-p|--print"));
100 }
101
102 #[test]
103 fn emit_unknown_shell_errors() {
104 let err = emit("fish").unwrap_err();
105 assert!(err.to_string().contains("unsupported shell"));
106 assert!(err.to_string().contains("fish"));
107 }
108}