fzf — Interfaces#

Interface catalog#

fzf defines only two explicit Go interfaces, both in the tui package. The rest of the codebase communicates through concrete structs, callbacks, and channels. This extreme minimalism is intentional: fzf is a single tightly-coupled binary, not a library.


Renderer#

  • Package: github.com/junegunn/fzf/src/tui
  • File: src/tui/tui.go:777
  • Methods:
    DefaultTheme() *ColorTheme
    Init() error
    Resize(maxHeightFunc func(int) int)
    Pause(clear bool)
    Resume(clear bool, sigcont bool)
    Clear()
    RefreshWindows(windows []Window)
    Refresh()
    Close()
    PassThrough(string)
    NeedScrollbarRedraw() bool
    ShouldEmitResizeEvent() bool
    Bell()
    HideCursor()
    ShowCursor()
    GetChar(cancellable bool) Event
    CancelGetChar()
    Top() int
    MaxX() int
    MaxY() int
    Size() TermSize
    NewWindow(top int, left int, width int, height int, windowType WindowType,
              borderStyle BorderStyle, erase bool) Window
  • Purpose: Abstracts the entire terminal rendering backend. Defines the full contract for terminal lifecycle (Init/Close/Pause/Resume), geometry queries (Top/MaxX/MaxY/Size), rendering operations (RefreshWindows/Refresh/Clear/Bell), input reading (GetChar/CancelGetChar), and window factory (NewWindow). The Terminal component depends exclusively on this interface, never on a concrete renderer type.
  • Implementations:
    • *LightRenderer (src/tui/light.go:110) — Direct termios + ANSI escape code implementation. No third-party TUI library. Default for all platforms. Constructed via NewLightRenderer(...) which returns a Renderer.
    • *FullscreenRenderer (src/tui/tcell.go:17) — Backed by github.com/gdamore/tcell/v2. Opt-in via -tags tcell build tag. Used when broader terminal compatibility is needed.
  • Design quality: Broad (22 methods) but coherent — every method maps to a genuine terminal capability. Does not fully satisfy ISP in the purist sense, but in practice all renderers must implement every capability, so splitting the interface would add no value. The factory method NewWindow returning Window is a classic Abstract Factory embedded into the Renderer itself, keeping the pair tightly bound.

Window#

  • Package: github.com/junegunn/fzf/src/tui
  • File: src/tui/tui.go:806
  • Methods:
    Top() int
    Left() int
    Width() int
    Height() int
    DrawBorder()
    DrawHBorder()
    Refresh()
    FinishFill()
    X() int
    Y() int
    EncloseX(x int) bool
    EncloseY(y int) bool
    Enclose(y int, x int) bool
    Move(y int, x int)
    MoveAndClear(y int, x int)
    Print(text string)
    CPrint(color ColorPair, text string)
    Fill(text string) FillReturn
    CFill(fg Color, bg Color, ul Color, attr Attr, text string) FillReturn
    LinkBegin(uri string, params string)
    LinkEnd()
    Erase()
    EraseMaybe() bool
    SetWrapSign(string, int)
  • Purpose: Represents a rectangular sub-region of the terminal. Defines the drawing API for text, styled text, borders, hyperlinks, and cursor movement. Used by Terminal to paint the prompt, item list, header, preview, and scrollbar as separate windows without knowing which renderer backs them.
  • Implementations:
    • *LightWindow (src/tui/light.go:144) — ANSI-escape-code backed. Constructed by (*LightRenderer).NewWindow(...).
    • *TcellWindow (src/tui/tcell.go) — tcell-backed. Constructed by (*FullscreenRenderer).NewWindow(...).
  • Design quality: Well-sized for its role. The 24 methods cover a non-trivial 2D painting API but are all genuinely needed for the variety of UI regions fzf renders. CFill (styled fill) and CPrint (styled print) are the workhorse methods; the rest handle geometry, borders, and hyperlinks.

Interface patterns#

  • Size distribution: 2 interfaces total — 22 methods (Renderer) and 24 methods (Window). Both are intentionally broad because they describe complete, indivisible abstractions (a terminal renderer and a drawable window). No micro-interfaces.
  • Embedding: Neither interface uses embedding. They are standalone definitions. Renderer and Window are coupled only through Renderer.NewWindow() returning Window — an implicit pairing enforced by the Abstract Factory pattern, not by interface embedding.
  • Implicit satisfaction: Interfaces are defined by the consumer (tui package, used by Terminal). Implementations (LightRenderer, FullscreenRenderer) satisfy them implicitly, as is idiomatic Go. The NewLightRenderer and NewFullscreenRenderer constructors return Renderer directly, making the contract explicit at construction time.
  • stdlib interfaces used: io.Reader appears as a parameter type in reader.go:153 (func (r *Reader) feed(src io.Reader)) — the one instance of a stdlib interface consumed directly. No io.Writer, fmt.Stringer, or sort.Interface implementations are defined. The project does not export any public library API and so has no incentive to satisfy stdlib contracts.

Key abstractions#

The two interfaces are the entire public contract surface of fzf’s internal abstraction system:

  1. Renderer — The most architecturally significant interface in the project. It is the sole seam between Terminal (which drives the interactive UI) and the underlying terminal I/O mechanism. The dual-backend design (LightRenderer vs FullscreenRenderer) is enabled entirely by this interface, and the build-tag selection (-tags tcell) makes the choice transparent to all callers.

  2. Window — The drawing primitive returned by Renderer.NewWindow. All five visual regions fzf renders (prompt, item list, header, preview, footer) are Window values. The Terminal’s rendering code targets the Window interface exclusively, making it backend-agnostic.

Everything else in fzf — ChunkList, Matcher, Reader, Terminal, EventBox, Pattern, Merger — is a concrete struct wired together manually in Run(). No service interfaces, no repository pattern, no injected dependencies beyond the two TUI abstractions.

Interface-driven extensibility#

fzf uses interfaces only for the TUI backend swap, not for general extensibility. There is no plugin system, no hook interface, no extension point for custom matchers or item sources. Extensibility is achieved entirely through the HTTP control plane (--listen): external processes POST action strings to a running fzf instance, driving it via its action language rather than via Go interfaces.

This is a deliberate architectural choice. fzf is a CLI tool, not a library. The interface count (2) reflects the minimum abstraction necessary: one seam that needed to exist (the renderer backend) and zero seams added speculatively. The result is a codebase that is easy to read, easy to trace, and impossible to misuse as a framework.