mirror of
https://github.com/zoriya/telescope.nvim.git
synced 2025-12-06 06:46:10 +00:00
feat(fps): Add refresh style display for telescope
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -155,35 +155,19 @@ end
|
||||
actions.select_all = function(prompt_bufnr)
|
||||
local current_picker = action_state.get_current_picker(prompt_bufnr)
|
||||
action_utils.map_entries(prompt_bufnr, function(entry, _, row)
|
||||
if not current_picker._multi:is_selected(entry) then
|
||||
current_picker._multi:add(entry)
|
||||
if current_picker:can_select_row(row) then
|
||||
local caret = current_picker:update_prefix(entry, row)
|
||||
if current_picker._selection_entry == entry and current_picker._selection_row == row then
|
||||
current_picker.highlighter:hi_selection(row, caret:match "(.*%S)")
|
||||
end
|
||||
current_picker.highlighter:hi_multiselect(row, current_picker._multi:is_selected(entry))
|
||||
end
|
||||
end
|
||||
current_picker._multi:add(entry)
|
||||
end)
|
||||
current_picker:get_status_updater(current_picker.prompt_win, current_picker.prompt_bufnr)()
|
||||
current_picker:_redraw(true)
|
||||
end
|
||||
|
||||
--- Drop all entries from the current multi selection.
|
||||
---@param prompt_bufnr number: The prompt bufnr
|
||||
actions.drop_all = function(prompt_bufnr)
|
||||
local current_picker = action_state.get_current_picker(prompt_bufnr)
|
||||
action_utils.map_entries(prompt_bufnr, function(entry, _, row)
|
||||
action_utils.map_entries(prompt_bufnr, function(entry)
|
||||
current_picker._multi:drop(entry)
|
||||
if current_picker:can_select_row(row) then
|
||||
local caret = current_picker:update_prefix(entry, row)
|
||||
if current_picker._selection_entry == entry and current_picker._selection_row == row then
|
||||
current_picker.highlighter:hi_selection(row, caret:match "(.*%S)")
|
||||
end
|
||||
current_picker.highlighter:hi_multiselect(row, current_picker._multi:is_selected(entry))
|
||||
end
|
||||
end)
|
||||
current_picker:get_status_updater(current_picker.prompt_win, current_picker.prompt_bufnr)()
|
||||
current_picker:_redraw(true)
|
||||
end
|
||||
|
||||
--- Toggle multi selection for all entries.
|
||||
@@ -191,17 +175,10 @@ end
|
||||
---@param prompt_bufnr number: The prompt bufnr
|
||||
actions.toggle_all = function(prompt_bufnr)
|
||||
local current_picker = action_state.get_current_picker(prompt_bufnr)
|
||||
action_utils.map_entries(prompt_bufnr, function(entry, _, row)
|
||||
action_utils.map_entries(prompt_bufnr, function(entry)
|
||||
current_picker._multi:toggle(entry)
|
||||
if current_picker:can_select_row(row) then
|
||||
local caret = current_picker:update_prefix(entry, row)
|
||||
if current_picker._selection_entry == entry and current_picker._selection_row == row then
|
||||
current_picker.highlighter:hi_selection(row, caret:match "(.*%S)")
|
||||
end
|
||||
current_picker.highlighter:hi_multiselect(row, current_picker._multi:is_selected(entry))
|
||||
end
|
||||
end)
|
||||
current_picker:get_status_updater(current_picker.prompt_win, current_picker.prompt_bufnr)()
|
||||
current_picker:_redraw(true)
|
||||
end
|
||||
|
||||
--- Scroll the preview window up
|
||||
|
||||
@@ -193,23 +193,16 @@ action_set.scroll_previewer = function(prompt_bufnr, direction)
|
||||
end
|
||||
|
||||
--- Scrolls the results up or down.
|
||||
--- Defaults to a half page scroll, but can be overridden using the `scroll_speed`
|
||||
--- option in `layout_config`. See |telescope.layout| for more details.
|
||||
--- Moves `count` movements.
|
||||
---@param prompt_bufnr number: The prompt bufnr
|
||||
---@param direction number: The direction of the scrolling
|
||||
-- Valid directions include: "1", "-1"
|
||||
action_set.scroll_results = function(prompt_bufnr, direction)
|
||||
local status = state.get_status(prompt_bufnr)
|
||||
local default_speed = vim.api.nvim_win_get_height(status.results_win) / 2
|
||||
local speed = status.picker.layout_config.scroll_speed or default_speed
|
||||
local count = vim.v.count
|
||||
count = count == 0 and 1 or count
|
||||
count = a.nvim_get_mode().mode == "n" and count or 1
|
||||
|
||||
local input = direction > 0 and [[]] or [[]]
|
||||
|
||||
vim.api.nvim_win_call(status.results_win, function()
|
||||
vim.cmd([[normal! ]] .. math.floor(speed) .. input)
|
||||
end)
|
||||
|
||||
action_set.shift_selection(prompt_bufnr, math.floor(speed) * direction)
|
||||
action_state.get_current_picker(prompt_bufnr):shift_offset(count * direction)
|
||||
end
|
||||
|
||||
-- ==================================================
|
||||
|
||||
@@ -1,28 +1,15 @@
|
||||
local log = require "telescope.log"
|
||||
|
||||
local LinkedList = require "telescope.algos.linked_list"
|
||||
|
||||
local EntryManager = {}
|
||||
EntryManager.__index = EntryManager
|
||||
|
||||
function EntryManager:new(max_results, set_entry, info)
|
||||
log.trace "Creating entry_manager..."
|
||||
|
||||
info = info or {}
|
||||
info.looped = 0
|
||||
info.inserted = 0
|
||||
info.find_loop = 0
|
||||
|
||||
-- state contains list of
|
||||
-- { entry, score }
|
||||
-- Stored directly in a table, accessed as [1], [2]
|
||||
set_entry = set_entry or function() end
|
||||
function EntryManager:new(num_sorted)
|
||||
num_sorted = num_sorted or 500
|
||||
|
||||
return setmetatable({
|
||||
linked_states = LinkedList:new { track_at = max_results },
|
||||
info = info,
|
||||
max_results = max_results,
|
||||
set_entry = set_entry,
|
||||
dirty = true,
|
||||
linked_states = LinkedList:new { track_at = num_sorted },
|
||||
num_sorted = num_sorted,
|
||||
worst_acceptable_score = math.huge,
|
||||
}, self)
|
||||
end
|
||||
@@ -31,12 +18,12 @@ function EntryManager:num_results()
|
||||
return self.linked_states.size
|
||||
end
|
||||
|
||||
function EntryManager:get_container(index)
|
||||
function EntryManager:get_container(offset, index)
|
||||
local count = 0
|
||||
for val in self.linked_states:iter() do
|
||||
count = count + 1
|
||||
|
||||
if count == index then
|
||||
if count == offset + index then
|
||||
return val
|
||||
end
|
||||
end
|
||||
@@ -44,33 +31,28 @@ function EntryManager:get_container(index)
|
||||
return {}
|
||||
end
|
||||
|
||||
function EntryManager:get_entry(index)
|
||||
return self:get_container(index)[1]
|
||||
function EntryManager:get_entry(offset, index)
|
||||
return self:get_container(offset, index)[1]
|
||||
end
|
||||
|
||||
function EntryManager:get_score(index)
|
||||
return self:get_container(index)[2]
|
||||
function EntryManager:get_score(offset, index)
|
||||
return self:get_container(offset, index)[2]
|
||||
end
|
||||
|
||||
function EntryManager:get_ordinal(index)
|
||||
return self:get_entry(index).ordinal
|
||||
function EntryManager:get_ordinal(offset, index)
|
||||
return self:get_entry(offset, 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
|
||||
|
||||
@@ -84,14 +66,12 @@ 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
|
||||
@@ -99,22 +79,15 @@ 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, prompt)
|
||||
score = score or 0
|
||||
|
||||
local max_res = self.max_results
|
||||
local num_sorted = self.num_sorted
|
||||
local worst_score = self.worst_acceptable_score
|
||||
local size = self.linked_states.size
|
||||
|
||||
local info = self.info
|
||||
info.maxed = info.maxed or 0
|
||||
|
||||
local new_container = { entry, score }
|
||||
|
||||
-- Short circuit for bad scores -- they never need to be displayed.
|
||||
@@ -123,16 +96,15 @@ function EntryManager:add_entry(picker, score, entry, prompt)
|
||||
return self.linked_states:append(new_container)
|
||||
end
|
||||
|
||||
self.dirty = true
|
||||
|
||||
-- Short circuit for first entry.
|
||||
if size == 0 then
|
||||
self.linked_states:prepend(new_container)
|
||||
self.set_entry(picker, 1, entry, score)
|
||||
return
|
||||
end
|
||||
|
||||
for index, container, node in self.linked_states:ipairs() do
|
||||
info.looped = info.looped + 1
|
||||
|
||||
if container[2] > score then
|
||||
return self:_insert_container_before(picker, index, node, new_container)
|
||||
end
|
||||
@@ -142,13 +114,12 @@ function EntryManager:add_entry(picker, score, entry, prompt)
|
||||
end
|
||||
|
||||
-- Don't add results that are too bad.
|
||||
if index >= max_res then
|
||||
info.maxed = info.maxed + 1
|
||||
if index >= num_sorted then
|
||||
return self:_append_container(picker, new_container, false)
|
||||
end
|
||||
end
|
||||
|
||||
if self.linked_states.size >= max_res then
|
||||
if self.linked_states.size >= num_sorted then
|
||||
self.worst_acceptable_score = math.min(self.worst_acceptable_score, score)
|
||||
end
|
||||
|
||||
@@ -165,4 +136,27 @@ function EntryManager:iter()
|
||||
end
|
||||
end
|
||||
|
||||
function EntryManager:window(start, finish)
|
||||
local results = {}
|
||||
|
||||
local idx = 0
|
||||
for val in self.linked_states:iter() do
|
||||
idx = idx + 1
|
||||
|
||||
if val == nil then
|
||||
return
|
||||
end
|
||||
|
||||
if idx >= start then
|
||||
table.insert(results, val[1])
|
||||
end
|
||||
|
||||
if idx >= finish then
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
return results
|
||||
end
|
||||
|
||||
return EntryManager
|
||||
|
||||
@@ -63,6 +63,8 @@ mappings.default_mappings = config.values.default_mappings
|
||||
|
||||
["<C-u>"] = actions.preview_scrolling_up,
|
||||
["<C-d>"] = actions.preview_scrolling_down,
|
||||
["<C-e>"] = actions.results_scrolling_down,
|
||||
["<C-y>"] = actions.results_scrolling_up,
|
||||
|
||||
["<PageUp>"] = actions.results_scrolling_up,
|
||||
["<PageDown>"] = actions.results_scrolling_down,
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,244 +0,0 @@
|
||||
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
|
||||
|
||||
local nvim_feed = function(text, feed_opts)
|
||||
feed_opts = feed_opts or "m"
|
||||
|
||||
vim.api.nvim_feedkeys(text, feed_opts, true)
|
||||
end
|
||||
|
||||
local writer = function(val)
|
||||
if type(val) == "table" then
|
||||
val = vim.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()
|
||||
for i = 1, #input do
|
||||
local char = input:sub(i, i)
|
||||
nvim_feed(char, "")
|
||||
|
||||
-- TODO: I'm not 100% sure this is a hack or not...
|
||||
-- it's possible these characters could still have an on_complete... but i'm not sure.
|
||||
if string.match(char, "%g") then
|
||||
coroutine.yield()
|
||||
end
|
||||
|
||||
if tester.debug then
|
||||
vim.wait(200)
|
||||
end
|
||||
end
|
||||
|
||||
vim.wait(10)
|
||||
|
||||
if tester.debug then
|
||||
coroutine.yield()
|
||||
end
|
||||
|
||||
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
|
||||
execute_test_case("post_close", k, v)
|
||||
end
|
||||
end
|
||||
|
||||
if tester.debug then
|
||||
return
|
||||
end
|
||||
|
||||
vim.defer_fn(end_test_cases, 20)
|
||||
end, 40)
|
||||
|
||||
coroutine.yield()
|
||||
end)
|
||||
end
|
||||
|
||||
local _VALID_KEYS = {
|
||||
post_typed = true,
|
||||
post_close = true,
|
||||
}
|
||||
|
||||
tester.builtin_picker = function(builtin_key, input, test_cases, opts)
|
||||
opts = opts or {}
|
||||
tester.debug = opts.debug or false
|
||||
|
||||
for k, _ in pairs(test_cases) do
|
||||
if not _VALID_KEYS[k] then
|
||||
return invalid_test_case(k)
|
||||
end
|
||||
end
|
||||
|
||||
opts.on_complete = {
|
||||
tester.picker_feed(input, test_cases),
|
||||
}
|
||||
|
||||
builtin[builtin_key](opts)
|
||||
end
|
||||
|
||||
local get_results_from_file = function(file)
|
||||
local j = Job:new {
|
||||
command = "nvim",
|
||||
args = {
|
||||
"--noplugin",
|
||||
"-u",
|
||||
"scripts/minimal_init.vim",
|
||||
"-c",
|
||||
string.format([[lua require("telescope.pickers._test")._execute("%s")]], file),
|
||||
},
|
||||
}
|
||||
|
||||
j:sync(10000)
|
||||
|
||||
local results = j:stderr_result()
|
||||
local result_table = {}
|
||||
for _, v in ipairs(results) do
|
||||
table.insert(result_table, vim.json.decode(v))
|
||||
end
|
||||
|
||||
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
|
||||
local assertion = asserters[v._type or "default"]
|
||||
|
||||
assertion(v.expected, v.actual, string.format("Test Case: %s // %s", v.location, v.case))
|
||||
end
|
||||
end
|
||||
|
||||
tester.run_string = function(contents)
|
||||
local tempname = vim.fn.tempname()
|
||||
|
||||
contents = [[
|
||||
local tester = require('telescope.pickers._test')
|
||||
local helper = require('telescope.pickers._test_helpers')
|
||||
|
||||
helper.make_globals()
|
||||
]] .. contents
|
||||
|
||||
vim.fn.writefile(vim.split(contents, "\n"), tempname)
|
||||
local result_table = get_results_from_file(tempname)
|
||||
vim.fn.delete(tempname)
|
||||
|
||||
check_results(result_table)
|
||||
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)
|
||||
|
||||
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
|
||||
@@ -2,8 +2,11 @@ local a = vim.api
|
||||
local log = require "telescope.log"
|
||||
local conf = require("telescope.config").values
|
||||
|
||||
local strdisplaywidth = require("plenary.strings").strdisplaywidth
|
||||
|
||||
local highlights = {}
|
||||
|
||||
local ns_telescope_matching = a.nvim_create_namespace "telescope_matching"
|
||||
local ns_telescope_selection = a.nvim_create_namespace "telescope_selection"
|
||||
local ns_telescope_multiselection = a.nvim_create_namespace "telescope_multiselection"
|
||||
local ns_telescope_entry = a.nvim_create_namespace "telescope_entry"
|
||||
@@ -14,10 +17,43 @@ Highlighter.__index = Highlighter
|
||||
function Highlighter:new(picker)
|
||||
return setmetatable({
|
||||
picker = picker,
|
||||
offset = picker._prefix_width,
|
||||
}, self)
|
||||
end
|
||||
|
||||
function Highlighter:hi_display(row, prefix, display_highlights)
|
||||
local DISPLAY_HIGHLIGHTS_PRIORITY = 110
|
||||
local SORTER_HIGHLIGHTS_PRIORITY = 120
|
||||
local SELECTION_HIGHLIGHTS_PRIORITY = 130
|
||||
|
||||
function Highlighter:highlight(row, opts)
|
||||
assert(row, "Must pass a row")
|
||||
|
||||
local picker = self.picker
|
||||
|
||||
local entry = opts.entry or picker:_get_entry_from_row(row)
|
||||
local prompt = opts.prompt or picker:_get_prompt()
|
||||
local is_selected = opts.is_selected or (picker._selection_row == row)
|
||||
local is_multi_selected = opts.is_multi_selected or picker:is_multi_selected(entry)
|
||||
|
||||
if is_selected then
|
||||
self:hi_selection(row)
|
||||
end
|
||||
|
||||
if not opts.skip_display then
|
||||
local display = opts.display
|
||||
local display_highlights = opts.display_highlights
|
||||
if not display then
|
||||
display, display_highlights = picker:_resolve_entry_display(entry)
|
||||
end
|
||||
|
||||
self:hi_display(row, display_highlights)
|
||||
self:hi_sorter(row, prompt, display)
|
||||
end
|
||||
|
||||
self:hi_multiselect(row, is_multi_selected)
|
||||
end
|
||||
|
||||
function Highlighter:hi_display(row, display_highlights)
|
||||
-- This is the bug that made my highlight fixes not work.
|
||||
-- We will leave the solution commented, so the test fails.
|
||||
if not display_highlights or vim.tbl_isempty(display_highlights) then
|
||||
@@ -25,95 +61,143 @@ function Highlighter:hi_display(row, prefix, display_highlights)
|
||||
end
|
||||
|
||||
local results_bufnr = assert(self.picker.results_bufnr, "Must have a results bufnr")
|
||||
if not a.nvim_buf_is_valid(results_bufnr) then
|
||||
return
|
||||
end
|
||||
|
||||
a.nvim_buf_clear_namespace(results_bufnr, ns_telescope_entry, row, row + 1)
|
||||
local len_prefix = #prefix
|
||||
|
||||
for _, hl_block in ipairs(display_highlights) do
|
||||
a.nvim_buf_add_highlight(
|
||||
results_bufnr,
|
||||
ns_telescope_entry,
|
||||
hl_block[2],
|
||||
row,
|
||||
len_prefix + hl_block[1][1],
|
||||
len_prefix + hl_block[1][2]
|
||||
)
|
||||
a.nvim_buf_set_extmark(results_bufnr, ns_telescope_entry, row, self.offset + hl_block[1][1], {
|
||||
end_col = self.offset + hl_block[1][2],
|
||||
hl_group = hl_block[2],
|
||||
priority = DISPLAY_HIGHLIGHTS_PRIORITY,
|
||||
strict = false,
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
function Highlighter:clear_display()
|
||||
function Highlighter:clear()
|
||||
if
|
||||
not self
|
||||
or not self.picker
|
||||
or not self.picker.results_bufnr
|
||||
or not vim.api.nvim_buf_is_valid(self.picker.results_bufnr)
|
||||
or not a.nvim_buf_is_valid(self.picker.results_bufnr)
|
||||
then
|
||||
return
|
||||
end
|
||||
|
||||
a.nvim_buf_clear_namespace(self.picker.results_bufnr, ns_telescope_entry, 0, -1)
|
||||
a.nvim_buf_clear_namespace(self.picker.results_bufnr, ns_telescope_matching, 0, -1)
|
||||
end
|
||||
|
||||
function Highlighter:hi_sorter(row, prompt, display)
|
||||
local picker = self.picker
|
||||
local sorter = picker.sorter
|
||||
if not picker.sorter or not picker.sorter.highlighter then
|
||||
return
|
||||
end
|
||||
|
||||
local results_bufnr = assert(self.picker.results_bufnr, "Must have a results bufnr")
|
||||
picker:highlight_one_row(results_bufnr, prompt, display, row)
|
||||
if not a.nvim_buf_is_valid(results_bufnr) then
|
||||
return
|
||||
end
|
||||
|
||||
local sorter_highlights = sorter:highlighter(prompt, display)
|
||||
|
||||
if sorter_highlights then
|
||||
for _, hl in ipairs(sorter_highlights) do
|
||||
local highlight, start, finish
|
||||
if type(hl) == "table" then
|
||||
highlight = hl.highlight or "TelescopeMatching"
|
||||
start = hl.start
|
||||
finish = hl.finish or hl.start
|
||||
elseif type(hl) == "number" then
|
||||
highlight = "TelescopeMatching"
|
||||
start = hl
|
||||
finish = hl
|
||||
else
|
||||
error "Invalid higlighter fn"
|
||||
end
|
||||
|
||||
a.nvim_buf_set_extmark(results_bufnr, ns_telescope_matching, row, start + self.offset - 1, {
|
||||
end_col = self.offset + finish,
|
||||
hl_group = highlight,
|
||||
priority = SORTER_HIGHLIGHTS_PRIORITY,
|
||||
strict = false,
|
||||
})
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function Highlighter:hi_selection(row, caret)
|
||||
caret = vim.F.if_nil(caret, "")
|
||||
function Highlighter:hi_selection(row)
|
||||
local results_bufnr = assert(self.picker.results_bufnr, "Must have a results bufnr")
|
||||
if not a.nvim_buf_is_valid(results_bufnr) then
|
||||
return
|
||||
end
|
||||
|
||||
a.nvim_buf_clear_namespace(results_bufnr, ns_telescope_selection, 0, -1)
|
||||
a.nvim_buf_add_highlight(results_bufnr, ns_telescope_selection, "TelescopeSelectionCaret", row, 0, #caret)
|
||||
|
||||
a.nvim_buf_set_extmark(
|
||||
results_bufnr,
|
||||
ns_telescope_selection,
|
||||
row,
|
||||
#caret,
|
||||
{ end_line = row + 1, hl_eol = conf.hl_result_eol, hl_group = "TelescopeSelection" }
|
||||
)
|
||||
-- If there isn't anything _on_ the line, then it's some edge case with
|
||||
-- loading the buffer or something like that.
|
||||
--
|
||||
-- We can just skip and we'll get the updates later.
|
||||
if a.nvim_buf_get_lines(results_bufnr, row, row + 1, false)[1] == "" then
|
||||
return
|
||||
end
|
||||
|
||||
-- TODO: Someone will complain about the highlighting here I'm sure.
|
||||
-- I don't know what to tell them except what are you doing w/ highlighting
|
||||
local caret = self.picker.selection_caret
|
||||
local offset = self.offset
|
||||
|
||||
-- Highlight the caret
|
||||
a.nvim_buf_set_extmark(results_bufnr, ns_telescope_selection, row, 0, {
|
||||
virt_text = { { caret, "TelescopeSelectionCaret" } },
|
||||
virt_text_pos = "overlay",
|
||||
end_col = offset,
|
||||
hl_group = "TelescopeSelectionCaret",
|
||||
priority = SELECTION_HIGHLIGHTS_PRIORITY,
|
||||
strict = true,
|
||||
})
|
||||
|
||||
-- Highlight the text after the caret
|
||||
a.nvim_buf_set_extmark(results_bufnr, ns_telescope_selection, row, offset, {
|
||||
end_line = row + 1,
|
||||
hl_eol = conf.hl_result_eol,
|
||||
hl_group = "TelescopeSelection",
|
||||
priority = SELECTION_HIGHLIGHTS_PRIORITY,
|
||||
})
|
||||
end
|
||||
|
||||
function Highlighter:hi_multiselect(row, is_selected)
|
||||
local results_bufnr = assert(self.picker.results_bufnr, "Must have a results bufnr")
|
||||
if not a.nvim_buf_is_valid(results_bufnr) then
|
||||
return
|
||||
end
|
||||
|
||||
a.nvim_buf_clear_namespace(results_bufnr, ns_telescope_multiselection, row, row + 1)
|
||||
|
||||
local line = a.nvim_buf_get_lines(results_bufnr, row, row + 1, false)[1]
|
||||
if not line then
|
||||
return
|
||||
end
|
||||
|
||||
if is_selected then
|
||||
vim.api.nvim_buf_add_highlight(results_bufnr, ns_telescope_multiselection, "TelescopeMultiSelection", row, 0, -1)
|
||||
if self.picker.multi_icon then
|
||||
local line = vim.api.nvim_buf_get_lines(results_bufnr, row, row + 1, false)[1]
|
||||
local pos = line:find(self.picker.multi_icon)
|
||||
if pos and pos <= math.max(#self.picker.selection_caret, #self.picker.entry_prefix) then
|
||||
vim.api.nvim_buf_add_highlight(
|
||||
results_bufnr,
|
||||
ns_telescope_multiselection,
|
||||
"TelescopeMultiIcon",
|
||||
row,
|
||||
pos - 1,
|
||||
pos - 1 + #self.picker.multi_icon
|
||||
)
|
||||
end
|
||||
end
|
||||
else
|
||||
local existing_marks = vim.api.nvim_buf_get_extmarks(
|
||||
results_bufnr,
|
||||
ns_telescope_multiselection,
|
||||
{ row, 0 },
|
||||
{ row, -1 },
|
||||
{}
|
||||
)
|
||||
a.nvim_buf_set_extmark(results_bufnr, ns_telescope_multiselection, row, self.offset, {
|
||||
end_col = #line,
|
||||
hl_group = "TelescopeMultiSelection",
|
||||
})
|
||||
|
||||
-- This is still kind of weird to me, since it seems like I'm erasing stuff
|
||||
-- when I shouldn't... Perhaps it's about the gravity of the extmark?
|
||||
if #existing_marks > 0 then
|
||||
log.trace("Clearing highlight multi select row: ", row)
|
||||
|
||||
vim.api.nvim_buf_clear_namespace(results_bufnr, ns_telescope_multiselection, row, row + 1)
|
||||
-- TEST WITH MULTI-BYTE CHARS
|
||||
if self.picker.multi_icon and self.offset > 0 then
|
||||
local icon = self.picker.multi_icon
|
||||
local cols = strdisplaywidth(icon)
|
||||
a.nvim_buf_set_extmark(results_bufnr, ns_telescope_multiselection, row, self.offset - cols, {
|
||||
end_col = self.offset,
|
||||
virt_text = { { self.picker.multi_icon, "TelescopeMultiIcon" } },
|
||||
virt_text_pos = "overlay",
|
||||
})
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
18
lua/telescope/pickers/prompt.lua
Normal file
18
lua/telescope/pickers/prompt.lua
Normal file
@@ -0,0 +1,18 @@
|
||||
local M = {}
|
||||
|
||||
M.set_prompt = function(picker)
|
||||
self._current_prefix_hl_group = hl_group or nil
|
||||
|
||||
if self.prompt_prefix ~= "" then
|
||||
vim.api.nvim_buf_add_highlight(
|
||||
self.prompt_bufnr,
|
||||
ns_telescope_prompt_prefix,
|
||||
self._current_prefix_hl_group or "TelescopePromptPrefix",
|
||||
0,
|
||||
0,
|
||||
strdisplaywidth(self.prompt_prefix)
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
return M
|
||||
@@ -40,7 +40,7 @@ local scroll_calculators = {
|
||||
end,
|
||||
}
|
||||
|
||||
scroller.create = function(scroll_strategy, sorting_strategy)
|
||||
scroller.new = 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))
|
||||
|
||||
116
lua/telescope/testharness/init.lua
Normal file
116
lua/telescope/testharness/init.lua
Normal file
@@ -0,0 +1,116 @@
|
||||
local assert = require "luassert"
|
||||
|
||||
local Path = require "plenary.path"
|
||||
|
||||
local tester = {}
|
||||
tester.debug = false
|
||||
|
||||
local get_results_from_contents = function(content)
|
||||
local nvim = vim.fn.jobstart(
|
||||
{ "nvim", "--noplugin", "-u", "scripts/minimal_init.vim", "--headless", "--embed" },
|
||||
{ rpc = true }
|
||||
)
|
||||
|
||||
local result = vim.fn.rpcrequest(nvim, "nvim_exec_lua", content, {})
|
||||
assert.are.same(true, result[1], vim.inspect(result))
|
||||
|
||||
local count = 0
|
||||
while
|
||||
vim.fn.rpcrequest(nvim, "nvim_exec_lua", "return require('telescope.testharness.runner').state.done", {}) ~= true
|
||||
do
|
||||
count = count + 1
|
||||
vim.wait(100)
|
||||
|
||||
-- TODO: Could maybe wait longer, but it's annoying to wait if the test is going to timeout.
|
||||
if count > 100 then
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
local state = vim.fn.rpcrequest(nvim, "nvim_exec_lua", "return require('telescope.testharness.runner').state", {})
|
||||
vim.fn.jobstop(nvim)
|
||||
|
||||
assert.are.same(true, state.done, vim.inspect(state))
|
||||
|
||||
local result_table = {}
|
||||
for _, v in ipairs(state.results) do
|
||||
table.insert(result_table, v)
|
||||
end
|
||||
|
||||
return result_table, state
|
||||
end
|
||||
|
||||
local check_results = function(results, state)
|
||||
assert(state, "Must pass state")
|
||||
|
||||
for _, v in ipairs(results) do
|
||||
local assertion
|
||||
if not v._type or v._type == "are" or v._type == "_default" then
|
||||
assertion = assert.are.same
|
||||
else
|
||||
assertion = assert.are_not.same
|
||||
end
|
||||
|
||||
-- TODO: I think it would be nice to be able to see the state,
|
||||
-- but it clutters up the test output so much here.
|
||||
--
|
||||
-- So we would have to consider how to do that I think.
|
||||
assertion(v.expected, v.actual, string.format("Test Case: %s // %s", v.location, v.case))
|
||||
end
|
||||
end
|
||||
|
||||
tester.run_string = function(contents)
|
||||
contents = [[
|
||||
return (function()
|
||||
local runner = require('telescope.testharness.runner')
|
||||
local helper = require('telescope.testharness.helpers')
|
||||
|
||||
helper.make_globals()
|
||||
local ok, msg = pcall(function()
|
||||
runner.log("Loading Test")
|
||||
|
||||
]] .. contents .. [[
|
||||
end)
|
||||
|
||||
return {ok, msg or runner.state}
|
||||
end)()
|
||||
]]
|
||||
|
||||
check_results(get_results_from_contents(contents))
|
||||
end
|
||||
|
||||
tester.run_file = function(filename)
|
||||
local file = "./lua/tests/pickers/" .. filename .. ".lua"
|
||||
local path = Path:new(file)
|
||||
|
||||
if not path:exists() then
|
||||
assert.are.same("<An existing file>", file)
|
||||
end
|
||||
|
||||
local contents = string.format(
|
||||
[[
|
||||
return (function()
|
||||
local runner = require('telescope.testharness.runner')
|
||||
local helper = require('telescope.testharness.helpers')
|
||||
|
||||
helper.make_globals()
|
||||
local ok, msg = pcall(function()
|
||||
runner.log("Loading Test")
|
||||
return loadfile("%s")()
|
||||
end)
|
||||
|
||||
return {ok, msg or runner.state}
|
||||
end)()
|
||||
]],
|
||||
path:absolute()
|
||||
)
|
||||
|
||||
check_results(get_results_from_contents(contents))
|
||||
end
|
||||
|
||||
tester.not_ = function(val)
|
||||
val._type = "are_not"
|
||||
return val
|
||||
end
|
||||
|
||||
return tester
|
||||
152
lua/telescope/testharness/runner.lua
Normal file
152
lua/telescope/testharness/runner.lua
Normal file
@@ -0,0 +1,152 @@
|
||||
local builtin = require "telescope.builtin"
|
||||
|
||||
local runner = {}
|
||||
|
||||
-- State is test variable
|
||||
runner.state = {
|
||||
done = false,
|
||||
results = {},
|
||||
msgs = {},
|
||||
}
|
||||
|
||||
local writer = function(val)
|
||||
table.insert(runner.state.results, val)
|
||||
end
|
||||
|
||||
local invalid_test_case = function(k)
|
||||
error { case = k, expected = "<a valid key>", actual = k }
|
||||
end
|
||||
|
||||
local _VALID_KEYS = {
|
||||
post_typed = true,
|
||||
post_close = true,
|
||||
}
|
||||
|
||||
local replace_terms = function(input)
|
||||
return vim.api.nvim_replace_termcodes(input, true, false, true)
|
||||
end
|
||||
|
||||
runner.nvim_feed = function(text, feed_opts)
|
||||
feed_opts = feed_opts or "m"
|
||||
|
||||
vim.api.nvim_feedkeys(text, feed_opts, true)
|
||||
end
|
||||
|
||||
local end_test_cases = function()
|
||||
runner.state.done = true
|
||||
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,
|
||||
}
|
||||
|
||||
end_test_cases()
|
||||
else
|
||||
writer {
|
||||
location = location,
|
||||
case = key,
|
||||
expected = spec[1],
|
||||
actual = actual,
|
||||
|
||||
_type = spec._type,
|
||||
}
|
||||
end
|
||||
|
||||
return ok
|
||||
end
|
||||
|
||||
runner.log = function(msg)
|
||||
table.insert(runner.state.msgs, msg)
|
||||
end
|
||||
|
||||
runner.picker = function(picker_name, input, test_cases, opts)
|
||||
opts = opts or {}
|
||||
|
||||
for k, _ in pairs(test_cases) do
|
||||
if not _VALID_KEYS[k] then
|
||||
return invalid_test_case(k)
|
||||
end
|
||||
end
|
||||
|
||||
opts.on_complete = {
|
||||
runner.create_on_complete(input, test_cases),
|
||||
}
|
||||
|
||||
opts._on_error = function(self, msg)
|
||||
runner.state.done = true
|
||||
writer {
|
||||
location = "Error while running on complete",
|
||||
expected = "To Work",
|
||||
actual = msg,
|
||||
}
|
||||
end
|
||||
|
||||
runner.log "Starting picker"
|
||||
builtin[picker_name](opts)
|
||||
runner.log "Called picker"
|
||||
end
|
||||
|
||||
runner.create_on_complete = function(input, test_cases)
|
||||
input = replace_terms(input)
|
||||
|
||||
local actions = {}
|
||||
for i = 1, #input do
|
||||
local char = input:sub(i, i)
|
||||
table.insert(actions, {
|
||||
cb = function()
|
||||
runner.log("Inserting char: " .. char)
|
||||
runner.nvim_feed(char, "")
|
||||
end,
|
||||
char = char,
|
||||
})
|
||||
end
|
||||
|
||||
return function()
|
||||
local action = {}
|
||||
repeat
|
||||
action = table.remove(actions, 1)
|
||||
if action then
|
||||
action.cb()
|
||||
end
|
||||
until not action or string.match(action.char, "%g")
|
||||
|
||||
if #actions > 0 then
|
||||
return
|
||||
end
|
||||
|
||||
vim.defer_fn(function()
|
||||
if test_cases.post_typed then
|
||||
for k, v in ipairs(test_cases.post_typed) do
|
||||
if not execute_test_case("post_typed", k, v) then
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
runner.nvim_feed(replace_terms "<CR>", "")
|
||||
|
||||
vim.defer_fn(function()
|
||||
if test_cases.post_close then
|
||||
for k, v in ipairs(test_cases.post_close) do
|
||||
if not execute_test_case("post_close", k, v) then
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
vim.defer_fn(end_test_cases, 20)
|
||||
end, 20)
|
||||
end, 20)
|
||||
end
|
||||
end
|
||||
|
||||
return runner
|
||||
@@ -4,7 +4,7 @@ local eq = assert.are.same
|
||||
|
||||
describe("process_result", function()
|
||||
it("works with one entry", function()
|
||||
local manager = EntryManager:new(5, nil)
|
||||
local manager = EntryManager:new(5)
|
||||
|
||||
manager:add_entry(nil, 1, "hello", "")
|
||||
|
||||
@@ -12,64 +12,32 @@ describe("process_result", function()
|
||||
end)
|
||||
|
||||
it("works with two entries", function()
|
||||
local manager = EntryManager:new(5, nil)
|
||||
local manager = EntryManager:new(5)
|
||||
|
||||
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)
|
||||
eq("hello", manager:get_entry(0, 1))
|
||||
eq("later", manager:get_entry(0, 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", "")
|
||||
local manager = EntryManager:new(5)
|
||||
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)
|
||||
eq("better result", manager:get_entry(0, 1))
|
||||
eq("worse result", manager:get_entry(0, 2))
|
||||
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", "")
|
||||
local manager = EntryManager:new(1)
|
||||
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)
|
||||
eq("better result", manager:get_entry(0, 1))
|
||||
end)
|
||||
|
||||
it("should allow simple entries", function()
|
||||
@@ -103,31 +71,6 @@ describe("process_result", function()
|
||||
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", "")
|
||||
@@ -138,20 +81,12 @@ describe("process_result", function()
|
||||
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", "")
|
||||
local manager = EntryManager:new(2)
|
||||
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("better result", manager:get_entry(0, 1))
|
||||
eq(4, manager.worst_acceptable_score)
|
||||
end)
|
||||
|
||||
@@ -167,8 +102,8 @@ describe("process_result", function()
|
||||
manager:add_entry(picker, 0.5, "same same", "asdf")
|
||||
manager:add_entry(picker, 0.5, "same", "asdf")
|
||||
|
||||
eq("same", manager:get_entry(1))
|
||||
eq("same same", manager:get_entry(2))
|
||||
eq("same", manager:get_entry(0, 1))
|
||||
eq("same same", manager:get_entry(0, 2))
|
||||
end)
|
||||
|
||||
it("should call tiebreaker if score is the same, keep initial", function()
|
||||
@@ -183,7 +118,21 @@ describe("process_result", function()
|
||||
manager:add_entry(picker, 0.5, "same same", "asdf")
|
||||
manager:add_entry(picker, 0.5, "same", "asdf")
|
||||
|
||||
eq("same", manager:get_entry(2))
|
||||
eq("same same", manager:get_entry(1))
|
||||
eq("same", manager:get_entry(0, 2))
|
||||
eq("same same", manager:get_entry(0, 1))
|
||||
end)
|
||||
|
||||
it(":window() should return table of resuls", function()
|
||||
local manager = EntryManager:new(5, nil)
|
||||
|
||||
manager:add_entry(nil, 1, "first")
|
||||
manager:add_entry(nil, 2, "second")
|
||||
manager:add_entry(nil, 3, "third")
|
||||
manager:add_entry(nil, 4, "fourth")
|
||||
manager:add_entry(nil, 5, "sixth")
|
||||
|
||||
eq(5, manager.linked_states.size)
|
||||
|
||||
eq({ "second", "third" }, manager:window(2, 3))
|
||||
end)
|
||||
end)
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
require("plenary.reload").reload_module "telescope"
|
||||
|
||||
local tester = require "telescope.pickers._test"
|
||||
local tester = require "telescope.testharness"
|
||||
|
||||
local disp = function(val)
|
||||
return vim.inspect(val, { newline = " ", indent = "" })
|
||||
@@ -11,6 +9,7 @@ describe("builtin.find_files", function()
|
||||
tester.run_file "find_files__readme"
|
||||
end)
|
||||
|
||||
<<<<<<< HEAD
|
||||
it("should be able to move selections", function()
|
||||
tester.run_file "find_files__with_ctrl_n"
|
||||
end)
|
||||
@@ -19,6 +18,14 @@ describe("builtin.find_files", function()
|
||||
{ sorting_strategy = "descending" },
|
||||
{ sorting_strategy = "ascending" },
|
||||
} do
|
||||
=======
|
||||
for _, configuration in
|
||||
ipairs {
|
||||
{ sorting_strategy = "descending" },
|
||||
{ sorting_strategy = "ascending" },
|
||||
}
|
||||
do
|
||||
>>>>>>> 20a1519 (feat(fps): Add refresh style display for telescope)
|
||||
it("should not display devicons when disabled: " .. disp(configuration), function()
|
||||
tester.run_string(string.format(
|
||||
[[
|
||||
|
||||
109
lua/tests/automated/pickers/scroll_cycle_spec.lua
Normal file
109
lua/tests/automated/pickers/scroll_cycle_spec.lua
Normal file
@@ -0,0 +1,109 @@
|
||||
local tester = require "telescope.testharness"
|
||||
|
||||
--[[
|
||||
Available functions are
|
||||
- fixtures/file_a.txt
|
||||
- fixtures/file_abc.txt
|
||||
--]]
|
||||
|
||||
describe("scroll_cycle", function()
|
||||
it("should be able to cycle selections: cycle", function()
|
||||
tester.run_string [[
|
||||
runner.picker("find_files", "fixtures/file<c-p>", {
|
||||
post_close = {
|
||||
{ "lua/tests/fixtures/file_abc.txt", helper.get_selection_value },
|
||||
},
|
||||
}, {
|
||||
sorting_strategy = "ascending",
|
||||
scroll_strategy = "cycle",
|
||||
})
|
||||
]]
|
||||
end)
|
||||
|
||||
for _, sorting in ipairs { "ascending", "descending" } do
|
||||
for _, key in ipairs { "<c-n>", "<c-p>" } do
|
||||
it(string.format("Cycle: %sx2 %s", key, sorting), function()
|
||||
tester.run_string(([[
|
||||
runner.picker("find_files", "fixtures/file%s%s", {
|
||||
post_typed = {
|
||||
{ "lua/tests/fixtures/file_a.txt", helper.get_selection_value },
|
||||
},
|
||||
}, {
|
||||
sorting_strategy = "%s",
|
||||
scroll_strategy = "cycle",
|
||||
}
|
||||
) ]]):format(key, key, sorting))
|
||||
end)
|
||||
|
||||
it(string.format("Cycle: %sx3 %s", key, sorting), function()
|
||||
tester.run_string(([[
|
||||
runner.picker("find_files", "fixtures/file%s%s%s", {
|
||||
post_typed = {
|
||||
{ "lua/tests/fixtures/file_abc.txt", helper.get_selection_value },
|
||||
},
|
||||
}, {
|
||||
sorting_strategy = "%s",
|
||||
scroll_strategy = "cycle",
|
||||
}
|
||||
) ]]):format(key, key, key, sorting))
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
it("should be able to cycle selections: limit", function()
|
||||
tester.run_string [[
|
||||
runner.picker("find_files", "fixtures/file<c-p>", {
|
||||
post_close = {
|
||||
{ "lua/tests/fixtures/file_a.txt", helper.get_selection_value },
|
||||
},
|
||||
}, {
|
||||
sorting_strategy = "ascending",
|
||||
scroll_strategy = "limit",
|
||||
})
|
||||
]]
|
||||
end)
|
||||
|
||||
it("long: cycle to top", function()
|
||||
tester.run_string [[
|
||||
runner.picker("find_files", "fixtures/long<c-p>", {
|
||||
post_close = {
|
||||
{ "lua/tests/fixtures/long_11111111111.md", helper.get_selection_value },
|
||||
},
|
||||
}, {
|
||||
sorting_strategy = "ascending",
|
||||
scroll_strategy = "cycle",
|
||||
height = 10,
|
||||
})
|
||||
]]
|
||||
end)
|
||||
|
||||
it("long: cycle to top", function()
|
||||
tester.run_string [[
|
||||
runner.picker("find_files", "fixtures/long<c-n>", {
|
||||
post_close = {
|
||||
{ "lua/tests/fixtures/long_11111111111.md", helper.get_selection_value },
|
||||
},
|
||||
}, {
|
||||
sorting_strategy = "descending",
|
||||
scroll_strategy = "cycle",
|
||||
height = 10,
|
||||
})
|
||||
]]
|
||||
end)
|
||||
|
||||
it("long: smash <c-p>", function()
|
||||
tester.run_string [[
|
||||
runner.picker("find_files", "fixtures/long<c-p><c-p><c-p><c-p><c-p>", {
|
||||
post_typed = {
|
||||
{ "lua/tests/fixtures/long_111111.md", helper.get_selection_value },
|
||||
},
|
||||
}, {
|
||||
sorting_strategy = "descending",
|
||||
scroll_strategy = "cycle",
|
||||
layout_config = {
|
||||
height = 8,
|
||||
},
|
||||
})
|
||||
]]
|
||||
end)
|
||||
end)
|
||||
@@ -1,9 +1,4 @@
|
||||
require("plenary.reload").reload_module "telescope"
|
||||
|
||||
local tester = require "telescope.pickers._test"
|
||||
|
||||
local log = require "telescope.log"
|
||||
log.use_console = false
|
||||
local tester = require "telescope.testharness"
|
||||
|
||||
describe("scrolling strategies", function()
|
||||
it("should handle cycling for full list", function()
|
||||
|
||||
39
lua/tests/automated/pickers/testing_harness_spec.lua
Normal file
39
lua/tests/automated/pickers/testing_harness_spec.lua
Normal file
@@ -0,0 +1,39 @@
|
||||
local testharness = require "telescope.testharness"
|
||||
|
||||
describe("testing harness", function()
|
||||
it("should find the readme, using lowercase", function()
|
||||
testharness.run_string [[
|
||||
runner.picker('find_files', 'readme.md', {
|
||||
post_typed = {
|
||||
{ "> readme.md", GetPrompt },
|
||||
{ " README.md", GetBestResult },
|
||||
},
|
||||
post_close = {
|
||||
{ 'README.md', GetFile },
|
||||
}
|
||||
}, {
|
||||
disable_devicons = true,
|
||||
})
|
||||
]]
|
||||
end)
|
||||
|
||||
it("should find the readme, using uppercase", function()
|
||||
testharness.run_string [[
|
||||
runner.picker('find_files', 'RE', {
|
||||
post_close = {
|
||||
{ 'README.md', GetFile },
|
||||
}
|
||||
})
|
||||
]]
|
||||
end)
|
||||
|
||||
it("Should find telescope prompt file", function()
|
||||
testharness.run_string [[
|
||||
runner.picker('find_files', 'TelescopePrompt', {
|
||||
post_close = {
|
||||
{ 'TelescopePrompt.lua', GetFile },
|
||||
}
|
||||
})
|
||||
]]
|
||||
end)
|
||||
end)
|
||||
0
lua/tests/fixtures/long_1.md
vendored
Normal file
0
lua/tests/fixtures/long_1.md
vendored
Normal file
0
lua/tests/fixtures/long_11.md
vendored
Normal file
0
lua/tests/fixtures/long_11.md
vendored
Normal file
0
lua/tests/fixtures/long_111.md
vendored
Normal file
0
lua/tests/fixtures/long_111.md
vendored
Normal file
0
lua/tests/fixtures/long_1111.md
vendored
Normal file
0
lua/tests/fixtures/long_1111.md
vendored
Normal file
0
lua/tests/fixtures/long_11111.md
vendored
Normal file
0
lua/tests/fixtures/long_11111.md
vendored
Normal file
0
lua/tests/fixtures/long_111111.md
vendored
Normal file
0
lua/tests/fixtures/long_111111.md
vendored
Normal file
0
lua/tests/fixtures/long_1111111.md
vendored
Normal file
0
lua/tests/fixtures/long_1111111.md
vendored
Normal file
0
lua/tests/fixtures/long_11111111.md
vendored
Normal file
0
lua/tests/fixtures/long_11111111.md
vendored
Normal file
0
lua/tests/fixtures/long_111111111.md
vendored
Normal file
0
lua/tests/fixtures/long_111111111.md
vendored
Normal file
0
lua/tests/fixtures/long_1111111111.md
vendored
Normal file
0
lua/tests/fixtures/long_1111111111.md
vendored
Normal file
0
lua/tests/fixtures/long_11111111111.md
vendored
Normal file
0
lua/tests/fixtures/long_11111111111.md
vendored
Normal file
@@ -1,7 +1,7 @@
|
||||
local tester = require "telescope.pickers._test"
|
||||
local helper = require "telescope.pickers._test_helpers"
|
||||
local helper = require "telescope.testharness.helpers"
|
||||
local runner = require "telescope.testharness.runner"
|
||||
|
||||
tester.builtin_picker("find_files", "README.md", {
|
||||
runner.picker("find_files", "README.md", {
|
||||
post_close = {
|
||||
{ "README.md", helper.get_file },
|
||||
},
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
require("plenary.reload").reload_module "plenary"
|
||||
require("plenary.reload").reload_module "telescope"
|
||||
local tester = require "telescope.testharness"
|
||||
local runner = require "telescope.testharness.runner"
|
||||
local helper = require "telescope.testharness.helpers"
|
||||
|
||||
local tester = require "telescope.pickers._test"
|
||||
local helper = require "telescope.pickers._test_helpers"
|
||||
|
||||
tester.builtin_picker("find_files", "telescope<c-n>", {
|
||||
runner.picker("find_files", "plugin<c-n>", {
|
||||
post_close = {
|
||||
tester.not_ { "plugin/telescope.vim", helper.get_file },
|
||||
tester.not_ { "telescope.vim", helper.get_file },
|
||||
{ "TelescopePrompt.lua", helper.get_file },
|
||||
},
|
||||
}, {
|
||||
sorting_strategy = "descending",
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
local tester = require "telescope.pickers._test"
|
||||
local helper = require "telescope.pickers._test_helpers"
|
||||
|
||||
tester.builtin_picker("find_files", "fixtures/file<c-p>", {
|
||||
post_close = {
|
||||
{ "lua/tests/fixtures/file_abc.txt", helper.get_selection_value },
|
||||
},
|
||||
})
|
||||
89
scratch/files.py
Normal file
89
scratch/files.py
Normal file
@@ -0,0 +1,89 @@
|
||||
from pathlib import Path
|
||||
import subprocess
|
||||
|
||||
something = subprocess.run(["rg", "--files"], capture_output=True)
|
||||
files = something.stdout.decode('utf-8').split('\n')
|
||||
files.sort()
|
||||
|
||||
user_input = "lua finders/async"
|
||||
|
||||
REJECTED = -1.0
|
||||
GOOD = 2.0
|
||||
|
||||
def is_subset(item, prompt) -> float:
|
||||
prompt_chars = set()
|
||||
for c in prompt:
|
||||
prompt_chars.add(c)
|
||||
|
||||
item_chars = set()
|
||||
for c in item:
|
||||
item_chars.add(c)
|
||||
|
||||
return prompt_chars.issubset(item_chars)
|
||||
|
||||
def proportion_of_contained_letters(prompt, item) -> float:
|
||||
prompt_chars = set()
|
||||
for c in prompt:
|
||||
prompt_chars.add(c)
|
||||
|
||||
item_chars = set()
|
||||
for c in item:
|
||||
item_chars.add(c)
|
||||
|
||||
contained = 0
|
||||
for prompt_char in prompt_chars:
|
||||
if prompt_char in item_chars:
|
||||
contained += 1
|
||||
|
||||
return contained / len(prompt_chars)
|
||||
|
||||
def jerry_match(prompt: str, item) -> float:
|
||||
p = Path(item)
|
||||
|
||||
split = prompt.split(" ", maxsplit=2)
|
||||
language = split[0]
|
||||
filter = split[1]
|
||||
|
||||
if p.suffix != "." + language:
|
||||
return REJECTED
|
||||
|
||||
if filter in item:
|
||||
return GOOD
|
||||
|
||||
proprotion = proportion_of_contained_letters(filter, item)
|
||||
if proprotion < 0.75:
|
||||
return REJECTED
|
||||
|
||||
return proprotion
|
||||
|
||||
def score_results(prompt, files):
|
||||
results = []
|
||||
for f in files:
|
||||
score = jerry_match(prompt, f)
|
||||
if score == REJECTED:
|
||||
continue
|
||||
|
||||
results.append({'score': score, 'item': f})
|
||||
|
||||
|
||||
results.sort(key=lambda x: x["score"], reverse=True)
|
||||
return results
|
||||
|
||||
while True:
|
||||
i = input("Filter Phrase > ")
|
||||
if not i:
|
||||
break
|
||||
|
||||
results = score_results(i, files)
|
||||
for result in results[:10]:
|
||||
print(result["item"])
|
||||
|
||||
# x = [1, 2, 3, 4, 4, 2, 1, 3]
|
||||
# print(x)
|
||||
# print(set(x))
|
||||
|
||||
# x = {1, 2, 3}
|
||||
# y = {1}
|
||||
#
|
||||
# print("x.issubset(y)", x.issubset(y))
|
||||
# print("y.issubset(x)", y.issubset(x))
|
||||
Reference in New Issue
Block a user