How fzf Handles Word Wrapping with the --wrap Option: Implementation Deep Dive

fzf implements line wrapping through the --wrap option by preprocessing logical lines into visual sub-lines before rendering, using (*Chars).Lines in src/util/chars.go for character-level breaks and back-tracking to the last space or tab for word-level breaks (--wrap=word), while subtracting the wrap-sign width from continuation lines.

The junegunn/fzf fuzzy finder provides flexible text wrapping to prevent long strings from being truncated in the terminal window. When enabled via the --wrap option, fzf processes text through a multi-stage pipeline that respects ANSI escape sequences, tab stops, and user-configurable wrap signs before rendering the final output to the screen.

Understanding the --wrap Option and Its Modes

The wrapping behavior is controlled by three related command-line options parsed in src/options.go (lines 610–618):

Option Meaning Default
--wrap[=MODE] Enable wrapping. MODE can be char (default) or word. disabled (false)
--wrap-sign=STR String printed at the beginning of each continuation line. "↳ "
--wrap-word Shortcut for --wrap=word. false

When wrapping is disabled (the default), lines overflow and the rightmost content is hidden. When enabled, fzf splits lines into visual sub-lines before rendering, accounting for the wrap-sign width on continuation lines.

Character-Level Wrapping (--wrap or --wrap=char)

In the default character mode, fzf cuts the line at the exact column where the text would overflow. The algorithm in src/util/chars.go repeatedly calls RunesWidth to determine how many runes fit within the available columns (wrapCols), emitting slices until the line is consumed.

Word-Level Wrapping (--wrap=word or --wrap-word)

When word mode is enabled, the algorithm back-tracks from the overflow point to the last space or tab character before breaking. If no whitespace exists (a single long word), it falls back to character-level breaking at that position. This logic is implemented in the Lines method of src/util/chars.go (lines 298–327), where breakIdx tracks the last whitespace position.

Core Implementation in fzf Source Code

Option Parsing (src/options.go)

The command-line parser fills the Options struct with wrapping preferences. Lines 610–618 handle the --wrap and --wrap-word flags:

// Default values
Wrap:     false,
WrapWord: false,

// Parsing
case "--wrap":
    opts.Wrap = true
    opts.WrapWord = false
case "--wrap-word":
    opts.Wrap = true
    opts.WrapWord = true

The wrap sign is stored in opts.WrapSign with a default value of "↳ ".

Text Processing Logic (src/util/chars.go)

The (*Chars).Lines method (lines 298–327) performs the actual splitting. It takes parameters including wrapCols (available width), wrapSignWidth (width of the continuation marker), and wrapWord (boolean flag).

The algorithm:

  1. Iterates through runes, tracking cumulative width using RunesWidth
  2. When width exceeds wrapCols, determines the break point
  3. If wrapWord is true, back-tracks to the last space/tab (breakIdx)
  4. Emits the substring as a visual line
  5. Subtracts wrapSignWidth from wrapCols for subsequent continuation lines

ANSI-Aware Wrapping for Previews (src/terminal.go)

For the preview window, which may contain ANSI color codes, Terminal.wordWrapAnsiLine (lines 4211–4240) handles wrapping while preserving escape sequences. It uses the same width calculation logic but operates on lines that may contain non-printing ANSI characters, ensuring that color codes do not interfere with width calculations.

UI Rendering (src/tui/tcell.go)

The final rendering occurs in renderWrapSign (lines 945–971), which draws the wrap sign at the beginning of continuation lines. The sign is printed with the appropriate style (tui.ColPreview for preview windows, tui.ColList for the main list).

Practical Usage Examples

Basic Character-Wrap

Enable default character-level wrapping to prevent truncation:

printf '%s\n' "aaaaaaaaaaaaaaaaaaaa" "bbbbbbbbbbbbbbbbbbbb" | fzf --wrap

Visual output:


aaaaaaaaaaaaaaaaa↳ a
bbbbbbbbbbbbbbbbb↳ b

Word-Wrap with Custom Sign

Use word-breaking and a custom continuation marker:

printf '%s\n' "this is a long line that should wrap nicely" | \
    fzf --wrap=word --wrap-sign='↪ '

Result:


this is a long line↪ that should wrap nicely

Preview Window Wrapping

Enable wrapping in the preview pane:

printf 'file1\nfile2\n' | fzf \
    --preview 'head -n 20 {}' \
    --preview-window=right:50%:wrap

The preview window inherits wrap settings and uses wordWrapAnsiLine to handle colored output.

Runtime Toggling and Configuration

Users can toggle wrapping without restarting fzf:

  • Ctrl-/: Toggles wrapping on/off (character mode)
  • Ctrl-Shift-/ or Alt-/: Toggles between character and word wrap modes

These bindings trigger actToggleWrap and actToggleWrapWord in src/terminal.go (lines 844–845 and 6879–6883), which invert the boolean flags on the live Terminal instance and trigger a full redraw.

Summary

  • fzf implements line wrapping through the --wrap option, processing text in src/util/chars.go before rendering.
  • Character mode (--wrap or --wrap=char) breaks lines at exact column limits.
  • Word mode (--wrap=word or --wrap-word) back-tracks to the last whitespace before breaking, falling back to character breaks for single long words.
  • The wrap sign (default ↳ ) is prepended to continuation lines, with its width subtracted from available columns as implemented in src/tui/tcell.go.
  • Preview windows use Terminal.wordWrapAnsiLine in src/terminal.go to handle ANSI escape sequences while wrapping.
  • Users can toggle modes at runtime using Ctrl-/ and Ctrl-Shift-/ without restarting the application.

Frequently Asked Questions

What is the default wrap sign in fzf?

The default wrap sign is the string "↳ " (a rightwards arrow followed by a space). This is defined in the options parsing code in src/options.go. You can customize it using the --wrap-sign flag, for example --wrap-sign='↪ '.

How do I enable word wrapping instead of character wrapping?

Use the --wrap=word option or the shorthand --wrap-word. This changes the breaking behavior from cutting at the exact column to back-tracking to the last space or tab before the overflow point. If no whitespace exists in the current segment, it falls back to character-level breaking for that segment.

Can I toggle wrapping dynamically while fzf is running?

Yes. Press Ctrl-/ to toggle wrapping on and off. Press Ctrl-Shift-/ (or Alt-/ on some terminals) to toggle between character wrap and word wrap modes. These key bindings call the internal actions actToggleWrap and actToggleWrapWord in src/terminal.go, which update the live terminal state and trigger a redraw.

Does wrapping affect the preview window in fzf?

Yes. The preview window supports independent wrapping via the wrap flag in the --preview-window option (e.g., --preview-window=right:50%:wrap). The preview uses Terminal.wordWrapAnsiLine in src/terminal.go to handle ANSI color codes while calculating visual width, ensuring that escape sequences do not interfere with the wrapping logic.

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 →