What Search Algorithms Does FFF Use for Fuzzy Matching?

FFF relies on the neo_frizbee crate to power its fuzzy matching, utilizing SIMD-accelerated algorithms for file-path search and Smith-Waterman dynamic programming for content grep operations.

The dmtrKovalenko/fff repository implements a high-performance fuzzy finder for Neovim by leveraging the Rust-based neo_frizbee library (version 0.10.2), which provides optimized search algorithms capable of handling large codebases with minimal latency.

FFF's Core Fuzzy Matching Engine

At the foundation of FFF's search capability lies the neo_frizbee crate, a Rust port of the original frizbee library. This dependency is declared in the workspace Cargo.toml and exposes two distinct algorithmic approaches tailored to different search contexts. The crate handles both path-based file selection and content-based line matching through SIMD-optimized implementations that run across multiple CPU cores.

In crates/fff-core/src/score.rs, FFF initializes the matching engine by constructing a neo_frizbee::Config struct and invoking neo_frizbee::match_list_parallel_resolved to process queries against file lists. This architecture allows FFF to separate the pre-filtering phase from the scoring phase, maximizing throughput while maintaining typo tolerance.

File-Path Search: SIMD-Accelerated Fuzzy Matching

For file-path selection in the picker UI, FFF employs a SIMD-accelerated fuzzy matching algorithm that tolerates dropped characters and reordered fragments. According to the implementation in crates/fff-core/src/score.rs, the algorithm executes in two distinct phases:

  • SIMD Pre-Filter: The query is split into "chunks" and processed using vectorized byte-wise comparisons to instantly discard impossible candidates. This vectorized approach enables sub-millisecond filtering even on directories containing hundreds of thousands of files.

  • Scoring Refinement: Surviving candidates pass through a lightweight scoring phase using the neo_frizbee::Scoring struct, which calculates match quality based on consecutive character matches and position bonuses.

The parallel variant match_list_parallel_resolved distributes this workload across available CPU cores, ensuring that full-project scans remain responsive regardless of repository size.

Content Search: Smith-Waterman Dynamic Programming

When performing fuzzy grep operations via :FffGrep, FFF switches to the Smith-Waterman algorithm, a classic dynamic-programming approach for local sequence alignment. As implemented in crates/fff-core/src/grep.rs, this algorithm produces typo-tolerant similarity scores for each line in the searched files.

The Smith-Waterman implementation within neo_frizbee runs on SIMD-enabled code paths, allowing it to compute the dynamic-programming matrix in a few CPU cycles per line rather than relying on naive iterative comparisons. This provides the same alignment quality as traditional fuzzy grep tools while maintaining the performance characteristics required for interactive search.

Parallel Processing and Configuration

FFF exposes configurable tolerance parameters through the neo_frizbee::Config struct, which the Lua UI layer modifies before passing to Rust via FFI. Key configuration options include:

  • max_typos: Controls the maximum allowable edit distance for a match to be considered valid.
  • case_sensitive: Determines whether the SIMD comparisons respect character case.
  • Parallel batch processing: All matching operations use the *_parallel_resolved variants to batch-process candidates across CPU cores.

The scoring logic in crates/fff-core/src/score.rs combines these fuzzy match scores with frecency weights (backed by LMDB) to produce the final ranking presented to users.

Practical Implementation Examples

To invoke the SIMD-accelerated path search from Lua:

local fff = require('fff')
-- Opens picker; queries matched using frizbee's SIMD fuzzy algorithm
fff.find_files({ cwd = vim.fn.getcwd() })

For fuzzy grep with Smith-Waterman scoring:

local fff = require('fff')
-- Searches lines ranked by neo_frizbee's Smith-Waterman implementation
fff.grep({
  query = 'async await',
  cwd   = vim.fn.getcwd(),
  fuzzy = true,  -- Enables typo-tolerant mode
})

Internally, the Rust core constructs the matching configuration as follows:

let config = neo_frizbee::Config {
    case_sensitive: false,
    max_typos: 2,
    ..Default::default()
};

let matches = neo_frizbee::match_list_parallel_resolved(&query, &paths, &config);

Summary

  • File-path search utilizes SIMD-accelerated fuzzy matching with a vectorized pre-filter phase implemented in crates/fff-core/src/score.rs.
  • Content grep employs Smith-Waterman dynamic programming alignment for typo-tolerant line matching, found in crates/fff-core/src/grep.rs.
  • Both algorithms leverage the neo_frizbee crate (version 0.10.2) and run on SIMD-enabled parallel code paths via match_list_parallel_resolved.
  • The neo_frizbee::Config struct controls tolerance settings including max_typos and case_sensitive.
  • FFF combines fuzzy scores with LMDB-backed frecency data to rank results.

Frequently Asked Questions

What is the difference between FFF's path search and grep search algorithms?

Path search uses a SIMD-accelerated fuzzy matcher optimized for file paths, which splits queries into chunks for vectorized byte-wise comparison. Grep search uses the Smith-Waterman algorithm, a dynamic-programming approach designed for local sequence alignment of text lines. Both algorithms tolerate typos but serve different data structures: directory hierarchies versus file contents.

How does FFF achieve fast fuzzy matching on large repositories?

FFF achieves speed through three mechanisms: SIMD vectorization that processes multiple bytes simultaneously in neo_frizbee, a pre-filter phase that eliminates impossible candidates before scoring, and parallel batch processing across CPU cores using match_list_parallel_resolved. These optimizations allow sub-millisecond response times even on projects with hundreds of thousands of files.

Can I configure the fuzziness tolerance in FFF?

Yes. FFF exposes the neo_frizbee::Config struct to the Lua layer, allowing you to set max_typos for edit distance tolerance and case_sensitive for character matching rules. These parameters are passed from Lua through FFI to the Rust core in crates/fff-core/src/score.rs, affecting both path and grep search behaviors.

Where does FFF store the frecency data used for ranking?

FFF stores frecency weights in an LMDB-backed database. The scoring logic in crates/fff-core/src/score.rs combines these pre-computed frequency/recency weights with the raw fuzzy match scores from neo_frizbee to determine the final presentation order of results.

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 →