sugarcraft/candy-fuzzy

Fuzzy string matching with scored matched indices — Smith-Waterman + Sahilm algorithms. Unblocks filter-highlighting UI across the ecosystem.

Maintainers

Package info

github.com/sugarcraft/candy-fuzzy

Documentation

pkg:composer/sugarcraft/candy-fuzzy

Statistics

Installs: 37

Dependents: 8

Suggesters: 0

Stars: 0

Open Issues: 0

dev-master 2026-06-01 15:23 UTC

This package is not auto-updated.

Last update: 2026-06-02 12:56:08 UTC


README

Fuzzy string matching library with scored matched character indices — enables filter highlighting UI across the SugarCraft ecosystem.

Installation

composer require sugarcraft/candy-fuzzy

Role

Extracts the canonical Smith-Waterman fuzzy matcher from candy-forms and adds the key feature that was previously impossible: ranked matches WITH scored matched character indices, enabling UI filter highlighting.

Provides two algorithms:

  • SmithWatermanMatcher — Smith-Waterman local alignment with adjacency bonus. Bit-equivalent to the original candy-forms implementation.
  • SahilmMatcher — Ports the sahilm/fuzzy algorithm used by charmbracelet/gum filter. Includes separator bonus, camelCase bonus, exact-prefix bonus.

Quickstart

use SugarCraft\Fuzzy\Matcher\SmithWatermanMatcher;
use SugarCraft\Fuzzy\Highlighter;

$matcher = new SmithWatermanMatcher();

// Match a single candidate
$result = $matcher->match('foo', 'foobar');
// MatchResult(needle: 'foo', haystack: 'foobar', score: 16, matchedIndices: [0, 1, 2])

// Match against multiple candidates (sorted by score desc)
$results = $matcher->matchAll('app', ['apple', 'applet', 'application', 'apricot']);
// Returns list of MatchResult sorted by score

// Highlight matched runs
$highlighter = new Highlighter();
$styled = $highlighter->highlight($result, fn($matched) => "\033[1m$matched\033[0m");
// Returns 'foobar' with matched chars styled

MatchResult

final class MatchResult
{
    public readonly string $needle;      // Search query
    public readonly string $haystack;   // Matched candidate
    public readonly int $score;         // Higher = better match
    public readonly array $matchedIndices; // 0-based char indices of matched chars
}

Interface

Swap matchers without touching call-sites:

use SugarCraft\Fuzzy\MatcherInterface;
use SugarCraft\Fuzzy\FuzzyMatcher;

function filter(MatcherInterface $matcher, string $query, array $candidates): array
{
    return $matcher->matchAll($query, $candidates);
}

Algorithm Differences

Feature SmithWaterman Sahilm
Local alignment
Adjacent bonus ✅ (5) ✅ (consecutive: 5)
Separator bonus ✅ (10)
CamelCase bonus ✅ (10)
First-char bonus ✅ (15)
Case sensitive Optional

Backward Compatibility

The existing SugarCraft\Forms\Fuzzy\FuzzyMatcher and SugarCraft\Lister\FuzzyMatch classes remain as deprecated shims that delegate to SugarCraft\Fuzzy\Matcher\SmithWatermanMatcher. Consumers will migrate in subsequent steps.

Links

codecov