tmux 弹窗功能与 fzf 高效搜索集成指南

1. 功能简介

tmux 3.2 版本引入了强大的 Popup(弹窗)功能,允许用户在当前会话之上悬浮显示一个新的 pane。结合 fzf(命令行模糊搜索工具),我们可以在不离开当前工作上下文的情况下,快速进行文件搜索、命令执行或会话切换。

tmux popup 演示

2. 环境配置

2.1 升级 Tmux

Popup 功能要求 tmux 版本不低于 3.2。首先检查当前版本:

1
tmux -V

若版本过低,建议通过源码编译安装最新版:

1
2
3
4
# 下载 release 包 (推荐使用最新稳定版)
# https://github.com/tmux/tmux/releases
./configure && make
sudo make install

注意:升级后若启用了 tmux-resurrect 插件,旧的 session 可能会卡死。建议在升级前把 session 杀完后再 tmux kill-server

2.2 基础弹窗用法

使用 display-popup 命令(简写为 popup)即可呼出弹窗。

示例:弹出一个宽高均为 80% 的浮窗

1
tmux popup -w 80% -h 80%

推荐别名配置

.tmux.conf 或 shell 配置文件中添加别名以提高效率:

1
2
3
4
5
6
# 快速呼出空白弹窗
alias p='tmux popup -w 80% -h 80%'

# 呼出弹窗并尝试 attach 到名为 popup 的 session,若不存在则创建
# 适合作为临时的 scratchpad 使用
alias pp='tmux popup -w 90% -h 90% "tmux attach -t popup || tmux new -s popup"'

3. Fzf-tmux (在弹窗使用 fzf)

我们可以编写脚本,使 fzf 在 tmux 弹窗中运行,从而获得更好的视觉体验。

3.1 脚本逻辑

该脚本 (fzfp) 智能判断当前环境:若满足条件则在 tmux 弹窗中运行 fzf,否则降级为普通 fzf。

%%{init: {'theme': 'base', 'themeVariables': { 'primaryColor': '#4F46E5', 'primaryTextColor': '#fff', 'primaryBorderColor': '#3730A3', 'lineColor': '#6366F1', 'secondaryColor': '#10B981', 'tertiaryColor': '#F59E0B'}}}%%
flowchart TD
    Start("脚本开始") --> CheckFZF{"检查 fzf 是否安装"}
    CheckFZF -- "否" --> Error("报错退出")
    CheckFZF -- "是" --> CheckNested{"是否在嵌套弹窗中?"}
    
    CheckNested -- "是" --> RunNormal["直接运行 fzf"]
    CheckNested -- "否" --> CheckVer{"tmux 版本 >= 3.2?"}
    
    CheckVer -- "否" --> RunNormal
    CheckVer -- "是" --> Prep["准备管道/临时文件"]
    Prep --> Popup["启动 tmux popup"]
    Popup --> RunInPopup["在弹窗中运行 fzf"]
    RunInPopup --> Output["输出结果"]

    classDef primary fill:#4F46E5,stroke:#3730A3,color:#fff
    classDef success fill:#10B981,stroke:#059669,color:#fff
    classDef warning fill:#F59E0B,stroke:#D97706,color:#fff
    classDef danger fill:#EF4444,stroke:#DC2626,color:#fff

    class Start,Prep,Popup,RunInPopup primary
    class CheckFZF,CheckNested,CheckVer warning
    class RunNormal,Output success
    class Error danger

3.2 安装脚本

将以下脚本保存为 fzfp(fzf popup),并添加执行权限 (chmod +x fzfp),确保其所在目录在 $PATH 中(如 ~/.local/bin)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
#!/usr/bin/env bash

fail() {
echo "$1" >&2
exit 2
}

# 1. 定位 fzf
fzf=$(command -v fzf 2>/dev/null) || fzf=$(dirname $0)/fzf
[[ -x "$fzf" ]] || fail 'fzf executable not found'

# 2. 避免嵌套弹窗
if [[ -n $TMUX_POPUP_NESTED_FB ]]; then
eval "$TMUX_POPUP_NESTED_FB" && exec fzf "$@"
fi

# 3. 检查 tmux 版本 >= 3.2
tmux -V | awk '{match($0, /[0-9]+\.[0-9]+/, m);exit m[0]<3.2}' || exec fzf "$@"

# 4. 解析参数
args=('--no-height')
while (($#)); do
arg=$1
case $arg in
--height | --width)
eval "${arg:2}=$2"
shift
;;
--height=* | --width=*)
eval "${arg:2}"
;;
*)
args+=("$arg")
;;
esac
shift
done

opts=$(printf '%q ' "${args[@]}")

[[ -z $height ]] && height=${TMUX_POPUP_HEIGHT:-80%}
[[ -z $width ]] && width=${TMUX_POPUP_WIDTH:-80%}

# 5. 传递环境变量
envs="SHELL=$SHELL"
[[ -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")"

# 6. 准备管道与临时文件
id=$RANDOM
cmd_file="${TMPDIR:-/tmp}/fzf-cmd-file-$id"
pstdin="${TMPDIR:-/tmp}/fzf-pstdin-$id"
pstdout="${TMPDIR:-/tmp}/fzf-pstdout-$id"

clean_cmd="command rm -f $cmd_file $pstdin $pstdout"
cleanup() { eval "$clean_cmd"; }
trap 'cleanup' EXIT

mkfifo "$pstdout"
echo -n "trap '$clean_cmd' EXIT SIGINT SIGTERM SIGHUP;" >"$cmd_file"

if [[ -t 0 ]]; then
echo -n "$fzf $opts > $pstdout" >>"$cmd_file"
else
mkfifo "$pstdin"
echo -n "$fzf $opts < $pstdin > $pstdout" >>"$cmd_file"
cat <&0 >"$pstdin" &
fi

# 7. 启动 Popup
cat "$pstdout" &
tmux popup -d '#{pane_current_path}' -xC -yC -w$width -h$height -E "$envs bash $cmd_file"

3.3 配置 Shell 集成

.zshrc.bashrc 中添加辅助配置,防止在已经处于 popup 的 session 中再次尝试弹出 popup(避免递归错误)。

1
2
3
4
5
6
7
8
if [[ -n $TMUX_PANE ]] && (( $+commands[tmux] )) && (( $+commands[fzfp] )); then
# 如果当前 session 名为 floating,则降级为普通 fzf
export TMUX_POPUP_NESTED_FB='test $(tmux display -pF "#{==:#S,floating}") == 1'
export TMUX_POPUP_WIDTH=80%
fi

# 创建便捷别名:预览文件内容
alias f="fzfp --preview 'cat {}'"

效果演示:

fzf 弹窗效果

4. Tmux-fzf 插件 (fzf 管理 tmux)

可以使用 tmux-fzf 插件来管理 tmux 自身的环境(如切换 session、window 等),它完美支持 popup 模式。

4.1 安装配置

TPM 安装方式

~/.tmux.conf 中添加:

1
2
3
set -g @plugin 'sainnhe/tmux-fzf'
# 启用 popup 模式(默认可能为 split)
TMUX_FZF_OPTIONS="-p -w 80% -h 80% -m"

按下 prefix + I 安装插件。

4.2 使用

默认快捷键为 prefix + F。按下后将弹出一个管理菜单,可快速进行会话切换、窗口管理等操作。

tmux-fzf 演示

5. 参考资料