Use floating pane instead of popup on tmux 3.7 or above (#4850) (#4852)
CodeQL / Analyze (go) (push) Has been cancelled
build / build (push) Has been cancelled
Test fzf on macOS / build (push) Has been cancelled

Unlike a popup, a floating pane is not modal; you can switch to other
panes and windows while fzf is running, move and resize the pane with
the mouse, zoom it to fullscreen, and use copy-mode in it.

- Floating pane always has a native border, so 'border-native' is
  implied; give new 'border-fzf' option to fall back to a popup where
  fzf draws its own border
- Popup is also used on tmux versions below 3.7, or when the window is
  too small to fit a floating pane
- new-pane does not block until the command finishes and does not
  propagate the exit status; block on a wait-for channel signaled by
  the pane and pass the exit status through a temporary file
- Watchdog process signals the channel when the pane is closed
  abnormally (e.g. kill-pane)
- Kill the pane when the proxy process is interrupted, like a popup
  dying with its client
- Unzoom the window before creating the floating pane; doing so over a
  zoomed window crashes the tmux server on 3.7b, and newer versions of
  tmux unzoom the window anyway
- Floating pane size excludes the border and the position is that of
  the content area; treat the requested size as the total footprint
  including the border for consistency with popups
- Close the pane on exit even when remain-on-exit is on
- Pre-create the exit status file with O_EXCL to prevent tampering on
  a shared TMPDIR
This commit is contained in:
Junegunn Choi
2026-07-05 14:15:11 +09:00
committed by GitHub
parent a1fb01462d
commit 1e31e5dfbe
7 changed files with 240 additions and 14 deletions
+19 -7
View File
@@ -75,9 +75,9 @@ Usage: fzf [options]
--min-height=HEIGHT[+] Minimum height when --height is given as a percentage.
Add '+' to automatically increase the value
according to the other layout options (default: 10+).
--popup[=OPTS] Start fzf in a popup window (requires tmux 3.3+ or Zellij 0.44+)
--popup[=OPTS] Start fzf in a floating pane (requires tmux 3.3+ or Zellij 0.44+)
[center|top|bottom|left|right][,SIZE[%]][,SIZE[%]]
[,border-native] (default: center,50%)
[,border-native|border-fzf] (default: center,50%)
--tmux[=OPTS] Alias for --popup
LAYOUT
@@ -335,12 +335,20 @@ const (
posNext // adjacent to the input section, on the list side
)
type tmuxBorder int
const (
tmuxBorderAuto tmuxBorder = iota
tmuxBorderNative
tmuxBorderFzf
)
type tmuxOptions struct {
width sizeSpec
height sizeSpec
position windowPosition
index int
border bool
border tmuxBorder
}
type layoutType int
@@ -427,15 +435,19 @@ func parseTmuxOptions(arg string, index int) (*tmuxOptions, error) {
var err error
opts := defaultTmuxOptions(index)
tokens := splitRegexp.Split(arg, -1)
errorToReturn := errors.New("invalid popup option: " + arg + " (expected: [center|top|bottom|left|right][,SIZE[%]][,SIZE[%][,border-native]])")
errorToReturn := errors.New("invalid popup option: " + arg + " (expected: [center|top|bottom|left|right][,SIZE[%]][,SIZE[%]][,border-native|border-fzf])")
if len(tokens) == 0 || len(tokens) > 4 {
return nil, errorToReturn
}
for i, token := range tokens {
if token == "border-native" {
tokens = append(tokens[:i], tokens[i+1:]...) // cut the 'border-native' option
opts.border = true
if token == "border-native" || token == "border-fzf" {
tokens = append(tokens[:i], tokens[i+1:]...) // cut the border option
if token == "border-native" {
opts.border = tmuxBorderNative
} else {
opts.border = tmuxBorderFzf
}
break
}
}