How fzf Handles ANSI Color Codes with the `--ansi` Option
When you enable the --ansi flag, fzf parses ANSI escape sequences in input to strip them for fuzzy matching while preserving the color information to re-apply during rendering.
The junegunn/fzf repository implements sophisticated ANSI processing that allows you to search through colorized text without the escape sequences interfering with the fuzzy finder algorithm. This feature enables seamless integration with tools like git, ls, or syntax highlighters that output color codes, maintaining visual clarity while ensuring accurate text matching.
The ANSI Processing Pipeline
When --ansi is passed on the command line, fzf activates a multi-stage pipeline that separates display formatting from search logic.
Option Parsing and Activation
The flag first sets opts.Ansi = true during initialization. According to the source code in src/options.go#L2831-L2834, this boolean flag determines whether the input reader should invoke the color extraction logic. Once enabled, every incoming line passes through a specialized parser before reaching the matching engine.
Extracting Colors with extractColor
The core parsing logic resides in the extractColor function defined in src/ansi.go#L259-L340. This function performs three critical operations:
- Sequence Detection: Uses
nextAnsiEscapeSequenceto locate escape sequences matching\x1b[...mpatterns - State Interpretation: Calls
interpretCodeto create anansiStateobject describing foreground colors, background colors, and text attributes like underline or bold - Text Trimming: Builds a stripped version of the line containing only the visible characters for fuzzy matching
As the scanner processes each line, it builds a slice of ansiOffset structures. Each entry records the start and end positions ([2]int32{start, end}) alongside the corresponding ansiState, creating a precise map of where color changes occur in the visible text.
State Management and Line Endings
Special handling ensures background colors persist across display operations. When the parser encounters a newline character, any pending full-line background color is stored as a special marker. This logic in src/ansi.go#L308-L322 guarantees that background highlighting spans the complete line width in the terminal interface, not just the text content.
Rendering and Word-Wrap Handling
The terminal layer consumes the trimmed string and ANSI offsets separately. In src/terminal.go#L4755-L4860, the Terminal struct re-applies the stored color states when drawing lines to the screen. For wrapped lines, the wordWrapAnsiLine function in src/terminal.go#L4211-L4228 receives both the raw content and offset data, ensuring that color sequences remain intact across line breaks without breaking the terminal display.
Practical Usage Examples
The --ansi option integrates seamlessly with standard Unix tools that produce colorized output.
Basic Colorized Input
printf '\e[31mRed\e[0m\n\e[32mGreen\e[0m\n\e[34mBlue\e[0m\n' | \
fzf --ansi --prompt='Pick a colour: '
In this example, the pipe feeds text containing literal escape sequences (\e[31m for red, etc.). The fuzzy matcher operates on the plain text (Red, Green, Blue) while the preview displays the original colors.
Git Integration with Preview
git --no-pager log --oneline --decorate=short | \
fzf --ansi \
--preview='git --no-pager show --color=always {+1}' \
--preview-window=up:60%
This command searches through colorized git history while maintaining syntax highlighting in the preview pane. The --color=always flag forces git to output ANSI codes, which fzf preserves through the extractColor offset mechanism.
Key Source Files and Functions
Understanding the codebase structure helps when debugging ANSI display issues:
| File | Role |
|---|---|
src/options.go |
Parses --ansi / --no-ansi flags into Opts.Ansi |
src/core.go |
Invokes extractColor during input reading when ANSI mode is active |
src/ansi.go |
Contains the core parser: extractColor, nextAnsiEscapeSequence, interpretCode, and ansiState management |
src/item.go |
Provides Item.AsString(stripAnsi bool) for the matching pipeline |
src/terminal.go |
Handles rendering via wordWrapAnsiLine and applies stored offsets to the UI |
Summary
- Flag activation: The
--ansioption setsopts.Ansi = truein [src/options.go](https://github.com/junegunn/fzf/blob/master/src/options.go#L2831), triggering the color processing pipeline. - Separation of concerns: The
extractColorfunction strips escape sequences for matching while storing color offsets in [src/ansi.go](https://github.com/junegunn/fzf/blob/master/src/ansi.go#L259). - Accurate mapping:
ansiOffsetstructures using[2]int32pairs map visible character positions to their original ANSI states. - Terminal rendering: The display layer in [
src/terminal.go](https://github.com/junegunn/fzf/blob/master/src/terminal.go#L4755) re-applies colors using stored offsets, handling word wrapping viawordWrapAnsiLine.
Frequently Asked Questions
Does fzf match against the ANSI escape sequences or just the visible text?
Fzf matches only against the visible text. The extractColor function creates a trimmed string containing no escape sequences, which is what the fuzzy matching algorithm processes. The original ANSI codes are stored separately as offsets and do not participate in the search scoring.
What happens to background colors that span entire lines?
When parsing reaches a newline character, fzf stores pending full-line background colors as special markers. This ensures that background highlighting extends to the full terminal width during rendering, as implemented in src/ansi.go#L308-L322.
Can I use --ansi with the preview window?
Yes, the --ansi flag affects both the main list and preview content. The preview receives the original line with ANSI codes preserved through the offset mechanism, allowing commands like git show --color=always to display with full syntax highlighting inside the preview pane.
How does fzf handle malformed or incomplete ANSI sequences?
The nextAnsiEscapeSequence scanner in [src/ansi.go](https://github.com/junegunn/fzf/blob/master/src/ansi.go#L259) looks for valid \x1b[ sequences followed by m terminators. Malformed sequences that do not match this pattern are treated as literal text and passed through to the matching engine without color state changes.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →