feat: quickfix (#293)

* feat: quickfix (not implemented)

* [WIP]: Wed 09 Dec 2020 11:11:30 PM EST

* somewhat working linked list impl

* getting closer

* might be working

* might be working for real

* works and implemented basic example

* dont forget to close prompt

* fix descending and add more tests

* test fixes

* fix test

* more logging

* Fix some more tests

* Fix logging messing up tests

* fix: lint

* fix: multi select stuffs
This commit is contained in:
TJ DeVries
2021-01-11 13:29:37 -05:00
committed by GitHub
parent de80a9837c
commit 8783bea06e
19 changed files with 1152 additions and 369 deletions

View File

@@ -1,131 +1,192 @@
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)
function EntryManager:new(max_results, set_entry, info, id)
log.trace("Creating entry_manager...")
info = info or {}
info.looped = 0
info.inserted = 0
info.find_loop = 0
-- state contains list of
-- {
-- score = ...
-- line = ...
-- metadata ? ...
-- }
local entry_state = {}
-- { entry, score }
-- Stored directly in a table, accessed as [1], [2]
set_entry = set_entry or function() end
return setmetatable({
set_entry = set_entry,
max_results = max_results,
worst_acceptable_score = math.huge,
entry_state = entry_state,
id = id,
linked_states = LinkedList:new { track_at = max_results },
info = info,
num_results = function()
return #entry_state
end,
get_ordinal = function(em, index)
return em:get_entry(index).ordinal
end,
get_entry = function(_, index)
return (entry_state[index] or {}).entry
end,
get_score = function(_, index)
return (entry_state[index] or {}).score
end,
find_entry = function(_, entry)
if entry == nil then
return nil
end
for k, v in ipairs(entry_state) do
local existing_entry = v.entry
-- FIXME: This has the problem of assuming that display will not be the same for two different entries.
if existing_entry == entry then
return k
end
end
return nil
end,
_get_state = function()
return entry_state
end,
max_results = max_results,
set_entry = set_entry,
worst_acceptable_score = math.huge,
}, self)
end
function EntryManager:should_save_result(index)
return index <= self.max_results
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
if score >= self.worst_acceptable_score then
return
end
for index, item in ipairs(self.entry_state) do
self.info.looped = self.info.looped + 1
if item.score > score then
return self:insert(picker, index, {
score = score,
entry = entry,
})
end
-- Don't add results that are too bad.
if not self:should_save_result(index) then
if picker and picker.id then
if picker.request_number ~= self.id then
error("ADDING ENTRY TOO LATE!")
return
end
end
return self:insert(picker, {
score = score,
entry = entry,
})
end
score = score or 0
function EntryManager:insert(picker, index, entry)
if entry == nil then
entry = index
index = #self.entry_state + 1
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
-- To insert something, we place at the next available index (or specified index)
-- and then shift all the corresponding items one place.
local next_entry, last_score
repeat
self.info.inserted = self.info.inserted + 1
next_entry = self.entry_state[index]
self.set_entry(picker, index, entry.entry, entry.score)
self.entry_state[index] = entry
last_score = entry.score
index = index + 1
entry = next_entry
until not next_entry or not self:should_save_result(index)
if not self:should_save_result(index) then
self.worst_acceptable_score = last_score
-- 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