mirror of
https://github.com/folke/snacks.nvim
synced 2025-08-04 10:49:08 +00:00
feat(snacks): added snacks.picker
(#445)
## Description More info coming tomorrow. In short: - very fast. pretty much realtime filtering/sorting in huge repos (like 1.7 million files) - extensible - easy to customize the layout (and lots of presets) with `snacks.layout` - simple to create custom pickers - `vim.ui.select` - lots of builtin pickers - uses treesitter highlighting wherever it makes sense - fast lua fuzzy matcher which supports the [fzf syntax](https://junegunn.github.io/fzf/search-syntax/) and additionally supports field filters, like `file:lua$ 'function` There's no snacks picker command, just use lua. ```lua -- all pickers Snacks.picker() -- run files picker Snacks.picker.files(opts) Snacks.picker.pick("files", opts) Snacks.picker.pick({source = "files", ...}) ``` <!-- Describe the big picture of your changes to communicate to the maintainers why we should accept this pull request. --> ## Todo - [x] issue with preview loc not always correct when scrolling fast in list (probably due to `snacks.scroll`) - [x] `grep` (`live_grep`) is sometimes too fast in large repos and can impact ui rendering. Not very noticeable, but something I want to look at. - [x] docs - [x] treesitter highlights are broken. Messed something up somewhere ## Related Issue(s) <!-- If this PR fixes any issues, please link to the issue here. - Fixes #<issue_number> --> ## Screenshots <!-- Add screenshots of the changes if applicable. -->
This commit is contained in:
parent
1b7a57a0b1
commit
559d6c6bf2
67 changed files with 12013 additions and 126 deletions
147
lua/snacks/picker/util/minheap.lua
Normal file
147
lua/snacks/picker/util/minheap.lua
Normal file
|
@ -0,0 +1,147 @@
|
|||
---@class snacks.picker.MinHeap
|
||||
---@field data any[] -- the heap array
|
||||
---@field cmp fun(a:any, b:any):boolean -- determines "priority"; if cmp(a,b) == true, a is considered 'larger' for top-k
|
||||
---@field capacity number
|
||||
---@field sorted? snacks.picker.Item[]
|
||||
local M = {}
|
||||
M.__index = M
|
||||
|
||||
---@class snacks.picker.minheap.Config
|
||||
---@field cmp? fun(a, b):boolean
|
||||
---@field capacity number
|
||||
|
||||
---@param opts? snacks.picker.minheap.Config
|
||||
function M.new(opts)
|
||||
opts = opts or {}
|
||||
local self = setmetatable({}, M)
|
||||
|
||||
-- Default comparator means: a > b => a is 'better' (we want the top by value)
|
||||
-- So if we want the top K largest items, the heap is min-heap based on that comparator
|
||||
self.cmp = opts.cmp or function(a, b)
|
||||
return a > b
|
||||
end
|
||||
self.capacity = assert(opts.capacity, "capacity is required")
|
||||
assert(self.capacity > 0, "capacity must be greater than 0")
|
||||
self.data = {}
|
||||
return self
|
||||
end
|
||||
|
||||
function M:clear()
|
||||
self.data = {}
|
||||
end
|
||||
|
||||
-- Private: swap two indices
|
||||
function M:_swap(i, j)
|
||||
self.data[i], self.data[j] = self.data[j], self.data[i]
|
||||
end
|
||||
|
||||
-- Private: heapify up (bubble up)
|
||||
function M:_heapify_up(idx)
|
||||
while idx > 1 do
|
||||
local parent = math.floor(idx / 2)
|
||||
-- If child is 'less' than parent under the min-heap logic, swap
|
||||
-- Because self.cmp(child, parent) == true => child is 'bigger' => for min-heap we want bigger below
|
||||
-- So we invert self.cmp because we want to keep the smallest at top:
|
||||
if self.cmp(self.data[parent], self.data[idx]) then
|
||||
self:_swap(parent, idx)
|
||||
idx = parent
|
||||
else
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Private: heapify down
|
||||
function M:_heapify_down(idx)
|
||||
local size = #self.data
|
||||
while true do
|
||||
local left = 2 * idx
|
||||
local right = left + 1
|
||||
local smallest = idx
|
||||
|
||||
if left <= size and self.cmp(self.data[smallest], self.data[left]) then
|
||||
smallest = left
|
||||
end
|
||||
if right <= size and self.cmp(self.data[smallest], self.data[right]) then
|
||||
smallest = right
|
||||
end
|
||||
if smallest ~= idx then
|
||||
self:_swap(idx, smallest)
|
||||
idx = smallest
|
||||
else
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- Insert value into the min-heap of capacity K.
|
||||
--- If the heap is not full, just insert.
|
||||
--- If it's full and the value is 'larger' than the min (root), replace the root & heapify.
|
||||
---@generic T
|
||||
---@param value T
|
||||
---@return boolean added, T? evicted
|
||||
function M:add(value)
|
||||
local size = #self.data
|
||||
if size < self.capacity then
|
||||
-- Just insert at the end, heapify up
|
||||
table.insert(self.data, value)
|
||||
self:_heapify_up(#self.data)
|
||||
self.sorted = nil
|
||||
return true
|
||||
else
|
||||
-- If new value is larger than the root (which is the smallest in the min-heap),
|
||||
-- then pop root & insert new value
|
||||
if self.cmp(value, self.data[1]) then
|
||||
local evicted = self.data[1]
|
||||
self.data[1] = value
|
||||
self:_heapify_down(1)
|
||||
self.sorted = nil
|
||||
return true, evicted
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function M:count()
|
||||
return #self.data
|
||||
end
|
||||
|
||||
---@return any|nil
|
||||
function M:min()
|
||||
return self.data[1]
|
||||
end
|
||||
|
||||
---@return any|nil
|
||||
function M:max()
|
||||
-- might need to scan if you want the max element in a min-heap
|
||||
local size = #self.data
|
||||
if size == 0 then
|
||||
return nil
|
||||
end
|
||||
local maximum = self.data[1]
|
||||
for i = 2, size do
|
||||
if self.cmp(self.data[i], maximum) then
|
||||
maximum = self.data[i]
|
||||
end
|
||||
end
|
||||
return maximum
|
||||
end
|
||||
|
||||
---@param idx number
|
||||
---@return snacks.picker.Item?
|
||||
---@overload fun(self: snacks.picker.MinHeap): snacks.picker.Item[]
|
||||
function M:get(idx)
|
||||
if not self.sorted then
|
||||
self.sorted = {}
|
||||
for i = 1, #self.data do
|
||||
table.insert(self.sorted, self.data[i])
|
||||
end
|
||||
table.sort(self.sorted, self.cmp)
|
||||
end
|
||||
if idx then
|
||||
return self.sorted[idx]
|
||||
end
|
||||
return self.sorted
|
||||
end
|
||||
|
||||
return M
|
Loading…
Add table
Add a link
Reference in a new issue