feat(picker): added custom options to vim.ui.select that snacks can use for a better select

This commit is contained in:
Folke Lemaitre 2025-09-29 09:01:19 +02:00
parent 5e0e869852
commit 264cab1380
No known key found for this signature in database
GPG key ID: 9B52594D560070AB
2 changed files with 42 additions and 24 deletions

View file

@ -312,18 +312,18 @@ function M.lsp_symbol(item, picker)
return ret
end
---@param kind? string
---@param count number
---@param opts snacks.picker.ui_select.Opts
---@return snacks.picker.format
function M.ui_select(kind, count)
return function(item)
function M.ui_select(opts)
return function(item, picker)
local count = picker:count()
local ret = {} ---@type snacks.picker.Highlight[]
local idx = tostring(item.idx)
idx = (" "):rep(#tostring(count) - #idx) .. idx
ret[#ret + 1] = { idx .. ".", "SnacksPickerIdx" }
ret[#ret + 1] = { " " }
if kind == "codeaction" then
if opts.kind == "codeaction" then
---@type lsp.Command|lsp.CodeAction, lsp.HandlerContext
local action, ctx = item.item.action, item.item.ctx
local client = vim.lsp.get_client_by_id(ctx.client_id)
@ -332,6 +332,13 @@ function M.ui_select(kind, count)
ret[#ret + 1] = { " " }
ret[#ret + 1] = { ("[%s]"):format(client.name), "SnacksPickerSpecial" }
end
elseif opts.kind == "snacks" and opts.format_item then
local t = opts.format_item(item.item, true)
if type(t) == "string" then
ret[#ret + 1] = { t }
elseif type(t) == "table" then
vim.list_extend(ret, t)
end
else
ret[#ret + 1] = { item.formatted }
end

View file

@ -2,37 +2,44 @@ local M = {}
---@alias snacks.picker.ui_select fun(items: any[], opts?: {prompt?: string, format_item?: (fun(item: any): string), kind?: string}, on_choice: fun(item?: any, idx?: number))
---@class snacks.picker.ui_select.Opts: vim.ui.select.Opts
---@field format_item? fun(item: any, is_snacks: boolean):(string|snacks.picker.Highlight[])
---@field picker? snacks.picker.Config
---@generic T
---@param items T[] Arbitrary items
---@param opts? {prompt?: string, format_item?: (fun(item: T): string), kind?: string}
---@param opts? snacks.picker.ui_select.Opts
---@param on_choice fun(item?: T, idx?: number)
function M.select(items, opts, on_choice)
assert(type(on_choice) == "function", "on_choice must be a function")
opts = opts or {}
---@type snacks.picker.finder.Item[]
local finder_items = {}
for idx, item in ipairs(items) do
local text = (opts.format_item or tostring)(item)
table.insert(finder_items, {
formatted = text,
text = idx .. " " .. text,
item = item,
idx = idx,
})
end
local title = opts.prompt or "Select"
title = title:gsub("^%s*", ""):gsub("[%s:]*$", "")
local completed = false
opts.kind = opts.kind or (opts.picker and "snacks") or opts.kind
---@type snacks.picker.finder.Item[]
return Snacks.picker.pick({
---@type snacks.picker.Config
local picker_opts = {
source = "select",
items = finder_items,
format = Snacks.picker.format.ui_select(opts.kind, #items),
finder = function()
---@type snacks.picker.finder.Item[]
local ret = {}
for idx, item in ipairs(items) do
local text = (opts.format_item or tostring)(item)
---@type snacks.picker.finder.Item
local it = type(item) == "table" and setmetatable({}, { __index = item }) or {}
it.formatted = text
it.text = idx .. " " .. text
it.item = item
it.idx = idx
ret[#ret + 1] = it
end
return ret
end,
format = Snacks.picker.format.ui_select(opts),
title = title,
layout = {
layout = opts.picker and opts.picker.layout or {
preview = false,
layout = {
height = math.floor(math.min(vim.o.lines * 0.8 - 10, #items + 2) + 0.5),
@ -57,7 +64,11 @@ function M.select(items, opts, on_choice)
completed = true
vim.schedule(on_choice)
end,
})
}
if opts.picker then
picker_opts = Snacks.config.merge({}, vim.deepcopy(picker_opts), opts.picker)
end
return Snacks.picker.pick(picker_opts)
end
return M