feat(picker): reworked toggles (flags). they're now configurable. Closes #770

This commit is contained in:
Folke Lemaitre 2025-01-30 14:51:10 +01:00
parent 847509e12c
commit e16a6a4413
7 changed files with 53 additions and 37 deletions

View file

@ -448,12 +448,6 @@ function M.focus_preview(picker)
picker.preview.win:focus()
end
function M.toggle_ignored(picker)
local opts = picker.opts --[[@as snacks.picker.files.Config]]
opts.ignored = not opts.ignored
picker:find()
end
function M.item_action(picker, item, action)
if item.action then
picker:norm(function()
@ -463,19 +457,6 @@ function M.item_action(picker, item, action)
end
end
function M.toggle_hidden(picker)
local opts = picker.opts --[[@as snacks.picker.files.Config]]
opts.hidden = not opts.hidden
picker.list:set_target()
picker:find()
end
function M.toggle_follow(picker)
local opts = picker.opts --[[@as snacks.picker.files.Config]]
opts.follow = not opts.follow
picker:find()
end
function M.list_top(picker)
picker.list:move(1, true)
end

View file

@ -8,6 +8,7 @@ local M = {}
---@alias snacks.picker.sort fun(a:snacks.picker.Item, b:snacks.picker.Item):boolean
---@alias snacks.picker.transform fun(item:snacks.picker.finder.Item, ctx:snacks.picker.finder.ctx):(boolean|snacks.picker.finder.Item|nil)
---@alias snacks.picker.Pos {[1]:number, [2]:number}
---@alias snacks.picker.toggle {icon?:string, enabled?:boolean, value?:boolean}
--- Generic filter used by finders to pre-filter items
---@class snacks.picker.filter.Config
@ -166,6 +167,14 @@ local defaults = {
tagstack = false, -- save the current position in the tagstack
reuse_win = false, -- reuse an existing window if the buffer is already open
},
---@type table<string, string|false|snacks.picker.toggle>
toggles = {
follow = "f",
hidden = "h",
ignored = "i",
modified = "m",
regex = { icon = "R", value = false },
},
win = {
-- input window
input = {
@ -244,6 +253,8 @@ local defaults = {
["<ScrollWheelDown>"] = "list_scroll_wheel_down",
["<ScrollWheelUp>"] = "list_scroll_wheel_up",
["<c-a>"] = "select_all",
["<a-m>"] = { "toggle_maximize" },
["<a-p>"] = { "toggle_preview" },
["<c-f>"] = "preview_scroll_down",
["<c-b>"] = "preview_scroll_up",
["<c-l>"] = "preview_scroll_right",
@ -256,6 +267,9 @@ local defaults = {
["<c-p>"] = "list_up",
["<a-w>"] = "cycle_win",
["<Esc>"] = "close",
["<a-i>"] = "toggle_ignored",
["<a-h>"] = "toggle_hidden",
["<a-f>"] = "toggle_follow",
},
wo = {
conceallevel = 2,

View file

@ -12,10 +12,7 @@ Snacks.util.set_hl({
File = "", -- basename of a file path
Directory = "Directory", -- basename of a directory path
Dir = "NonText", -- dirname of a path
Flag = "DiagnosticVirtualTextInfo",
FlagHidden = "SnacksPickerFlag",
FlagIgnored = "SnacksPickerFlag",
FlagFollow = "SnacksPickerFlag",
Toggle = "DiagnosticVirtualTextInfo",
Dimmed = "Conceal",
Row = "String",
Col = "LineNr",

View file

@ -87,6 +87,21 @@ function M.get(opts)
opts = t.config(opts) or opts
end
end
-- add hl groups and actions for toggles
opts.actions = opts.actions or {}
for name in pairs(opts.toggles) do
local hl = table.concat(vim.tbl_map(function(a)
return a:sub(1, 1):upper() .. a:sub(2)
end, vim.split(name, "_")))
Snacks.util.set_hl({ [hl] = "SnacksPickerToggle" }, { default = true, prefix = "SnacksPickerToggle" })
opts.actions["toggle_" .. name] = function(picker)
picker.opts[name] = not picker.opts[name]
picker.list:set_target()
picker:find()
end
end
M.multi(opts)
return opts
end

View file

@ -16,6 +16,7 @@ M.autocmds = {
---@field unloaded? boolean show loaded buffers
---@field current? boolean show current buffer
---@field nofile? boolean show `buftype=nofile` buffers
---@field modified? boolean show only modified buffers
---@field sort_lastused? boolean sort by last used
---@field filter? snacks.picker.filter.Config
M.buffers = {
@ -232,6 +233,7 @@ M.git_diff = {
---@field rtp? boolean search in runtimepath
M.grep = {
finder = "grep",
regex = true,
format = "file",
live = true, -- live grep by default
supports_live = true,

View file

@ -355,19 +355,25 @@ function M:update_titles()
live = self.opts.live and self.opts.icons.ui.live or "",
preview = vim.trim(self.preview.title or ""),
}
local opts = self.opts --[[@as snacks.picker.files.Config]]
local flags = {} ---@type snacks.picker.Text[]
if opts.follow then
flags[#flags + 1] = { " " .. self.opts.icons.ui.follow .. " ", "SnacksPickerFlagFollow" }
flags[#flags + 1] = { " ", "FloatTitle" }
end
if opts.hidden then
flags[#flags + 1] = { " " .. self.opts.icons.ui.hidden .. " ", "SnacksPickerFlagHidden" }
flags[#flags + 1] = { " ", "FloatTitle" }
end
if opts.ignored then
flags[#flags + 1] = { " " .. self.opts.icons.ui.ignored .. " ", "SnacksPickerFlagIgnored" }
flags[#flags + 1] = { " ", "FloatTitle" }
local toggles = {} ---@type snacks.picker.Text[]
for name, toggle in pairs(self.opts.toggles) do
if toggle then
toggle = type(toggle) == "string" and { icon = toggle } or toggle
toggle = toggle == true and { icon = name:sub(1, 1) } or toggle
toggle = toggle == false and { enabled = false } or toggle
local want = toggle.value
if toggle.value == nil then
want = true
end
---@cast toggle snacks.picker.toggle
if toggle.enabled ~= false and self.opts[name] == want then
local hl = table.concat(vim.tbl_map(function(a)
return a:sub(1, 1):upper() .. a:sub(2)
end, vim.split(name, "_")))
toggles[#toggles + 1] = { " " .. toggle.icon .. " ", "SnacksPickerToggle" .. hl }
toggles[#toggles + 1] = { " ", "FloatTitle" }
end
end
end
local wins = { self.layout.root }
vim.list_extend(wins, vim.tbl_values(self.layout.wins))
@ -380,7 +386,7 @@ function M:update_titles()
local title = Snacks.picker.util.tpl(tpl, data)
if title:find("{flags}", 1, true) then
title = title:gsub("{flags}", "")
vim.list_extend(ret, flags)
vim.list_extend(ret, toggles)
end
title = vim.trim(title):gsub("%s+", " ")
if title ~= "" then

View file

@ -21,6 +21,7 @@ function M.buffers(opts, ctx)
and (opts.unloaded or vim.api.nvim_buf_is_loaded(buf))
and (opts.current or buf ~= current_buf)
and (opts.nofile or vim.bo[buf].buftype ~= "nofile")
and (not opts.modified or vim.bo[buf].modified)
if keep then
local name = vim.api.nvim_buf_get_name(buf)
if name == "" then