Skip to main content

tmx/
shell_init.rs

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: create and cd (or -p print).
32            local print_only=0 args=() a
33            for a in "$@"; do
34                case "$a" in
35                    -p|--print) print_only=1 ;;
36                    *) args+=("$a") ;;
37                esac
38            done
39            local out
40            out=$(command tmx worktree create "${args[@]}")
41            local rc=$?
42            (( rc != 0 )) && return $rc
43            [[ -z "$out" ]] && return 0
44            if (( print_only )); then
45                printf '%s\n' "$out"
46                return 0
47            fi
48            cd "$out"
49            ;;
50    esac
51}
52"#;
53
54pub fn emit(shell: &str) -> Result<&'static str> {
55    match shell {
56        "zsh" => Ok(ZSH_INIT),
57        other => bail!("unsupported shell: {other} (supported: zsh)"),
58    }
59}
60
61#[cfg(test)]
62mod tests {
63    use super::*;
64
65    #[test]
66    fn emit_zsh_contains_function_definition() {
67        let out = emit("zsh").unwrap();
68        assert!(out.contains("twt()"));
69        assert!(out.contains("tmx worktree create"));
70        assert!(out.contains("tmx worktree list"));
71        // Subcommands should pass through unchanged, not be re-interpreted as a branch.
72        assert!(out.contains("rm|create|help"));
73        // --plain must short-circuit out of the cd path.
74        assert!(out.contains("--plain"));
75    }
76
77    #[test]
78    fn emit_unknown_shell_errors() {
79        let err = emit("fish").unwrap_err();
80        assert!(err.to_string().contains("unsupported shell"));
81        assert!(err.to_string().contains("fish"));
82    }
83}