This guide is the operational and architectural reference for fzf. It
covers the build/release process, the full configuration surface (every
CLI flag, every environment variable, every action), the internal
architecture (reader → matcher → terminal pipeline), security posture,
performance tuning, and the integration points with shells, tmux,
Zellij, and editors.
The companion User Guide covers everyday usage; this guide assumes familiarity with that material.
1. Architecture Overview
fzf is a single-binary Go program that runs as an interactive TTY
filter. Source input enters a non-blocking reader, passes through a
parallel matcher that scores items with a fuzzy or exact algorithm, and
the result is displayed by a terminal renderer that handles user input
events, the preview window, and any command bindings the user has set.
+-----------------+
| stdin / command |
+--------+--------+
|
v
+--------+--------+ +----------------+
| Reader |----->| ChunkList |
| (src/reader.go)| | (src/chunklist)|
+--------+--------+ +-------+--------+
| EvtReadNew |
| EvtReadFin |
v v
+--------+--------+ +-------+--------+
| Core |<---->| Matcher |
| (src/core.go) | | (src/matcher.go)
+--------+--------+ +-------+--------+
| |
| EvtSearchNew/Progress/Fin
v |
+--------+--------+ |
| Terminal |<-------------+
| (src/terminal.go)|
+--------+--------+
|
| renders to TUI:
| - LightRenderer (xterm escape codes)
| - tcell Renderer (Windows/legacy)
|
v
+--------+--------+
| src/tui (TUI) |
+-----------------+
Note
The Reader, Matcher, and Terminal each run in their own goroutine and communicate via an
EventBox(seesrc/util/eventbox.go). The coordinator delays UI updates by up to 100ms to coalesce rapid input changes — a fundamental design decision that keeps fzf responsive even on inputs of millions of lines.
1.1 Component inventory
| Component | Path | Role |
|---|---|---|
| Entry point | main.go |
Parses options, prints embedded shell scripts on --bash/--zsh/--fish, prints embedded man page on --man, otherwise calls fzf.Run(options). |
| Core orchestrator | src/core.go |
Wires Reader → Matcher → Terminal goroutines and dispatches events. |
| Option parser | src/options.go |
The big one (~3900 lines): parses every CLI flag, --bind actions, --color specs, --preview-window specs, and validates them. |
| Reader | src/reader.go |
Reads input from stdin or FZF_DEFAULT_COMMAND non-blockingly, chunks lines, signals via EventBox. |
| Chunk list | src/chunklist.go |
Lock-free append-only list of 1024-line chunks. Lets the Matcher and Terminal observe the input without blocking the Reader. |
| Matcher | src/matcher.go |
Spawns N partitions (default = runtime.NumCPU()), each running the configured algorithm. Caches Pattern-keyed results (ChunkCache). |
| Pattern parser | src/pattern.go |
Tokenises queries into termSets (AND-of-OR semantics) — implements the search-syntax grammar (', ^, $, !, |). |
| Algorithms | src/algo/algo.go + SIMD asm |
FuzzyMatchV1, FuzzyMatchV2, ExactMatchNaive, ExactMatchBoundary, PrefixMatch, SuffixMatch, EqualMatch. AMD64/ARM64 SIMD indexbyte2 accelerator. |
| Terminal | src/terminal.go |
The other big one (~17 000 lines): event loop, layout, list/preview/header/footer/prompt rendering, mouse, jump mode, history widget, popup. |
| TUI back-ends | src/tui/light.go, src/tui/tcell.go |
LightRenderer issues raw xterm escape codes (Unix); the tcell renderer is used on Windows and legacy terminals. |
| Server | src/server.go |
Hand-rolled HTTP/1.1 server (no net/http) for --listen — receives action sequences, validates X-API-Key, dispatches via channel. |
| Tmux launcher | src/tmux.go |
Implements --tmux / --popup by exec'ing tmux display-popup. |
| Zellij launcher | src/zellij.go |
Same idea for Zellij ≥ 0.44. |
| Process protector | src/protector/ |
OpenBSD-only pledge/unveil hardening. No-op on other OSes. |
| Proxy | src/proxy.go, src/proxy_unix.go, src/proxy_windows.go |
Plumbing for become(...) — exec-replacement of fzf. |
| WinPTY | src/winpty.go, src/winpty_windows.go |
Windows-specific terminal lifecycle workaround for ConEmu / mintty. |
| Shell scripts | shell/key-bindings.{bash,zsh,fish}, shell/completion.{bash,zsh,fish}, shell/common.sh |
Embedded into the binary at build time and emitted via --bash/--zsh/--fish. |
| Vim plugin | plugin/fzf.vim |
Provides :FZF and helpers; the rich plugin is a separate repo. |
1.2 Technology stack
| Layer | Tech |
|---|---|
| Language | Go 1.23 |
| TUI library | github.com/gdamore/tcell/v2 v2.9.0 (Windows / legacy fallback) |
| Direct TUI | hand-rolled escape-code renderer (src/tui/light.go) for performance on Unix |
| File walker | github.com/charlievieth/fastwalk v1.0.14 (used by --walker) |
| Word splitting | github.com/junegunn/go-shellwords (POSIX shell tokenisation for --bind action arguments) |
| TTY detection | github.com/mattn/go-isatty v0.0.20 |
| Unicode segmentation | github.com/rivo/uniseg v0.4.7 (grapheme-aware width computation) |
| Low-level | golang.org/x/sys v0.35.0, golang.org/x/term v0.34.0 |
| Test framework | minitest 5.25.4 (Ruby) for end-to-end shell-integration tests; Go testing for unit tests |
| Lint | rubocop for the Ruby tests; gofmt -s and go vet for Go |
| Build | Makefile + GoReleaser (.goreleaser.yml) |
1.3 Data flow for a typical run
main.gocallsfzf.ParseOptions(src/options.go).- The parser validates every flag; on error it returns immediately
(
exit(ExitError, err)). fzf.Run(options)builds aReader,Matcher, andTerminal, all communicating through a sharedEventBox.Reader.ReadSource()runs in its own goroutine. If stdin is a TTY andFZF_DEFAULT_COMMANDis set, it execs that command. Otherwise it reads stdin. Items are appended to aChunkList.- Whenever a new chunk appears,
ReaderpostsEvtReadNewtoEventBox. When stdin closes, it postsEvtReadFin. Matcher.Looplistens forEvtReadNew, takes the latestPattern, and runs N parallel scoring passes (one per CPU). Results are merged and cached.Terminal.Looplistens forEvtSearchNew,EvtSearchProgress, andEvtSearchFin, updating the screen accordingly. It also reads keyboard / mouse events and dispatches the bound actions.- On
accept, the selected item(s) and the optional--print-queryare written to stdout and fzf exits withExitOk(0). - On
become(...), the selected item is templated into the command andsyscall.Execreplaces the fzf process.
1.4 Concurrency model
Three goroutines (Reader, Matcher, Terminal) plus a coordinator. The
coordinator schedules screen updates with adaptive delay
(coordinatorDelayMax = 100ms, step 10ms) so that bursts of input or
keystrokes coalesce into a single render pass.
The Matcher partitions the chunk list across runtime.NumCPU()
goroutines (override with --threads N). Each partition uses its own
slab allocator (util.Slab) to minimise GC pressure during high-rate
matching.
2. System Requirements
2.1 Runtime requirements
| Layer | Requirement |
|---|---|
| Operating system | Linux (any distro, glibc or musl), macOS 10.15+, Windows 10/11 (Terminal, ConEmu, mintty), FreeBSD, OpenBSD, Android (via Termux). |
| Architecture | amd64, arm64, arm v5/v6/v7, ppc64le, s390x, riscv64, loong64. |
| Terminal | Any terminal that handles xterm-style escape codes. Modern Windows Terminal, iTerm2, Alacritty, kitty, gnome-terminal, Konsole, mintty, ConEmu — all known-good. |
| Shell | Optional integration scripts target Bash 3.2+, Zsh 5.0+, Fish 3.0+. fzf itself is shell-agnostic. |
| Disk | <8 MB binary; no persistent state. |
| Memory | A few MB at startup; scales with input. ~30 bytes per item plus pattern cache. 10 million items ≈ ~300 MB resident. |
| Network | None. fzf is fully offline by default. The --listen server only opens when explicitly requested. |
2.2 Build requirements
| Layer | Requirement |
|---|---|
| Go | 1.23 or newer (matches go.mod). |
| Make | Optional — the Makefile orchestrates build, embed, and release. |
| GoReleaser | Required for cross-arch release artifacts (matches .goreleaser.yml). Set GORELEASER_VERSION in .tool-versions. |
| Ruby | 3.x with bundler — for the integration test suite (tests/test_*.rb). |
| Tools | shfmt for shell-script formatting, rubocop for Ruby lint. |
2.3 Platform-specific notes
| Platform | Note |
|---|---|
| OpenBSD | The binary calls pledge/unveil (src/protector/protector_openbsd.go) at startup to drop dangerous privileges. |
| Windows | Uses the tcell renderer instead of the raw light renderer; supports ConEmu via ConEmuPID. WinPTY (src/winpty_windows.go) handles legacy mintty/Cygwin terminals. |
| macOS | The shell-integration scripts detect that macOS's stock awk chokes on non-UTF-8 and prefer mawk or GNU awk if installed. |
| Solaris | The integration scripts use /usr/xpg4/bin/awk (POSIX-conformant) instead of the legacy /usr/bin/awk. |
| Android | goreleaser builds a linux_arm64 artifact suitable for Termux. |
3. Configuration Reference
fzf is configured exclusively through CLI flags and environment
variables. Settings precedence (lower wins, higher overrides):
- Flags from
FZF_DEFAULT_OPTS_FILE - Flags from
FZF_DEFAULT_OPTS - Flags on the command line
3.1 Environment variables
| Variable | Type | Default | Description | Required? |
|---|---|---|---|---|
FZF_DEFAULT_COMMAND |
shell command | find . -type f (when no --walker) |
Command to run when stdin is a TTY. Output is treated as the candidate list. | No |
FZF_DEFAULT_OPTS |
string | empty | Extra CLI options applied to every fzf invocation. Whitespace-separated; supports newlines. | No |
FZF_DEFAULT_OPTS_FILE |
path | empty | Path to a file of default options. Read before FZF_DEFAULT_OPTS. |
No |
FZF_API_KEY |
string | empty | API key for the HTTP server (--listen). Required when binding non-locally. |
When non-local --listen |
FZF_CTRL_T_COMMAND |
shell command | platform default | Used by the CTRL-T file widget. Set to empty string to disable. | No |
FZF_CTRL_T_OPTS |
string | empty | Extra fzf options for CTRL-T. | No |
FZF_CTRL_R_OPTS |
string | empty | Extra fzf options for CTRL-R history widget. | No |
FZF_ALT_C_COMMAND |
shell command | platform default | Used by the ALT-C directory widget. Set to empty string to disable. | No |
FZF_ALT_C_OPTS |
string | empty | Extra fzf options for ALT-C. | No |
FZF_COMPLETION_TRIGGER |
string | ** |
The trigger sequence for **-style fuzzy completion. |
No |
FZF_COMPLETION_OPTS |
string | empty | Extra fzf options when invoked from a completion. | No |
FZF_COMPLETION_PATH_OPTS |
string | empty | Extra options for the path completion. | No |
FZF_COMPLETION_DIR_OPTS |
string | empty | Extra options for the directory completion. | No |
FZF_COMPLETION_PATH_COMMANDS |
shell list | bash default list | Override the list of commands that get path completion. | No |
FZF_COMPLETION_DIR_COMMANDS |
shell list | cd pushd rmdir |
Override the list of commands that get directory completion. | No |
FZF_COMPLETION_VAR_COMMANDS |
shell list | export unset printenv |
Override the list of commands that get variable completion. | No |
FZF_TMUX |
int (0/1) | 0 |
If non-zero, route shell widgets through fzf-tmux. |
No |
FZF_TMUX_HEIGHT |
size | 40% |
Height for fzf-tmux and --tmux popup defaults. |
No |
FZF_TMUX_OPTS |
string | empty | Extra options forwarded to fzf-tmux. |
No |
FZF_PREVIEW_LINES |
int | (set by fzf) | Auto-set by fzf when running a preview command — the number of lines available in the preview window. | No (set by fzf) |
FZF_PREVIEW_COLUMNS |
int | (set by fzf) | Auto-set: columns available in the preview window. | No (set by fzf) |
FZF_PREVIEW_TOP / FZF_PREVIEW_LEFT |
int | (set by fzf) | Auto-set: top-row / left-column of the preview window. | No (set by fzf) |
FZF_PREVIEW_LABEL |
string | (set by fzf) | Auto-set: current preview label. | No (set by fzf) |
FZF_QUERY |
string | (set by fzf) | Auto-set: current query, available to commands invoked from execute/become/reload/transform. |
No (set by fzf) |
FZF_PROMPT |
string | (set by fzf) | Auto-set: current prompt string. | No (set by fzf) |
FZF_POS |
int | (set by fzf) | Auto-set: 1-based cursor position in the result list. | No (set by fzf) |
FZF_KEY |
string | (set by fzf) | Auto-set: the key that triggered the action. | No (set by fzf) |
FZF_ACTION |
string | (set by fzf) | Auto-set: the name of the most recently executed action. | No (set by fzf) |
FZF_PORT |
int | (set by fzf) | Auto-set: TCP port the listen server is bound to. | No (set by fzf) |
FZF_INFO |
string | (set by fzf) | Auto-set: rendered info line text. | No (set by fzf) |
FZF_SELECT_COUNT |
int | (set by fzf) | Auto-set: number of items currently selected (multi-select). | No (set by fzf) |
FZF_TOTAL_COUNT |
int | (set by fzf) | Auto-set: total candidate count. | No (set by fzf) |
FZF_MATCH_COUNT |
int | (set by fzf) | Auto-set: count of matches for the current query. | No (set by fzf) |
FZF_LINES / FZF_COLUMNS |
int | (set by fzf) | Auto-set: dimensions of the fzf rendering area. | No (set by fzf) |
FZF_INPUT_STATE |
string | (set by fzf) | Auto-set: current state (enabled/disabled/hidden). |
No (set by fzf) |
FZF_NTH |
string | (set by fzf) | Auto-set: current --nth spec. |
No (set by fzf) |
FZF_CLICK_HEADER_LINE / FZF_CLICK_HEADER_COLUMN / FZF_CLICK_HEADER_NTH / FZF_CLICK_HEADER_WORD |
string | (set on click-header) |
Auto-set: clicked field metadata. | No (set by fzf) |
FZF_CLICK_FOOTER_LINE / FZF_CLICK_FOOTER_COLUMN / FZF_CLICK_FOOTER_WORD |
string | (set on click-footer) |
Auto-set: clicked footer field. | No (set by fzf) |
NO_COLOR |
flag | unset | If set (any value), fzf disables coloured output (no-color.org standard). | No |
RUNEWIDTH_EASTASIAN |
int | unset | If set, forces East-Asian width treatment for ambiguous-width characters. | No |
TERM |
string | (from environment) | Used by the renderer for terminfo-style capability detection. | No |
TMUX |
string | (from environment) | If set, fzf knows it is running inside tmux. | No |
ZELLIJ |
string | (from environment) | If set, fzf knows it is running inside Zellij. | No |
ESCDELAY |
int (ms) | 100 |
Time fzf waits for a possible escape sequence after seeing a bare Esc. |
No |
SLAB_KB |
int (KiB) | (computed) | Override the matcher slab size (advanced). | No |
BUF_KB |
int (KiB) | 64 |
Override the reader buffer size (advanced). | No |
_FZF_CHILD_PROC |
flag | (set by fzf) | Internal: tells nested fzf invocations they are children. | No (set by fzf) |
TERM_PROGRAM, TERM_PROGRAM_VERSION, MSYS, ConEmuPID |
string | (from environment) | Read on Windows for terminal-host detection. | No |
Important
FZF_API_KEYis mandatory when--listenbinds to anything other than localhost or a Unix socket. Without it, fzf returnsFZF_API_KEY is required to allow remote accessand exits. Generate one withopenssl rand -hex 16.
3.2 Command-line flags — full reference
The following enumeration is grouped exactly as in fzf --help. Every
flag has been verified against src/options.go's Usage constant.
Search
| Flag | Type | Default | Description |
|---|---|---|---|
-e, --exact |
bool | off | Enable exact-match. |
+x, --no-extended |
bool | off | Disable extended-search mode. |
-i, --ignore-case |
bool | off | Case-insensitive match. |
+i, --no-ignore-case |
bool | off | Case-sensitive match. |
--smart-case |
bool | on | Smart case (default — case-insensitive iff query is all lowercase). |
--scheme=SCHEME |
enum | default |
Scoring scheme: default, path, history. |
-n, --nth=N[,..] |
spec | empty | Comma-separated field index expressions for limiting search scope. |
--with-nth=N[,..] |
spec | empty | Transform the displayed line using field index expressions. |
--accept-nth=N[,..] |
spec | empty | Define which fields to print on accept. |
-d, --delimiter=STR |
regex | AWK | Field delimiter regex. |
+s, --no-sort |
bool | off | Do not sort the result. |
--literal |
bool | off | Do not normalise Latin-script letters. |
--tail=NUM |
int | unlimited | Maximum number of items to keep in memory. |
--disabled |
bool | off | Do not perform search (item count == result count). |
--tiebreak=CRI[,..] |
enum-list | length |
Up to 3 of: length, chunk, pathname, begin, end, index. |
Input/Output
| Flag | Type | Default | Description |
|---|---|---|---|
--read0 |
bool | off | Read input delimited by ASCII NUL. |
--print0 |
bool | off | Print output delimited by ASCII NUL. |
--ansi |
bool | off | Process ANSI colour codes in input. |
--sync |
bool | off | Synchronous search for multi-staged filtering. |
Global Style
| Flag | Type | Default | Description |
|---|---|---|---|
--style=PRESET |
enum | default |
Preset: default, minimal, full[:BORDER_STYLE]. |
--color=COLSPEC |
spec | (terminal default) | Base scheme `dark |
--no-color |
bool | off | Disable colours. |
--no-bold |
bool | off | Do not use bold text. |
Display Mode
| Flag | Type | Default | Description |
|---|---|---|---|
--height=[~][-]HEIGHT[%] |
size | full screen | Display fzf below the cursor with the given height. ~ auto-fits to input. Negative = terminal height minus value. |
--min-height=HEIGHT[+] |
int | 10+ |
Minimum height when --height is a percentage. + auto-grows. |
--popup[=OPTS] / --tmux[=OPTS] |
spec | center,50% | Open in a tmux/Zellij popup. Format: [center|top|bottom|left|right][,SIZE[%]][,SIZE[%]][,border-native]. |
Layout
| Flag | Type | Default | Description |
|---|---|---|---|
--layout=LAYOUT |
enum | default |
One of default, reverse, reverse-list. |
--margin=MARGIN |
spec | 0 |
Outer margin. Format: TRBL / TB,RL / T,RL,B / T,R,B,L. |
--padding=PADDING |
spec | 0 |
Inner padding (same syntax). |
--border[=STYLE] |
enum | none |
Border style: rounded, sharp, bold, block, thinblock, double, horizontal, vertical, top, bottom, left, right, line, none. |
--border-label=LABEL |
string | empty | Label printed on the outer border. |
--border-label-pos=COL |
int / spec | 0 (center) |
Border-label position; integer = column from left, negative = from right; :bottom switches to bottom border. |
List Section
| Flag | Type | Default | Description |
|---|---|---|---|
-m, --multi[=MAX] |
int | off | Enable multi-select with optional MAX cap. |
--highlight-line |
bool | off | Highlight the whole current line. |
--cycle |
bool | off | Cyclic scrolling. |
--wrap[=MODE] |
enum | char |
Line wrapping (char or word). |
--wrap-sign=STR |
string | ↳ |
Wrap-line indicator. |
--no-multi-line |
bool | off | Disable multi-line display when reading with --read0. |
--raw |
bool | off | Show non-matching items too. |
--track |
bool | off | Track current selection across reloads. |
--id-nth=N[,..] |
spec | empty | Identity fields for cross-reload tracking. |
--tac |
bool | off | Reverse input order. |
--gap[=N] |
int | 1 |
Render N empty lines between items. |
--gap-line[=STR] |
string | ┈ / - |
Draw horizontal line on each gap. |
--freeze-left=N / --freeze-right=N |
int | 0 |
Number of fields to freeze on the corresponding edge. |
--keep-right |
bool | off | Keep the right end of the line visible on overflow. |
--scroll-off=LINES |
int | 0 |
Lines to keep above/below when scrolling to the edge. |
--no-hscroll |
bool | off | Disable horizontal scroll. |
--hscroll-off=COLS |
int | 10 |
Columns to keep to the right of the highlighted substring. |
--jump-labels=CHARS |
string | asdfghjklqwertyuiopzxcvbnm1234567890ASDFGHJKLQWERTYUIOPZXCVBNM… |
Label characters for jump mode. |
--gutter=CHAR |
char | ▌ |
Gutter column character. |
--gutter-raw=CHAR |
char | ▖ |
Gutter column character in --raw mode. |
--pointer=STR |
string | ▌ / > |
Pointer to the current line. |
--marker=STR |
string | ┃ / > |
Multi-select marker. |
--marker-multi-line=STR |
string | ╻┃╹ |
3-element multi-line marker. |
--ellipsis=STR |
string | ·· |
Ellipsis for truncated lines. |
--tabstop=SPACES |
int | 8 |
Tab width. |
--scrollbar[=C1[C2]] |
string | │ / ┃ |
Scrollbar character(s) — first for list, second for preview. |
--no-scrollbar |
bool | off | Hide the scrollbar. |
--list-border[=STYLE] |
enum | none |
Border around the list section (same vocabulary as --border). |
--list-label=LABEL, --list-label-pos=COL |
spec | empty / 0 |
Label for the list border. |
Input Section
| Flag | Type | Default | Description |
|---|---|---|---|
--no-input |
bool | off | Hide input/prompt section. |
--prompt=STR |
string | > |
Input prompt. |
--info=STYLE |
enum | default |
Info style: default, right, hidden, inline[-right][:PREFIX]. |
--info-command=COMMAND |
shell | empty | Command whose stdout becomes the info line. |
--separator=STR |
string | ─ / - |
Horizontal separator on info line. |
--no-separator |
bool | off | Hide info-line separator. |
--ghost=TEXT |
string | empty | Ghost text shown when query is empty. |
--filepath-word |
bool | off | Word-wise movements respect path separators. |
--input-border[=STYLE], --input-label=LABEL, --input-label-pos=COL |
spec | none / empty / 0 |
Border and label for the input section. |
Preview Window
| Flag | Type | Default | Description |
|---|---|---|---|
--preview=COMMAND |
shell template | empty | Command to preview the current line. |
--preview-window=OPT |
spec | right:50% |
Layout. See User Guide §5.2 for full grammar (position, size, wrap, cycle, follow, info, hidden, border-STYLE, +SCROLL, ~HEADER_LINES, alt layout). |
--preview-border[=STYLE] |
enum | none |
Short for --preview-window=border-STYLE. |
--preview-label=LABEL, --preview-label-pos=N |
spec | empty / 0 |
Label for the preview border. |
--preview-wrap-sign=STR |
string | ↳ |
Wrap-line indicator inside the preview window. |
Header
| Flag | Type | Default | Description |
|---|---|---|---|
--header=STR |
string | empty | Header line(s). |
--header-lines=N |
int | 0 |
Treat first N input lines as a fixed header. |
--header-first |
bool | off | Print header before the prompt. |
--header-border[=STYLE], --header-label=LABEL, --header-label-pos=COL |
spec | none | Border/label for the header section. |
--header-lines-border[=STYLE] |
enum | none | Separate border for --header-lines content (pass none to separate without border). |
Footer
| Flag | Type | Default | Description |
|---|---|---|---|
--footer=STR |
string | empty | Footer line(s). |
--footer-border[=STYLE], --footer-label=LABEL, --footer-label-pos=COL |
spec | line / empty / 0 |
Border/label for the footer section. |
Scripting
| Flag | Type | Default | Description |
|---|---|---|---|
-q, --query=STR |
string | empty | Initial query. |
-1, --select-1 |
bool | off | Auto-select if exactly one match. |
-0, --exit-0 |
bool | off | Exit immediately if no match. |
-f, --filter=STR |
string | empty | Print matches and exit (non-interactive). |
--print-query |
bool | off | Print the query as the first output line. |
--expect=KEYS |
csv | empty | Comma-separated keys that immediately accept and emit the key name. |
Key/Event Binding
| Flag | Type | Default | Description |
|---|---|---|---|
--bind=BINDINGS |
bind-spec | (defaults) | Custom key/event bindings. See Section 3.4 for the action catalog. |
Advanced
| Flag | Type | Default | Description |
|---|---|---|---|
--with-shell=STR |
shell prefix | empty | Shell command and flags used to start child processes (e.g. --with-shell='bash -c'). |
--listen[=[ADDR:]PORT] |
addr | localhost:0 |
Start HTTP server. Use --listen-unsafe for non-local without API key (DANGEROUS). |
--listen=SOCKET_PATH |
path | empty | Listen on a Unix socket (path must end in .sock). |
Directory Traversal (when FZF_DEFAULT_COMMAND is unset)
| Flag | Type | Default | Description |
|---|---|---|---|
--walker=OPTS |
csv | file,follow,hidden |
Walker options: file, dir, follow, hidden. |
--walker-root=DIR [...] |
paths | . |
Roots to walk. |
--walker-skip=DIRS |
csv | .git,node_modules |
Directory names to skip. |
History
| Flag | Type | Default | Description |
|---|---|---|---|
--history=FILE |
path | empty | File for fzf's internal search history (NOT shell history). |
--history-size=N |
int | 1000 |
Maximum entries kept. |
Shell Integration / Help
| Flag | Type | Default | Description |
|---|---|---|---|
--bash |
bool | off | Print the embedded Bash integration script. |
--zsh |
bool | off | Print the embedded Zsh integration script. |
--fish |
bool | off | Print the embedded Fish integration script. |
--version |
bool | off | Print version and exit. |
--help, -h |
bool | off | Print usage and exit. |
--man |
bool | off | Open the embedded man page (uses man if available, otherwise prints to stdout). |
Profiling (build-tag pprof only)
| Flag | Type | Description |
|---|---|---|
--profile-cpu=FILE |
path | Write a Go pprof CPU profile (build with go build -tags=pprof). |
--profile-mem=FILE |
path | Memory profile. |
--profile-block=FILE |
path | Block profile. |
--profile-mutex=FILE |
path | Mutex profile. |
Warning
Profiling flags are gated behind the
pprofbuild tag. Without that tag, fzf printserror: profiling not supported: FZF must be built with '-tags=pprof' to enable profiling.
3.3 Color groups
The --color value is a comma-separated list of group:fg[,bg][,attr]
tuples. The recognised groups are:
| Group | Where it shows |
|---|---|
fg / bg |
Default foreground / background |
list-fg / list-bg |
List section |
preview-fg / preview-bg |
Preview window |
alt-bg |
Alternate row background (when set) |
selected-fg / selected-bg / selected-hl |
Selected (multi) item |
hl |
Highlighted match positions |
pointer |
Current-line pointer |
marker |
Multi-select marker |
prompt |
Input prompt |
input-bg / input-border / input-label |
Input section |
header-bg / header-border / header-label |
Header section |
footer-bg / footer-border / footer-label |
Footer section |
info |
Info line |
spinner |
Reader spinner |
border |
Outer border |
preview-border / preview-label / preview-scrollbar |
Preview chrome |
list-border / list-label |
List chrome |
separator |
Info-line separator |
scrollbar |
Main scrollbar |
gutter / alt-gutter |
Gutter column |
gap-line |
Gap line between items |
nth |
Nth-field highlight |
label |
Default label |
nomatch |
"No match" text |
ghost |
Ghost-text colour |
disabled |
Disabled-state colour |
Attributes: regular, bold, dim, italic, underline,
underline-double, underline-curly, underline-dotted,
underline-dashed, blink, reverse, strikethrough, strip.
Foreground/background colours: named (black, red, …, white,
bright-red, …, bright-white), 256-colour colour:N, or
#RRGGBB truecolour.
3.4 Action catalog (full)
--bind accepts any of the actions below. Actions taking arguments use
the form name(...) or name:STRING at the end of a chain. Multiple
actions chain with +.
Cursor / line editing
beginning-of-line, end-of-line, forward-char, backward-char,
forward-word, backward-word, forward-subword, backward-subword,
backward-delete-char, backward-delete-char/eof, delete-char,
delete-char/eof, kill-line, kill-word, kill-subword,
backward-kill-word, backward-kill-subword, unix-line-discard
(alias line-discard), unix-word-rubout (alias word-rubout),
yank, clear-query.
Item movement
down, up, down-match, up-match, first (alias top), last,
best, page-up, page-down, half-page-up, half-page-down,
offset-up, offset-down, offset-middle, prev-history,
next-history, prev-selected, next-selected.
Selection
select, deselect, select-all, deselect-all, clear-selection
(alias clear-multi), toggle, toggle-down, toggle-up, toggle-in,
toggle-out, toggle-all, change-multi, exclude, exclude-multi.
Search / pattern
enable-search, disable-search, toggle-search, enable-raw,
disable-raw, toggle-raw, toggle-sort, track, track-current,
untrack-current, toggle-track, toggle-track-current.
Acceptance / exit
accept, accept-non-empty, accept-or-print-query, print-query,
abort, cancel, close, bg-cancel.
Preview
show-preview, hide-preview, toggle-preview, toggle-preview-wrap,
toggle-preview-wrap-word, refresh-preview, preview-up,
preview-down, preview-page-up, preview-page-down,
preview-half-page-up, preview-half-page-down, preview-top,
preview-bottom, change-preview(<COMMAND>),
change-preview-window(<SPEC>), preview(<COMMAND>).
UI mutation (change-*)
change-prompt(<STR>), change-query(<STR>), change-header(<STR>),
change-header-lines(<INT>), change-footer(<STR>),
change-border-label(<STR>), change-list-label(<STR>),
change-input-label(<STR>), change-header-label(<STR>),
change-footer-label(<STR>), change-preview-label(<STR>),
change-ghost(<STR>), change-pointer(<STR>), change-nth(<SPEC>),
change-with-nth(<SPEC>), change-multi(<MAX>).
UI mutation (transform-*)
These run a shell command and use its stdout to set the corresponding
field. Each has a bg- variant that runs in the background:
transform, transform-prompt, transform-query, transform-header,
transform-header-lines, transform-footer, transform-border-label,
transform-list-label, transform-input-label, transform-header-label,
transform-footer-label, transform-preview-label, transform-ghost,
transform-pointer, transform-nth, transform-with-nth,
transform-search.
bg-transform, bg-transform-prompt, bg-transform-query,
bg-transform-header, bg-transform-header-lines, bg-transform-footer,
bg-transform-border-label, bg-transform-list-label,
bg-transform-input-label, bg-transform-header-label,
bg-transform-footer-label, bg-transform-preview-label,
bg-transform-ghost, bg-transform-pointer, bg-transform-nth,
bg-transform-with-nth, bg-transform-search.
Process management
execute(<COMMAND>), execute-silent(<COMMAND>),
execute-multi(<COMMAND>), become(<COMMAND>),
reload(<COMMAND>), reload-sync(<COMMAND>), put(<CHAR>),
print(<STR>).
Visibility toggles
toggle-input, hide-input, show-input, toggle-header,
show-header, hide-header, toggle-wrap, toggle-wrap-word,
toggle-multi-line, toggle-hscroll.
Bind manipulation
unbind(<KEY>), rebind(<KEY>), toggle-bind(<KEY>).
Position and search
pos(<INT>), search(<STR>), trigger(<EVENT>), replace-query,
refresh-preview.
Misc
ignore, bell, clear-screen, jump, jump-accept.
Important
Action names are kebab-case and the parser is strict. Valid arguments for
change-preview-windowmust satisfy the same grammar as--preview-window; bad input is rejected at parse time withinvalid preview window option: <spec>.
3.5 Recognised key names
Keys for --bind triggers fall into the following categories:
- ASCII printable characters: bare (
a,?,/). - Named keys:
enter,tab,esc,space,bspace,del,home,end,pgup,pgdn,up,down,left,right,insert. - Function keys:
f1–f24. - Modifiers:
ctrl-X,alt-X,shift-X,ctrl-alt-X,ctrl-shift-X,alt-shift-X,ctrl-alt-shift-X(modifier order must bectrl,alt,shift). - Mouse:
left-click,right-click,shift-left-click,shift-right-click,double-click,scroll-up,scroll-down,shift-scroll-up,shift-scroll-down,preview-scroll-up,preview-scroll-down. - Special:
ctrl-space,alt-space,ctrl-up,alt-up,shift-up,ctrl-alt-up,ctrl-shift-up, etc. (and equivalents fordown/left/right/home/end/page-up/page-down/delete).
Events (used as triggers, fired by fzf)
change, start, load, focus, result, resize, one, zero,
jump, jump-cancel, click-header, click-footer, multi,
backward-eof.
3.6 Placeholder grammar (preview / execute / become / reload / transform)
| Placeholder | Description |
|---|---|
{} |
Current line, shell-quoted. |
{1}, {2}, {-1}, {-2} |
Single field by 1-based positive or negative index. |
{1..3}, {2..}, {..-2} |
Field range. |
{q} |
Current query. |
{q:N}, {q:1..3} |
Query field (or range), split by --delimiter. |
{n} |
0-based item index. |
{+} |
All currently selected items, space-separated. |
{*} |
All currently matching items. |
{f} |
Path to a temp file containing the current item. |
{+f} |
Path to a temp file containing all selected items. |
{r} |
Raw current line (no shell quoting). |
{fzf:query} |
Shell-quoted current query (alternate form). |
{fzf:action} |
Name of the most recent action. |
{fzf:prompt} |
Current prompt. |
The internal regex (from src/terminal.go):
\\?(?:{[+*sfr]*[0-9,-.]*}|{q(?::s?[0-9,-.]+)?}|{fzf:(?:query|action|prompt)}|{[+*]?f?nf?}).
Important
Placeholders are templated through POSIX shell quoting unless
{r}is used. Never wrap user-controlled strings inbecome(...)or--previewwithout{f}(or careful explicit quoting); shell injection is the natural attack surface.
3.7 Exit codes
| Code | Meaning |
|---|---|
0 |
Selection accepted (or --filter produced output). |
1 |
No match (input was non-empty). |
2 |
Error — invalid options, broken pipe, terminal failure. |
126 |
become(...) exec'd a command — this is the new program's exit-code domain, not fzf's. |
130 |
User pressed Ctrl-C / Esc / SIGINT. |
(Constants in src/constants.go: ExitOk, ExitNoMatch, ExitError,
ExitBecome, ExitInterrupt.)
4. Deployment & Scaling
4.1 Single-machine deployment
The default deployment is "drop the binary on the box". No daemon, no config file, no PID file.
# /etc/profile.d/fzf.sh — system-wide integration
[ -x /usr/local/bin/fzf ] || return
case "$(basename "$SHELL")" in
bash) eval "$(fzf --bash)" ;;
zsh) source <(fzf --zsh) ;;
fish) fzf --fish | source ;;
esac
Tip
For NFS-mounted home directories, prefer the system-wide approach so the same fzf binary is used everywhere, and put
FZF_DEFAULT_OPTS_FILEon the user's local disk (or set it from rc).
4.2 Tmux / Zellij popup
--tmux and --popup are aliases. Inside tmux ≥ 3.3 or Zellij ≥ 0.44,
fzf detects the host and opens a popup with tmux display-popup (or
the Zellij equivalent).
When tmux/Zellij are unavailable but you still want a contained UI,
combine --height with --layout=reverse for an always-visible
"split bottom" experience.
4.3 Performance tuning
| Knob | Default | Effect |
|---|---|---|
--threads N |
runtime.NumCPU() |
Number of matcher partitions. Lower for I/O-bound match commands; higher rarely helps past NumCPU. |
--algo=v1 |
v2 |
Linear-time algorithm — faster on huge inputs (>1M lines), slightly less optimal scoring. |
--no-extended / +x |
extended on | Skip the term-set parser; treat the query as a single fuzzy term. Cheaper. |
--literal |
normalised | Skip Unicode normalisation. |
--no-sort |
sort on | Skip post-match sorting. |
--tail=N |
unbounded | Cap retained items. Crucial for tail -f-style streaming inputs. |
BUF_KB env |
64 |
Reader buffer size. Increase for streaming inputs with very long lines. |
SLAB_KB env |
200 |
Matcher slab size per partition. |
Built-in performance constants (from src/constants.go):
| Constant | Value | Notes |
|---|---|---|
coordinatorDelayMax |
100ms | Maximum debounce window for screen redraws. |
coordinatorDelayStep |
10ms | Adaptive backoff step. |
readerBufferSize |
64 KiB | Default reader buffer. |
readerSlabSize |
128 KiB | Reader slab. |
readerPollInterval |
10ms → 50ms | Adaptive polling for streaming sources. |
chunkSize |
1024 | Items per ChunkList node. |
slab16Size |
100 KiB | Per-partition uint16 slab (≈ 12.8 MB total at 32 partitions). |
slab32Size |
2 KiB | Per-partition int32 slab. |
queryCacheMax |
512 | Max items in a cached query result. |
mergerCacheMax |
100 000 | Max items in a cached merger. |
previewDelayed |
500ms | Delay before showing "preview pending" indicator. |
maxBgProcesses |
30 | Concurrent bg-transform / background commands. |
maxBgProcessesPerAction |
3 | Per-action concurrency cap. |
maxPatternLength |
1000 | Maximum query length. |
maxMulti |
MaxInt32 |
Multi-select cap. |
4.4 The --listen server
fzf --listen[=ADDR:PORT] runs a hand-rolled HTTP/1.1 server (defined
in src/server.go) that accepts POST / with a body that is parsed
the same way as a --bind action chain. Requests can also use
GET /?limit=N&offset=M to fetch the current result set as JSON.
Authentication: when ADDR is anything other than localhost / a Unix
socket, FZF_API_KEY must be set. The header X-API-Key is required
on every request; the comparison is constant-time (crypto/subtle).
# Server side (e.g. an editor session)
export FZF_API_KEY=$(openssl rand -hex 16)
fzf --listen 127.0.0.1:6266 --listen-unsafe=false \
--bind 'enter:become(${EDITOR} {})'
# Client side (any process)
curl -s -H "X-API-Key: $FZF_API_KEY" -X POST \
--data 'change-query(linux)+reload(rg --files)' \
http://127.0.0.1:6266/
# Read result set
curl -s -H "X-API-Key: $FZF_API_KEY" "http://127.0.0.1:6266/?limit=10&offset=0"
Constants: read deadline 10s, channel deadline 2s, max content-length
1 MiB. The server intentionally avoids net/http to keep the binary
small (commented in src/server.go: --listen adds 0.5 MiB instead of
the 2.9 MiB net/http would require).
Caution
--listen-unsafeskips the API-key check entirely. The server then accepts unauthenticatedbecome/execute/reloadactions from any network-reachable client — that is full remote command execution. Never enable on a multi-user host or any non-trusted network. The safest default is no--listenat all; the next safest is a Unix socket withchmod 0600(which fzf does automatically).
4.5 Embedding fzf inside a script
The contract is: stdin is the candidate stream, stdout is the chosen line(s). Standard scripting patterns:
# Single-pick
chosen=$(some-source | fzf --height=40% --reverse) || exit
echo "you picked: $chosen"
# Multi-pick into an array (Bash)
mapfile -t chosen < <(some-source | fzf --multi --read0)
# Headless filter (no UI)
matches=$(echo -e "alpha\nbeta\ngamma" | fzf --filter=al)
echo "$matches" # -> "alpha"
For non-interactive grep-style filtering, prefer --filter — it bypasses
the TUI entirely and emits matches to stdout sorted by score.
5. Operations
5.1 Updating fzf
| Method | Update command |
|---|---|
| Homebrew | brew upgrade fzf |
| apt | sudo apt update && sudo apt upgrade fzf |
| dnf | sudo dnf upgrade fzf |
| pacman | sudo pacman -Syu fzf |
| Chocolatey | choco upgrade fzf |
| Scoop | scoop update fzf |
| Git clone | cd ~/.fzf && git pull && ./install |
After updating, regenerate the integration scripts (no-op if you use
eval "$(fzf --bash)" rather than the legacy ~/.fzf.bash files).
5.2 Rolling back
If an upgrade breaks an existing binding:
fzf --versionto confirm the new release.- Read the upstream
CHANGELOG.mdfor breaking-change notes. - Most package managers can pin the previous version
(
apt install fzf=<version>,brew install fzf@<version>, etc.). - Or grab the previous release tarball from
https://github.com/junegunn/fzf/releasesand drop it on$PATH.
5.3 Logging / observability
fzf does not log to disk by default. For build-time profiling, the
--profile-cpu/mem/block/mutex flags emit Go pprof files (requires
-tags=pprof build).
For runtime tracing during development:
fzf --bench=2s # internal benchmark mode (counts matches/sec)
--bench=DURATION is a developer-facing flag that runs an internal
benchmark and prints results to stderr — handy when tuning --algo,
--threads, --scheme.
5.4 Backup considerations
fzf is stateless except for:
| Path | Purpose | Recovery |
|---|---|---|
--history=FILE |
Per-instance fzf search history (NOT shell history). | Plain text, one line per query. |
FZF_DEFAULT_OPTS_FILE |
Default options. | Plain text, version-controllable. |
There is no cache directory, no lockfile, no PID file.
5.5 Health check
# Basic smoke test (use in deployment automation)
echo "hello" | timeout 5 fzf --filter=hello | grep -q hello && echo OK
--filter runs in headless mode, so it is safe in CI/CD scripts.
Note
--filter=STRINGis the closest thing fzf has to a non-interactive health probe. It exits 0 on a match, 1 on no match, 2 on argument errors — making it directly usable in monitoring scripts.
5.6 Operations runbook
- [x] Verify the binary on
$PATH(which fzf) - [x] Verify the version (
fzf --version) - [x] Smoke-test the integration (
echo a | fzf --filter=a) - [ ] Source the integration script in your shell rc
(
eval "$(fzf --bash)") - [ ] Decide on
FZF_DEFAULT_COMMANDandFZF_DEFAULT_OPTS - [ ] If using
--listen, generateFZF_API_KEYand store it securely - [ ] If on macOS, install
mawkor GNU awk for fastest history widget
6. Performance
6.1 Algorithm details
FuzzyMatchV2 (default) is a modified Smith-Waterman with bonuses for
boundaries and consecutive matches. From src/algo/algo.go:
- O(N×M) time, O(M) space (where N = haystack length, M = pattern length).
- Bonuses: word boundary, consecutive match, capital-letter heads, separator transition.
- Works on Unicode codepoints; uses
unisegfor grapheme width. - Falls through to
FuzzyMatchV1when input exceeds heuristic limits (very long lines), preserving correctness with linear time.
FuzzyMatchV1 is a single-pass algorithm: O(N) time, no slab needed.
Less optimal scoring but extremely fast on large inputs.
ExactMatchNaive, ExactMatchBoundary, PrefixMatch, SuffixMatch,
EqualMatch: simple substring matchers; trigger ', 'word', ^, $,
and ^word$ syntaxes respectively.
The SIMD-accelerated indexbyte2 (asm in src/algo/indexbyte2_amd64.s,
indexbyte2_arm64.s, plus a portable Go fallback) finds the next byte
in the haystack 2-5× faster than the standard library.
6.2 Memory model
The matcher uses a per-partition slab allocator (util.Slab) with two
pools (slab16Size = 100 KiB of uint16, slab32Size = 2 KiB of
int32). Each partition gets its own slabs to avoid contention.
The ChunkList is lock-free: append-only nodes of 1024 lines, each
with its own ref count. The Reader, Matcher, and Terminal observe the
list at different positions without synchronisation.
The ChunkCache keys query results by pattern hash (chunk-local). The
cache is bounded — queryCacheMax = chunkSize / 2 = 512 — and
discarded for patterns matching too many items.
6.3 Benchmarks (representative, x86_64 single core)
| Input | Default | --algo=v1 |
--no-extended |
|---|---|---|---|
| 100 K lines | 50 ms / sub-pixel jitter | 25 ms | 30 ms |
| 1 M lines | 400 ms first match, 30 ms incremental | 200 ms | 250 ms |
| 10 M lines | 4 s first match, 200 ms incremental | 2 s | 2.5 s |
(Numbers are illustrative; rerun --bench on your hardware to compare.)
Tip
If first-match latency on huge inputs matters, use
--sync(waits for Reader to finish before showing the UI). For "see results as soon as possible", combine the default async mode with--no-sortto skip the final sort.
7. Security
7.1 Threat model
fzf is a client-side filter. The threat surface is:
- Shell injection through preview / execute / become / reload.
Placeholders are POSIX-shell quoted by default. The
{r}placeholder is raw — using it with untrusted input is unsafe. --listenHTTP/Unix-socket server. WithoutFZF_API_KEY, only localhost / Unix-socket bindings are allowed.--listen-unsaferemoves the API-key check entirely; do not use on multi-user hosts.- Malicious
FZF_DEFAULT_COMMAND. fzf execs this verbatim. Audit it the way you audit$PROMPT_COMMANDor$LD_PRELOAD.
7.2 OpenBSD hardening
src/protector/protector_openbsd.go calls pledge("stdio rpath wpath cpath fattr proc exec tty") and unveil for the working directory.
Other OSes are no-op (src/protector/protector.go).
7.3 The --listen API key
When the listen address is non-local, fzf demands FZF_API_KEY. The
server compares the supplied X-API-Key header in constant time
(crypto/subtle.ConstantTimeCompare). The unauthenticated path
(socket already in use) → (error) is informational.
Caution
--listen-unsafeis a deliberate footgun. The codebase exposes it for single-user trusted environments only. Treat it the way you would treatxhost +: never on a shared host, never over the public network, never in a container that ships logs to a third party.
7.4 Embedded shell scripts
The integration scripts (shell/key-bindings.{bash,zsh,fish} and
shell/completion.{bash,zsh,fish}) are embedded into the binary at
build time via Go's //go:embed. They are emitted unmodified by
fzf --bash / --zsh / --fish. Audit them once per release; the
content is checked into the public repo so reproducible builds verify
trivially.
7.5 No telemetry, no auto-update
fzf does not phone home, fetch update info, or open any network
socket on startup. The only network code path is --listen, and it
only opens when the user passes the flag. There is no auto-update; new
releases come via your package manager.
7.6 Known security-relevant errors
| Error | When | Mitigation |
|---|---|---|
FZF_API_KEY is required to allow remote access |
--listen non-local without FZF_API_KEY. |
Set the env var, or bind to localhost / socket. |
socket already in use: <path> |
Stale Unix socket. | rm the path; verify ownership. |
invalid api key (HTTP 401) |
Wrong X-API-Key. |
Check both client and server have the same value. |
invalid become command |
become(...) parsed an empty / illegal command. |
Audit the binding template. |
8. Developer Internals
8.1 Repository structure
.
├── main.go # entry point, embeds shell scripts + manpage
├── src/
│ ├── core.go # Reader/Matcher/Terminal coordinator
│ ├── options.go # The big options parser (~3900 lines)
│ ├── reader.go # non-blocking source reader
│ ├── chunklist.go # lock-free chunk list
│ ├── matcher.go # parallel matcher (NumCPU partitions)
│ ├── pattern.go # query parser (extended-search grammar)
│ ├── terminal.go # UI loop + actions + preview (~17K lines)
│ ├── server.go # hand-rolled HTTP server for --listen
│ ├── proxy.go # become(...) exec helper
│ ├── tmux.go # tmux popup launcher
│ ├── zellij.go # Zellij popup launcher
│ ├── history.go # internal search-history store
│ ├── ansi.go # ANSI escape parser
│ ├── cache.go # ChunkCache (per-pattern result cache)
│ ├── item.go # Item struct + transform helpers
│ ├── result.go # Result + sort criteria
│ ├── tokenizer.go # field/range tokenizer (--nth / --with-nth)
│ ├── constants.go # all the named timing/size constants
│ ├── functions.go # misc helpers
│ ├── algo/
│ │ ├── algo.go # FuzzyMatchV1/V2, Exact*, Prefix/Suffix/Equal
│ │ ├── normalize.go # Latin-script normalisation
│ │ ├── indexbyte2_amd64.s # SIMD accelerator (x86_64)
│ │ ├── indexbyte2_arm64.s # SIMD accelerator (ARM64)
│ │ └── indexbyte2_other.go # portable fallback
│ ├── tui/ # rendering back-ends (LightRenderer, tcell)
│ ├── util/ # eventbox, slab, atomicbool, chars, etc.
│ └── protector/ # OpenBSD pledge/unveil
├── shell/
│ ├── common.sh # helpers shared between integration scripts
│ ├── completion.{bash,zsh,fish}
│ ├── key-bindings.{bash,zsh,fish}
│ └── update.sh # rebuilds the per-shell scripts from common.sh
├── plugin/
│ └── fzf.vim # minimal Vim plugin (rich plugin is separate repo)
├── man/man1/
│ ├── fzf.1 # canonical man page
│ └── fzf-tmux.1
├── doc/fzf.txt # vim-help format
├── test/ # Ruby integration tests (minitest)
│ ├── runner.rb
│ ├── test_*.rb
│ ├── lib/{common.rb,common.sh,common.fish}
│ └── vim/fzf.vader
├── go.mod / go.sum / Makefile / .goreleaser.yml
├── install / install.ps1 / uninstall # bootstrap / removal scripts
├── README.md / README-VIM.md / ADVANCED.md / BUILD.md / CHANGELOG.md / SECURITY.md
└── Dockerfile
8.2 Building from source
# Standard build
go build -o fzf .
# With profiling support
go build -tags=pprof -o fzf .
# Cross-compile (example: linux/arm64)
GOOS=linux GOARCH=arm64 go build -o fzf-linux-arm64 .
# Full release matrix (uses GoReleaser)
goreleaser release --clean --skip=publish --skip=sign --snapshot
The release ldflags strip and trim the binary:
-trimpath
-s -w -X main.version=<VER> -X main.revision=<COMMIT>
The Dockerfile builds an Alpine-based image with fzf in /usr/bin.
8.3 Code patterns and conventions
- Goroutines + EventBox. Cross-component state changes are messages
through
util.EventBoxrather than shared memory. New work that needs to communicate with the Terminal goes through this bus. - Slab allocators. Hot match paths use slab pools to avoid GC. New
algorithms should follow the same pattern (see
algo.FuzzyMatchV2for the reference). - String-enum constants. Sort criteria, layouts, info styles, etc.
are typed
iotaenums with*_string.gofiles generated bystringer. Never use string comparisons in hot paths. - Build tags for platform code. Look for
//go:build ...constraints (e.g.terminal_unix.go,terminal_windows.go,proxy_unix.go/proxy_windows.go,result_x86.go/result_others.go). - Embedded assets. Shell integration and man page are embedded via
//go:embed; updating them means recompiling. Theshell/update.shhelper keeps the per-shell scripts in sync withcommon.sh.
8.4 Module map
| Class / file | Purpose | LoC (approx) |
|---|---|---|
Options (src/options.go:570) |
All parsed CLI options. The mother of all structs. | (3900 LoC parser) |
Reader (src/reader.go:21) |
Non-blocking input reader with adaptive polling. | 200 |
ChunkList (src/chunklist.go:15) |
Lock-free append-only list of 1024-line chunks. | 60 |
ChunkCache (src/cache.go:12) |
Per-pattern result cache. | 80 |
Matcher (src/matcher.go:37) |
Parallel matcher; spawns N partitions. | 350 |
Pattern (src/pattern.go:48) |
Compiled search query (term sets, AND-of-OR). | 250 |
Item (src/item.go:17) |
One input line; supports nth/with-nth/transform. | 200 |
Result (src/algo/algo.go:105) |
A scored match position. | 50 |
Terminal (src/terminal.go:249) |
Event loop, layout, rendering, action dispatch, preview, history widget, jump mode, popup. | (17 000 LoC) |
Status / StatusItem (src/terminal.go:219) |
Per-item rendered status. | 80 |
LightRenderer (src/tui/light.go:110) |
Hand-rolled xterm-style renderer. | 1500 |
TcellWindow (src/tui/tcell.go:40) |
tcell-based renderer for Windows / legacy. | 800 |
httpServer (src/server.go:40) |
--listen HTTP/1.1 server (no net/http). |
180 |
History (src/history.go:10) |
Internal search-history file. | 60 |
8.5 Testing strategy
| Layer | Tooling | Path |
|---|---|---|
| Unit | Go testing |
src/*_test.go, src/algo/*_test.go, src/tui/*_test.go, src/util/*_test.go |
| Integration | minitest (Ruby) | test/test_*.rb, runner: test/runner.rb |
| Vim plugin | vader.vim | test/vim/fzf.vader |
Run unit tests:
go test ./...
Run integration tests (require a TTY emulator and tmux):
make test
# or
ruby test/runner.rb
Common test environment variables: TMUX, FZF_DEFAULT_COMMAND,
FZF_DEFAULT_OPTS. The integration test framework drives a real fzf
process inside tmux and asserts on the rendered output.
8.6 Contributing
- Read
CHANGELOG.mdandSECURITY.mdfrom the repo root before opening a PR. - Run
gofmt -s -w .andgo vet ./.... - Run
shfmt -d shell/for shell-script formatting. - Run
bundle exec rubocopfor the Ruby tests. - Add unit tests for any new algorithm or option; add integration tests for any new key/event/binding behaviour.
- Update
man/man1/fzf.1and theUsageconstant insrc/options.goin lock-step.
8.7 Potential improvements / known tech debt
- The mega-
terminal.go(17 000 LoC) handles event loop, rendering, and action dispatch in a single file. A split intoevent_loop.go,render.go, andactions.gowould improve readability. --listen-unsafeexists for backwards compatibility; an explicit deprecation timeline (with auto-localhost fallback) would reduce foot-gun surface.- The
tcellback-end is the default on Windows; a native ConPTY renderer would reduce binary size and improve rendering speed on modern Windows Terminal. - The Ruby test suite is slow; porting the core integration tests to
Go would simplify CI. (Acknowledged in
BUILD.md.)
9. Integration Points
9.1 Shell integration
- Bash / Zsh / Fish. Source the embedded scripts:
eval "$(fzf --bash)",source <(fzf --zsh),fzf --fish | source. These provide CTRL-T, CTRL-R, ALT-C, and the**completion trigger. - PowerShell / CMD. Use the community
PSFzfmodule. Not officially supported in-tree.
9.2 Tmux
- Popup mode.
--tmux=center,80%,40%or--popup=.... Requires tmux 3.3+. fzf-tmuxwrapper. Separate shell script that ships with most packages. Provides finer-grained popup control on older tmux.
9.3 Zellij
- Popup mode.
--tmuxworks on Zellij ≥ 0.44 (the same flag branches at runtime).
9.4 Vim / Neovim
- In-tree plugin.
plugin/fzf.vimprovides:FZFand core helpers. - Companion plugin. The richer plugin (
:Files,:Buffers,:Rg,:Lines, etc.) is the separatejunegunn/fzf.vimrepository. - Embedding fzf in another plugin. Spawn fzf with
vim.systemor:!and parse stdout. The--expect=KEYSand--print-queryflags produce well-defined output that's easy to parse.
9.5 Editors and IDEs
- VS Code, IntelliJ. Community extensions wrap fzf as a quick-open
back-end. Use
--listenfor low-latency repeated invocations. - Emacs.
helm-fzf,consult-fzf, and similar packages. - Termux (Android). Same fzf binary; integrate via Termux's built-in shell integration.
9.6 The --listen HTTP / Unix-socket API
External processes drive fzf actions over HTTP:
POST / Body: action chain ("change-query(foo)+reload(...)")
GET /?limit=N&offset=M Returns JSON: {"items": [...], "selection": [...], "current": ...}
Headers: X-API-Key: <FZF_API_KEY> (mandatory when non-local).
Content type: text/plain (request) / application/json (response).
Max body length: 1 MiB. Read deadline: 10 s.
Note
The server is intentionally minimal — no
net/http, no TLS, no session state. For TLS, front it with a reverse proxy such ascaddy reverse_proxy localhost:6266over a TLS-terminated listener.
9.7 Programmatic invocation patterns
| Use case | Pattern |
|---|---|
| Quick pick | `result=$(some-source |
| Multi-pick | `mapfile -t result < <(some-source |
| Headless filter | `some-source |
| Two-step (query first, then act) | `query=$(printf '\0' |
| Editor launcher | fzf --bind 'enter:become(${EDITOR} {})' |
| Live grep | (see User Guide §8.2 — --disabled + change:reload) |