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

@@ -38,6 +38,16 @@ function actions.add_selection(prompt_bufnr)
current_picker:add_selection(current_picker:get_selection_row()) current_picker:add_selection(current_picker:get_selection_row())
end end
function actions.remove_selection(prompt_bufnr)
local current_picker = actions.get_current_picker(prompt_bufnr)
current_picker:remove_selection(current_picker:get_selection_row())
end
function actions.toggle_selection(prompt_bufnr)
local current_picker = actions.get_current_picker(prompt_bufnr)
current_picker:toggle_selection(current_picker:get_selection_row())
end
--- Get the current entry --- Get the current entry
function actions.get_selected_entry() function actions.get_selected_entry()
return state.get_global_key('selected_entry') return state.get_global_key('selected_entry')
@@ -273,6 +283,45 @@ actions.git_staging_toggle = function(prompt_bufnr)
require('telescope.builtin').git_status() require('telescope.builtin').git_status()
end end
local entry_to_qf = function(entry)
return {
bufnr = entry.bufnr,
filename = entry.filename,
lnum = entry.lnum,
col = entry.col,
text = entry.value,
}
end
actions.send_selected_to_qflist = function(prompt_bufnr)
local picker = actions.get_current_picker(prompt_bufnr)
local qf_entries = {}
for entry in pairs(picker.multi_select) do
table.insert(qf_entries, entry_to_qf(entry))
end
actions.close(prompt_bufnr)
vim.fn.setqflist(qf_entries, 'r')
vim.cmd [[copen]]
end
actions.send_to_qflist = function(prompt_bufnr)
local picker = actions.get_current_picker(prompt_bufnr)
local manager = picker.manager
local qf_entries = {}
for entry in manager:iter() do
table.insert(qf_entries, entry_to_qf(entry))
end
actions.close(prompt_bufnr)
vim.fn.setqflist(qf_entries, 'r')
vim.cmd [[copen]]
end
-- ================================================== -- ==================================================
-- Transforms modules and sets the corect metatables. -- Transforms modules and sets the corect metatables.
-- ================================================== -- ==================================================

View File

@@ -0,0 +1,222 @@
local LinkedList = {}
LinkedList.__index = LinkedList
function LinkedList:new(opts)
opts = opts or {}
local track_at = opts.track_at
return setmetatable({
size = 0,
head = false,
tail = false,
-- track_at: Track at can track a particular node
-- Use to keep a node tracked at a particular index
-- This greatly decreases looping for checking values at this location.
track_at = track_at,
_tracked_node = nil,
tracked = nil,
}, self)
end
function LinkedList:_increment()
self.size = self.size + 1
return self.size
end
local create_node = function(item)
return {
item = item
}
end
function LinkedList:append(item)
local final_size = self:_increment()
local node = create_node(item)
if not self.head then
self.head = node
end
if self.tail then
self.tail.next = node
node.prev = self.tail
end
self.tail = node
if self.track_at then
if final_size == self.track_at then
self.tracked = item
self._tracked_node = node
end
end
end
function LinkedList:prepend(item)
local final_size = self:_increment()
local node = create_node(item)
if not self.tail then
self.tail = node
end
if self.head then
self.head.prev = node
node.next = self.head
end
self.head = node
if self.track_at then
if final_size == self.track_at then
self._tracked_node = self.tail
elseif final_size > self.track_at then
self._tracked_node = self._tracked_node.prev
else
return
end
self.tracked = self._tracked_node.item
end
end
-- [a, b, c]
-- b.prev = a
-- b.next = c
--
-- a.next = b
-- c.prev = c
--
-- insert d after b
-- [a, b, d, c]
--
-- b.next = d
-- b.prev = a
--
-- Place "item" after "node" (which is at index `index`)
function LinkedList:place_after(index, node, item)
local new_node = create_node(item)
assert(node.prev ~= node)
assert(node.next ~= node)
local final_size = self:_increment()
-- Update tail to be the next node.
if self.tail == node then
self.tail = new_node
end
new_node.prev = node
new_node.next = node.next
node.next = new_node
if new_node.prev then
new_node.prev.next = new_node
end
if new_node.next then
new_node.next.prev = new_node
end
if self.track_at then
if index == self.track_at then
self._tracked_node = new_node
elseif index < self.track_at then
if final_size == self.track_at then
self._tracked_node = self.tail
elseif final_size > self.track_at then
self._tracked_node = self._tracked_node.prev
else
return
end
end
self.tracked = self._tracked_node.item
end
end
function LinkedList:place_before(index, node, item)
local new_node = create_node(item)
assert(node.prev ~= node)
assert(node.next ~= node)
local final_size = self:_increment()
-- Update head to be the node we are inserting.
if self.head == node then
self.head = new_node
end
new_node.prev = node.prev
new_node.next = node
node.prev = new_node
-- node.next = node.next
if new_node.prev then
new_node.prev.next = new_node
end
if new_node.next then
new_node.next.prev = new_node
end
if self.track_at then
if index == self.track_at - 1 then
self._tracked_node = node
elseif index < self.track_at then
if final_size == self.track_at then
self._tracked_node = self.tail
elseif final_size > self.track_at then
self._tracked_node = self._tracked_node.prev
else
return
end
end
self.tracked = self._tracked_node.item
end
end
-- Do you even do this in linked lists...?
-- function LinkedList:remove(item)
-- end
function LinkedList:iter()
local current_node = self.head
return function()
local node = current_node
if not node then
return nil
end
current_node = current_node.next
return node.item
end
end
function LinkedList:ipairs()
local index = 0
local current_node = self.head
return function()
local node = current_node
if not node then
return nil
end
current_node = current_node.next
index = index + 1
return index, node.item, node
end
end
return LinkedList

View File

@@ -34,7 +34,7 @@ function config.set_defaults(defaults)
set("sorting_strategy", "descending") set("sorting_strategy", "descending")
set("selection_strategy", "reset") set("selection_strategy", "reset")
set("scroll_strategy", nil) set("scroll_strategy", "cycle")
set("layout_strategy", "horizontal") set("layout_strategy", "horizontal")
set("layout_defaults", {}) set("layout_defaults", {})
@@ -53,7 +53,7 @@ function config.set_defaults(defaults)
set("borderchars", { '', '', '', '', '', '', '', ''}) set("borderchars", { '', '', '', '', '', '', '', ''})
set("get_status_text", function(self) set("get_status_text", function(self)
return string.format("%s / %s", self.stats.processed - self.stats.filtered, self.stats.processed) return string.format("%s / %s", (self.stats.processed or 0) - (self.stats.filtered or 0), self.stats.processed)
end) end)
-- Builtin configuration -- Builtin configuration

View File

@@ -1,131 +1,192 @@
local log = require("telescope.log") 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 = {} local EntryManager = {}
EntryManager.__index = 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...") log.trace("Creating entry_manager...")
info = info or {} info = info or {}
info.looped = 0 info.looped = 0
info.inserted = 0 info.inserted = 0
info.find_loop = 0
-- state contains list of -- state contains list of
-- { -- { entry, score }
-- score = ... -- Stored directly in a table, accessed as [1], [2]
-- line = ...
-- metadata ? ...
-- }
local entry_state = {}
set_entry = set_entry or function() end set_entry = set_entry or function() end
return setmetatable({ return setmetatable({
set_entry = set_entry, id = id,
max_results = max_results, linked_states = LinkedList:new { track_at = max_results },
worst_acceptable_score = math.huge,
entry_state = entry_state,
info = info, info = info,
max_results = max_results,
num_results = function() set_entry = set_entry,
return #entry_state worst_acceptable_score = math.huge,
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,
}, self) }, self)
end end
function EntryManager:should_save_result(index) function EntryManager:num_results()
return index <= self.max_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 end
function EntryManager:add_entry(picker, score, entry) function EntryManager:add_entry(picker, score, entry)
score = score or 0 if picker and picker.id then
if picker.request_number ~= self.id then
if score >= self.worst_acceptable_score then error("ADDING ENTRY TOO LATE!")
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
return return
end end
end end
return self:insert(picker, { score = score or 0
score = score,
entry = entry,
})
end
function EntryManager:insert(picker, index, entry) local max_res = self.max_results
if entry == nil then local worst_score = self.worst_acceptable_score
entry = index local size = self.linked_states.size
index = #self.entry_state + 1
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 end
-- To insert something, we place at the next available index (or specified index) -- Short circuit for first entry.
-- and then shift all the corresponding items one place. if size == 0 then
local next_entry, last_score self.linked_states:prepend(new_container)
repeat self.set_entry(picker, 1, entry, score)
self.info.inserted = self.info.inserted + 1 return
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
end 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 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 return EntryManager

View File

@@ -221,14 +221,17 @@ function Picker:is_done()
end end
function Picker:clear_extra_rows(results_bufnr) function Picker:clear_extra_rows(results_bufnr)
if self:is_done() then return end if self:is_done() then
log.trace("Not clearing due to being already complete")
return
end
if not vim.api.nvim_buf_is_valid(results_bufnr) then if not vim.api.nvim_buf_is_valid(results_bufnr) then
log.debug("Invalid results_bufnr for clearing:", results_bufnr) log.debug("Invalid results_bufnr for clearing:", results_bufnr)
return return
end end
local worst_line local worst_line, ok, msg
if self.sorting_strategy == 'ascending' then if self.sorting_strategy == 'ascending' then
local num_results = self.manager:num_results() local num_results = self.manager:num_results()
worst_line = self.max_results - num_results worst_line = self.max_results - num_results
@@ -237,7 +240,7 @@ function Picker:clear_extra_rows(results_bufnr)
return return
end end
pcall(vim.api.nvim_buf_set_lines, results_bufnr, num_results, self.max_results, false, {}) ok, msg = pcall(vim.api.nvim_buf_set_lines, results_bufnr, num_results, -1, false, {})
else else
worst_line = self:get_row(self.manager:num_results()) worst_line = self:get_row(self.manager:num_results())
if worst_line <= 0 then if worst_line <= 0 then
@@ -245,10 +248,14 @@ function Picker:clear_extra_rows(results_bufnr)
end end
local empty_lines = utils.repeated_table(worst_line, "") local empty_lines = utils.repeated_table(worst_line, "")
pcall(vim.api.nvim_buf_set_lines, results_bufnr, 0, worst_line, false, empty_lines) ok, msg = pcall(vim.api.nvim_buf_set_lines, results_bufnr, 0, worst_line, false, empty_lines)
end end
log.trace("Clearing:", worst_line) if not ok then
log.debug(msg)
end
log.debug("Clearing:", worst_line)
end end
function Picker:highlight_displayed_rows(results_bufnr, prompt) function Picker:highlight_displayed_rows(results_bufnr, prompt)
@@ -296,6 +303,9 @@ function Picker:highlight_one_row(results_bufnr, prompt, display, row)
) )
end end
end end
local entry = self.manager:get_entry(self:get_index(row))
self.highlighter:hi_multiselect(row, entry)
end end
function Picker:can_select_row(row) function Picker:can_select_row(row)
@@ -416,7 +426,9 @@ function Picker:find()
local debounced_status = debounce.throttle_leading(update_status, 50) local debounced_status = debounce.throttle_leading(update_status, 50)
self.request_number = 0
local on_lines = function(_, _, _, first_line, last_line) local on_lines = function(_, _, _, first_line, last_line)
self.request_number = self.request_number + 1
self:_reset_track() self:_reset_track()
if not vim.api.nvim_buf_is_valid(prompt_bufnr) then if not vim.api.nvim_buf_is_valid(prompt_bufnr) then
@@ -424,6 +436,9 @@ function Picker:find()
return return
end end
if not first_line then first_line = 0 end
if not last_line then last_line = 1 end
if first_line > 0 or last_line > 1 then if first_line > 0 or last_line > 1 then
log.debug("ON_LINES: Bad range", first_line, last_line) log.debug("ON_LINES: Bad range", first_line, last_line)
return return
@@ -434,13 +449,8 @@ function Picker:find()
self.sorter:_start(prompt) self.sorter:_start(prompt)
end end
-- TODO: Statusbar possibilities here.
-- vim.api.nvim_buf_set_virtual_text(prompt_bufnr, 0, 1, { {"hello", "Error"} }, {})
-- TODO: Entry manager should have a "bulk" setter. This can prevent a lot of redraws from display -- TODO: Entry manager should have a "bulk" setter. This can prevent a lot of redraws from display
self.manager = EntryManager:new(self.max_results, self.entry_adder, self.stats, self.request_number)
self.manager = EntryManager:new(self.max_results, self.entry_adder)
-- self.manager = EntryManager:new(self.max_results, self.entry_adder, self.stats)
local process_result = function(entry) local process_result = function(entry)
if self:is_done() then return end if self:is_done() then return end
@@ -690,24 +700,48 @@ end
function Picker:add_selection(row) function Picker:add_selection(row)
local entry = self.manager:get_entry(self:get_index(row)) local entry = self.manager:get_entry(self:get_index(row))
self.multi_select[entry] = true self.multi_select[entry] = true
self.highlighter:hi_multiselect(row, entry)
end
function Picker:add_selection(row)
local entry = self.manager:get_entry(self:get_index(row))
self.multi_select[entry] = true
self.highlighter:hi_multiselect(row, entry)
end
function Picker:remove_selection(row)
local entry = self.manager:get_entry(self:get_index(row))
self.multi_select[entry] = nil
self.highlighter:hi_multiselect(row, entry)
end
function Picker:toggle_selection(row)
local entry = self.manager:get_entry(self:get_index(row))
if self.multi_select[entry] then
self:remove_selection(row)
else
self:add_selection(row)
end
end end
function Picker:display_multi_select(results_bufnr) function Picker:display_multi_select(results_bufnr)
if true then return end for entry, _ in pairs(self.multi_select) do
local index = self.manager:find_entry(entry)
-- for entry, _ in pairs(self.multi_select) do if index then
-- local index = self.manager:find_entry(entry) vim.api.nvim_buf_add_highlight(
-- if index then results_bufnr,
-- vim.api.nvim_buf_add_highlight( a.nvim_create_namespace('telescope_selection'),
-- results_bufnr, "TelescopeMultiSelection",
-- ns_telescope_selection, self:get_row(index),
-- "TelescopeMultiSelection", 0,
-- self:get_row(index), -1
-- 0, )
-- -1 end
-- ) end
-- end
-- end
end end
function Picker:reset_selection() function Picker:reset_selection()
@@ -718,25 +752,30 @@ function Picker:reset_selection()
end end
function Picker:set_selection(row) function Picker:set_selection(row)
-- TODO: Loop around behavior?
-- TODO: Scrolling past max results
row = self.scroller(self.max_results, self.manager:num_results(), row) row = self.scroller(self.max_results, self.manager:num_results(), row)
if not self:can_select_row(row) then if not self:can_select_row(row) then
-- If the current selected row exceeds number of currently displayed -- If the current selected row exceeds number of currently displayed
-- elements we have to reset it. Affectes sorting_strategy = 'row'. -- elements we have to reset it. Affects sorting_strategy = 'row'.
if not self:can_select_row(self:get_selection_row()) then if not self:can_select_row(self:get_selection_row()) then
row = self:get_row(self.manager:num_results()) row = self:get_row(self.manager:num_results())
else else
log.debug("Cannot select row:", row, self.manager:num_results(), self.max_results) log.trace("Cannot select row:", row, self.manager:num_results(), self.max_results)
return return
end end
end end
-- local entry = self.manager:get_entry(self.max_results - row + 1)
local entry = self.manager:get_entry(self:get_index(row)) local entry = self.manager:get_entry(self:get_index(row))
local status = state.get_status(self.prompt_bufnr) local status = state.get_status(self.prompt_bufnr)
local results_bufnr = status.results_bufnr local results_bufnr = self.results_bufnr
if row > a.nvim_buf_line_count(results_bufnr) then
error(string.format(
"Should not be possible to get row this large %s %s",
row,
a.nvim_buf_line_count(results_bufnr)
))
end
state.set_global_key("selected_entry", entry) state.set_global_key("selected_entry", entry)
@@ -764,6 +803,7 @@ function Picker:set_selection(row)
self.highlighter:hi_display(self._selection_row, ' ', display_highlights) self.highlighter:hi_display(self._selection_row, ' ', display_highlights)
self.highlighter:hi_sorter(self._selection_row, prompt, display) self.highlighter:hi_sorter(self._selection_row, prompt, display)
self.highlighter:hi_multiselect(self._selection_row, self._selection_entry)
end end
end end
@@ -785,9 +825,7 @@ function Picker:set_selection(row)
self.highlighter:hi_selection(row, caret) self.highlighter:hi_selection(row, caret)
self.highlighter:hi_display(row, ' ', display_highlights) self.highlighter:hi_display(row, ' ', display_highlights)
self.highlighter:hi_sorter(row, prompt, display) self.highlighter:hi_sorter(row, prompt, display)
self.highlighter:hi_multiselect(row, entry)
-- TODO: Actually implement this for real TJ, don't leave around half implemented code plz :)
-- self:display_multi_select(results_bufnr)
end) end)
if not set_ok then if not set_ok then
@@ -814,7 +852,7 @@ function Picker:set_selection(row)
end end
function Picker:entry_adder(index, entry, score) function Picker:entry_adder(index, entry, score, insert)
local row = self:get_row(index) local row = self:get_row(index)
-- If it's less than 0, then we don't need to show it at all. -- If it's less than 0, then we don't need to show it at all.
@@ -838,17 +876,39 @@ function Picker:entry_adder(index, entry, score)
self:_increment("displayed") self:_increment("displayed")
-- TODO: Don't need to schedule this if we schedule the adder. -- TODO: Don't need to schedule this if we schedule the adder.
local offset = insert and 0 or 1
local scheduled_request = self.request_number
vim.schedule(function() vim.schedule(function()
if not vim.api.nvim_buf_is_valid(self.results_bufnr) then if not vim.api.nvim_buf_is_valid(self.results_bufnr) then
log.debug("ON_ENTRY: Invalid buffer") log.debug("ON_ENTRY: Invalid buffer")
return return
end end
local set_ok = pcall(vim.api.nvim_buf_set_lines, self.results_bufnr, row, row + 1, false, {display}) if self.request_number ~= scheduled_request then
log.debug("Cancelling request number:", self.request_number, " // ", scheduled_request)
return
end
local line_count = vim.api.nvim_buf_line_count(self.results_bufnr)
if row > line_count then
return
end
if insert then
if self.sorting_strategy == 'descending' then
vim.api.nvim_buf_set_lines(self.results_bufnr, 0, 1, false, {})
end
end
local set_ok, msg = pcall(vim.api.nvim_buf_set_lines, self.results_bufnr, row, row + offset, false, {display})
if set_ok and display_highlights then if set_ok and display_highlights then
self.highlighter:hi_display(row, prefix, display_highlights) self.highlighter:hi_display(row, prefix, display_highlights)
end end
if not set_ok then
log.debug("Failed to set lines...", msg)
end
-- This pretty much only fails when people leave newlines in their results. -- This pretty much only fails when people leave newlines in their results.
-- So we'll clean it up for them if it fails. -- So we'll clean it up for them if it fails.
if not set_ok and display:find("\n") then if not set_ok and display:find("\n") then
@@ -893,7 +953,7 @@ function Picker:_track(key, func, ...)
end end
function Picker:_increment(key) function Picker:_increment(key)
self.stats[key] = self.stats[key] + 1 self.stats[key] = (self.stats[key] or 0) + 1
end end

View File

@@ -1,10 +1,14 @@
local assert = require('luassert') local assert = require('luassert')
local builtin = require('telescope.builtin') local builtin = require('telescope.builtin')
local log = require('telescope.log')
local Job = require("plenary.job") local Job = require("plenary.job")
local Path = require("plenary.path")
local tester = {} local tester = {}
tester.debug = false
local replace_terms = function(input) local replace_terms = function(input)
return vim.api.nvim_replace_termcodes(input, true, false, true) return vim.api.nvim_replace_termcodes(input, true, false, true)
end end
@@ -15,7 +19,53 @@ local nvim_feed = function(text, feed_opts)
vim.api.nvim_feedkeys(text, feed_opts, true) vim.api.nvim_feedkeys(text, feed_opts, true)
end end
tester.picker_feed = function(input, test_cases, debug) local writer = function(val)
if type(val) == "table" then
val = vim.fn.json_encode(val) .. "\n"
end
if tester.debug then
print(val)
else
io.stderr:write(val)
end
end
local execute_test_case = function(location, key, spec)
local ok, actual = pcall(spec[2])
if not ok then
writer {
location = 'Error: ' .. location,
case = key,
expected = 'To succeed and return: ' .. tostring(spec[1]),
actual = actual,
_type = spec._type,
}
else
writer {
location = location,
case = key,
expected = spec[1],
actual = actual,
_type = spec._type,
}
end
end
local end_test_cases = function()
vim.cmd [[qa!]]
end
local invalid_test_case = function(k)
writer { case = k, expected = '<a valid key>', actual = k }
end_test_cases()
end
tester.picker_feed = function(input, test_cases)
input = replace_terms(input) input = replace_terms(input)
return coroutine.wrap(function() return coroutine.wrap(function()
@@ -28,75 +78,66 @@ tester.picker_feed = function(input, test_cases, debug)
if string.match(char, "%g") then if string.match(char, "%g") then
coroutine.yield() coroutine.yield()
end end
if tester.debug then
vim.wait(200)
end
end end
vim.wait(10, function() end) vim.wait(10)
local timer = vim.loop.new_timer() if tester.debug then
timer:start(20, 0, vim.schedule_wrap(function() coroutine.yield()
if test_cases.post_close then end
for k, v in ipairs(test_cases.post_close) do
io.stderr:write(vim.fn.json_encode({ case = k, expected = v[1], actual = v[2]() })) vim.defer_fn(function()
io.stderr:write("\n") if test_cases.post_typed then
for k, v in ipairs(test_cases.post_typed) do
execute_test_case('post_typed', k, v)
end end
end end
if debug then nvim_feed(replace_terms("<CR>"), "")
end, 20)
vim.defer_fn(function()
if test_cases.post_close then
for k, v in ipairs(test_cases.post_close) do
execute_test_case('post_close', k, v)
end
end
if tester.debug then
return return
end end
vim.defer_fn(function() vim.defer_fn(end_test_cases, 20)
vim.cmd [[qa!]] end, 40)
end, 10)
end))
if not debug then
vim.schedule(function()
if test_cases.post_typed then
for k, v in ipairs(test_cases.post_typed) do
io.stderr:write(vim.fn.json_encode({ case = k, expected = v[1], actual = v[2]() }))
io.stderr:write("\n")
end
end
nvim_feed(replace_terms("<CR>"), "")
end)
end
coroutine.yield() coroutine.yield()
end) end)
end end
-- local test_cases = {
-- post_typed = {
-- },
-- post_close = {
-- { "README.md", function() return "README.md" end },
-- },
-- }
local _VALID_KEYS = { local _VALID_KEYS = {
post_typed = true, post_typed = true,
post_close = true, post_close = true,
} }
tester.builtin_picker = function(key, input, test_cases, opts) tester.builtin_picker = function(builtin_key, input, test_cases, opts)
opts = opts or {} opts = opts or {}
local debug = opts.debug or false tester.debug = opts.debug or false
for k, _ in pairs(test_cases) do for k, _ in pairs(test_cases) do
if not _VALID_KEYS[k] then if not _VALID_KEYS[k] then
-- TODO: Make an error type for the json protocol. return invalid_test_case(k)
io.stderr:write(vim.fn.json_encode({ case = k, expected = '<a valid key>', actual = k }))
io.stderr:write("\n")
vim.cmd [[qa!]]
end end
end end
opts.on_complete = { opts.on_complete = {
tester.picker_feed(input, test_cases, debug) tester.picker_feed(input, test_cases),
} }
builtin[key](opts) builtin[builtin_key](opts)
end end
local get_results_from_file = function(file) local get_results_from_file = function(file)
@@ -107,11 +148,14 @@ local get_results_from_file = function(file)
'-u', '-u',
'scripts/minimal_init.vim', 'scripts/minimal_init.vim',
'-c', '-c',
'luafile ' .. file string.format(
[[lua require("telescope.pickers._test")._execute("%s")]],
file
),
}, },
} }
j:sync() j:sync(1000)
local results = j:stderr_result() local results = j:stderr_result()
local result_table = {} local result_table = {}
@@ -122,10 +166,27 @@ local get_results_from_file = function(file)
return result_table return result_table
end end
local asserters = {
_default = assert.are.same,
are = assert.are.same,
are_not = assert.are_not.same,
}
local check_results = function(results) local check_results = function(results)
-- TODO: We should get all the test cases here that fail, not just the first one. -- TODO: We should get all the test cases here that fail, not just the first one.
for _, v in ipairs(results) do for _, v in ipairs(results) do
assert.are.same(v.expected, v.actual) local assertion = asserters[v._type or 'default']
assertion(
v.expected,
v.actual,
string.format("Test Case: %s // %s",
v.location,
v.case)
)
end end
end end
@@ -144,14 +205,52 @@ tester.run_string = function(contents)
vim.fn.delete(tempname) vim.fn.delete(tempname)
check_results(result_table) check_results(result_table)
-- assert.are.same(result_table.expected, result_table.actual)
end end
tester.run_file = function(filename) tester.run_file = function(filename)
local file = './lua/tests/pickers/' .. filename .. '.lua' local file = './lua/tests/pickers/' .. filename .. '.lua'
if not Path:new(file):exists() then
assert.are.same("<An existing file>", file)
end
local result_table = get_results_from_file(file) local result_table = get_results_from_file(file)
assert.are.same(result_table.expected, result_table.actual)
check_results(result_table)
end end
tester.not_ = function(val)
val._type = 'are_not'
return val
end
tester._execute = function(filename)
-- Important so that the outputs don't get mixed
log.use_console = false
vim.cmd(string.format("luafile %s", filename))
local f = loadfile(filename)
if not f then
writer {
location = 'Error: ' .. filename,
case = filename,
expected = 'To succeed',
actual = nil,
}
end
local ok, msg = pcall(f)
if not ok then
writer {
location = "Error: " .. msg,
case = msg,
expected = msg,
}
end
end_test_cases()
end
return tester return tester

View File

@@ -22,9 +22,15 @@ test_helpers.get_results = function()
return vim.api.nvim_buf_get_lines(test_helpers.get_results_bufnr(), 0, -1, false) return vim.api.nvim_buf_get_lines(test_helpers.get_results_bufnr(), 0, -1, false)
end end
test_helpers.get_last_result = function() test_helpers.get_best_result = function()
local results = test_helpers.get_results() local results = test_helpers.get_results()
return results[#results] local picker = test_helpers.get_picker ()
if picker.sorting_strategy == 'ascending' then
return results[1]
else
return results[#results]
end
end end
test_helpers.get_selection = function() test_helpers.get_selection = function()
@@ -41,7 +47,7 @@ test_helpers.make_globals = function()
GetPrompt = test_helpers.get_prompt -- luacheck: globals GetPrompt GetPrompt = test_helpers.get_prompt -- luacheck: globals GetPrompt
GetResults = test_helpers.get_results -- luacheck: globals GetResults GetResults = test_helpers.get_results -- luacheck: globals GetResults
GetLastResult = test_helpers.get_last_result -- luacheck: globals GetLastResult GetBestResult = test_helpers.get_best_result -- luacheck: globals GetBestResult
GetSelection = test_helpers.get_selection -- luacheck: globals GetSelection GetSelection = test_helpers.get_selection -- luacheck: globals GetSelection
GetSelectionValue = test_helpers.get_selection_value -- luacheck: globals GetSelectionValue GetSelectionValue = test_helpers.get_selection_value -- luacheck: globals GetSelectionValue

View File

@@ -3,6 +3,7 @@ local a = vim.api
local highlights = {} local highlights = {}
local ns_telescope_selection = a.nvim_create_namespace('telescope_selection') local ns_telescope_selection = a.nvim_create_namespace('telescope_selection')
local ns_telescope_multiselection = a.nvim_create_namespace('telescope_mulitselection')
local ns_telescope_entry = a.nvim_create_namespace('telescope_entry') local ns_telescope_entry = a.nvim_create_namespace('telescope_entry')
local Highlighter = {} local Highlighter = {}
@@ -75,6 +76,28 @@ function Highlighter:hi_selection(row, caret)
) )
end end
function Highlighter:hi_multiselect(row, entry)
local results_bufnr = assert(self.picker.results_bufnr, "Must have a results bufnr")
if self.picker.multi_select[entry] then
vim.api.nvim_buf_add_highlight(
results_bufnr,
ns_telescope_multiselection,
"TelescopeMultiSelection",
row,
0,
-1
)
else
vim.api.nvim_buf_clear_namespace(
results_bufnr,
ns_telescope_multiselection,
row,
row + 1
)
end
end
highlights.new = function(...) highlights.new = function(...)
return Highlighter:new(...) return Highlighter:new(...)
end end

View File

@@ -1,56 +1,75 @@
local scroller = {} local scroller = {}
local calc_count_fn = function(sorting_strategy) local range_calculators = {
if sorting_strategy == 'ascending' then ascending = function(max_results, num_results)
return function(a, b) return math.min(a, b) end return 0, math.min(max_results, num_results)
else end,
return function(a, b, row)
if a == b or not row then descending = function(max_results, num_results)
return math.max(a, b) return math.max(max_results - num_results, 0), max_results
else end,
local x = a - b }
if row < x then
return math.max(a, b) - 1, true local scroll_calculators = {
elseif row == a then cycle = function(range_fn)
return x, true return function(max_results, num_results, row)
else local start, finish = range_fn(max_results, num_results)
return math.max(a, b)
end if row >= finish then
return start
elseif row < start then
return finish - 1
end end
return row
end end
end,
limit = function(range_fn)
return function(max_results, num_results, row)
local start, finish = range_fn(max_results, num_results)
if row >= finish then
return finish - 1
elseif row < start then
return start
end
return row
end
end,
}
scroller.create = function(scroll_strategy, sorting_strategy)
local range_fn = range_calculators[sorting_strategy]
if not range_fn then
error(debug.traceback("Unknown sorting strategy: " .. sorting_strategy))
end end
end
scroller.create = function(strategy, sorting_strategy) local scroll_fn = scroll_calculators[scroll_strategy]
local calc_count = calc_count_fn(sorting_strategy) if not scroll_fn then
error(debug.traceback("Unknown scroll strategy: " .. (scroll_strategy or '')))
end
if strategy == 'cycle' then local calculator = scroll_fn(range_fn)
return function(max_results, num_results, row) return function(max_results, num_results, row)
local count, b = calc_count(max_results, num_results, row) local result = calculator(max_results, num_results, row)
if b then return count end
if row >= count then if result < 0 then
return 0 error(string.format(
elseif row < 0 then "Must never return a negative row: { result = %s, args = { %s %s %s } }",
return count - 1 result, max_results, num_results, row
end ))
return row
end end
elseif strategy == 'limit' or strategy == nil then
return function(max_results, num_results, row)
local count = calc_count(max_results, num_results)
if row >= count then if result >= max_results then
return count - 1 error(string.format(
elseif row < 0 then "Must never exceed max results: { result = %s, args = { %s %s %s } }",
return 0 result, max_results, num_results, row
end ))
return row
end end
else
error("Unsupported strategy: " .. strategy) return result
end end
end end

View File

@@ -0,0 +1,142 @@
local EntryManager = require('telescope.entry_manager')
local eq = assert.are.same
describe('process_result', function()
it('works with one entry', function()
local manager = EntryManager:new(5, nil)
manager:add_entry(nil, 1, "hello")
eq(1, manager:get_score(1))
end)
it('works with two entries', function()
local manager = EntryManager:new(5, nil)
manager:add_entry(nil, 1, "hello")
manager:add_entry(nil, 2, "later")
eq(2, manager.linked_states.size)
eq("hello", manager:get_entry(1))
eq("later", manager:get_entry(2))
end)
it('calls functions when inserting', function()
local called_count = 0
local manager = EntryManager:new(5, function() called_count = called_count + 1 end)
assert(called_count == 0)
manager:add_entry(nil, 1, "hello")
assert(called_count == 1)
end)
it('calls functions when inserting twice', function()
local called_count = 0
local manager = EntryManager:new(5, function() called_count = called_count + 1 end)
assert(called_count == 0)
manager:add_entry(nil, 1, "hello")
manager:add_entry(nil, 2, "world")
assert(called_count == 2)
end)
it('correctly sorts lower scores', function()
local called_count = 0
local manager = EntryManager:new(5, function() called_count = called_count + 1 end)
manager:add_entry(nil, 5, "worse result")
manager:add_entry(nil, 2, "better result")
eq("better result", manager:get_entry(1))
eq("worse result", manager:get_entry(2))
eq(2, called_count)
end)
it('respects max results', function()
local called_count = 0
local manager = EntryManager:new(1, function() called_count = called_count + 1 end)
manager:add_entry(nil, 2, "better result")
manager:add_entry(nil, 5, "worse result")
eq("better result", manager:get_entry(1))
eq(1, called_count)
end)
it('should allow simple entries', function()
local manager = EntryManager:new(5)
local counts_executed = 0
manager:add_entry(nil, 1, setmetatable({}, {
__index = function(t, k)
local val = nil
if k == "ordinal" then
counts_executed = counts_executed + 1
-- This could be expensive, only call later
val = "wow"
end
rawset(t, k, val)
return val
end,
}))
eq("wow", manager:get_ordinal(1))
eq("wow", manager:get_ordinal(1))
eq("wow", manager:get_ordinal(1))
eq(1, counts_executed)
end)
it('should not loop a bunch', function()
local info = {}
local manager = EntryManager:new(5, nil, info)
manager:add_entry(nil, 4, "better result")
manager:add_entry(nil, 3, "better result")
manager:add_entry(nil, 2, "better result")
-- Loops once to find 3 < 4
-- Loops again to find 2 < 3
eq(2, info.looped)
end)
it('should not loop a bunch, part 2', function()
local info = {}
local manager = EntryManager:new(5, nil, info)
manager:add_entry(nil, 4, "better result")
manager:add_entry(nil, 2, "better result")
manager:add_entry(nil, 3, "better result")
-- Loops again to find 2 < 4
-- Loops once to find 3 > 2
-- but less than 4
eq(3, info.looped)
end)
it('should update worst score in all append case', function()
local manager = EntryManager:new(2, nil)
manager:add_entry(nil, 2, "result 2")
manager:add_entry(nil, 3, "result 3")
manager:add_entry(nil, 4, "result 4")
eq(3, manager.worst_acceptable_score)
end)
it('should update worst score in all prepend case', function()
local called_count = 0
local manager = EntryManager:new(2, function() called_count = called_count + 1 end)
manager:add_entry(nil, 5, "worse result")
manager:add_entry(nil, 4, "less worse result")
manager:add_entry(nil, 2, "better result")
-- Once for insert 5
-- Once for prepend 4
-- Once for prepend 2
eq(3, called_count)
eq("better result", manager:get_entry(1))
eq(4, manager.worst_acceptable_score)
end)
end)

View File

@@ -0,0 +1,133 @@
local LinkedList = require('telescope.algos.linked_list')
describe('LinkedList', function()
it('can create a list', function()
local l = LinkedList:new()
assert.are.same(0, l.size)
end)
it('can add a single entry to the list', function()
local l = LinkedList:new()
l:append('hello')
assert.are.same(1, l.size)
end)
it('can iterate over one item', function()
local l = LinkedList:new()
l:append('hello')
for val in l:iter() do
assert.are.same('hello', val)
end
end)
it('iterates in order', function()
local l = LinkedList:new()
l:append('hello')
l:append('world')
local x = {}
for val in l:iter() do
table.insert(x, val)
end
assert.are.same({'hello', 'world'}, x)
end)
it('iterates in order, for prepend', function()
local l = LinkedList:new()
l:prepend('world')
l:prepend('hello')
local x = {}
for val in l:iter() do
table.insert(x, val)
end
assert.are.same({'hello', 'world'}, x)
end)
it('iterates in order, for combo', function()
local l = LinkedList:new()
l:prepend('world')
l:prepend('hello')
l:append('last')
l:prepend('first')
local x = {}
for val in l:iter() do
table.insert(x, val)
end
assert.are.same({'first', 'hello', 'world', 'last'}, x)
assert.are.same(#x, l.size)
end)
it('has ipairs', function()
local l = LinkedList:new()
l:prepend('world')
l:prepend('hello')
l:append('last')
l:prepend('first')
local x = {}
for v in l:iter() do
table.insert(x, v)
end
assert.are.same({'first', 'hello', 'world', 'last'}, x)
local expected = {}
for i, v in ipairs(x) do
table.insert(expected, {i, v})
end
local actual = {}
for i, v in l:ipairs() do
table.insert(actual, {i, v})
end
assert.are.same(expected, actual)
end)
describe('track_at', function()
it('should update tracked when only appending', function()
local l = LinkedList:new { track_at = 2 }
l:append("first")
l:append("second")
l:append("third")
assert.are.same("second", l.tracked)
end)
it('should update tracked when first some prepend and then append', function()
local l = LinkedList:new { track_at = 2 }
l:prepend("first")
l:append("second")
l:append("third")
assert.are.same("second", l.tracked)
end)
it('should update when only prepending', function()
local l = LinkedList:new { track_at = 2 }
l:prepend("third")
l:prepend("second")
l:prepend("first")
assert.are.same("second", l.tracked)
end)
it('should update when lots of prepend and append', function()
local l = LinkedList:new { track_at = 2 }
l:prepend("third")
l:prepend("second")
l:prepend("first")
l:append("fourth")
l:prepend("zeroth")
assert.are.same("first", l.tracked)
end)
end)
end)

View File

@@ -2,6 +2,10 @@ require('plenary.reload').reload_module('telescope')
local tester = require('telescope.pickers._test') local tester = require('telescope.pickers._test')
local disp = function(val)
return vim.inspect(val, { newline = " ", indent = "" })
end
describe('builtin.find_files', function() describe('builtin.find_files', function()
it('should find the readme', function() it('should find the readme', function()
tester.run_file('find_files__readme') tester.run_file('find_files__readme')
@@ -11,45 +15,76 @@ describe('builtin.find_files', function()
tester.run_file('find_files__with_ctrl_n') tester.run_file('find_files__with_ctrl_n')
end) end)
it('should not display devicons when disabled', function() for _, configuration in ipairs {
tester.run_string [[ { sorting_strategy = 'descending', },
tester.builtin_picker('find_files', 'README.md', { { sorting_strategy = 'ascending', },
post_typed = { } do
{ "> README.md", GetPrompt }, it('should not display devicons when disabled: ' .. disp(configuration), function()
{ "> README.md", GetLastResult }, tester.run_string(string.format([[
}, local max_results = 5
post_close = {
{ 'README.md', GetFile },
{ 'README.md', GetFile },
}
}, {
disable_devicons = true,
sorter = require('telescope.sorters').get_fzy_sorter(),
})
]]
end)
it('use devicons, if it has it when enabled', function() tester.builtin_picker('find_files', 'README.md', {
if not pcall(require, 'nvim-web-devicons') then post_typed = {
return { "> README.md", GetPrompt },
end { "> README.md", GetBestResult },
},
post_close = {
{ 'README.md', GetFile },
{ 'README.md', GetFile },
}
}, vim.tbl_extend("force", {
disable_devicons = true,
sorter = require('telescope.sorters').get_fzy_sorter(),
results_height = max_results,
layout_strategy = 'center',
}, vim.fn.json_decode([==[%s]==])))
]], vim.fn.json_encode(configuration)))
end)
tester.run_string [[ it('should only save one line for ascending, but many for descending', function()
tester.builtin_picker('find_files', 'README.md', { local expected
post_typed = { if configuration.sorting_strategy == 'descending' then
{ "> README.md", GetPrompt }, expected = 5
{ ">  README.md", GetLastResult } else
}, expected = 1
post_close = { end
{ 'README.md', GetFile },
{ 'README.md', GetFile }, tester.run_string(string.format([[
} tester.builtin_picker('find_files', 'README.md', {
}, { post_typed = {
disable_devicons = false, { %s, function() return #GetResults() end },
sorter = require('telescope.sorters').get_fzy_sorter(), },
}) }, vim.tbl_extend("force", {
]] disable_devicons = true,
end) sorter = require('telescope.sorters').get_fzy_sorter(),
results_height = 5,
layout_strategy = 'center',
}, vim.fn.json_decode([==[%s]==])))
]], expected, vim.fn.json_encode(configuration)))
end)
it('use devicons, if it has it when enabled', function()
if not pcall(require, 'nvim-web-devicons') then
return
end
tester.run_string(string.format([[
tester.builtin_picker('find_files', 'README.md', {
post_typed = {
{ "> README.md", GetPrompt },
{ ">  README.md", GetBestResult }
},
post_close = {
{ 'README.md', GetFile },
{ 'README.md', GetFile },
}
}, vim.tbl_extend("force", {
disable_devicons = false,
sorter = require('telescope.sorters').get_fzy_sorter(),
}, vim.fn.json_decode([==[%s]==])))
]], vim.fn.json_encode(configuration)))
end)
end
it('should find the readme, using lowercase', function() it('should find the readme, using lowercase', function()
tester.run_string [[ tester.run_string [[

View File

@@ -0,0 +1,9 @@
require('plenary.reload').reload_module('telescope')
local tester = require('telescope.pickers._test')
describe('scrolling strategies', function()
it('should handle cycling for full list', function()
tester.run_file [[find_files__scrolling_descending_cycle]]
end)
end)

View File

@@ -99,7 +99,16 @@ describe('scroller', function()
it('should stay at current results when current results is less than max_results', function() it('should stay at current results when current results is less than max_results', function()
local current = 5 local current = 5
eq(current - 1, limit_scroller(max_results, current, 4)) eq(max_results - current, limit_scroller(max_results, current, 4))
end)
end)
describe('https://github.com/nvim-telescope/telescope.nvim/pull/293#issuecomment-751463224', function()
it('should handle having many more results than necessary', function()
local scroller = p_scroller.create('cycle', 'descending')
-- 23 112 23
eq(0, scroller(23, 112, 23))
end) end)
end) end)
end) end)

View File

@@ -4,8 +4,6 @@ local log = require('telescope.log')
log.level = 'info' log.level = 'info'
-- log.use_console = false -- log.use_console = false
local EntryManager = require('telescope.entry_manager')
--[[ --[[
lua RELOAD('plenary'); require("plenary.test_harness"):test_directory("busted", "./tests/automated") lua RELOAD('plenary'); require("plenary.test_harness"):test_directory("busted", "./tests/automated")
--]] --]]
@@ -16,103 +14,6 @@ describe('Picker', function()
assert(true) assert(true)
end) end)
end) end)
describe('process_result', function()
it('works with one entry', function()
local manager = EntryManager:new(5, nil)
manager:add_entry(nil, 1, "hello")
assert.are.same(1, manager:get_score(1))
end)
it('works with two entries', function()
local manager = EntryManager:new(5, nil)
manager:add_entry(nil, 1, "hello")
manager:add_entry(nil, 2, "later")
assert.are.same("hello", manager:get_entry(1))
assert.are.same("later", manager:get_entry(2))
end)
it('calls functions when inserting', function()
local called_count = 0
local manager = EntryManager:new(5, function() called_count = called_count + 1 end)
assert(called_count == 0)
manager:add_entry(nil, 1, "hello")
assert(called_count == 1)
end)
it('calls functions when inserting twice', function()
local called_count = 0
local manager = EntryManager:new(5, function() called_count = called_count + 1 end)
assert(called_count == 0)
manager:add_entry(nil, 1, "hello")
manager:add_entry(nil, 2, "world")
assert(called_count == 2)
end)
it('correctly sorts lower scores', function()
local called_count = 0
local manager = EntryManager:new(5, function() called_count = called_count + 1 end)
manager:add_entry(nil, 5, "worse result")
manager:add_entry(nil, 2, "better result")
assert.are.same("better result", manager:get_entry(1))
assert.are.same("worse result", manager:get_entry(2))
-- once to insert "worse"
-- once to insert "better"
-- and then to move "worse"
assert.are.same(3, called_count)
end)
it('respects max results', function()
local called_count = 0
local manager = EntryManager:new(1, function() called_count = called_count + 1 end)
manager:add_entry(nil, 2, "better result")
manager:add_entry(nil, 5, "worse result")
assert.are.same("better result", manager:get_entry(1))
assert.are.same(1, called_count)
end)
-- TODO: We should decide if we want to add this or not.
-- it('should handle no scores', function()
-- local manager = EntryManager:new(5, nil)
-- manager:add_entry(nil,
-- end)
it('should allow simple entries', function()
local manager = EntryManager:new(5)
local counts_executed = 0
manager:add_entry(nil, 1, setmetatable({}, {
__index = function(t, k)
local val = nil
if k == "ordinal" then
counts_executed = counts_executed + 1
-- This could be expensive, only call later
val = "wow"
end
rawset(t, k, val)
return val
end,
}))
assert.are.same("wow", manager:get_ordinal(1))
assert.are.same("wow", manager:get_ordinal(1))
assert.are.same("wow", manager:get_ordinal(1))
assert.are.same(1, counts_executed)
end)
end)
end) end)
describe('Sorters', function() describe('Sorters', function()

View File

@@ -1,7 +1,8 @@
local tester = require('telescope.pickers._test') local tester = require('telescope.pickers._test')
local helper = require('telescope.pickers._test_helpers')
tester.builtin_picker('find_files', 'README.md', { tester.builtin_picker('find_files', 'README.md', {
post_close = { post_close = {
{'README.md', function() return vim.fn.fnamemodify(vim.api.nvim_buf_get_name(0), ":.") end }, {'README.md', helper.get_file },
} }
}) })

View File

@@ -0,0 +1,14 @@
require('plenary.reload').reload_module('plenary')
require('plenary.reload').reload_module('telescope')
local tester = require('telescope.pickers._test')
local helper = require('telescope.pickers._test_helpers')
tester.builtin_picker('find_files', 'telescope<c-n>', {
post_close = {
tester.not_ { 'plugin/telescope.vim', helper.get_file },
},
}, {
sorting_strategy = "descending",
scroll_strategy = "cycle",
})

View File

@@ -3,7 +3,7 @@ local helper = require('telescope.pickers._test_helpers')
tester.builtin_picker('find_files', 'fixtures/file<c-p>', { tester.builtin_picker('find_files', 'fixtures/file<c-p>', {
post_close = { post_close = {
{ 'lua/tests/fixtures/file_2.txt', helper.get_file } { 'lua/tests/fixtures/file_abc.txt', helper.get_selection_value },
}, },
}) })

View File

@@ -2,8 +2,8 @@ local ffi = require("ffi")
-- ffi.load("/home/tj/build/neovim/build/include/eval/funcs.h.generated.h") -- ffi.load("/home/tj/build/neovim/build/include/eval/funcs.h.generated.h")
ffi.cdef [[ ffi.cdef [[
typedef unsigned char char_u; typedef unsigned char char_u;
char_u *shorten_dir(char_u *str); char_u *shorten_dir(char_u *str);
]] ]]
local text = "scratch/file.lua" local text = "scratch/file.lua"