# How fzf Handles Unicode Normalization with the `--literal` Option

> Discover how fzf's --literal option disables Unicode normalization for exact code-point matching. Find what you need without character equivalence.

- Repository: [Junegunn Choi/fzf](https://github.com/junegunn/fzf)
- Tags: internals
- Published: 2026-03-01

---

**The `--literal` flag disables fzf's default Unicode normalization, forcing exact code-point matching for accented characters instead of treating "é" and "e" as equivalent.**

By default, fzf normalizes Latin-script characters so that searches ignore diacritics and accent marks. This behavior is controlled by the `Normalize` option, which defaults to `true` in the standard configuration. When you invoke fzf with the `--literal` flag, you disable this normalization pipeline, ensuring that only exact Unicode matches are returned.

## How fzf Unicode Normalization Works by Default

Out of the box, fzf treats accented characters as equivalent to their base forms. This is enabled by the default value of `Normalize` set in [`src/options.go`](https://github.com/junegunn/fzf/blob/main/src/options.go):

```go
// Default options
func defaultOptions() *Options {
    return &Options{
        Normalize:   true,
        // ... other defaults
    }
}

```

The actual mapping of Unicode code points occurs in [`src/algo/normalize.go`](https://github.com/junegunn/fzf/blob/main/src/algo/normalize.go). This file contains a static table that maps accented Latin characters to their ASCII equivalents. For example, the rune `0x00E9` ("é") maps to `'e'`. During the matching process, fzf consults this table to convert characters before comparison, allowing "cafe" to match "café".

## How the `--literal` Flag Disables Normalization

When you pass `--literal` on the command line, fzf's option parser in [`src/options.go`](https://github.com/junegunn/fzf/blob/main/src/options.go) (lines 2679-2682) explicitly sets `Normalize` to `false`:

```go
case "--literal":
    opts.Normalize = false          // disable normalization
case "--no-literal":
    opts.Normalize = true           // keep the default behaviour

```

Conversely, `--no-literal` (or simply omitting the flag) maintains the default `true` value. This boolean flag is then propagated through the entire search pipeline, determining whether the normalization lookup table is consulted during string comparison.

## The Technical Flow from Flag to Match

The `Normalize` boolean flows through three critical stages before affecting the final match result.

### Option Parsing in [`src/options.go`](https://github.com/junegunn/fzf/blob/main/src/options.go)

As shown above, the command-line parser sets `opts.Normalize` based on the presence of `--literal`. This value remains attached to the options structure throughout the program's execution.

### Pattern Construction in [`src/pattern.go`](https://github.com/junegunn/fzf/blob/main/src/pattern.go)

The `BuildPattern` function receives the `normalize` boolean and stores it inside each `Pattern` object (lines 77-84). When the pattern is prepared for matching, this flag is passed to the low-level matching algorithms. At line 457, the pattern invokes the matching functions with the normalize parameter:

```go
res, pos := pfun(caseSensitive, normalize, forward, part.text, pattern, withPos, slab)

```

### Algorithm Execution in [`src/algo/algo.go`](https://github.com/junegunn/fzf/blob/main/src/algo/algo.go)

Inside the core algorithms (such as `FuzzyMatchV2`), the `normalize` flag determines whether each rune is processed through `normalizeRune`. At lines 492-496, the code checks this flag before conversion:

```go
if normalize {
    char = normalizeRune(char)
}

```

If `normalize` is `false` (due to `--literal`), the original Unicode code point is used for comparison, bypassing the mapping defined in [`src/algo/normalize.go`](https://github.com/junegunn/fzf/blob/main/src/algo/normalize.go).

## Practical Examples

The difference between normalized and literal matching is best demonstrated with accented text:

| Flag | Normalization | Example query | "café" in list | Matching behaviour |
|------|---------------|---------------|----------------|--------------------|
| default (no flag) | **enabled** | `cafe` | `café` | **matches** (diacritic ignored) |
| `--literal` | disabled | `cafe` | `café` | **does not match** (exact Unicode code points required) |
| `--literal` | disabled | `café` | `café` | **matches** (exact literal) |

**Command-line examples:**

```bash

# List contains "café"

printf "café\ncafe\n" | fzf --filter cafe  # default: normalizes, both lines match

# Preserve diacritics – literal mode

printf "café\ncafe\n" | fzf --filter cafe --literal

# Only the exact "café" line is returned when searching for "café",

# and "cafe" will not match "café"

```

**Scripting context:**

```bash

# Script that needs exact Unicode matching

search_term="café"
matches=$(printf "café\ncafe\n" | fzf --filter --literal --query "$search_term")
echo "$matches"   # → prints only "café"

```

## Summary

- **Default behavior**: fzf enables Unicode normalization via the `Normalize` option (default `true`), mapping accented Latin characters to their ASCII equivalents during matching.
- **The `--literal` flag**: Parsed in [`src/options.go`](https://github.com/junegunn/fzf/blob/main/src/options.go), this flag sets `opts.Normalize = false`, disabling the normalization pipeline.
- **Propagation**: The boolean flows through [`src/pattern.go`](https://github.com/junegunn/fzf/blob/main/src/pattern.go) into `Pattern` objects, then into the core algorithms in [`src/algo/algo.go`](https://github.com/junegunn/fzf/blob/main/src/algo/algo.go).
- **Matching impact**: When normalization is disabled, `normalizeRune` is bypassed, forcing exact Unicode code-point comparison. This means "e" will not match "é" when `--literal` is active.

## Frequently Asked Questions

### What is Unicode normalization in fzf?

Unicode normalization in fzf refers to the process of converting accented Latin characters (such as "é", "ñ", or "ü") to their base ASCII equivalents before performing string matching. This allows users to search for "cafe" and still match "café" without typing the accent. This behavior is controlled by the `Normalize` option and implemented through a static mapping table in [`src/algo/normalize.go`](https://github.com/junegunn/fzf/blob/main/src/algo/normalize.go).

### Does `--literal` affect case sensitivity?

No, `--literal` only controls Unicode normalization (accented character handling). Case sensitivity in fzf is managed separately by the `Case` option, typically controlled via `--case-sensitive` or `--ignore-case` flags. You can use `--literal` together with case sensitivity options to achieve exact Unicode matching while still ignoring or respecting letter case as needed.

### Which characters are affected by fzf's normalization?

fzf's normalization primarily affects Latin-script characters with diacritics and accent marks. The mapping table in [`src/algo/normalize.go`](https://github.com/junegunn/fzf/blob/main/src/algo/normalize.go) includes conversions for characters such as "é" (U+00E9) to "e", "ñ" (U+00F1) to "n", and various other accented vowels and consonants. Characters outside the Latin script (such as Cyrillic, CJK characters, or emoji) are not modified by this normalization process.

### How do I enable normalization after using `--literal`?

If you have previously used `--literal` in a script or alias and want to re-enable normalization, simply remove the `--literal` flag or explicitly add `--no-literal`. The `--no-literal` flag explicitly sets `opts.Normalize = true`, restoring the default behavior where accented characters are mapped to their base forms during matching.