fzf/bin/fzf-tmux

242 lines
6.9 KiB
Plaintext
Raw Normal View History

2015-03-06 09:51:50 +00:00
#!/usr/bin/env bash
# fzf-tmux: starts fzf in a tmux pane
# usage: fzf-tmux [LAYOUT OPTIONS] [--] [FZF OPTIONS]
2015-03-06 09:51:50 +00:00
fail() {
>&2 echo "$1"
exit 2
}
fzf="$(command -v fzf 2> /dev/null)" || fzf="$(dirname "$0")/fzf"
[[ -x "$fzf" ]] || fail 'fzf executable not found'
2015-03-06 09:51:50 +00:00
args=()
opt=""
2015-03-08 07:36:37 +00:00
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() {
>&2 echo 'usage: fzf-tmux [LAYOUT OPTIONS] [--] [FZF 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
'
exit
}
while [[ $# -gt 0 ]]; do
2015-03-06 09:51:50 +00:00
arg="$1"
shift
[[ -z "$skip" ]] && case "$arg" in
-)
term=1
;;
--help)
help
;;
--version)
echo "fzf-tmux (with fzf $("$fzf" --version))"
exit
;;
-p*|-w*|-h*|-x*|-y*|-d*|-u*|-r*|-l*)
if [[ "$arg" =~ ^-[pwhxy] ]]; then
[[ "$opt" =~ "-E" ]] || opt="-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
2015-03-06 09:51:50 +00:00
size="${arg:2}"
else
if [[ "$1" =~ ^[0-9%,]+$ ]] || [[ "$1" =~ ^[A-Z]$ ]]; then
size="$1"
shift
else
continue
fi
2015-03-06 09:51:50 +00:00
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 -l $(( 100 - size ))%"
else
opt="$opt -l $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
2015-03-06 09:51:50 +00:00
;;
--)
# "--" can be used to separate fzf-tmux options from fzf options to
# avoid conflicts
2015-03-08 07:36:37 +00:00
skip=1
continue
2015-03-06 09:51:50 +00:00
;;
*)
args+=("$arg")
2015-03-06 09:51:50 +00:00
;;
esac
[[ -n "$skip" ]] && args+=("$arg")
2015-03-06 09:51:50 +00:00
done
if [[ -z "$TMUX" ]]; then
"$fzf" "${args[@]}"
2015-03-06 09:51:50 +00:00
exit $?
fi
2021-01-01 13:43:36 +00:00
# --height option is not allowed. CTRL-Z is also disabled.
args=("${args[@]}" "--no-height" "--bind=ctrl-z:ignore")
2017-01-07 16:30:31 +00:00
# Handle zoomed tmux pane without popup options by moving it to a temp window
if [[ ! "$opt" =~ "-E" ]] && tmux list-panes -F '#F' | grep -q Z; then
zoomed_without_popup=1
2015-12-08 01:45:22 +00:00
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 fzf-tmux is running\\r\"; done; done'")
2015-12-08 01:45:22 +00:00
tmux swap-pane -t $tmp_window \; select-window -t $tmp_window
fi
2015-03-06 09:51:50 +00:00
set -e
# Clean up named pipes on exit
id=$RANDOM
argsf="${TMPDIR:-/tmp}/fzf-args-$id"
fifo1="${TMPDIR:-/tmp}/fzf-fifo1-$id"
fifo2="${TMPDIR:-/tmp}/fzf-fifo2-$id"
fifo3="${TMPDIR:-/tmp}/fzf-fifo3-$id"
[fzf-tmux] Turn off remain-on-exit only on fzf-tmux pane (#3410) * fix: turn off remain-on-exit only on fzf-tmux pane Using `fzf-tmux` overwrites `remain-on-exit` for all panes in a window, if it is only set globally or at a higher scope than window. set-option -wg remain-on-exit on set-option -s remain-on-exit on This makes other panes in that window close immediately on exit after using `fzf-tmux`, even though I expect them to remain open. Since TMux 3.0, `remain-on-exit` is a pane option that can be set via `set-option -p`. This will limit the option's scope to just the `fzf-tmux` pane, thus allowing us to close it immediately without overriding `remain-on-exit` on other panes in the window. Co-authored-by: Junegunn Choi <junegunn.c@gmail.com> Link: https://github.com/tmux/tmux/blob/11e69f6025f5783fe17d43247de1c3f659a19b69/CHANGES#L753-L760 Link: https://github.com/tmux/tmux/releases/tag/3.0 Related: https://github.com/junegunn/fzf/issues/3397 * fix: turn off synchronize-panes only on fzf-tmux pane Similar reason to 482fd2b (fix: turn off remain-on-exit only on fzf-tmux pane, 2023-08-24). Limit scope on which option is set to bare minimum. Have confirmed this will not feed input back to other panes which are set to be synchronized. However, note that this will not stop `fzf-tmux` from being launched by two synchronized panes in parallel. Link: https://github.com/junegunn/fzf/issues/3397#issuecomment-1689295351 --------- Co-authored-by: Junegunn Choi <junegunn.c@gmail.com>
2023-08-24 07:31:38 +00:00
if tmux_win_opts=$(tmux show-options -p remain-on-exit \; show-options -p synchronize-panes 2> /dev/null); then
tmux_win_opts=( $(sed '/ off/d; s/synchronize-panes/set-option -p synchronize-panes/; s/remain-on-exit/set-option -p remain-on-exit/; s/$/ \\;/' <<< "$tmux_win_opts") )
tmux_off_opts='; set-option -p synchronize-panes off ; set-option -p remain-on-exit off'
else
tmux_win_opts=( $(tmux show-window-options remain-on-exit \; show-window-options synchronize-panes | sed '/ off/d; s/^/set-window-option /; s/$/ \\;/') )
tmux_off_opts='; set-window-option synchronize-panes off ; set-window-option remain-on-exit off'
fi
2015-03-06 09:51:50 +00:00
cleanup() {
\rm -f $argsf $fifo1 $fifo2 $fifo3
2015-12-08 01:45:22 +00:00
# Restore tmux window options
if [[ "${#tmux_win_opts[@]}" -gt 1 ]]; then
eval "tmux ${tmux_win_opts[*]}"
fi
# Remove temp window if we were zoomed without popup options
if [[ -n "$zoomed_without_popup" ]]; then
tmux display-message -p "#{window_id}" > /dev/null
2015-12-08 01:45:22 +00:00
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
exit 130
fi
2015-03-06 09:51:50 +00:00
}
trap 'cleanup 1' SIGUSR1
trap 'cleanup' EXIT
2015-03-06 09:51:50 +00:00
envs="export TERM=$TERM "
if [[ "$opt" =~ "-E" ]]; then
tmux_version=$(tmux -V | sed 's/[^0-9.]//g')
2023-04-11 07:29:29 +00:00
if [[ $(awk '{print ($1 > 3.2)}' <<< "$tmux_version" 2> /dev/null || bc -l <<< "$tmux_version > 3.2") = 1 ]]; then
FZF_DEFAULT_OPTS="--border $FZF_DEFAULT_OPTS"
opt="-B $opt"
elif [[ $tmux_version = 3.2 ]]; then
FZF_DEFAULT_OPTS="--margin 0,1 $FZF_DEFAULT_OPTS"
else
echo "fzf-tmux: tmux 3.2 or above is required for popup mode" >&2
exit 2
fi
fi
[[ -n "$FZF_DEFAULT_OPTS" ]] && envs="$envs FZF_DEFAULT_OPTS=$(printf %q "$FZF_DEFAULT_OPTS")"
[[ -n "$FZF_DEFAULT_COMMAND" ]] && envs="$envs FZF_DEFAULT_COMMAND=$(printf %q "$FZF_DEFAULT_COMMAND")"
[[ -n "$RUNEWIDTH_EASTASIAN" ]] && envs="$envs RUNEWIDTH_EASTASIAN=$(printf %q "$RUNEWIDTH_EASTASIAN")"
[[ -n "$BAT_THEME" ]] && envs="$envs BAT_THEME=$(printf %q "$BAT_THEME")"
echo "$envs;" > "$argsf"
2015-04-20 13:42:02 +00:00
# Build arguments to fzf
opts=$(printf "%q " "${args[@]}")
2015-04-20 13:42:02 +00:00
pppid=$$
echo -n "trap 'kill -SIGUSR1 -$pppid' EXIT SIGINT SIGTERM;" >> $argsf
close="; trap - EXIT SIGINT SIGTERM $close"
export TMUX=$(cut -d , -f 1,2 <<< "$TMUX")
mkfifo -m o+w $fifo2
if [[ "$opt" =~ "-E" ]]; then
cat $fifo2 &
if [[ -n "$term" ]] || [[ -t 0 ]]; then
cat <<< "\"$fzf\" $opts > $fifo2; out=\$? $close; exit \$out" >> $argsf
else
mkfifo $fifo1
cat <<< "\"$fzf\" $opts < $fifo1 > $fifo2; out=\$? $close; exit \$out" >> $argsf
cat <&0 > $fifo1 &
fi
tmux popup -d "$PWD" $opt "bash $argsf" > /dev/null 2>&1
exit $?
fi
mkfifo -m o+w $fifo3
if [[ -n "$term" ]] || [[ -t 0 ]]; then
cat <<< "\"$fzf\" $opts > $fifo2; echo \$? > $fifo3 $close" >> $argsf
2015-03-06 09:51:50 +00:00
else
mkfifo $fifo1
cat <<< "\"$fzf\" $opts < $fifo1 > $fifo2; echo \$? > $fifo3 $close" >> $argsf
2015-03-06 09:51:50 +00:00
cat <&0 > $fifo1 &
fi
[fzf-tmux] Turn off remain-on-exit only on fzf-tmux pane (#3410) * fix: turn off remain-on-exit only on fzf-tmux pane Using `fzf-tmux` overwrites `remain-on-exit` for all panes in a window, if it is only set globally or at a higher scope than window. set-option -wg remain-on-exit on set-option -s remain-on-exit on This makes other panes in that window close immediately on exit after using `fzf-tmux`, even though I expect them to remain open. Since TMux 3.0, `remain-on-exit` is a pane option that can be set via `set-option -p`. This will limit the option's scope to just the `fzf-tmux` pane, thus allowing us to close it immediately without overriding `remain-on-exit` on other panes in the window. Co-authored-by: Junegunn Choi <junegunn.c@gmail.com> Link: https://github.com/tmux/tmux/blob/11e69f6025f5783fe17d43247de1c3f659a19b69/CHANGES#L753-L760 Link: https://github.com/tmux/tmux/releases/tag/3.0 Related: https://github.com/junegunn/fzf/issues/3397 * fix: turn off synchronize-panes only on fzf-tmux pane Similar reason to 482fd2b (fix: turn off remain-on-exit only on fzf-tmux pane, 2023-08-24). Limit scope on which option is set to bare minimum. Have confirmed this will not feed input back to other panes which are set to be synchronized. However, note that this will not stop `fzf-tmux` from being launched by two synchronized panes in parallel. Link: https://github.com/junegunn/fzf/issues/3397#issuecomment-1689295351 --------- Co-authored-by: Junegunn Choi <junegunn.c@gmail.com>
2023-08-24 07:31:38 +00:00
tmux \
split-window -c "$PWD" $opt "bash -c 'exec -a fzf bash $argsf'" $swap \
[fzf-tmux] Turn off remain-on-exit only on fzf-tmux pane (#3410) * fix: turn off remain-on-exit only on fzf-tmux pane Using `fzf-tmux` overwrites `remain-on-exit` for all panes in a window, if it is only set globally or at a higher scope than window. set-option -wg remain-on-exit on set-option -s remain-on-exit on This makes other panes in that window close immediately on exit after using `fzf-tmux`, even though I expect them to remain open. Since TMux 3.0, `remain-on-exit` is a pane option that can be set via `set-option -p`. This will limit the option's scope to just the `fzf-tmux` pane, thus allowing us to close it immediately without overriding `remain-on-exit` on other panes in the window. Co-authored-by: Junegunn Choi <junegunn.c@gmail.com> Link: https://github.com/tmux/tmux/blob/11e69f6025f5783fe17d43247de1c3f659a19b69/CHANGES#L753-L760 Link: https://github.com/tmux/tmux/releases/tag/3.0 Related: https://github.com/junegunn/fzf/issues/3397 * fix: turn off synchronize-panes only on fzf-tmux pane Similar reason to 482fd2b (fix: turn off remain-on-exit only on fzf-tmux pane, 2023-08-24). Limit scope on which option is set to bare minimum. Have confirmed this will not feed input back to other panes which are set to be synchronized. However, note that this will not stop `fzf-tmux` from being launched by two synchronized panes in parallel. Link: https://github.com/junegunn/fzf/issues/3397#issuecomment-1689295351 --------- Co-authored-by: Junegunn Choi <junegunn.c@gmail.com>
2023-08-24 07:31:38 +00:00
$tmux_off_opts \
> /dev/null 2>&1 || { "$fzf" "${args[@]}"; exit $?; }
2015-03-06 09:51:50 +00:00
cat $fifo2
exit "$(cat $fifo3)"