mirror of
https://github.com/getcompanion-ai/co-mono.git
synced 2026-04-15 10:05:14 +00:00
More fuzzy finder (#860)
This commit is contained in:
parent
d276c9fbe0
commit
d37b5a52d7
2 changed files with 73 additions and 42 deletions
|
|
@ -13,53 +13,79 @@ export function fuzzyMatch(query: string, text: string): FuzzyMatch {
|
|||
const queryLower = query.toLowerCase();
|
||||
const textLower = text.toLowerCase();
|
||||
|
||||
if (queryLower.length === 0) {
|
||||
return { matches: true, score: 0 };
|
||||
}
|
||||
|
||||
if (queryLower.length > textLower.length) {
|
||||
return { matches: false, score: 0 };
|
||||
}
|
||||
|
||||
let queryIndex = 0;
|
||||
let score = 0;
|
||||
let lastMatchIndex = -1;
|
||||
let consecutiveMatches = 0;
|
||||
|
||||
for (let i = 0; i < textLower.length && queryIndex < queryLower.length; i++) {
|
||||
if (textLower[i] === queryLower[queryIndex]) {
|
||||
const isWordBoundary = i === 0 || /[\s\-_./:]/.test(textLower[i - 1]!);
|
||||
|
||||
// Reward consecutive matches
|
||||
if (lastMatchIndex === i - 1) {
|
||||
consecutiveMatches++;
|
||||
score -= consecutiveMatches * 5;
|
||||
} else {
|
||||
consecutiveMatches = 0;
|
||||
// Penalize gaps
|
||||
if (lastMatchIndex >= 0) {
|
||||
score += (i - lastMatchIndex - 1) * 2;
|
||||
}
|
||||
}
|
||||
|
||||
// Reward word boundary matches
|
||||
if (isWordBoundary) {
|
||||
score -= 10;
|
||||
}
|
||||
|
||||
// Slight penalty for later matches
|
||||
score += i * 0.1;
|
||||
|
||||
lastMatchIndex = i;
|
||||
queryIndex++;
|
||||
const matchQuery = (normalizedQuery: string): FuzzyMatch => {
|
||||
if (normalizedQuery.length === 0) {
|
||||
return { matches: true, score: 0 };
|
||||
}
|
||||
|
||||
if (normalizedQuery.length > textLower.length) {
|
||||
return { matches: false, score: 0 };
|
||||
}
|
||||
|
||||
let queryIndex = 0;
|
||||
let score = 0;
|
||||
let lastMatchIndex = -1;
|
||||
let consecutiveMatches = 0;
|
||||
|
||||
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
|
||||
if (lastMatchIndex === i - 1) {
|
||||
consecutiveMatches++;
|
||||
score -= consecutiveMatches * 5;
|
||||
} else {
|
||||
consecutiveMatches = 0;
|
||||
// Penalize gaps
|
||||
if (lastMatchIndex >= 0) {
|
||||
score += (i - lastMatchIndex - 1) * 2;
|
||||
}
|
||||
}
|
||||
|
||||
// Reward word boundary matches
|
||||
if (isWordBoundary) {
|
||||
score -= 10;
|
||||
}
|
||||
|
||||
// Slight penalty for later matches
|
||||
score += i * 0.1;
|
||||
|
||||
lastMatchIndex = i;
|
||||
queryIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
if (queryIndex < normalizedQuery.length) {
|
||||
return { matches: false, score: 0 };
|
||||
}
|
||||
|
||||
return { matches: true, score };
|
||||
};
|
||||
|
||||
const primaryMatch = matchQuery(queryLower);
|
||||
if (primaryMatch.matches) {
|
||||
return primaryMatch;
|
||||
}
|
||||
|
||||
if (queryIndex < queryLower.length) {
|
||||
return { matches: false, score: 0 };
|
||||
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;
|
||||
}
|
||||
|
||||
return { matches: true, score };
|
||||
const swappedMatch = matchQuery(swappedQuery);
|
||||
if (!swappedMatch.matches) {
|
||||
return primaryMatch;
|
||||
}
|
||||
|
||||
return { matches: true, score: swappedMatch.score + 5 };
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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", () => {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue