feat(profiler): added support for Snacks.profiler and dropped support for fzf-lua / telescope. Closes #695

This commit is contained in:
Folke Lemaitre 2025-01-23 13:52:15 +01:00
parent 84ee7ddf54
commit ada83de952
No known key found for this signature in database
GPG key ID: 41F8B1FBACAE2040
2 changed files with 37 additions and 157 deletions

View file

@ -37,7 +37,7 @@ require("snacks")
---@class snacks.profiler.Pick: snacks.profiler.Find ---@class snacks.profiler.Pick: snacks.profiler.Find
---@field picker? snacks.profiler.Picker ---@field picker? snacks.profiler.Picker
---@alias snacks.profiler.Picker "auto"|"fzf-lua"|"telescope"|"trouble" ---@alias snacks.profiler.Picker "snacks"|"trouble"
---@alias snacks.profiler.Pick.spec snacks.profiler.Pick|{preset?:string}|fun():snacks.profiler.Pick ---@alias snacks.profiler.Pick.spec snacks.profiler.Pick|{preset?:string}|fun():snacks.profiler.Pick
---@alias snacks.profiler.Field ---@alias snacks.profiler.Field
@ -133,7 +133,7 @@ local defaults = {
align = 80, align = 80,
}, },
pick = { pick = {
picker = "auto", ---@type snacks.profiler.Picker picker = "snacks", ---@type snacks.profiler.Picker
---@type snacks.profiler.Badge.type[] ---@type snacks.profiler.Badge.type[]
badges = { "time", "count", "name" }, badges = { "time", "count", "name" },
---@type snacks.profiler.Highlights ---@type snacks.profiler.Highlights

View file

@ -6,18 +6,6 @@ function M.open(opts)
opts = opts or {} opts = opts or {}
local picker = opts and opts.picker or Snacks.profiler.config.pick.picker local picker = opts and opts.picker or Snacks.profiler.config.pick.picker
if picker == "auto" then
if pcall(require, "fzf-lua") then
picker = "fzf-lua"
elseif pcall(require, "telescope") then
picker = "telescope"
elseif pcall(require, "trouble") then
picker = "trouble"
else
return Snacks.notify.error("No picker found")
end
end
-- special case for trouble, since it does its own thing -- special case for trouble, since it does its own thing
if picker == "trouble" then if picker == "trouble" then
return require("trouble").open({ mode = "profiler", params = opts }) return require("trouble").open({ mode = "profiler", params = opts })
@ -25,154 +13,46 @@ function M.open(opts)
local traces, _, fopts = Snacks.profiler.tracer.find(opts) local traces, _, fopts = Snacks.profiler.tracer.find(opts)
---@alias snacks.profiler.Pick.entry {badges:snacks.profiler.Badge[], path:string, line:number, col:number, text:string[][]}|snacks.profiler.Trace return Snacks.picker({
---@type snacks.profiler.Pick.entry[] title = "Snacks Profiler",
local entries = {} finder = function()
local widths = {} ---@type number[] local items = {} ---@type snacks.picker.finder.Item[]
for _, trace in ipairs(traces) do for _, trace in ipairs(traces) do
local badges = Snacks.profiler.ui.badges(trace, { items[#items + 1] = {
badges = Snacks.profiler.config.pick.badges, text = trace.name,
indent = fopts.group == false or fopts.structure, file = trace.loc and trace.loc.file,
}) pos = trace.loc and { trace.loc.line, 0 },
for b, badge in ipairs(badges) do item = trace,
widths[b] = math.max(widths[b] or 0, vim.api.nvim_strwidth(badge.text)) }
end
local loc = trace.loc
table.insert(
entries,
setmetatable({ badges = badges, path = loc and loc.file, line = loc and loc.line, col = 1 }, { __index = trace })
)
end
for _, entry in ipairs(entries) do
entry.text = Snacks.profiler.ui.format(entry.badges, { widths = widths })
for _, text in ipairs(entry.text) do
if text[2] == "Normal" or text[2] == "SnacksProfilerBadgeTrace" then
text[2] = nil
end end
end return items
end end,
format = function(item)
if #entries == 0 then ---@type snacks.profiler.Trace
return Snacks.notify.warn("No traces found") local trace = item.item
end local ret = Snacks.profiler.ui.format(
Snacks.profiler.ui.badges(trace, {
if picker == "telescope" then badges = Snacks.profiler.config.pick.badges,
M.telescope(entries) indent = fopts.group == false or fopts.structure,
elseif picker == "fzf-lua" then }),
M.fzf_lua(entries) { widths = { 8, 4, 1 } }
else )
return Snacks.notify.error("Not a valid picker `" .. picker .. "`") for _, text in ipairs(ret) do
end if text[2] == "Normal" or text[2] == "SnacksProfilerBadgeTrace" then
end text[2] = nil
---@param entries snacks.profiler.Pick.entry[]
function M.telescope(entries)
local finder = require("telescope.finders").new_table({
results = entries,
---@param entry snacks.profiler.Pick.entry
entry_maker = function(entry)
local text, hl = {}, {} ---@type string[], string[][]
local col = 0
for _, t in ipairs(entry.text) do
text[#text + 1] = t[1]
if t[2] then
table.insert(hl, { { col, col + #t[1] }, t[2] })
end end
col = col + #t[1]
end end
return vim.tbl_extend("force", entry, { return ret
lnum = entry.line, end,
ordinal = entry.name, preview = function(ctx)
display = function() Snacks.picker.preview.file(ctx)
return table.concat(text), hl Snacks.util.wo(ctx.win, { cursorline = true })
end, Snacks.profiler.ui.highlight(
}) ctx.buf,
vim.tbl_extend("force", {}, Snacks.profiler.config.pick.preview, { file = ctx.item.file })
)
end, end,
}) })
local conf = require("telescope.config").values
local topts = {}
local previewer = require("telescope.previewers").new_buffer_previewer({
title = "File Preview",
define_preview = function(self, entry, _status)
conf.buffer_previewer_maker(entry.path, self.state.bufnr, {
bufname = self.state.bufname,
winid = self.state.winid,
callback = function(bufnr)
Snacks.util.wo(self.state.winid, { cursorline = true })
Snacks.profiler.ui.highlight(
self.state.bufnr,
vim.tbl_extend("force", {}, Snacks.profiler.config.pick.preview, { file = entry.path })
)
pcall(vim.api.nvim_win_set_cursor, self.state.winid, { entry.lnum, 0 })
vim.api.nvim_buf_call(bufnr, function()
vim.cmd("norm! zz")
end)
end,
})
end,
})
require("telescope.pickers")
.new(topts, {
results_title = "Snacks Profiler",
prompt_title = "Filter",
finder = finder,
previewer = previewer,
sorter = conf.generic_sorter(topts),
})
:find()
end end
---@param entries snacks.profiler.Pick.entry[]
function M.fzf_lua(entries)
local fzf = require("fzf-lua")
local builtin = require("fzf-lua.previewer.builtin")
local previewer = builtin.buffer_or_file:extend()
function previewer:new(o, fzf_opts, fzf_win)
previewer.super.new(self, o, fzf_opts, fzf_win)
setmetatable(self, previewer)
return self
end
function previewer:parse_entry(entry_str)
local id = tonumber(entry_str:match("^(%d+)") or "0")
return entries[id] or {}
end
function previewer:preview_buf_post(entry, min_winopts)
builtin.buffer_or_file.preview_buf_post(self, entry, min_winopts)
Snacks.profiler.ui.highlight(
self.preview_bufnr,
vim.tbl_extend("force", {}, Snacks.profiler.config.pick.preview, { file = entry.path })
)
end
local contents = {} ---@type string[]
for e, entry in ipairs(entries) do
local display = { e .. " " } ---@type string[]
for _, text in ipairs(entry.text) do
display[#display + 1] = text[2] and fzf.utils.ansi_from_hl(text[2], text[1]) or text[1]
end
contents[#contents + 1] = table.concat(display)
end
require("fzf-lua").fzf_exec(contents, {
previewer = previewer,
-- multiline = true,
actions = {
-- Use fzf-lua builtin actions or your own handler
["default"] = function(selection, fzf_opts)
fzf.actions.file_edit(
vim.tbl_map(function(sel)
local id = tonumber(sel:match("^(%d+)") or "0")
return entries[id].path .. ":" .. entries[id].line
end, selection),
fzf_opts
)
end,
},
fzf_opts = {
["--no-multi"] = "",
["--with-nth"] = "2..",
["--no-sort"] = true,
},
})
end
return M return M