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"
--[[
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

View File

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

View File

@@ -1,6 +1,4 @@
local user = vim.loop.os_getenv "USER"
return require("plenary.log").new {
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
if not ok then
log.debug(msg)
log.debug("Failed to set lines:", msg)
end
log.trace("Clearing:", worst_line)
end
function Picker:highlight_displayed_rows(results_bufnr, prompt)
if not self.sorter or not self.sorter.highlighter then
function Picker:highlight_one_row(results_bufnr, prompt, display, row)
if not self.sorter.highlighter then
return
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)
if highlights then
@@ -352,22 +341,19 @@ function Picker:find()
self.prompt_prefix = prompt_prefix
self:_reset_prefix_color()
-- First thing we want to do is set all the lines to blank.
self.max_results = popup_opts.results.height
-- TODO: This could be configurable in the future, but I don't know why you would
-- 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, ""))
-- 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 debounced_status = debounce.throttle_leading(status_updater, 50)
local tx, rx = channel.mpsc()
self.__on_lines = tx.send
self._on_lines = tx.send
local find_id = self:_next_find_id()
@@ -412,25 +398,14 @@ function Picker:find()
local start_time = vim.loop.hrtime()
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
local prompt = self:_get_next_filtered_prompt()
-- 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
self.sorter:_start(prompt)
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_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)
end
else
-- 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()
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
-- TODO(scroll): This can only happen once, I don't like where it is.
self:_resume_picker()
end
end
end)
@@ -476,10 +433,10 @@ function Picker:find()
on_lines = function(...)
find_id = self:_next_find_id()
self._result_completed = false
status_updater { completed = false }
tx.send(...)
self._on_lines(...)
end,
on_detach = function()
self:_detach()
end,
@@ -753,7 +710,7 @@ function Picker:refresh(finder, opts)
self._multi = MultiSelect:new()
end
self.__on_lines(nil, nil, nil, 0, 1)
self._on_lines(nil, nil, nil, 0, 1)
end
function Picker:set_selection(row)
@@ -856,6 +813,8 @@ function Picker:set_selection(row)
self._selection_row = row
self:refresh_previewer()
vim.api.nvim_win_set_cursor(self.results_win, { row + 1, 0 })
end
function Picker:refresh_previewer()
@@ -918,43 +877,41 @@ function Picker:entry_adder(index, entry, _, insert)
self:_increment "displayed"
-- TODO: Don't need to schedule this if we schedule the adder.
local offset = insert and 0 or 1
vim.schedule(function()
if not vim.api.nvim_buf_is_valid(self.results_bufnr) then
log.debug "ON_ENTRY: Invalid buffer"
return
end
if not vim.api.nvim_buf_is_valid(self.results_bufnr) then
log.debug "ON_ENTRY: Invalid buffer"
return
end
-- TODO: Does this every get called?
-- local line_count = vim.api.nvim_win_get_height(self.results_win)
local line_count = vim.api.nvim_buf_line_count(self.results_bufnr)
if row > line_count then
return
end
-- TODO: Does this every get called?
-- local line_count = vim.api.nvim_win_get_height(self.results_win)
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
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
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)
self:highlight_one_row(self.results_bufnr, self:_get_prompt(), display, row)
end
if not set_ok then
log.debug("Failed to set lines...", msg)
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
display = display:gsub("\n", " | ")
vim.api.nvim_buf_set_lines(self.results_bufnr, row, row + 1, false, { display })
end
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
display = display:gsub("\n", " | ")
vim.api.nvim_buf_set_lines(self.results_bufnr, row, row + 1, false, { display })
end
end
function Picker:_reset_track()
@@ -1025,6 +982,8 @@ function Picker:get_status_updater(prompt_win, prompt_bufnr)
end
function Picker:get_result_processor(find_id, prompt, status_updater)
local count = 0
local cb_add = function(score, entry)
self.manager:add_entry(self, score, entry)
status_updater { completed = false }
@@ -1045,6 +1004,8 @@ function Picker:get_result_processor(find_id, prompt, status_updater)
return
end
count = count + 1
-- TODO: Probably should asyncify this / cache this / do something because this probably takes
-- a ton of time on large results.
log.trace("Processing result... ", entry)
@@ -1060,6 +1021,13 @@ function Picker:get_result_processor(find_id, prompt, status_updater)
end
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
@@ -1075,12 +1043,9 @@ function Picker:get_result_completor(results_bufnr, find_id, prompt, status_upda
status_updater { completed = true }
self:clear_extra_rows(results_bufnr)
self:highlight_displayed_rows(results_bufnr, prompt)
self.sorter:_finish(prompt)
self:_on_complete()
self._result_completed = true
end)
end
@@ -1226,6 +1191,46 @@ function Picker:_detach()
self.closed = true
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
return pickers