Files
roboco/panel/src/lib/fuzzy-match.ts
T

34 lines
1.2 KiB
TypeScript
Raw Normal View History

/**
* Case-insensitive fuzzy match: a query matches a target when every query
* character appears in the target in order (not necessarily contiguous).
* Lower scores rank first. An exact substring hit ranks by how early it
* starts; a non-contiguous subsequence hit is penalized by the total gap
* between matched characters so tighter matches still outrank loose ones.
* Returns null when the query isn't a subsequence of the target at all.
*/
export function fuzzyScore(query: string, target: string): number | null {
const q = query.trim().toLowerCase();
if (!q) return 0;
const t = target.toLowerCase();
const substringIndex = t.indexOf(q);
if (substringIndex !== -1) return substringIndex;
let searchFrom = 0;
let gap = 0;
let lastMatch = -1;
for (const ch of q) {
const found = t.indexOf(ch, searchFrom);
if (found === -1) return null;
if (lastMatch !== -1) gap += found - lastMatch - 1;
lastMatch = found;
searchFrom = found + 1;
}
return t.length + gap;
}
/** True when `query` fuzzy-matches `target` (see `fuzzyScore`). */
export function fuzzyMatch(query: string, target: string): boolean {
return fuzzyScore(query, target) !== null;
}