Open-Source Wikis

/

fzf

/

How to contribute

/

Patterns and conventions

junegunn/fzf

Patterns and conventions

This page documents the recurring code patterns that show up across the Go core. Read this before adding a new feature so the new code fits the existing seams.

Event coordination via EventBox

fzf's primary message bus is util.EventBox (src/util/eventbox.go). It's a sync.Cond-backed map of EventType → any. Producers call Set(evt, value). Consumers call Wait(callback) and process the event map under the lock, then Clear() the events they handled.

Three event boxes run concurrently:

Owner Consumer Events
Reader (src/reader.go) Run coordinator (src/core.go) EvtReadNew, EvtReadFin
Terminal (src/terminal.go) Run coordinator EvtSearchNew, EvtQuit
Matcher (src/matcher.go) Run coordinator EvtSearchProgress, EvtSearchFin

When you add a feature that needs to coordinate between Reader, Matcher, and Terminal, route it through these events instead of adding direct method calls. That keeps the goroutines decoupled.

Bumping the input revision

src/core.go defines a revision struct (major, minor). Whenever input changes (a reload action restarts the reader, or change-nth/change-with-nth rewrites items in place), bumpMajor or bumpMinor is called. The matcher's cached results are keyed on the revision, so stale results are discarded automatically. New code that mutates inputs must bump the revision (search for inputRevision.bumpMinor() for examples).

The action engine

Every key press, mouse event, named event (change, focus, result, load, start, jump, one), and HTTP --listen command flows through the same machinery in src/terminal.go:

  1. Parsed --bind definitions live in the Options.Keymap map.
  2. The terminal loop reads the next event, looks it up, and walks the chained list of action structs.
  3. Each action has an actionType (200+ entries in src/actiontype_string.go) and an optional argument string.
  4. executeCommand / executeBecome / transformValue handle the actions that shell out.

When adding a new action:

  • Add a constant to the actionType enum in src/options.go.
  • Run make generate (or go generate ./...) to regenerate src/actiontype_string.go.
  • Wire parsing in parseAction (also src/options.go).
  • Implement the behavior in src/terminal.go's big action switch.
  • Update the man page (man/man1/fzf.1) and the Usage constant.

Placeholders

src/terminal.go defines a placeholder regex covering {}, {q}, {n}, {1..3}, {+f}, {fzf:query}, {fzf:action}, {fzf:prompt}, etc. Substitution happens through replacePlaceholder and parsePlaceholder. Any new placeholder must be added to the regex and to those two functions, with a corresponding test in src/terminal_test.go and test/test_preview.rb / test/test_core.rb.

Error handling

  • Library code returns errors. src/core.go translates errors into exit codes via the ExitOk / ExitNoMatch / ExitError / ExitInterrupt / ExitBecome constants in src/constants.go.
  • The main function in main.go only ever calls os.Exit through the small exit(code, err) helper. Don't sprinkle os.Exit calls elsewhere — defer util.RunAtExitFuncs() (in src/core.go) needs to fire so registered cleanup hooks (like os.Remove(temp)) actually run.
  • Renderer cleanup is handled by the Renderer.Close() defer in Terminal.Loop, which restores the terminal to a sane state on panic too. Don't bypass it.

--no-X flags and "flag inversion"

Most boolean flags have a --no-X pair (e.g. --bold / --no-bold, --mouse / --no-mouse, --ansi / --no-ansi). Both forms parse to the same field via the optionalNumeric and validateBoolean helpers in src/options.go. When adding a boolean, follow the same pattern so users can override FZF_DEFAULT_OPTS from the command line.

Lazy / transform-* values

A subset of options can be either a literal value or a shell command whose output is the value:

  • --prompt / transform-prompt
  • --header / transform-header
  • --query / transform-query
  • --nth / change-nth
  • --with-nth / change-with-nth

The transform path runs the shell command, captures stdout, and treats the output as the new value. Implementations parse a leading transform: prefix or distinguish via the action name. New "transformable" options should follow the same convention, and must mark the change with the appropriate revision bump as described above.

Concurrency rules

  • The ChunkList is the only shared mutable state during a search. It uses an internal sync.Mutex. Always go through Push / Snapshot / ForEachItem; never poke at chunks directly.
  • The Matcher partitions a snapshot across runtime.NumCPU() goroutines and uses pre-allocated Slabs in src/util/slab.go. Avoid per-scan heap allocation — that is the single biggest reason fzf is fast on millions of items.
  • Terminal owns the screen. The Reader and Matcher must never write to the renderer.
  • Cancellation: Matcher.CancelScan and Terminal.PauseRendering / ResumeRendering exist precisely so an action can pause the matcher and renderer while it mutates items. Use them when adding any operation that touches items in place.

Build tags

  • tcell — uses src/tui/tcell.go instead of src/tui/light.go. Keep both renderers feature-equivalent. New rendering features need to land in both files (or be guarded explicitly).
  • pprof — flips the implementation of profiling flags between src/options_pprof.go and src/options_no_pprof.go. Don't reference profiling APIs outside those files.
  • The Windows path uses _windows.go suffixed files (e.g. src/util/util_windows.go, src/tui/light_windows.go, src/winpty_windows.go). Match the existing split when adding OS-specific code.

Test conventions

  • Go tests live next to their subject and use the standard testing package, no third-party assertion library.
  • Ruby integration tests use Minitest; the Tmux helper in test/lib/ is the only reasonable way to assert on TUI behavior.
  • When adding a flag, add at least one Ruby integration test exercising the user-visible behavior. Unit tests on src/options_test.go cover parsing, but rendering needs the tmux harness.

Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.

Patterns and conventions – fzf wiki | Factory