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 viaNewLightRenderer(...)which returns aRenderer.*FullscreenRenderer(src/tui/tcell.go:17) — Backed bygithub.com/gdamore/tcell/v2. Opt-in via-tags tcellbuild 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
NewWindowreturningWindowis 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
Terminalto 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) andCPrint(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.
RendererandWindoware coupled only throughRenderer.NewWindow()returningWindow— an implicit pairing enforced by the Abstract Factory pattern, not by interface embedding. - Implicit satisfaction: Interfaces are defined by the consumer (
tuipackage, used byTerminal). Implementations (LightRenderer,FullscreenRenderer) satisfy them implicitly, as is idiomatic Go. TheNewLightRendererandNewFullscreenRendererconstructors returnRendererdirectly, making the contract explicit at construction time. - stdlib interfaces used:
io.Readerappears as a parameter type inreader.go:153(func (r *Reader) feed(src io.Reader)) — the one instance of a stdlib interface consumed directly. Noio.Writer,fmt.Stringer, orsort.Interfaceimplementations 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:
Renderer— The most architecturally significant interface in the project. It is the sole seam betweenTerminal(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.Window— The drawing primitive returned byRenderer.NewWindow. All five visual regions fzf renders (prompt, item list, header, preview, footer) areWindowvalues. The Terminal’s rendering code targets theWindowinterface 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.