dotfiles/zsh/bin/sk-tmux
2020-04-21 17:01:02 -04:00

239 lines
6.9 KiB
Bash
Executable file

#!/usr/bin/env zsh
fail() {
echo >&2 "$1"
return 2
}
sk="$(command -v sk 2>/dev/null)" || sk="$(dirname "$0")/sk"
[[ -x "$sk" ]] || fail 'sk executable not found'
tmux_args=()
args=()
opt=""
skip=""
swap=""
close=""
term=""
[[ -n "$LINES" ]] && lines=$LINES || lines=$(tput lines) || lines=$(tmux display-message -p "#{pane_height}")
[[ -n "$COLUMNS" ]] && columns=$COLUMNS || columns=$(tput cols) || columns=$(tmux display-message -p "#{pane_width}")
help() {
echo >&2 'usage: sk-tmux [LAYOUT OPTIONS] [--] [sk OPTIONS]
LAYOUT OPTIONS:
(default layout: -d 50%)
Popup window (requires tmux 3.2 or above):
-p [WIDTH[%][,HEIGHT[%]]] (default: 50%)
-w WIDTH[%]
-h HEIGHT[%]
-x COL
-y ROW
Split pane:
-u [HEIGHT[%]] Split above (up)
-d [HEIGHT[%]] Split below (down)
-l [WIDTH[%]] Split left
-r [WIDTH[%]] Split right
'
return
}
while [[ $# -gt 0 ]]; do
arg="$1"
shift
[[ -z "$skip" ]] && case "$arg" in
-)
term=1
;;
--help)
help
;;
--version)
echo "sk-tmux (with sk $("$sk" --version))"
return
;;
-p* | -w* | -h* | -x* | -y* | -d* | -u* | -r* | -l*)
if [[ "$arg" =~ ^-[pwhxy] ]]; then
[[ "$opt" =~ "-K -E" ]] || opt="-K -E"
elif [[ "$arg" =~ ^.[lr] ]]; then
opt="-h"
if [[ "$arg" =~ ^.l ]]; then
opt="$opt -d"
swap="; swap-pane -D ; select-pane -L"
close="; tmux swap-pane -D"
fi
else
opt=""
if [[ "$arg" =~ ^.u ]]; then
opt="$opt -d"
swap="; swap-pane -D ; select-pane -U"
close="; tmux swap-pane -D"
fi
fi
if [[ ${#arg} -gt 2 ]]; then
size="${arg:2}"
else
if [[ "$1" =~ ^[0-9%,]+$ ]] || [[ "$1" =~ ^[A-Z]$ ]]; then
size="$1"
shift
else
continue
fi
fi
if [[ "$arg" =~ ^-p ]]; then
if [[ -n "$size" ]]; then
w=${size%%,*}
h=${size##*,}
opt="$opt -w$w -h$h"
fi
elif [[ "$arg" =~ ^-[whxy] ]]; then
opt="$opt ${arg:0:2}$size"
elif [[ "$size" =~ %$ ]]; then
size=${size:0:((${#size} - 1))}
if [[ -n "$swap" ]]; then
opt="$opt -p $((100 - size))"
else
opt="$opt -p $size"
fi
else
if [[ -n "$swap" ]]; then
if [[ "$arg" =~ ^.l ]]; then
max=$columns
else
max=$lines
fi
size=$((max - size))
[[ $size -lt 0 ]] && size=0
opt="$opt -l $size"
else
opt="$opt -l $size"
fi
fi
;;
--)
# "--" can be used to separate sk-tmux options from sk options to
# avoid conflicts
skip=1
tmux_args=("${args[@]}")
args=()
continue
;;
*)
args+=("$arg")
;;
esac
[[ -n "$skip" ]] && args+=("$arg")
done
if [[ -z "$TMUX" ]]; then
"$sk" "${args[@]}"
return $?
fi
# --height option is not allowed
args=("--no-height" "${args[@]}")
# Handle zoomed tmux pane by moving it to a temp window
if tmux list-panes -F '#F' | grep -q Z; then
zoomed=1
original_window=$(tmux display-message -p "#{window_id}")
tmp_window=$(tmux new-window -d -P -F "#{window_id}" "bash -c 'while :; do for c in \\| / - '\\;' do sleep 0.2; printf \"\\r\$c sk-tmux is running\\r\"; done; done'")
tmux swap-pane -t $tmp_window \; select-window -t $tmp_window
fi
set -e
# Clean up named pipes on exit
id=$RANDOM
argsf="${TMPDIR:-/tmp}/sk-argsf"
fifo1="${TMPDIR:-/tmp}/sk-tmp1"
fifo2="${TMPDIR:-/tmp}/sk-tmp2"
fifo3="${TMPDIR:-/tmp}/sk-tmp3"
touch $fifo3 $fifo2 $fifo1 $argsf
# argsf="${TMPDIR:-/tmp}/sk-args-$id"
# fifo1="${TMPDIR:-/tmp}/sk-fifo1-$id"
# fifo2="${TMPDIR:-/tmp}/sk-fifo2-$id"
# fifo3="${TMPDIR:-/tmp}/sk-fifo3-$id"
cleanup() {
\rm -f $argsf $fifo1 $fifo2 $fifo3
# Restore tmux window options
if [[ "${#tmux_win_opts[@]}" -gt 0 ]]; then
eval "tmux ${tmux_win_opts[@]}"
fi
# Remove temp window if we were zoomed
if [[ -n "$zoomed" ]]; then
tmux display-message -p "#{window_id}" >/dev/null
tmux swap-pane -t $original_window \; \
select-window -t $original_window \; \
kill-window -t $tmp_window \; \
resize-pane -Z
fi
if [ $# -gt 0 ]; then
trap - EXIT
return 130
fi
}
trap 'cleanup 1' SIGUSR1
trap 'cleanup' EXIT
envs="env TERM=$TERM "
[[ "$opt" =~ "-K -E" ]] && sk_DEFAULT_OPTS="--margin 0,1 $sk_DEFAULT_OPTS"
[[ -n "$sk_DEFAULT_OPTS" ]] && envs="$envs sk_DEFAULT_OPTS=$(printf %q "$sk_DEFAULT_OPTS")"
[[ -n "$sk_DEFAULT_COMMAND" ]] && envs="$envs sk_DEFAULT_COMMAND=$(printf %q "$sk_DEFAULT_COMMAND")"
# Build arguments to sk
opts=""
for arg in "${args[@]}"; do
arg="${arg//\\/\\\\}"
arg="${arg//\"/\\\"}"
arg="${arg//\`/\\\`}"
arg="${arg//$/\\$}"
opts="$opts \"$arg\""
done
pppid=$$
echo -n "trap 'kill -SIGUSR1 -$pppid' EXIT SIGINT SIGTERM;" >$argsf
close="; trap - EXIT SIGINT SIGTERM $close"
tmux_win_opts=($(tmux show-window-options remain-on-exit \; show-window-options synchronize-panes | sed '/ off/d; s/^/set-window-option /; s/$/ \\;/'))
echo $opts
if [[ "$opt" =~ "-K -E" ]]; then
cat $fifo2 &
if [[ -n "$term" ]] || [[ -t 0 ]]; then
cat <<<"\"$sk\" $opts > $fifo2; out=\$? $close; return \$out" >>$argsf
TMUX=$(echo $TMUX | cut -d , -f 2,2) tmux popup -d "$PWD" "${tmux_args[@]}" $opt -R "$envs bash $argsf" >/dev/null 2>&1
else
mkfifo $fifo1
cat <<<"\"$sk\" $opts < $fifo1 > $fifo2; out=\$? $close; return \$out" >>$argsf
cat <&0 >$fifo1 &
TMUX=$(echo $TMUX | cut -d , -f 1,2) tmux popup -d "$PWD" "${tmux_args[@]}" $opt -R "$envs bash $argsf" >/dev/null 2>&1
fi
return $?
fi
if [[ -n "$term" ]] || [[ -t 0 ]]; then
cat <<<"\"$sk\" $opts > $fifo2; echo \$? > $fifo3 $close" >>$argsf
echo $argsf
TMUX=$(echo $TMUX | cut -d , -f 1,2) tmux set-window-option synchronize-panes off \; set-window-option remain-on-exit off \; split-window $opt "${tmux_args[@]}" "$envs bash -c 'cd $(printf %q "$PWD"); exec -a sk bash $argsf'" $swap \
>/dev/null 2>&1 || {
"$sk" "${args[@]}"
return $?
}
else
cat <<<"\"$sk\" $opts < ${fifo1} > ${fifo2}; echo \$? > ${fifo3} $close" >>$argsf
TMUX=$(echo $TMUX | cut -d , -f 1,2) tmux set-window-option synchronize-panes off \; set-window-option remain-on-exit off \; split-window $opt "${tmux_args[@]}" "$envs zsh -c 'exec -a sk zsh $argsf'" $swap \
>/dev/null 2>&1 || {
"$sk" "${args[@]}"
return $?
}
cat <&0 >$fifo1
fi
cat $fifo2
return "$(cat $fifo3)"