Something went wrong. Try again.
🐻 minimal ui2 fuzzy finder for Neovim codeberg.org/comfysage/artio.nvim
Something went wrong. Try again.
5.4 kB · 217 lines
Lua
at main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218local function lzrq(modname) return setmetatable({}, { __index = function(_, key) return require(modname)[key] end, })end
local config = lzrq("artio.config")
local artio = {}
---@param cfg? artio.configartio.setup = function(cfg) cfg = cfg or {} config.set(cfg)end
---@param a integer[]---@param ... integer[]---@return integer[]local function mergehl(a, ...) local hl_lists = { a, ... }
local t = vim.iter(hl_lists):fold({}, function(hls, hl_list) for i = 1, #hl_list do hls[hl_list[i]] = true end return hls end) return vim.tbl_keys(t)end
---@param a artio.Picker.match---@param b artio.Picker.match---@return artio.Picker.matchlocal function mergematches(a, b) return { a[1], mergehl(a[2], b[2]), a[3] + b[3] }end
---@param strat 'combine'|'intersect'|'base'--- combine:--- a, b -> a + ab + b--- intersect:--- a, b -> ab--- base:--- a, b -> a + ab---@param a artio.Picker.sorter---@param ... artio.Picker.sorter---@return artio.Picker.sorterfunction artio.mergesorters(strat, a, ...) local sorters = { a, ... } ---@type artio.Picker.sorter[]
return function(lst, input) return vim.iter(ipairs(sorters)):fold({}, function(oldmatches, it, sorter) ---@type artio.Picker.matches local newmatches = sorter(lst, input)
return vim .iter(pairs(newmatches)) :fold(strat == "intersect" and {} or oldmatches, function(matches, idx, newmatch) local oldmatch = oldmatches[idx] if oldmatch then local next = mergematches(oldmatch, newmatch) matches[idx] = next elseif strat == "combine" or it == 1 then matches[idx] = newmatch end return matches end) end) endend
---@type artio.Picker.sorterartio.fuzzy_sorter = function(lst, input) if not lst or #lst == 0 then return {} end
if not input or #input == 0 then return vim.iter(lst):fold({}, function(acc, v) acc[v.id] = { v.id, {}, 0 } return acc end) end
local matches = vim.fn.matchfuzzypos(lst, input, { key = "text" })
local items = {} for i = 1, #matches[1] do items[matches[1][i].id] = { matches[1][i].id, matches[2][i], matches[3][i] } end return itemsend
---@type artio.Picker.sorterartio.pattern_sorter = function(lst, input) local match = string.match(input, "^/[^/]*/") local pattern = match and string.match(match, "^/([^/]*)/$")
return vim.iter(lst):fold({}, function(acc, v) if pattern and not string.match(v.text, pattern) then return acc end
acc[v.id] = { v.id, {}, 0 } return acc end)end
--- the default sorter provides support for pattern matching. a `/.../` match--- at the start of the input will limit the fuzzy sorter to items matching the--- pattern. if you want to use `/.../` in your fuzzy matches, make sure to--- escape it by starting the input with an empty space (` /.../`). fuzzy--- sorting will be done on the input with the pattern removed.---@type artio.Picker.sorterartio.sorter = artio.mergesorters("intersect", artio.pattern_sorter, function(lst, input) input = string.gsub(input, "^/[^/]*/", "") return artio.fuzzy_sorter(lst, input)end)
---@generic T---@param items T[] Arbitrary items---@param opts vim.ui.select.Opts Additional options---@param on_choice fun(item: T|nil, idx: integer|nil)---@param start_opts? artio.Picker.configartio.select = function(items, opts, on_choice, start_opts) return artio.generic( items, vim.tbl_deep_extend( "force", { on_close = function(_, idx) artio.schedule(function() on_choice(items[idx], idx) end) end, on_quit = function() artio.schedule(function() on_choice(nil, nil) end) end, }, opts or {}, -- opts.prompt, opts.format_item start_opts or {} ) )end
---@generic T---@param items T[]---@param props artio.Picker.configartio.generic = function(items, props) return artio.pick(vim.tbl_deep_extend("force", { fn = artio.sorter, items = items, }, props))end
---@param ... artio.Picker.configartio.pick = function(...) local Picker = require("artio.picker") return Picker:new(...):open()end
---@param f fun(): anyartio.schedule = function(f) local Picker = require("artio.picker") local current = Picker.active_picker if not current or not current.closed then return end
vim.schedule(function() vim.defer_fn(f, 10) vim.wait(1000, function() return coroutine.status(current.co) == "dead" end) end)end
artio.resume = function() local picker = require("artio.picker").active_picker if not picker or not picker.closed then return end picker:resume()end
---@param fn artio.Picker.action---@param scheduled_fn? artio.Picker.actionartio.wrap = function(fn, scheduled_fn) return function() local Picker = require("artio.picker") local current = Picker.active_picker if not current or current.closed then return end
-- whether to accept key inputs if coroutine.status(current.co) ~= "suspended" then return end
pcall(fn, current)
if scheduled_fn == nil then return end artio.schedule(function() pcall(scheduled_fn, current) end) endend
return artio