fzf — Structure#
Layout pattern#
Custom / Flat-with-subpackages
fzf departs from the standard Go layout (cmd/, internal/, pkg/). The single binary’s main.go lives at the repo root, and all core logic is placed in a flat src/ package (package fzf). Sub-concerns (algo, tui, util, protector) get their own sub-packages under src/, but there is no cmd/ directory, no internal/, and no pkg/. This is a deliberately minimal, tool-oriented layout that prioritises simplicity over Go convention.
Directory map#
fzf/
├── main.go # Single entry point; wires options → fzf.Run(), embeds shell scripts
├── go.mod / go.sum # Module definition
├── Makefile # Build, test, release, lint targets
├── Dockerfile # Integration-test container (Ruby test runner + Go build)
├── install / install.ps1 # Shell/PowerShell install scripts (not Go)
│
├── src/ # Core package (package fzf) — all business logic
│ ├── algo/ # Fuzzy matching algorithm (Smith-Waterman-like, SIMD variants)
│ ├── tui/ # Terminal UI abstraction (light renderer + tcell backend)
│ ├── util/ # Low-level utilities (eventbox, slab, atomics, chars)
│ └── protector/ # OS-specific process protector (OpenBSD pledge(2))
│
├── shell/ # Shell integration scripts (bash/zsh/fish key-bindings & completion)
├── bin/ # Compiled binary destination + helper scripts (fzf-tmux, fzf-preview.sh)
├── plugin/ # Vim/Neovim plugin (fzf.vim — single Vimscript file)
├── man/man1/ # Man pages (fzf.1, fzf-tmux.1)
├── doc/ # Extra documentation (CHANGELOG mirror, screenshots)
├── test/ # Integration tests (Ruby minitest runner + lib helpers + vim tests)
└── .github/workflows/ # CI: linux.yml, macos.yml, winget.yml, codeql, depsreview, typosEntry points#
| File | Binary | What it does |
|---|---|---|
main.go | fzf | Parses CLI options, prints shell integration scripts on request (--bash/--zsh/--fish/--man), then calls fzf.Run(options) for normal operation |
There is exactly one binary. fzf-tmux (in bin/) is a shell script wrapper, not a Go binary.
Package organization#
github.com/junegunn/fzf/src(package fzf) — The monolithic core. Contains the reader (stdin/file/command), matcher (pattern + chunk-based parallel search), merger (result aggregation), terminal (interactive TUI event loop), server (HTTP event-action API), options parser, and all supporting types (Item,Result,Chunk, etc.). No internal/ or public/private split — everything insrc/is a single package exported at the module level.github.com/junegunn/fzf/src/algo— Fuzzy matching engine. Implements Smith-Waterman-inspired dynamic programming with bonus scoring for camelCase/path boundaries. Has assembly-backed SIMD helpers (indexbyte2_amd64.s,indexbyte2_arm64.s) and platform fallbacks (indexbyte2_other.go).github.com/junegunn/fzf/src/tui— Terminal UI abstraction layer. Defines theRendererinterface and ships two implementations:LightRenderer(direct termios/ANSI; default) andTcellRenderer(tcell-backed; selected with-tags tcell). Platform splits:light_unix.go/light_windows.go,ttyname_unix.go/ttyname_windows.go.github.com/junegunn/fzf/src/util— Shared low-level utilities:Chars(rune/byte string wrapper),Slab(memory pool for algo),EventBox(thread-safe multi-event notification bus),AtomicBool,atexitregistration, and platform-split I/O helpers.github.com/junegunn/fzf/src/protector— Minimal OS security hardening. On OpenBSD, callspledge(2)to restrict syscalls; on other platforms is a no-op (protector.go).Layering: The dependency direction is:
main.go→src→src/tui,src/algo,src/util;src/protectoris used only bymain.go. There is no circular dependency. Thesrcpackage is the heaviest layer and owns all cross-cutting state. Sub-packages are kept deliberately thin and algorithm/infrastructure-focused.
Build system#
- Build tool:
make(primary),goreleaser(release distribution) - Key targets:
make all/make target/<BINARY>—go buildfor the host architecture, producingtarget/fzf-<os>_<arch>make test—go testacross all four Go packagesmake itest— Ruby integration test runner (test/runner.rb)make bench— Go benchmark suite insrc/make build—goreleaser build --snapshot(multi-arch local build)make release— Full goreleaser release to GitHub (requiresGITHUB_TOKEN)make lint—gofmt,rubocop, shell script lintingmake install— Copies built binary tobin/fzfmake docker/make docker-test— Build and run the integration test container
- Cross-compilation: Makefile defines targets for 10 architectures (386, amd64, arm5–8, s390x, ppc64le, riscv64, loong64);
goreleasermanages the full release matrix. - Version injection:
-ldflags "-X main.version=... -X main.revision=..."bakes git tag and commit hash into the binary. - Docker: Yes — single-stage
Dockerfilebased onrubylang/ruby:3.4.1-noble, installs Go and runs the Ruby integration test suite. Not a production image; purely for CI-equivalent local testing.
Notable structural decisions#
Root-level
main.goinstead ofcmd/: By placingmain.goat the root, fzf keeps the project feeling like a single-purpose tool rather than a multi-binary application. This also meansgo install github.com/junegunn/fzf@latestworks without a subpath — a significant usability win for end-users.Single monolithic
src/package: All interactive session logic — reader, matcher, terminal, server, options — lives in one package. This avoids unnecessary abstraction churn and makes the codebase easier to navigate, at the cost of package-level encapsulation. For a project of this size (~57 non-test source files) it is an appropriate trade-off.Dual TUI backend via build tags: The
tui/package ships aLightRenderer(default, minimal deps) and aTcellRenderer(selected via-tags tcell). This lets the default binary stay smaller and faster while still supporting the full tcell feature set for users who need it.Shell scripts embedded via
//go:embed: All shell integrations (key-bindings and completions for bash/zsh/fish) and the man page are embedded into the binary at compile time. Users getfzf --bash,fzf --zsh,fzf --fishsub-commands with no external file dependencies — a clean zero-install-step design.Ruby integration tests alongside Go unit tests: The
test/directory holds a Ruby minitest suite that drives a real terminal through tmux. This is an intentional choice to test the full interactive UX (keystrokes, rendering, selection) in ways that Go’stestingpackage cannot easily do. TheDockerfilemakes this reproducible.Platform splits without CGo: Cross-platform terminal handling is achieved entirely through Go build constraints (
_unix.go/_windows.gofile suffixes and//go:buildtags), keeping the binary CGo-free and trivially cross-compilable.