feat: quickfix (#293)

* feat: quickfix (not implemented)

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

* somewhat working linked list impl

* getting closer

* might be working

* might be working for real

* works and implemented basic example

* dont forget to close prompt

* fix descending and add more tests

* test fixes

* fix test

* more logging

* Fix some more tests

* Fix logging messing up tests

* fix: lint

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

View File

@@ -1,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)
local timer = vim.loop.new_timer()
timer:start(20, 0, vim.schedule_wrap(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")
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
if debug then
nvim_feed(replace_terms("<CR>"), "")
end, 20)
vim.defer_fn(function()
if test_cases.post_close then
for k, v in ipairs(test_cases.post_close) do
execute_test_case('post_close', k, v)
end
end
if tester.debug then
return
end
vim.defer_fn(function()
vim.cmd [[qa!]]
end, 10)
end))
vim.defer_fn(end_test_cases, 20)
end, 40)
if not debug then
vim.schedule(function()
if test_cases.post_typed then
for k, v in ipairs(test_cases.post_typed) do
io.stderr:write(vim.fn.json_encode({ case = k, expected = v[1], actual = v[2]() }))
io.stderr:write("\n")
end
end
nvim_feed(replace_terms("<CR>"), "")
end)
end
coroutine.yield()
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

View File

@@ -22,9 +22,15 @@ test_helpers.get_results = function()
return vim.api.nvim_buf_get_lines(test_helpers.get_results_bufnr(), 0, -1, false)
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

View File

@@ -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

View File

@@ -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
local range_calculators = {
ascending = function(max_results, num_results)
return 0, math.min(max_results, num_results)
end,
descending = function(max_results, num_results)
return math.max(max_results - num_results, 0), max_results
end,
}
local scroll_calculators = {
cycle = function(range_fn)
return function(max_results, num_results, row)
local start, finish = range_fn(max_results, num_results)
if row >= finish then
return start
elseif row < start then
return finish - 1
end
return row
end
end,
limit = function(range_fn)
return function(max_results, num_results, row)
local start, finish = range_fn(max_results, num_results)
if row >= finish then
return finish - 1
elseif row < start then
return start
end
return row
end
end,
}
scroller.create = function(scroll_strategy, sorting_strategy)
local range_fn = range_calculators[sorting_strategy]
if not range_fn then
error(debug.traceback("Unknown sorting strategy: " .. sorting_strategy))
end
end
scroller.create = function(strategy, sorting_strategy)
local calc_count = calc_count_fn(sorting_strategy)
local scroll_fn = scroll_calculators[scroll_strategy]
if not scroll_fn then
error(debug.traceback("Unknown scroll strategy: " .. (scroll_strategy or '')))
end
if strategy == 'cycle' then
return function(max_results, num_results, row)
local count, b = calc_count(max_results, num_results, row)
if b then return count end
local calculator = scroll_fn(range_fn)
return function(max_results, num_results, row)
local result = calculator(max_results, num_results, row)
if row >= count then
return 0
elseif row < 0 then
return count - 1
end
return 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
elseif strategy == 'limit' or strategy == nil then
return function(max_results, num_results, row)
local count = calc_count(max_results, num_results)
if row >= count then
return count - 1
elseif row < 0 then
return 0
end
return row
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
else
error("Unsupported strategy: " .. strategy)
return result
end
end