feat: Add scrolling through results (#1232)

* some scrollin

* [WIP]: Fri 10 Sep 2021 02:24:20 PM EDT

* ok, I think scrolling works

* change to 1000 for now, dont need to scroll that far :)
This commit is contained in:
TJ DeVries
2021-10-08 07:56:01 -07:00
committed by GitHub
parent ec6c13fc09
commit dea927d0eb
5 changed files with 101 additions and 116 deletions

View File

@@ -0,0 +1,2 @@
-- Don't have scrolloff, it makes things weird.
vim.opt.scrolloff = 0

View File

@@ -2,27 +2,6 @@ local log = require "telescope.log"
local LinkedList = require "telescope.algos.linked_list" local LinkedList = require "telescope.algos.linked_list"
--[[
OK, new idea.
We can do linked list here.
To convert at the end to quickfix, just run the list.
...
start node
end node
if past loop of must have scores,
then we can just add to end node and shift end node to current node.
etc.
always inserts a row, because we clear everything before?
can also optimize by keeping worst acceptable score around.
--]]
local EntryManager = {} local EntryManager = {}
EntryManager.__index = EntryManager EntryManager.__index = EntryManager

View File

@@ -7,6 +7,7 @@ local log = require "telescope.log"
return function(opts) return function(opts)
log.trace("Creating async_job:", opts) log.trace("Creating async_job:", opts)
local entry_maker = opts.entry_maker or make_entry.gen_from_string() local entry_maker = opts.entry_maker or make_entry.gen_from_string()
local fn_command = function(prompt) local fn_command = function(prompt)
local command_list = opts.command_generator(prompt) local command_list = opts.command_generator(prompt)
if command_list == nil then if command_list == nil then

View File

@@ -1,6 +1,4 @@
local user = vim.loop.os_getenv "USER"
return require("plenary.log").new { return require("plenary.log").new {
plugin = "telescope", plugin = "telescope",
level = ((user == "tj" or user == "tjdevries") and "debug") or "warn", level = "info",
} }

View File

@@ -213,28 +213,17 @@ function Picker:clear_extra_rows(results_bufnr)
end end
if not ok then if not ok then
log.debug(msg) log.debug("Failed to set lines:", msg)
end end
log.trace("Clearing:", worst_line) log.trace("Clearing:", worst_line)
end end
function Picker:highlight_displayed_rows(results_bufnr, prompt) function Picker:highlight_one_row(results_bufnr, prompt, display, row)
if not self.sorter or not self.sorter.highlighter then if not self.sorter.highlighter then
return return
end end
vim.api.nvim_buf_clear_namespace(results_bufnr, ns_telescope_matching, 0, -1)
local displayed_rows = vim.api.nvim_buf_get_lines(results_bufnr, 0, -1, false)
for row_index = 1, math.min(#displayed_rows, self.max_results) do
local display = displayed_rows[row_index]
self:highlight_one_row(results_bufnr, prompt, display, row_index - 1)
end
end
function Picker:highlight_one_row(results_bufnr, prompt, display, row)
local highlights = self.sorter:highlighter(prompt, display) local highlights = self.sorter:highlighter(prompt, display)
if highlights then if highlights then
@@ -352,22 +341,19 @@ function Picker:find()
self.prompt_prefix = prompt_prefix self.prompt_prefix = prompt_prefix
self:_reset_prefix_color() self:_reset_prefix_color()
-- First thing we want to do is set all the lines to blank. -- TODO: This could be configurable in the future, but I don't know why you would
self.max_results = popup_opts.results.height -- want to scroll through more than 10,000 items.
--
-- This just lets us stop doing stuff after tons of things.
self.max_results = 1000
-- TODO(scrolling): This may be a hack when we get a little further into implementing scrolling.
vim.api.nvim_buf_set_lines(results_bufnr, 0, self.max_results, false, utils.repeated_table(self.max_results, "")) vim.api.nvim_buf_set_lines(results_bufnr, 0, self.max_results, false, utils.repeated_table(self.max_results, ""))
-- TODO(status): I would love to get the status text not moving back and forth. Perhaps it is just a problem with
-- virtual text & prompt buffers or something though. I can't figure out why it would redraw the way it does.
--
-- A "hacked" version of this would be to calculate where the area I want the status to go and put a new window there.
-- With this method, I do not need to worry about padding or antying, just make it take up X characters or something.
local status_updater = self:get_status_updater(prompt_win, prompt_bufnr) local status_updater = self:get_status_updater(prompt_win, prompt_bufnr)
local debounced_status = debounce.throttle_leading(status_updater, 50) local debounced_status = debounce.throttle_leading(status_updater, 50)
local tx, rx = channel.mpsc() local tx, rx = channel.mpsc()
self.__on_lines = tx.send self._on_lines = tx.send
local find_id = self:_next_find_id() local find_id = self:_next_find_id()
@@ -412,25 +398,14 @@ function Picker:find()
local start_time = vim.loop.hrtime() local start_time = vim.loop.hrtime()
local prompt = self:_get_prompt() local prompt = self:_get_next_filtered_prompt()
local on_input_result = self._on_input_filter_cb(prompt) or {}
local new_prompt = on_input_result.prompt
if new_prompt then
prompt = new_prompt
end
local new_finder = on_input_result.updated_finder
if new_finder then
self.finder:close()
self.finder = new_finder
end
-- TODO: Entry manager should have a "bulk" setter. This can prevent a lot of redraws from display -- TODO: Entry manager should have a "bulk" setter. This can prevent a lot of redraws from display
if self.cache_picker == false or not (self.cache_picker.is_cached == true) then if self.cache_picker == false or not (self.cache_picker.is_cached == true) then
self.sorter:_start(prompt) self.sorter:_start(prompt)
self.manager = EntryManager:new(self.max_results, self.entry_adder, self.stats) self.manager = EntryManager:new(self.max_results, self.entry_adder, self.stats)
vim.api.nvim_buf_clear_namespace(results_bufnr, ns_telescope_matching, 0, -1)
local process_result = self:get_result_processor(find_id, prompt, debounced_status) local process_result = self:get_result_processor(find_id, prompt, debounced_status)
local process_complete = self:get_result_completor(self.results_bufnr, find_id, prompt, status_updater) local process_complete = self:get_result_completor(self.results_bufnr, find_id, prompt, status_updater)
@@ -447,26 +422,8 @@ function Picker:find()
async.util.sleep(self.debounce - diff_time) async.util.sleep(self.debounce - diff_time)
end end
else else
-- resume previous picker -- TODO(scroll): This can only happen once, I don't like where it is.
local index = 1 self:_resume_picker()
for entry in self.manager:iter() do
self:entry_adder(index, entry, _, true)
index = index + 1
end
self.cache_picker.is_cached = false
-- if text changed, required to set anew to restart finder; otherwise hl and selection
if self.cache_picker.cached_prompt ~= self.default_text then
self:reset_prompt()
self:set_prompt(self.default_text)
else
-- scheduling required to apply highlighting and selection appropriately
await_schedule(function()
self:highlight_displayed_rows(self.results_bufnr, self.cache_picker.cached_prompt)
if self.cache_picker.selection_row ~= nil then
self:set_selection(self.cache_picker.selection_row)
end
end)
end
end end
end end
end) end)
@@ -476,10 +433,10 @@ function Picker:find()
on_lines = function(...) on_lines = function(...)
find_id = self:_next_find_id() find_id = self:_next_find_id()
self._result_completed = false
status_updater { completed = false } status_updater { completed = false }
tx.send(...) self._on_lines(...)
end, end,
on_detach = function() on_detach = function()
self:_detach() self:_detach()
end, end,
@@ -753,7 +710,7 @@ function Picker:refresh(finder, opts)
self._multi = MultiSelect:new() self._multi = MultiSelect:new()
end end
self.__on_lines(nil, nil, nil, 0, 1) self._on_lines(nil, nil, nil, 0, 1)
end end
function Picker:set_selection(row) function Picker:set_selection(row)
@@ -856,6 +813,8 @@ function Picker:set_selection(row)
self._selection_row = row self._selection_row = row
self:refresh_previewer() self:refresh_previewer()
vim.api.nvim_win_set_cursor(self.results_win, { row + 1, 0 })
end end
function Picker:refresh_previewer() function Picker:refresh_previewer()
@@ -918,9 +877,7 @@ function Picker:entry_adder(index, entry, _, insert)
self:_increment "displayed" self:_increment "displayed"
-- TODO: Don't need to schedule this if we schedule the adder.
local offset = insert and 0 or 1 local offset = insert and 0 or 1
vim.schedule(function()
if not vim.api.nvim_buf_is_valid(self.results_bufnr) then if not vim.api.nvim_buf_is_valid(self.results_bufnr) then
log.debug "ON_ENTRY: Invalid buffer" log.debug "ON_ENTRY: Invalid buffer"
return return
@@ -942,6 +899,7 @@ function Picker:entry_adder(index, entry, _, insert)
local set_ok, msg = pcall(vim.api.nvim_buf_set_lines, self.results_bufnr, row, row + offset, false, { display }) local set_ok, msg = pcall(vim.api.nvim_buf_set_lines, self.results_bufnr, row, row + offset, false, { display })
if set_ok and display_highlights then if set_ok and display_highlights then
self.highlighter:hi_display(row, prefix, display_highlights) self.highlighter:hi_display(row, prefix, display_highlights)
self:highlight_one_row(self.results_bufnr, self:_get_prompt(), display, row)
end end
if not set_ok then if not set_ok then
@@ -954,7 +912,6 @@ function Picker:entry_adder(index, entry, _, insert)
display = display:gsub("\n", " | ") display = display:gsub("\n", " | ")
vim.api.nvim_buf_set_lines(self.results_bufnr, row, row + 1, false, { display }) vim.api.nvim_buf_set_lines(self.results_bufnr, row, row + 1, false, { display })
end end
end)
end end
function Picker:_reset_track() function Picker:_reset_track()
@@ -1025,6 +982,8 @@ function Picker:get_status_updater(prompt_win, prompt_bufnr)
end end
function Picker:get_result_processor(find_id, prompt, status_updater) function Picker:get_result_processor(find_id, prompt, status_updater)
local count = 0
local cb_add = function(score, entry) local cb_add = function(score, entry)
self.manager:add_entry(self, score, entry) self.manager:add_entry(self, score, entry)
status_updater { completed = false } status_updater { completed = false }
@@ -1045,6 +1004,8 @@ function Picker:get_result_processor(find_id, prompt, status_updater)
return return
end end
count = count + 1
-- TODO: Probably should asyncify this / cache this / do something because this probably takes -- TODO: Probably should asyncify this / cache this / do something because this probably takes
-- a ton of time on large results. -- a ton of time on large results.
log.trace("Processing result... ", entry) log.trace("Processing result... ", entry)
@@ -1060,6 +1021,13 @@ function Picker:get_result_processor(find_id, prompt, status_updater)
end end
self.sorter:score(prompt, entry, cb_add, cb_filter) self.sorter:score(prompt, entry, cb_add, cb_filter)
-- Only on the first addition do we want to set the selection.
-- This allows us to handle moving the cursor to the bottom or top of the window
-- depending on the strategy.
if count == 1 then
self:_do_selection(prompt)
end
end end
end end
@@ -1075,12 +1043,9 @@ function Picker:get_result_completor(results_bufnr, find_id, prompt, status_upda
status_updater { completed = true } status_updater { completed = true }
self:clear_extra_rows(results_bufnr) self:clear_extra_rows(results_bufnr)
self:highlight_displayed_rows(results_bufnr, prompt)
self.sorter:_finish(prompt) self.sorter:_finish(prompt)
self:_on_complete() self:_on_complete()
self._result_completed = true
end) end)
end end
@@ -1226,6 +1191,46 @@ function Picker:_detach()
self.closed = true self.closed = true
end end
function Picker:_get_next_filtered_prompt()
local prompt = self:_get_prompt()
local on_input_result = self._on_input_filter_cb(prompt) or {}
local new_prompt = on_input_result.prompt
if new_prompt then
prompt = new_prompt
end
local new_finder = on_input_result.updated_finder
if new_finder then
self.finder:close()
self.finder = new_finder
end
return prompt
end
function Picker:_resume_picker()
-- resume previous picker
local index = 1
for entry in self.manager:iter() do
self:entry_adder(index, entry, _, true)
index = index + 1
end
self.cache_picker.is_cached = false
-- if text changed, required to set anew to restart finder; otherwise hl and selection
if self.cache_picker.cached_prompt ~= self.default_text then
self:reset_prompt()
self:set_prompt(self.default_text)
else
-- scheduling required to apply highlighting and selection appropriately
await_schedule(function()
if self.cache_picker.selection_row ~= nil then
self:set_selection(self.cache_picker.selection_row)
end
end)
end
end
pickers._Picker = Picker pickers._Picker = Picker
return pickers return pickers