Open-Source Wikis

/

fzf

/

Systems

/

Core coordinator

junegunn/fzf

Core coordinator

Purpose

src/core.go owns the lifecycle of an interactive fzf run. The Run function constructs the Reader, Matcher, and Terminal, kicks off their goroutines, and spins an event loop that drains a util.EventBox until the user accepts, aborts, or the input runs out.

It also handles the non-interactive --filter mode (which short-circuits to a single scan and print) and the benchmark mode (--bench).

Directory layout

src/
├── core.go         # Run() and the event-loop coordinator
├── constants.go    # ExitOk/ExitError/..., chunkSize, slab sizes, poll intervals
├── functions.go    # Small inline helpers used by core
└── proxy.go        # Process-mode wrapper invoked before Run when applicable

Key abstractions

Symbol File Description
Run(opts *Options) (int, error) src/core.go Top-level entry point called from main.go. Returns an exit code and an optional error.
revision src/core.go (major, minor) counter bumped on input mutations; used to invalidate match caches.
EvtReadNew, EvtReadFin, EvtSearchNew, EvtSearchProgress, EvtSearchFin, EvtQuit src/core.go The events drained by the coordinator.
searchRequest src/core.go Payload sent from Terminal to Matcher when query/sort/nth/with-nth/header-lines/denylist changes.
quitSignal src/core.go Wraps the exit code and error when the Terminal asks to terminate.
fitpad src/core.go, src/terminal.go Pair (fit, pad) describing the height and pad fzf should occupy; used for --height adaptive mode.

How it works

graph TD
    RUN[Run] --> POPUP{Popup or<br/>winpty?}
    POPUP -- yes --> PROXY[runProxy]
    POPUP -- no --> POST[postProcessOptions]
    POST --> CHUNKS[NewChunkList]
    POST --> READER[NewReader]
    POST --> TERMINAL[NewTerminal]
    POST --> MATCHER[NewMatcher]
    POST --> LOOP[Coordinator loop]
    LOOP --> WAIT[eventBox.Wait]
    WAIT --> READ[Handle Read events:<br/>snapshot, reset matcher]
    WAIT --> SEARCH[Handle Search events:<br/>update list, update progress]
    WAIT --> QUIT[Handle EvtQuit:<br/>exit]
    LOOP -. delay .-> LOOP

The body of Run reads almost like a script:

  1. Pre-launch checks. opts.useTmux(), opts.useZellij(), and needWinpty(opts) route to runTmux, runZellij, or runWinpty respectively before any goroutines start. See Process modes.
  2. Post-process options. postProcessOptions validates and finalizes the parsed options.
  3. Construct primitives. NewChunkCache, NewChunkList, NewReader, NewMatcher, NewTerminal, util.NewEventBox.
  4. Kick off Reader. go reader.ReadSource(...) runs the configured input source in its own goroutine. Read source can be: a Go channel passed via opts.Input, a child process (FZF_DEFAULT_COMMAND or --bind=start:reload), the built-in walker, or stdin.
  5. Filter mode short-circuit. If opts.Filter != nil, the coordinator runs a single scan/print and returns immediately. Streaming filter (when --no-sort and --no-tac and !--sync) runs the matcher line-by-line as items are read, never building a chunk list snapshot for sorting.
  6. Start Matcher and Terminal. go matcher.Loop() and go terminal.Loop() then enter the event loop.
  7. Coordinator loop. Drains eventBox.Wait. On each event:
    • EvtReadNew / EvtReadFin → snapshot the chunk list, update the count, possibly reset the matcher.
    • EvtSearchNew → apply mutations from searchRequest (nth, with-nth, header-lines, reload command, denylist), bump revision if needed, then matcher.Reset(...).
    • EvtSearchProgress → forward the percentage to the terminal.
    • EvtSearchFin → push the match list to the terminal; handle the deferred-start case for --height=~, --select-1, --exit-0, --sync.
    • EvtQuit → propagate the exit code and break the loop.
  8. Adaptive sleep. While reading, the loop sleeps for min(ticks * coordinatorDelayStep, coordinatorDelayMax) to avoid spamming CPU when items arrive in tiny bursts.
  9. Cleanup. defer util.RunAtExitFuncs() runs registered cleanup hooks (temp files, fifos, sockets) before Run returns.

Integration points

  • From main.go: the binary calls fzf.Run(options) once, prints any error to stderr, and exits with the returned code.
  • To Reader (src/reader.go): the coordinator owns the Reader's lifecycle. It can call reader.terminate() to kill the running command and start a new one (for reload and reload-sync).
  • To Matcher (src/matcher.go): matcher.Reset(snapshot, query, ...) is called on every relevant event. matcher.Stop() is deferred for clean shutdown.
  • To Terminal (src/terminal.go): terminal.UpdateCount, terminal.UpdateHeader, terminal.UpdateProgress, terminal.UpdateList all flow from the coordinator. The coordinator never reads back from the terminal except via the event box.
  • runProxy in src/proxy.go calls back into Run indirectly by re-execing the binary with the popup or winpty wrapper.

Entry points for modification

  • Adding a new event: declare it in the EventType block of src/core.go, add a case in the event-loop switch, and a producer somewhere (Reader, Matcher, Terminal). Make sure the Watch/Unwatch calls are updated if the event needs to be coalesced.
  • Adding state that should reset on reload: thread it through restart (the inner closure in Run) and call bumpMajor on the revision.
  • Changing the deferred-start behavior: the determine, deferred, and heightUnknown variables coordinate the --select-1 / --exit-0 / --sync / --height=~ cases. They are tightly coupled — read the comments in core.go carefully before tweaking.

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

Core coordinator – fzf wiki | Factory