Files
telescope.nvim/lua/telescope/entry_manager.lua
TJ DeVries 64e59060b1 feat: asyncify pickers - except for live_grep (#709)
* something kind of works already

* yayayayayayayayayayayayayayayayayayayayayayayayayayayayayayayayaya

* use async for everything besides live jobs

* fix: fixup autocmds previewer

* fix: lints for prime

* temp: Add example of how we can think about async sorters

* feat: Allow picker to decide when to cancel

* fix: simplify scoring logic and tests

* fixup: name

* fix: Move back towards more backwards compat methods

* fixup: Remove results from opts

* fixup: remove trailing quote

* fixup: Attempt to clean up some more async items. Next is status

* wip: Add todo for when bfredl implements extmarks over the EOL

* wip

* fixup: got em

* fixup: cleaning

* fixup: docs
2021-04-08 10:35:44 -04:00

185 lines
4.5 KiB
Lua

local log = require("telescope.log")
local LinkedList = require('telescope.algos.linked_list')
--[[
OK, new idea.
We can do linked list here.
To convert at the end to quickfix, just run the list.
...
start node
end node
if past loop of must have scores,
then we can just add to end node and shift end node to current node.
etc.
always inserts a row, because we clear everything before?
can also optimize by keeping worst acceptable score around.
--]]
local EntryManager = {}
EntryManager.__index = EntryManager
function EntryManager:new(max_results, set_entry, info)
log.trace("Creating entry_manager...")
info = info or {}
info.looped = 0
info.inserted = 0
info.find_loop = 0
-- state contains list of
-- { entry, score }
-- Stored directly in a table, accessed as [1], [2]
set_entry = set_entry or function() end
return setmetatable({
linked_states = LinkedList:new { track_at = max_results },
info = info,
max_results = max_results,
set_entry = set_entry,
worst_acceptable_score = math.huge,
}, self)
end
function EntryManager:num_results()
return self.linked_states.size
end
function EntryManager:get_container(index)
local count = 0
for val in self.linked_states:iter() do
count = count + 1
if count == index then
return val
end
end
return {}
end
function EntryManager:get_entry(index)
return self:get_container(index)[1]
end
function EntryManager:get_score(index)
return self:get_container(index)[2]
end
function EntryManager:get_ordinal(index)
return self:get_entry(index).ordinal
end
function EntryManager:find_entry(entry)
local info = self.info
local count = 0
for container in self.linked_states:iter() do
count = count + 1
if container[1] == entry then
info.find_loop = info.find_loop + count
return count
end
end
info.find_loop = info.find_loop + count
return nil
end
function EntryManager:_update_score_from_tracked()
local linked = self.linked_states
if linked.tracked then
self.worst_acceptable_score = math.min(self.worst_acceptable_score, linked.tracked[2])
end
end
function EntryManager:_insert_container_before(picker, index, linked_node, new_container)
self.linked_states:place_before(index, linked_node, new_container)
self.set_entry(picker, index, new_container[1], new_container[2], true)
self:_update_score_from_tracked()
end
function EntryManager:_insert_container_after(picker, index, linked_node, new_container)
self.linked_states:place_after(index, linked_node, new_container)
self.set_entry(picker, index, new_container[1], new_container[2], true)
self:_update_score_from_tracked()
end
function EntryManager:_append_container(picker, new_container, should_update)
self.linked_states:append(new_container)
self.worst_acceptable_score = math.min(self.worst_acceptable_score, new_container[2])
if should_update then
self.set_entry(picker, self.linked_states.size, new_container[1], new_container[2])
end
end
function EntryManager:add_entry(picker, score, entry)
score = score or 0
local max_res = self.max_results
local worst_score = self.worst_acceptable_score
local size = self.linked_states.size
local info = self.info
info.maxed = info.maxed or 0
local new_container = { entry, score, }
-- Short circuit for bad scores -- they never need to be displayed.
-- Just save them and we'll deal with them later.
if score >= worst_score then
return self.linked_states:append(new_container)
end
-- Short circuit for first entry.
if size == 0 then
self.linked_states:prepend(new_container)
self.set_entry(picker, 1, entry, score)
return
end
for index, container, node in self.linked_states:ipairs() do
info.looped = info.looped + 1
if container[2] > score then
-- print("Inserting: ", picker, index, node, new_container)
return self:_insert_container_before(picker, index, node, new_container)
end
-- Don't add results that are too bad.
if index >= max_res then
info.maxed = info.maxed + 1
return self:_append_container(picker, new_container, false)
end
end
if self.linked_states.size >= max_res then
self.worst_acceptable_score = math.min(self.worst_acceptable_score, score)
end
return self:_insert_container_after(picker, size + 1, self.linked_states.tail, new_container)
end
function EntryManager:iter()
return coroutine.wrap(function()
for val in self.linked_states:iter() do
coroutine.yield(val[1])
end
end)
end
return EntryManager