More fuzzy finder (#860)

This commit is contained in:
Armin Ronacher 2026-01-19 22:22:51 +01:00 committed by GitHub
parent d276c9fbe0
commit d37b5a52d7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 73 additions and 42 deletions

View file

@ -13,11 +13,12 @@ export function fuzzyMatch(query: string, text: string): FuzzyMatch {
const queryLower = query.toLowerCase();
const textLower = text.toLowerCase();
if (queryLower.length === 0) {
const matchQuery = (normalizedQuery: string): FuzzyMatch => {
if (normalizedQuery.length === 0) {
return { matches: true, score: 0 };
}
if (queryLower.length > textLower.length) {
if (normalizedQuery.length > textLower.length) {
return { matches: false, score: 0 };
}
@ -26,8 +27,8 @@ export function fuzzyMatch(query: string, text: string): FuzzyMatch {
let lastMatchIndex = -1;
let consecutiveMatches = 0;
for (let i = 0; i < textLower.length && queryIndex < queryLower.length; i++) {
if (textLower[i] === queryLower[queryIndex]) {
for (let i = 0; i < textLower.length && queryIndex < normalizedQuery.length; i++) {
if (textLower[i] === normalizedQuery[queryIndex]) {
const isWordBoundary = i === 0 || /[\s\-_./:]/.test(textLower[i - 1]!);
// Reward consecutive matches
@ -55,11 +56,36 @@ export function fuzzyMatch(query: string, text: string): FuzzyMatch {
}
}
if (queryIndex < queryLower.length) {
if (queryIndex < normalizedQuery.length) {
return { matches: false, score: 0 };
}
return { matches: true, score };
};
const primaryMatch = matchQuery(queryLower);
if (primaryMatch.matches) {
return primaryMatch;
}
const alphaNumericMatch = queryLower.match(/^(?<letters>[a-z]+)(?<digits>[0-9]+)$/);
const numericAlphaMatch = queryLower.match(/^(?<digits>[0-9]+)(?<letters>[a-z]+)$/);
const swappedQuery = alphaNumericMatch
? `${alphaNumericMatch.groups?.digits ?? ""}${alphaNumericMatch.groups?.letters ?? ""}`
: numericAlphaMatch
? `${numericAlphaMatch.groups?.letters ?? ""}${numericAlphaMatch.groups?.digits ?? ""}`
: "";
if (!swappedQuery) {
return primaryMatch;
}
const swappedMatch = matchQuery(swappedQuery);
if (!swappedMatch.matches) {
return primaryMatch;
}
return { matches: true, score: swappedMatch.score + 5 };
}
/**

View file

@ -53,6 +53,11 @@ describe("fuzzyMatch", () => {
assert.strictEqual(notAtBoundary.matches, true);
assert.ok(atBoundary.score < notAtBoundary.score);
});
it("matches swapped alpha numeric tokens", () => {
const result = fuzzyMatch("codex52", "gpt-5.2-codex");
assert.strictEqual(result.matches, true);
});
});
describe("fuzzyFilter", () => {