Blocking run-shell suspends client key processing until the command
exits, so a floating pane deadlocks; popup input bypasses the suspended
path. Fall back to display-popup when TMUX_PANE is not set.
The native border is the handle for moving and resizing the pane with
the mouse, so use it by default, consistent with tmux. fzf draws its
own border only when a border style is explicitly specified with
--border. Extract the shared native-border decision into a helper.
Prevents another user on a shared TMPDIR from planting a file or a
symbolic link at the predictable path while fzf is running, like the
exit status file of the floating pane. Reject an empty become command.
The pane sets the options itself before running fzf, so that they are
in place no matter how quickly the command exits, targeted at
$TMUX_PANE; the default target would resolve to the active pane of
the session's current window.
- pane-border-format is set to '#{pane_title}' so that the label is
displayed on the border when pane-border-status is enabled;
pane-border-status itself is a window option in released tmux
versions and is left alone
- When a border style is explicitly specified with --border, a popup
is used instead of a floating pane so that the fzf-drawn border is
the only border shown; give 'border-native' to force a floating pane
- 'none' and 'line' are treated as no border; fzf draws no box for
either, so the label is displayed on the native border
- Remove 'border-fzf' which is now redundant; it was never released
- The title is escaped for select-pane -T which expands format
expressions; a lone ';' is escaped as tmux would treat it as a
command separator
- The label is skipped when ANSI stripping leaves an empty string
- --border-label-pos is ignored
- Fix remain-on-exit set on the original pane instead of the floating
pane
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
[]rune(t.input) is a same-type conversion that aliases t.input;
in-place query edits during the paste corrupt the snapshot and skew
the paste-end comparison.
A pasted character firing a wait-arming binding swallowed the
subsequent bracketed-paste-end, leaving t.pasting set forever and
suppressing queryChanged for all later input. Let paste begin/end
through the block so pasting state is maintained and the search for
the edited query is dispatched.
Background transform results are unrelated to the block; dropping them
would silently lose completed work. Applies both the dedicated
bg-transform-* setters and actions parsed from generic bg-transform
output (inBgCallback); a 'wait' in such output joins the ongoing wait.
Searches initiated by a bg result are not waited for.
Blocks execution of subsequent actions until the current search
completes, so motion actions operate on the complete result set.
# Move to the best match only after the search is done
fzf --bind 'start:change-query(foo)+wait+best'
- Blocks when a search is in progress, will be triggered by preceding
actions, or the query was edited in the same binding; no-op otherwise
- Initial input load counts as a search in progress, so 'start:wait'
blocks until the input is fully loaded and searched
- User input ignored while blocked, including --expect keys; keys bound
to abort/cancel cancel the wait and discard pending actions instead
- Actions deferred by the wait survive across nested lists (trigger
chords) and cancel/re-arm chains
- Debounced feedback after 200ms: dimmed prompt, hidden cursor, and
(..) on the info line
- Fires like result, but only after the input stream closes
- Use for one-shot per-query actions that would otherwise re-fire on
every intermediate snapshot during loading
Close#4835
- handleHttpRequest used `body += text` per token, allocating a new
backing array on every append (O(n^2) total copy work)
- a single ~390 KB POST monopolised the single-threaded server for
~8 s, blocking all other --listen clients
- switch to strings.Builder for amortised O(n)
Reported with fix by Michal Majchrowicz and Marcin Wyczechowski
(AFINE Team).
- exec(2) rejects env entries containing NUL, breaking preview and
other child commands when the input has NUL bytes
- skip the export and document the limitation
Fix#2395
On 32-bit platforms (GOARCH=386, arm), N*M overflows int when N is
large and M approaches 1000, wrapping negative. The wrapped value
slips past both `N*M > cap(slab.I16)` and `M > 1000`, so the V1
fallback is skipped and alloc16 panics on a negative slice bound.
Cast to int64 before multiplying.
Affects shipped 32-bit ARM builds (linux_armv5/6/7, windows_armv5/6/7).
Reported with fix by Michal Majchrowicz and Marcin Wyczechowski
(AFINE Team).
Places preview adjacent to input on the list side: above input in the
default layout, below it in --layout=reverse.
fzf --preview 'cat {}' --preview-window=next
Close#4798
Async callbacks fire a later iteration than the one that scheduled
them, so newCommand/reloadSync/denylist must persist across iterations.
fzf --bind 'space:bg-transform:echo reload:date'
- every(N) fires every N seconds (fractional, floored to 0.01s)
- Encoded as tui.Every with duration in Char as milliseconds, so
every(1) and every(2) coexist as distinct keymap entries
- FZF_IDLE_TIME exposes whole seconds since the last user activity
(keystroke or mouse event); pair with every() for idle-based
patterns like auto-accept/auto-quit
Close#1211
A non-word character (e.g. '.') used to receive a flat bonusNonWord
regardless of context. Now it gets bonusBoundaryWhite at the start of
input and bonusBoundaryDelimiter right after a delimiter, matching the
treatment of word characters at the same boundaries.
Without this, '.completion' matching '.completion' lost to
'bash_completion.d/completions/X' because the consecutive chunk anchor
in the long path (the 'c' after '/') received bonusBoundaryDelimiter
while the exact match's '.' was capped at bonusNonWord.
Fix#4795
The display sanitizer already stripped raw 8-bit C1 bytes (0x80-0x9F)
because they decode to RuneError as standalone bytes. Their valid UTF-8
encodings (0xC2 0x80 .. 0xC2 0x9F) decode to the same code points but
were passed through, allowing a filename or input line containing CSI
(U+009B), OSC (U+009D), or DCS (U+0090) to inject terminal control
sequences when rendered.
toggle-preview-wrap (and -wrap-word) modifies t.activePreviewOpts.wrap,
but change-preview-window resets t.previewOpts to t.initialPreviewOpts,
discarding the user's toggle. Carry wrap and wrapWord over so toggles
survive a layout change. Explicit wrap / nowrap tokens in the new spec
still win, so cycling and the empty-token reset are unaffected.
Close#4791
Mirror of the earlier change-header fix. The inline footer slot's row
budget depends on footer content length, but resizeIfNeeded() tolerates
a shorter-than-wanted inline window, so extra lines get clipped. Drive
a redraw on length change to re-run the layout.
New --border=dashed / --list-border=dashed / --header-border=dashed etc.
Uses U+2576 (╶) for horizontal edges and U+2506 (┆) for verticals, with
rounded corners (╭╮╰╯) and sharp T-junction mids (├┤). Terminal cells
are taller than wide (~2:1), so horizontals use a sparse stub per cell
while verticals need more dashes per cell to look evenly dashed.
Works with inline sections.
The inline header slot's row budget depends on header content length,
but resizeIfNeeded() tolerates a shorter-than-wanted inline window, so
the stale slot stays. Drive a redraw on length change to re-run the
layout.
--header-first previously was rejected with --header-border=inline or
--header-lines-border=inline. Now, inline placement wins: an inline
section stays inside the list frame, and --header-first only affects
non-inline sections (mainly the main --header).
The guard fires when hasHeaderWindow() returned false at resize time,
not when addInline had no budget (placeInlineStack always leaves a
non-nil 0-height placeholder).
InitTheme was called before the runtime coerced BorderInline to
BorderLine, so HeaderBorder / FooterBorder inherited from ListBorder
even when the effective shape was 'line'. Mirror the coercion so
color inheritance matches the rendered shape.
New BorderShape that embeds the section inside the --list-border
frame, joined to the list content by a horizontal separator with
T-junctions where the list shape has side borders. Requires a list
border with both top and bottom segments; falls back to 'line'
otherwise. Stacks when multiple sections are inline.
Sections inherit --color list-border by default and are colored as a
uniform block via their own --color *-border and *-bg.
Incompatible with --header-first. --header-border=inline requires
--header-lines-border to be inline or unset.
Windows doesn't have signals, the default Kill doesn't actually kill
anything, and other forms of termination don't allow cleanup. So we
spawn preview processes in a new process group and send them a
CTRL_BREAK_EVENT to terminate.
However, we only do this for "pwsh" (PowerShell 7+) and unknown/
posix-ish shells, because cmd.exe and Windows PowerShell
("powershell.exe") don't always exit on Ctrl-Break. pwsh also needs
the -NonInteractive flag to exit on Ctrl-Break.
If the process wasn't given its own group, or if sending the console
control event fails, we fall back to the standard Kill (which likely
won't help, but doesn't hurt to try).
Fix#3134
Use make([]NthParts, 0, len(indexes)) so the slice starts empty with
reserved capacity. The previous length-len(indexes) allocation left
leading zero NthParts entries before appended elements.