minimal extui fuzzy finder for neovim

feat: add marks

+82 -8
+7 -8
lua/artio/builtins.lua
··· 10 10 local config = lzrq("artio.config") 11 11 local utils = lzrq("artio.utils") 12 12 13 - local function extend(t1, t2) 14 - return vim.tbl_deep_extend("force", t1, t2) 13 + local function extend(t1, ...) 14 + return vim.tbl_deep_extend("force", t1, ...) 15 15 end 16 16 17 17 local builtins = {} ··· 39 39 preview_item = function(item) 40 40 return vim.fn.bufadd(item) 41 41 end, 42 - actions = { 43 - setqflist = utils.make_setqflist(function(item) 42 + actions = extend( 43 + {}, 44 + utils.make_setqflistactions(function(item) 44 45 return { filename = item.v } 45 46 end), 46 - }, 47 - mappings = { 48 - ["<c-q>"] = "setqflist", 49 - }, 47 + props.actions or {} 48 + ), 50 49 }, props) 51 50 ) 52 51 end
+5
lua/artio/config.lua
··· 12 12 ---@field promptprefix string 13 13 ---@field prompt_title boolean 14 14 ---@field pointer string 15 + ---@field marker string 15 16 ---@field use_icons boolean 16 17 17 18 ---@class artio.config.win ··· 32 33 promptprefix = "", 33 34 prompt_title = true, 34 35 pointer = "", 36 + marker = "│", 35 37 use_icons = package.loaded["mini.icons"] and true or false, 36 38 }, 37 39 win = { ··· 51 53 ["<up>"] = "up", 52 54 ["<cr>"] = "accept", 53 55 ["<esc>"] = "cancel", 56 + ["<tab>"] = "mark", 54 57 ["<c-l>"] = "togglepreview", 58 + ["<c-q>"] = "setqflist", 59 + ["<c-w>"] = "setqflistmark", 55 60 }, 56 61 } 57 62
+28
lua/artio/picker.lua
··· 27 27 ---@class artio.Picker : artio.Picker.config 28 28 ---@field idx integer 1-indexed 29 29 ---@field matches artio.Picker.match[] 30 + ---@field marked table<integer, true|nil> 30 31 ---@field actions? artio.Actions 31 32 local Picker = {} 32 33 Picker.__index = Picker ··· 55 56 cancel = function(_, co) 56 57 coroutine.resume(co, action_enum.cancel) 57 58 end, 59 + mark = function(self, _) 60 + local match = self.matches[self.idx] 61 + if not match then 62 + return 63 + end 64 + local idx = match[1] 65 + self:mark(idx, not self.marked[idx]) 66 + self.view:showmatches() -- redraw marker 67 + self.view:hlselect() 68 + end, 58 69 togglepreview = function(self, _) 59 70 self.view:togglepreview() 60 71 end, ··· 72 83 idx = 0, 73 84 items = {}, 74 85 matches = {}, 86 + marked = {}, 75 87 }, require("artio.config").get(), props) 76 88 77 89 if not t.prompttext then ··· 165 177 table.sort(self.matches, function(a, b) 166 178 return a[3] > b[3] 167 179 end) 180 + end 181 + 182 + ---@param idx integer 183 + ---@param yes? boolean 184 + function Picker:mark(idx, yes) 185 + self.marked[idx] = yes == nil and true or yes 186 + end 187 + 188 + ---@return integer[] 189 + function Picker:getmarked() 190 + return vim 191 + .iter(pairs(self.marked)) 192 + :map(function(k, v) 193 + return v and k or nil 194 + end) 195 + :totable() 168 196 end 169 197 170 198 return Picker
+30
lua/artio/utils.lua
··· 30 30 end 31 31 end 32 32 33 + ---@param fn fun(item: artio.Picker.item): vim.quickfix.entry 34 + ---@return artio.Picker.action 33 35 function utils.make_setqflist(fn) 34 36 return function(self, co) 35 37 vim.fn.setqflist(vim ··· 45 47 end) 46 48 coroutine.resume(co, 1) 47 49 end 50 + end 51 + 52 + ---@param fn fun(item: artio.Picker.item): vim.quickfix.entry 53 + ---@return artio.Picker.action 54 + function utils.make_setqflistmark(fn) 55 + return function(self, co) 56 + vim.fn.setqflist(vim 57 + .iter(ipairs(self:getmarked())) 58 + :map(function(_, id) 59 + local item = self.items[id] 60 + local qfitem = fn(item) 61 + return qfitem 62 + end) 63 + :totable()) 64 + vim.schedule(function() 65 + vim.cmd.copen() 66 + end) 67 + coroutine.resume(co, 1) 68 + end 69 + end 70 + 71 + ---@param fn fun(item: artio.Picker.item): vim.quickfix.entry 72 + ---@return table<string, artio.Picker.action> 73 + function utils.make_setqflistactions(fn) 74 + return { 75 + setqflist = utils.make_setqflist(fn), 76 + setqflistmark = utils.make_setqflistmark(fn), 77 + } 48 78 end 49 79 50 80 return utils
+12
lua/artio/view.lua
··· 441 441 local hls = {} 442 442 local icons = {} ---@type ([string, string]|false)[] 443 443 local custom_hls = {} ---@type (artio.Picker.hl[]|false)[] 444 + local marks = {} ---@type boolean[] 444 445 for i = 1 + offset, math.min(#self.picker.matches, maxlistheight + offset) do 445 446 local match = self.picker.matches[i] 446 447 local item = self.picker.items[match[1]] ··· 459 460 item.hls = hl 460 461 end 461 462 custom_hls[#custom_hls + 1] = hl or false 463 + 464 + marks[#marks + 1] = self.picker.marked[item.id] or false 462 465 463 466 lines[#lines + 1] = ("%s%s%s"):format(prefix, icon, item.text) 464 467 hls[#hls + 1] = match[2] ··· 500 503 }) 501 504 ) 502 505 end 506 + end 507 + 508 + if marks[i] then 509 + self:mark(cmdline.srow + i - 1, indent - 1, { 510 + virt_text = { { self.picker.opts.marker, "ArtioMarker" } }, 511 + hl_mode = "combine", 512 + virt_text_pos = "overlay", 513 + invalidate = true, 514 + }) 503 515 end 504 516 505 517 if hls[i] then