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 nextAnsiEscapeSequence to locate escape sequences matching \x1b[ ... m patterns
  • State Interpretation: Calls interpretCode to create an ansiState object 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

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →