minimal extui fuzzy finder for neovim

perf!: optimize sorter by changing `matches` to a table of ids

+38 -57
+35 -55
lua/artio/init.lua
··· 51 51 function artio.mergesorters(strat, a, ...) 52 52 local sorters = { a, ... } ---@type artio.Picker.sorter[] 53 53 54 - ---@generic T 55 - ---@param t T[] 56 - ---@param cmp fun(T): boolean 57 - ---@return integer? 58 - local function findi(t, cmp) 59 - for i = 1, #t do 60 - if t[i] and cmp(t[i]) then 61 - return i 62 - end 63 - end 64 - end 65 - 66 54 return function(lst, input) 67 - local it = 0 68 - return vim.iter(sorters):fold({}, function(oldmatches, sorter) 69 - it = it + 1 70 - ---@type artio.Picker.match[] 55 + return vim.iter(ipairs(sorters)):fold({}, function(oldmatches, it, sorter) 56 + ---@type artio.Picker.matches 71 57 local newmatches = sorter(lst, input) 72 58 73 - return vim.iter(newmatches):fold(strat == "intersect" and {} or oldmatches, function(matches, newmatch) 74 - local oldmatchidx = findi(oldmatches, function(v) 75 - return v[1] == newmatch[1] 76 - end) 77 - 78 - if oldmatchidx then 79 - local oldmatch = oldmatches[oldmatchidx] 80 - local next = mergematches(oldmatch, newmatch) 81 - if strat == "intersect" then 82 - matches[#matches + 1] = next 83 - else 84 - matches[oldmatchidx] = next 59 + return vim 60 + .iter(pairs(newmatches)) 61 + :fold(strat == "intersect" and {} or oldmatches, function(matches, idx, newmatch) 62 + local oldmatch = oldmatches[idx] 63 + if oldmatch then 64 + local next = mergematches(oldmatch, newmatch) 65 + matches[idx] = next 66 + elseif strat == "combine" or it == 1 then 67 + matches[idx] = newmatch 85 68 end 86 - elseif strat == "combine" or it == 1 then 87 - matches[#matches + 1] = newmatch 88 - end 89 - return matches 90 - end) 69 + return matches 70 + end) 91 71 end) 92 72 end 93 73 end ··· 99 79 end 100 80 101 81 if not input or #input == 0 then 102 - return vim.tbl_map(function(v) 103 - return { v.id, {}, 0 } 104 - end, lst) 82 + return vim.iter(lst):fold({}, function(acc, v) 83 + acc[v.id] = { v.id, {}, 0 } 84 + return acc 85 + end) 105 86 end 106 87 107 88 local matches = vim.fn.matchfuzzypos(lst, input, { key = "text" }) 108 89 109 90 local items = {} 110 91 for i = 1, #matches[1] do 111 - items[#items + 1] = { matches[1][i].id, matches[2][i], matches[3][i] } 92 + items[matches[1][i].id] = { matches[1][i].id, matches[2][i], matches[3][i] } 112 93 end 113 94 return items 114 95 end ··· 118 99 local match = string.match(input, "^/[^/]*/") 119 100 local pattern = match and string.match(match, "^/([^/]*)/$") 120 101 121 - return vim 122 - .iter(lst) 123 - :map(function(v) 124 - if pattern and not string.match(v.text, pattern) then 125 - return 126 - end 102 + return vim.iter(lst):fold({}, function(acc, v) 103 + if pattern and not string.match(v.text, pattern) then 104 + return acc 105 + end 127 106 128 - return { v.id, {}, 0 } 129 - end) 130 - :totable() 107 + acc[v.id] = { v.id, {}, 0 } 108 + return acc 109 + end) 131 110 end 132 111 133 112 ---@type artio.Picker.sorter ··· 144 123 artio.select = function(items, opts, on_choice, start_opts) 145 124 return artio.generic( 146 125 items, 147 - vim.tbl_deep_extend("force", { 148 - prompt = opts.prompt, 149 - on_close = function(_, idx) 150 - return on_choice(items[idx], idx) 151 - end, 152 - format_item = opts.format_item and function(item) 153 - return opts.format_item(item) 154 - end or nil, 155 - }, start_opts or {}) 126 + vim.tbl_deep_extend( 127 + "force", 128 + { 129 + on_close = function(_, idx) 130 + return on_choice(items[idx], idx) 131 + end, 132 + }, 133 + opts or {}, -- opts.prompt, opts.format_item 134 + start_opts or {} 135 + ) 156 136 ) 157 137 end 158 138
+3 -2
lua/artio/picker.lua
··· 2 2 3 3 ---@alias artio.Picker.item { id: integer, v: any, text: string, icon?: string, icon_hl?: string, hls?: artio.Picker.hl[] } 4 4 ---@alias artio.Picker.match [integer, integer[], integer] [item, pos[], score] 5 - ---@alias artio.Picker.sorter fun(lst: artio.Picker.item[], input: string): artio.Picker.match[] 5 + ---@alias artio.Picker.matches table<integer, artio.Picker.match> id: match 6 + ---@alias artio.Picker.sorter fun(lst: artio.Picker.item[], input: string): artio.Picker.matches 6 7 ---@alias artio.Picker.hl [[integer, integer], string] 7 8 ---@alias artio.Picker.action fun(self: artio.Picker) 8 9 ··· 206 207 function Picker:getmatches(input) 207 208 input = input or self.input 208 209 self:getitems(input) 209 - self.matches = self.fn(self.items, input) 210 + self.matches = vim.tbl_values(self.fn(self.items, input)) 210 211 table.sort(self.matches, function(a, b) 211 212 return a[3] > b[3] 212 213 end)