241 lines
6.3 KiB
Bash
Executable file
241 lines
6.3 KiB
Bash
Executable file
#!/bin/sh
|
|
|
|
require() {
|
|
for cmd in "$@"; do
|
|
command -v "$cmd" >/dev/null 2>&1 || {
|
|
echo "mux: missing dependency: $cmd" >&2
|
|
exit 1
|
|
}
|
|
done
|
|
}
|
|
|
|
require tmux
|
|
|
|
get_scope() {
|
|
_wname=$(tmux display-message -p '#{window_name}')
|
|
case "$_wname" in
|
|
ai@*|code@*|git@*|run@*|term@*|misc@*) printf '%s' "${_wname#*@}" ;;
|
|
*) printf '%s' "$_wname" ;;
|
|
esac
|
|
}
|
|
|
|
spawn_or_focus() {
|
|
scope=$(get_scope)
|
|
name="${1}@${scope}"
|
|
cmd="$2"
|
|
|
|
if tmux list-windows -F '#{window_name}' | grep -Fx "$name" >/dev/null; then
|
|
tmux select-window -t "${name}"
|
|
else
|
|
if [ -n "$cmd" ]; then
|
|
tmux new-window -c '#{pane_current_path}' -n "${name}" "$cmd"
|
|
else
|
|
tmux new-window -c '#{pane_current_path}' -n "${name}"
|
|
fi
|
|
fi
|
|
}
|
|
|
|
pick_session() {
|
|
require fzf column
|
|
sel=$({
|
|
printf 'name\twindows\tstatus\n'
|
|
tmux list-sessions -F '#{session_name} #{session_windows}w #{?session_attached,*,}'
|
|
} | column -t -s "$(printf '\t')" |
|
|
fzf --reverse --header-lines 1 --prompt 'select-session> ')
|
|
[ -n "$sel" ] && tmux switch-client -t "${sel%% *}"
|
|
}
|
|
|
|
pick_window() {
|
|
require fzf column
|
|
sel=$({
|
|
printf 'target\tname\tcommand\tpanes\n'
|
|
tmux list-windows -a -F '#{window_index}:#{session_name} #{window_name} #{pane_current_command} #{window_panes}p'
|
|
} | column -t -s "$(printf '\t')" |
|
|
fzf --reverse --header-lines 1 --prompt 'select-window> ')
|
|
sel="${sel%% *}"
|
|
[ -n "$sel" ] && tmux switch-client -t "${sel#*:}:${sel%%:*}"
|
|
}
|
|
|
|
pick_pane() {
|
|
require fzf column
|
|
sel=$({
|
|
printf 'target\tcommand\tpath\n'
|
|
tmux list-panes -a -F '#{window_index}.#{pane_index}:#{session_name} #{pane_current_command} #{pane_current_path}' |
|
|
sed "s|$HOME|~|g"
|
|
} | column -t -s "$(printf '\t')" |
|
|
fzf --reverse --header-lines 1 --prompt 'select-pane> ')
|
|
sel="${sel%% *}"
|
|
[ -n "$sel" ] && tmux switch-client -t "${sel#*:}:${sel%%:*}"
|
|
}
|
|
|
|
target_pane() {
|
|
require fzf column
|
|
sel=$({
|
|
printf 'target\tcommand\tpath\n'
|
|
tmux list-panes -a -F '#{window_index}.#{pane_index}:#{session_name} #{pane_current_command} #{pane_current_path}' |
|
|
sed "s|$HOME|~|g"
|
|
} | column -t -s "$(printf '\t')" |
|
|
fzf --reverse --header-lines 1 --prompt "$1> ")
|
|
sel="${sel%% *}"
|
|
[ -n "$sel" ] && printf '%s' "${sel#*:}:${sel%%:*}"
|
|
}
|
|
|
|
confirm() {
|
|
printf '%s ' "$1"
|
|
old=$(stty -g)
|
|
stty raw -echo
|
|
c=$(dd bs=1 count=1 2>/dev/null)
|
|
stty "$old"
|
|
printf '\n'
|
|
[ "$c" = "y" ]
|
|
}
|
|
|
|
case "$1" in
|
|
bar)
|
|
session=$(tmux display-message -p '#S')
|
|
[ "$session" ] || exit
|
|
if [ "$(tmux show-options -gv mouse)" = "on" ]; then
|
|
indicator='#{?pane_in_mode,[mouse#{@c}copy],[mouse]}'
|
|
else
|
|
indicator='#{?pane_in_mode,[copy],}'
|
|
fi
|
|
set -f
|
|
keys="H J K L"
|
|
bar_content=""
|
|
i=0
|
|
total=$(tmux ls -F x | wc -l)
|
|
for line in $(tmux ls -F '#{session_id}:#{session_name}'); do
|
|
sid="${line%%:*}"
|
|
sname="${line#*:}"
|
|
if [ $i -lt 4 ]; then
|
|
key=$(echo "$keys" | cut -d' ' -f $((i + 1)))
|
|
elif [ $i -eq $((total - 1)) ]; then
|
|
key='$'
|
|
else
|
|
key='?'
|
|
fi
|
|
star=""
|
|
[ "$sname" = "$session" ] && star="*"
|
|
[ -n "$bar_content" ] && bar_content="$bar_content │ "
|
|
bar_content="$bar_content#[range=session|${sid}]$key:$sname$star#[norange]"
|
|
i=$((i + 1))
|
|
done
|
|
set +f
|
|
left='#[align=left list=on] #{W:#[range=window|#{window_index}]#{window_index}:#{window_name}#{window_flags}#[norange]#{?window_end_flag,, │ }}#[nolist]'
|
|
right="#[align=right]$indicator $bar_content"
|
|
tmux set -g 'status-format[0]' "$left$right"
|
|
;;
|
|
switch)
|
|
session="$(tmux ls -F '#S' | tail -n "+$(($2 + 1))" | head -1)"
|
|
tmux switch -t "$session"
|
|
;;
|
|
pick-session)
|
|
pick_session
|
|
;;
|
|
pick-window)
|
|
pick_window
|
|
;;
|
|
pick-pane)
|
|
pick_pane
|
|
;;
|
|
ai)
|
|
require claude
|
|
spawn_or_focus ai 'claude --dangerously-skip-permissions'
|
|
;;
|
|
code)
|
|
require nvim
|
|
spawn_or_focus code 'nvim -c "lua require([[config.tmux]]).run([[nvim]])"'
|
|
;;
|
|
git)
|
|
require nvim git
|
|
pane_path=$(tmux display-message -p '#{pane_current_path}')
|
|
if ! git -C "$pane_path" rev-parse --is-inside-work-tree >/dev/null 2>&1; then
|
|
tmux display-message "Not a git repository"
|
|
else
|
|
spawn_or_focus git 'nvim -c "lua require([[config.tmux]]).run([[git]])"'
|
|
fi
|
|
;;
|
|
run)
|
|
require nvim
|
|
spawn_or_focus run 'nvim -c "lua require([[config.tmux]]).run([[run]])"'
|
|
;;
|
|
term)
|
|
spawn_or_focus term
|
|
;;
|
|
misc)
|
|
spawn_or_focus misc
|
|
;;
|
|
cmd)
|
|
require fzf
|
|
result=$(tmux list-commands |
|
|
sed 's/ / /' |
|
|
fzf --reverse --prompt ':' --print-query \
|
|
--delimiter '\t' --with-nth '1,2' --accept-nth '1' \
|
|
--bind 'ctrl-y:transform-query(echo {1})+disable-search')
|
|
rc=$?
|
|
query=$(printf '%s' "$result" | head -1)
|
|
action=$(printf '%s' "$result" | sed -n '2p')
|
|
[ $rc -eq 130 ] && exit
|
|
if [ -n "$action" ]; then
|
|
case "$query" in
|
|
"$action "*)
|
|
tmux $query
|
|
exit
|
|
;;
|
|
esac
|
|
case "$action" in
|
|
switch-client) pick_session ;;
|
|
select-window) pick_window ;;
|
|
select-pane) pick_pane ;;
|
|
rename-window)
|
|
cur=$(tmux display-message -p '#{window_name}')
|
|
printf 'name [%s]: ' "$cur"
|
|
read -r name
|
|
[ -n "$name" ] && tmux rename-window "$name"
|
|
;;
|
|
rename-session)
|
|
cur=$(tmux display-message -p '#S')
|
|
printf 'name [%s]: ' "$cur"
|
|
read -r name
|
|
[ -n "$name" ] && tmux rename-session "$name"
|
|
;;
|
|
join-pane)
|
|
target=$(target_pane 'join-pane')
|
|
[ -n "$target" ] && tmux join-pane -s "$target"
|
|
;;
|
|
swap-pane)
|
|
target=$(target_pane 'swap-pane')
|
|
[ -n "$target" ] && tmux swap-pane -t "$target"
|
|
;;
|
|
select-layout)
|
|
layout=$(printf '%s\n' \
|
|
'even-horizontal' \
|
|
'even-vertical' \
|
|
'main-horizontal' \
|
|
'main-vertical' \
|
|
'tiled' |
|
|
fzf --reverse --prompt 'layout> ')
|
|
[ -n "$layout" ] && tmux select-layout "$layout"
|
|
;;
|
|
kill-pane)
|
|
confirm 'kill-pane? [y/N]:' && tmux kill-pane
|
|
;;
|
|
kill-window)
|
|
confirm "kill-window \"$(tmux display-message -p '#{window_name}')\"? [y/N]:" && tmux kill-window
|
|
;;
|
|
kill-session)
|
|
confirm "kill-session \"$(tmux display-message -p '#S')\"? [y/N]:" && tmux kill-session
|
|
;;
|
|
kill-server)
|
|
confirm 'kill-server? [y/N]:' && tmux kill-server
|
|
;;
|
|
*) tmux "$action" ;;
|
|
esac
|
|
elif [ -n "$query" ]; then
|
|
tmux $query
|
|
fi
|
|
;;
|
|
*)
|
|
tmux attach 2>/dev/null || tmux new-session
|
|
;;
|
|
esac
|