summaryrefslogtreecommitdiff
path: root/lua
diff options
context:
space:
mode:
Diffstat (limited to 'lua')
-rw-r--r--lua/telescope/actions/init.lua49
-rw-r--r--lua/telescope/algos/linked_list.lua222
-rw-r--r--lua/telescope/config.lua4
-rw-r--r--lua/telescope/entry_manager.lua221
-rw-r--r--lua/telescope/pickers.lua136
-rw-r--r--lua/telescope/pickers/_test.lua187
-rw-r--r--lua/telescope/pickers/_test_helpers.lua12
-rw-r--r--lua/telescope/pickers/highlights.lua23
-rw-r--r--lua/telescope/pickers/scroller.lua93
-rw-r--r--lua/tests/automated/entry_manager_spec.lua142
-rw-r--r--lua/tests/automated/linked_list_spec.lua133
-rw-r--r--lua/tests/automated/pickers/find_files_spec.lua109
-rw-r--r--lua/tests/automated/pickers/scrolling_spec.lua9
-rw-r--r--lua/tests/automated/scroller_spec.lua11
-rw-r--r--lua/tests/automated/telescope_spec.lua99
-rw-r--r--lua/tests/pickers/find_files__readme.lua3
-rw-r--r--lua/tests/pickers/find_files__scrolling_descending_cycle.lua14
-rw-r--r--lua/tests/pickers/find_files__with_ctrl_n.lua2
18 files changed, 1126 insertions, 343 deletions
diff --git a/lua/telescope/actions/init.lua b/lua/telescope/actions/init.lua
index 6ffb3ef..f4c3b08 100644
--- a/lua/telescope/actions/init.lua
+++ b/lua/telescope/actions/init.lua
@@ -38,6 +38,16 @@ function actions.add_selection(prompt_bufnr)
current_picker:add_selection(current_picker:get_selection_row())
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
function actions.get_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()
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.
-- ==================================================
diff --git a/lua/telescope/algos/linked_list.lua b/lua/telescope/algos/linked_list.lua
new file mode 100644
index 0000000..cf1c47d
--- /dev/null
+++ b/lua/telescope/algos/linked_list.lua
@@ -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
diff --git a/lua/telescope/config.lua b/lua/telescope/config.lua
index eb527eb..6403a54 100644
--- a/lua/telescope/config.lua
+++ b/lua/telescope/config.lua
@@ -34,7 +34,7 @@ function config.set_defaults(defaults)
set("sorting_strategy", "descending")
set("selection_strategy", "reset")
- set("scroll_strategy", nil)
+ set("scroll_strategy", "cycle")
set("layout_strategy", "horizontal")
set("layout_defaults", {})
@@ -53,7 +53,7 @@ function config.set_defaults(defaults)
set("borderchars", { '─', '│', '─', '│', '╭', '╮', '╯', '╰'})
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)
-- Builtin configuration
diff --git a/lua/telescope/entry_manager.lua b/lua/telescope/entry_manager.lua
index 237f0e5..c7350cb 100644
--- a/lua/telescope/entry_manager.lua
+++ b/lua/telescope/entry_manager.lua
@@ -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,
+ id = id,
+ 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
- entry_state = entry_state,
- info = info,
+function EntryManager:num_results()
+ return self.linked_states.size
+end
- num_results = function()
- return #entry_state
- end,
+function EntryManager:get_container(index)
+ local count = 0
+ for val in self.linked_states:iter() do
+ count = count + 1
- get_ordinal = function(em, index)
- return em:get_entry(index).ordinal
- end,
+ if count == index then
+ return val
+ end
+ end
- get_entry = function(_, index)
- return (entry_state[index] or {}).entry
- end,
+ return {}
+end
+
+function EntryManager:get_entry(index)
+ return self:get_container(index)[1]
+end
- get_score = function(_, index)
- return (entry_state[index] or {}).score
- end,
+function EntryManager:get_score(index)
+ return self:get_container(index)[2]
+end
- find_entry = function(_, entry)
- if entry == nil then
- return nil
- end
+function EntryManager:get_ordinal(index)
+ return self:get_entry(index).ordinal
+end
- for k, v in ipairs(entry_state) do
- local existing_entry = v.entry
+function EntryManager:find_entry(entry)
+ local info = self.info
- -- 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
+ local count = 0
+ for container in self.linked_states:iter() do
+ count = count + 1
- return nil
- end,
+ if container[1] == entry then
+ info.find_loop = info.find_loop + count
- _get_state = function()
- return entry_state
- end,
- }, self)
-end
+ return count
+ end
+ end
-function EntryManager:should_save_result(index)
- return index <= self.max_results
+ info.find_loop = info.find_loop + count
+ return nil
end
-function EntryManager:add_entry(picker, score, entry)
- score = score or 0
+function EntryManager:_update_score_from_tracked()
+ local linked = self.linked_states
- if score >= self.worst_acceptable_score then
- return
+ if linked.tracked then
+ self.worst_acceptable_score = math.min(self.worst_acceptable_score, linked.tracked[2])
end
+end
- for index, item in ipairs(self.entry_state) do
- self.info.looped = self.info.looped + 1
+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)
- if item.score > score then
- return self:insert(picker, index, {
- score = score,
- entry = entry,
- })
- end
+ self:_update_score_from_tracked()
+end
- -- Don't add results that are too bad.
- if not self:should_save_result(index) then
+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)
+ 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
+
+ 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
-function EntryManager:insert(picker, index, entry)
- if entry == nil then
- entry = index
- index = #self.entry_state + 1
+ 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]
+ -- Short circuit for first entry.
+ if size == 0 then
+ self.linked_states:prepend(new_container)
+ self.set_entry(picker, 1, entry, score)
+ return
+ end
- self.set_entry(picker, index, entry.entry, entry.score)
- self.entry_state[index] = entry
+ for index, container, node in self.linked_states:ipairs() do
+ info.looped = info.looped + 1
- last_score = entry.score
+ if container[2] > score then
+ -- print("Inserting: ", picker, index, node, new_container)
+ return self:_insert_container_before(picker, index, node, new_container)
+ end
- index = index + 1
- entry = next_entry
- until not next_entry or not self:should_save_result(index)
+ -- 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 not self:should_save_result(index) then
- self.worst_acceptable_score = last_score
+ 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
diff --git a/lua/telescope/pickers.lua b/lua/telescope/pickers.lua
index 67553c2..d129737 100644
--- a/lua/telescope/pickers.lua
+++ b/lua/telescope/pickers.lua
@@ -221,14 +221,17 @@ function Picker:is_done()
end
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
log.debug("Invalid results_bufnr for clearing:", results_bufnr)
return
end
- local worst_line
+ local worst_line, ok, msg
if self.sorting_strategy == 'ascending' then
local num_results = self.manager:num_results()
worst_line = self.max_results - num_results
@@ -237,7 +240,7 @@ function Picker:clear_extra_rows(results_bufnr)
return
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
worst_line = self:get_row(self.manager:num_results())
if worst_line <= 0 then
@@ -245,10 +248,14 @@ function Picker:clear_extra_rows(results_bufnr)
end
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
+
+ if not ok then
+ log.debug(msg)
end
- log.trace("Clearing:", worst_line)
+ log.debug("Clearing:", worst_line)
end
function Picker:highlight_displayed_rows(results_bufnr, prompt)
@@ -296,6 +303,9 @@ function Picker:highlight_one_row(results_bufnr, prompt, display, row)
)
end
end
+
+ local entry = self.manager:get_entry(self:get_index(row))
+ self.highlighter:hi_multiselect(row, entry)
end
function Picker:can_select_row(row)
@@ -416,7 +426,9 @@ function Picker:find()
local debounced_status = debounce.throttle_leading(update_status, 50)
+ self.request_number = 0
local on_lines = function(_, _, _, first_line, last_line)
+ self.request_number = self.request_number + 1
self:_reset_track()
if not vim.api.nvim_buf_is_valid(prompt_bufnr) then
@@ -424,6 +436,9 @@ function Picker:find()
return
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
log.debug("ON_LINES: Bad range", first_line, last_line)
return
@@ -434,13 +449,8 @@ function Picker:find()
self.sorter:_start(prompt)
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
-
- self.manager = EntryManager:new(self.max_results, self.entry_adder)
- -- self.manager = EntryManager:new(self.max_results, self.entry_adder, self.stats)
+ self.manager = EntryManager:new(self.max_results, self.entry_adder, self.stats, self.request_number)
local process_result = function(entry)
if self:is_done() then return end
@@ -690,24 +700,48 @@ 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: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
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)
- -- if index then
- -- vim.api.nvim_buf_add_highlight(
- -- results_bufnr,
- -- ns_telescope_selection,
- -- "TelescopeMultiSelection",
- -- self:get_row(index),
- -- 0,
- -- -1
- -- )
- -- end
- -- end
+ for entry, _ in pairs(self.multi_select) do
+ local index = self.manager:find_entry(entry)
+ if index then
+ vim.api.nvim_buf_add_highlight(
+ results_bufnr,
+ a.nvim_create_namespace('telescope_selection'),
+ "TelescopeMultiSelection",
+ self:get_row(index),
+ 0,
+ -1
+ )
+ end
+ end
end
function Picker:reset_selection()
@@ -718,25 +752,30 @@ function Picker:reset_selection()
end
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)
if not self:can_select_row(row) then
-- 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
row = self:get_row(self.manager:num_results())
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
end
end
- -- local entry = self.manager:get_entry(self.max_results - row + 1)
local entry = self.manager:get_entry(self:get_index(row))
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)
@@ -764,6 +803,7 @@ function Picker:set_selection(row)
self.highlighter:hi_display(self._selection_row, ' ', display_highlights)
self.highlighter:hi_sorter(self._selection_row, prompt, display)
+ self.highlighter:hi_multiselect(self._selection_row, self._selection_entry)
end
end
@@ -785,9 +825,7 @@ function Picker:set_selection(row)
self.highlighter:hi_selection(row, caret)
self.highlighter:hi_display(row, ' ', display_highlights)
self.highlighter:hi_sorter(row, prompt, display)
-
- -- TODO: Actually implement this for real TJ, don't leave around half implemented code plz :)
- -- self:display_multi_select(results_bufnr)
+ self.highlighter:hi_multiselect(row, entry)
end)
if not set_ok then
@@ -814,7 +852,7 @@ function Picker:set_selection(row)
end
-function Picker:entry_adder(index, entry, score)
+function Picker:entry_adder(index, entry, score, insert)
local row = self:get_row(index)
-- 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")
-- 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()
if not vim.api.nvim_buf_is_valid(self.results_bufnr) then
log.debug("ON_ENTRY: Invalid buffer")
return
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
self.highlighter:hi_display(row, prefix, display_highlights)
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.
-- So we'll clean it up for them if it fails.
if not set_ok and display:find("\n") then
@@ -893,7 +953,7 @@ function Picker:_track(key, func, ...)
end
function Picker:_increment(key)
- self.stats[key] = self.stats[key] + 1
+ self.stats[key] = (self.stats[key] or 0) + 1
end
diff --git a/lua/telescope/pickers/_test.lua b/lua/telescope/pickers/_test.lua
index ddd245a..1ab5a97 100644
--- a/lua/telescope/pickers/_test.lua
+++ b/lua/telescope/pickers/_test.lua
@@ -1,10 +1,14 @@
local assert = require('luassert')
local builtin = require('telescope.builtin')
+local log = require('telescope.log')
local Job = require("plenary.job")
+local Path = require("plenary.path")
local tester = {}
+tester.debug = false
+
local replace_terms = function(input)
return vim.api.nvim_replace_termcodes(input, true, false, true)
end
@@ -15,7 +19,53 @@ local nvim_feed = function(text, feed_opts)
vim.api.nvim_feedkeys(text, feed_opts, true)
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)
return coroutine.wrap(function()
@@ -28,75 +78,66 @@ tester.picker_feed = function(input, test_cases, debug)
if string.match(char, "%g") then
coroutine.yield()
end
+
+ if tester.debug then
+ vim.wait(200)
+ end
end
- vim.wait(10, function() end)
+ vim.wait(10)
+
+ if tester.debug then
+ coroutine.yield()
+ end
- local timer = vim.loop.new_timer()
- timer:start(20, 0, vim.schedule_wrap(function()
+ vim.defer_fn(function()
+ 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
+
+ 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
- io.stderr:write(vim.fn.json_encode({ case = k, expected = v[1], actual = v[2]() }))
- io.stderr:write("\n")
+ execute_test_case('post_close', k, v)
end
end
- if debug then
+ if tester.debug then
return
end
- vim.defer_fn(function()
- vim.cmd [[qa!]]
- 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
+ vim.defer_fn(end_test_cases, 20)
+ end, 40)
- nvim_feed(replace_terms("<CR>"), "")
- end)
- end
coroutine.yield()
end)
end
--- local test_cases = {
--- post_typed = {
--- },
--- post_close = {
--- { "README.md", function() return "README.md" end },
--- },
--- }
-
local _VALID_KEYS = {
post_typed = 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 {}
- local debug = opts.debug or false
+ tester.debug = opts.debug or false
for k, _ in pairs(test_cases) do
if not _VALID_KEYS[k] then
- -- TODO: Make an error type for the json protocol.
- io.stderr:write(vim.fn.json_encode({ case = k, expected = '<a valid key>', actual = k }))
- io.stderr:write("\n")
- vim.cmd [[qa!]]
+ return invalid_test_case(k)
end
end
opts.on_complete = {
- tester.picker_feed(input, test_cases, debug)
+ tester.picker_feed(input, test_cases),
}
- builtin[key](opts)
+ builtin[builtin_key](opts)
end
local get_results_from_file = function(file)
@@ -107,11 +148,14 @@ local get_results_from_file = function(file)
'-u',
'scripts/minimal_init.vim',
'-c',
- 'luafile ' .. file
+ string.format(
+ [[lua require("telescope.pickers._test")._execute("%s")]],
+ file
+ ),
},
}
- j:sync()
+ j:sync(1000)
local results = j:stderr_result()
local result_table = {}
@@ -122,10 +166,27 @@ local get_results_from_file = function(file)
return result_table
end
+
+local asserters = {
+ _default = assert.are.same,
+
+ are = assert.are.same,
+ are_not = assert.are_not.same,
+}
+
+
local check_results = function(results)
-- TODO: We should get all the test cases here that fail, not just the first one.
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
@@ -144,14 +205,52 @@ tester.run_string = function(contents)
vim.fn.delete(tempname)
check_results(result_table)
- -- assert.are.same(result_table.expected, result_table.actual)
end
tester.run_file = function(filename)
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)
- assert.are.same(result_table.expected, result_table.actual)
+
+ check_results(result_table)
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
diff --git a/lua/telescope/pickers/_test_helpers.lua b/lua/telescope/pickers/_test_helpers.lua
index a430fae..63d1a0f 100644
--- a/lua/telescope/pickers/_test_helpers.lua
+++ b/lua/telescope/pickers/_test_helpers.lua
@@ -22,9 +22,15 @@ test_helpers.get_results = function()
return vim.api.nvim_buf_get_lines(test_helpers.get_results_bufnr(), 0, -1, false)
end
-test_helpers.get_last_result = function()
+test_helpers.get_best_result = function()
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
test_helpers.get_selection = function()
@@ -41,7 +47,7 @@ test_helpers.make_globals = function()
GetPrompt = test_helpers.get_prompt -- luacheck: globals GetPrompt
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
GetSelectionValue = test_helpers.get_selection_value -- luacheck: globals GetSelectionValue
diff --git a/lua/telescope/pickers/highlights.lua b/lua/telescope/pickers/highlights.lua
index 23e796e..62ffb4b 100644
--- a/lua/telescope/pickers/highlights.lua
+++ b/lua/telescope/pickers/highlights.lua
@@ -3,6 +3,7 @@ local a = vim.api
local highlights = {}
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 Highlighter = {}
@@ -75,6 +76,28 @@ function Highlighter:hi_selection(row, caret)
)
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(...)
return Highlighter:new(...)
end
diff --git a/lua/telescope/pickers/scroller.lua b/lua/telescope/pickers/scroller.lua
index 324627b..3169c4b 100644
--- a/lua/telescope/pickers/scroller.lua
+++ b/lua/telescope/pickers/scroller.lua
@@ -1,56 +1,75 @@
local scroller = {}
-local calc_count_fn = function(sorting_strategy)
- if sorting_strategy == 'ascending' then
- return function(a, b) return math.min(a, b) end
- else
- return function(a, b, row)
- if a == b or not row then
- return math.max(a, b)
- else
- local x = a - b
- if row < x then
- return math.max(a, b) - 1, true
- elseif row == a then
- return x, true
- else
- return math.max(a, b)
- end
- end
- end
- end
-end
+local range_calculators = {
+ ascending = function(max_results, num_results)
+ return 0, math.min(max_results, num_results)
+ end,
-scroller.create = function(strategy, sorting_strategy)
- local calc_count = calc_count_fn(sorting_strategy)
+ descending = function(max_results, num_results)
+ return math.max(max_results - num_results, 0), max_results
+ end,
+}
- if strategy == 'cycle' then
+local scroll_calculators = {
+ cycle = function(range_fn)
return function(max_results, num_results, row)
- local count, b = calc_count(max_results, num_results, row)
- if b then return count end
+ local start, finish = range_fn(max_results, num_results)
- if row >= count then
- return 0
- elseif row < 0 then
- return count - 1
+ if row >= finish then
+ return start
+ elseif row < start then
+ return finish - 1
end
return row
end
- elseif strategy == 'limit' or strategy == nil then
+ end,
+
+ limit = function(range_fn)
return function(max_results, num_results, row)
- local count = calc_count(max_results, num_results)
+ local start, finish = range_fn(max_results, num_results)
- if row >= count then
- return count - 1
- elseif row < 0 then
- return 0
+ if row >= finish then
+ return finish - 1
+ elseif row < start then
+ return start
end
return row
end
- else
- error("Unsupported strategy: " .. strategy)
+ 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
+
+ local scroll_fn = scroll_calculators[scroll_strategy]
+ if not scroll_fn then
+ error(debug.traceback("Unknown scroll strategy: " .. (scroll_strategy or '')))
+ end
+
+ local calculator = scroll_fn(range_fn)
+ return function(max_results, num_results, row)
+ local result = calculator(max_results, num_results, row)
+
+ if result < 0 then
+ error(string.format(
+ "Must never return a negative row: { result = %s, args = { %s %s %s } }",
+ result, max_results, num_results, row
+ ))
+ end
+
+ if result >= max_results then
+ error(string.format(
+ "Must never exceed max results: { result = %s, args = { %s %s %s } }",
+ result, max_results, num_results, row
+ ))
+ end
+
+ return result
end
end
diff --git a/lua/tests/automated/entry_manager_spec.lua b/lua/tests/automated/entry_manager_spec.lua
new file mode 100644
index 0000000..cc59ebd
--- /dev/null
+++ b/lua/tests/automated/entry_manager_spec.lua
@@ -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)
diff --git a/lua/tests/automated/linked_list_spec.lua b/lua/tests/automated/linked_list_spec.lua
new file mode 100644
index 0000000..49c035a
--- /dev/null
+++ b/lua/tests/automated/linked_list_spec.lua
@@ -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)
diff --git a/lua/tests/automated/pickers/find_files_spec.lua b/lua/tests/automated/pickers/find_files_spec.lua
index c5cf50e..b3af1d6 100644
--- a/lua/tests/automated/pickers/find_files_spec.lua
+++ b/lua/tests/automated/pickers/find_files_spec.lua
@@ -2,6 +2,10 @@ require('plenary.reload').reload_module('telescope')
local tester = require('telescope.pickers._test')
+local disp = function(val)
+ return vim.inspect(val, { newline = " ", indent = "" })
+end
+
describe('builtin.find_files', function()
it('should find the readme', function()
tester.run_file('find_files__readme')
@@ -11,45 +15,76 @@ describe('builtin.find_files', function()
tester.run_file('find_files__with_ctrl_n')
end)
- it('should not display devicons when disabled', function()
- tester.run_string [[
- tester.builtin_picker('find_files', 'README.md', {
- post_typed = {
- { "> README.md", GetPrompt },
- { "> README.md", GetLastResult },
- },
- post_close = {
- { 'README.md', GetFile },
- { 'README.md', GetFile },
- }
- }, {
- disable_devicons = true,
- sorter = require('telescope.sorters').get_fzy_sorter(),
- })
- ]]
- end)
+ for _, configuration in ipairs {
+ { sorting_strategy = 'descending', },
+ { sorting_strategy = 'ascending', },
+ } do
+ it('should not display devicons when disabled: ' .. disp(configuration), function()
+ tester.run_string(string.format([[
+ local max_results = 5
- it('use devicons, if it has it when enabled', function()
- if not pcall(require, 'nvim-web-devicons') then
- return
- end
+ 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 = 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 [[
- tester.builtin_picker('find_files', 'README.md', {
- post_typed = {
- { "> README.md", GetPrompt },
- { ">  README.md", GetLastResult }
- },
- post_close = {
- { 'README.md', GetFile },
- { 'README.md', GetFile },
- }
- }, {
- disable_devicons = false,
- sorter = require('telescope.sorters').get_fzy_sorter(),
- })
- ]]
- end)
+ it('should only save one line for ascending, but many for descending', function()
+ local expected
+ if configuration.sorting_strategy == 'descending' then
+ expected = 5
+ else
+ expected = 1
+ end
+
+ tester.run_string(string.format([[
+ tester.builtin_picker('find_files', 'README.md', {
+ post_typed = {
+ { %s, function() return #GetResults() end },
+ },
+ }, vim.tbl_extend("force", {
+ disable_devicons = true,
+ 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()
tester.run_string [[
diff --git a/lua/tests/automated/pickers/scrolling_spec.lua b/lua/tests/automated/pickers/scrolling_spec.lua
new file mode 100644
index 0000000..ab82378
--- /dev/null
+++ b/lua/tests/automated/pickers/scrolling_spec.lua
@@ -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)
diff --git a/lua/tests/automated/scroller_spec.lua b/lua/tests/automated/scroller_spec.lua
index f47edda..dcc55a0 100644
--- a/lua/tests/automated/scroller_spec.lua
+++ b/lua/tests/automated/scroller_spec.lua
@@ -99,7 +99,16 @@ describe('scroller', function()
it('should stay at current results when current results is less than max_results', function()
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)
diff --git a/lua/tests/automated/telescope_spec.lua b/lua/tests/automated/telescope_spec.lua
index bd9203c..3e9f0cb 100644
--- a/lua/tests/automated/telescope_spec.lua
+++ b/lua/tests/automated/telescope_spec.lua
@@ -4,8 +4,6 @@ local log = require('telescope.log')
log.level = 'info'
-- log.use_console = false
-local EntryManager = require('telescope.entry_manager')
-
--[[
lua RELOAD('plenary'); require("plenary.test_harness"):test_directory("busted", "./tests/automated")
--]]
@@ -16,103 +14,6 @@ describe('Picker', function()
assert(true)
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)
describe('Sorters', function()
diff --git a/lua/tests/pickers/find_files__readme.lua b/lua/tests/pickers/find_files__readme.lua
index 620c3be..534ee3e 100644
--- a/lua/tests/pickers/find_files__readme.lua
+++ b/lua/tests/pickers/find_files__readme.lua
@@ -1,7 +1,8 @@
local tester = require('telescope.pickers._test')
+local helper = require('telescope.pickers._test_helpers')
tester.builtin_picker('find_files', 'README.md', {
post_close = {
- {'README.md', function() return vim.fn.fnamemodify(vim.api.nvim_buf_get_name(0), ":.") end },
+ {'README.md', helper.get_file },
}
})
diff --git a/lua/tests/pickers/find_files__scrolling_descending_cycle.lua b/lua/tests/pickers/find_files__scrolling_descending_cycle.lua
new file mode 100644
index 0000000..7740003
--- /dev/null
+++ b/lua/tests/pickers/find_files__scrolling_descending_cycle.lua
@@ -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",
+})
diff --git a/lua/tests/pickers/find_files__with_ctrl_n.lua b/lua/tests/pickers/find_files__with_ctrl_n.lua
index 78a31fd..5d93f60 100644
--- a/lua/tests/pickers/find_files__with_ctrl_n.lua
+++ b/lua/tests/pickers/find_files__with_ctrl_n.lua
@@ -3,7 +3,7 @@ local helper = require('telescope.pickers._test_helpers')
tester.builtin_picker('find_files', 'fixtures/file<c-p>', {
post_close = {
- { 'lua/tests/fixtures/file_2.txt', helper.get_file }
+ { 'lua/tests/fixtures/file_abc.txt', helper.get_selection_value },
},
})