Download packages/pi-tui/src/fuzzy.ts from SaylorTwift/kimi-code: direct link, hf CLI and curl.
- Browser
- Download file 3.29 kB
-
https://huggingface.co/SaylorTwift/kimi-code/resolve/main/packages/pi-tui/src/fuzzy.ts
- Command line
-
hf download hf://SaylorTwift/kimi-code/packages/pi-tui/src/fuzzy.ts
-
curl -L -o fuzzy.ts https://huggingface.co/SaylorTwift/kimi-code/resolve/main/packages/pi-tui/src/fuzzy.ts
3.29 kB
| /** | |
| * Fuzzy matching utilities. | |
| * Matches if all query characters appear in order (not necessarily consecutive). | |
| * Lower score = better match. | |
| */ | |
| export interface FuzzyMatch { | |
| matches: boolean; | |
| score: number; | |
| } | |
| export function fuzzyMatch(query: string, text: string): FuzzyMatch { | |
| const queryLower = query.toLowerCase(); | |
| const textLower = text.toLowerCase(); | |
| 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 }; | |
| } | |
| if (normalizedQuery === textLower) { | |
| score -= 100; | |
| } | |
| 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 }; | |
| } | |
| /** | |
| * Filter and sort items by fuzzy match quality (best matches first). | |
| * Supports whitespace- and slash-separated tokens: all tokens must match. | |
| */ | |
| export function fuzzyFilter<T>(items: T[], query: string, getText: (item: T) => string): T[] { | |
| if (!query.trim()) { | |
| return items; | |
| } | |
| const tokens = query | |
| .trim() | |
| .split(/[\s/]+/) | |
| .filter((t) => t.length > 0); | |
| if (tokens.length === 0) { | |
| return items; | |
| } | |
| const results: { item: T; totalScore: number }[] = []; | |
| for (const item of items) { | |
| const text = getText(item); | |
| let totalScore = 0; | |
| let allMatch = true; | |
| for (const token of tokens) { | |
| const match = fuzzyMatch(token, text); | |
| if (match.matches) { | |
| totalScore += match.score; | |
| } else { | |
| allMatch = false; | |
| break; | |
| } | |
| } | |
| if (allMatch) { | |
| results.push({ item, totalScore }); | |
| } | |
| } | |
| results.sort((a, b) => a.totalScore - b.totalScore); | |
| return results.map((r) => r.item); | |
| } | |