102 lines
2.2 KiB
Lua
102 lines
2.2 KiB
Lua
local M = {}
|
|
|
|
-- Simple fuzzy match algorithm
|
|
-- Returns score (higher is better) or nil if no match
|
|
function M.fuzzy_match(str, pattern)
|
|
if pattern == '' then
|
|
return 1000 -- Empty pattern matches everything with high score
|
|
end
|
|
|
|
str = str:lower()
|
|
pattern = pattern:lower()
|
|
|
|
local str_idx = 1
|
|
local pattern_idx = 1
|
|
local score = 0
|
|
local consecutive = 0
|
|
local last_match_idx = 0
|
|
|
|
while pattern_idx <= #pattern do
|
|
local pattern_char = pattern:sub(pattern_idx, pattern_idx)
|
|
local found = false
|
|
|
|
-- Search for pattern character in remaining string
|
|
while str_idx <= #str do
|
|
local str_char = str:sub(str_idx, str_idx)
|
|
|
|
if str_char == pattern_char then
|
|
found = true
|
|
|
|
-- Score bonuses
|
|
score = score + 1
|
|
|
|
-- Bonus for consecutive matches
|
|
if str_idx == last_match_idx + 1 then
|
|
consecutive = consecutive + 1
|
|
score = score + consecutive * 5
|
|
else
|
|
consecutive = 0
|
|
end
|
|
|
|
-- Bonus for matching at start
|
|
if str_idx == 1 then
|
|
score = score + 10
|
|
end
|
|
|
|
-- Bonus for matching after separator
|
|
if str_idx > 1 then
|
|
local prev_char = str:sub(str_idx - 1, str_idx - 1)
|
|
if prev_char == '-' or prev_char == '_' or prev_char == ' ' then
|
|
score = score + 8
|
|
end
|
|
end
|
|
|
|
last_match_idx = str_idx
|
|
str_idx = str_idx + 1
|
|
break
|
|
end
|
|
|
|
str_idx = str_idx + 1
|
|
end
|
|
|
|
if not found then
|
|
return nil -- Pattern doesn't match
|
|
end
|
|
|
|
pattern_idx = pattern_idx + 1
|
|
end
|
|
|
|
return score
|
|
end
|
|
|
|
-- Filter and sort themes by fuzzy match
|
|
function M.filter_themes(themes, query)
|
|
if query == '' then
|
|
return themes
|
|
end
|
|
|
|
local matches = {}
|
|
|
|
for _, theme in ipairs(themes) do
|
|
local score = M.fuzzy_match(theme, query)
|
|
if score then
|
|
table.insert(matches, { theme = theme, score = score })
|
|
end
|
|
end
|
|
|
|
-- Sort by score (descending)
|
|
table.sort(matches, function(a, b)
|
|
return a.score > b.score
|
|
end)
|
|
|
|
-- Extract just the theme names
|
|
local filtered = {}
|
|
for _, match in ipairs(matches) do
|
|
table.insert(filtered, match.theme)
|
|
end
|
|
|
|
return filtered
|
|
end
|
|
|
|
return M
|