From 2ac311a2666edb447db5139b326777c44adc1e2a Mon Sep 17 00:00:00 2001 From: JINNOUCHI Yasushi Date: Sat, 12 Aug 2023 15:30:25 +0900 Subject: [PATCH] fix: reflow results to show valid ones (#123) * fix: clear non-matched results in results buffer * fix: show high-scored results in the first view This is for sorting_strategy = 'descending' --- lua/frecency/async_finder.lua | 36 ++++++++++++++++++++++-- lua/frecency/finder.lua | 5 ++-- lua/frecency/picker.lua | 13 ++++++--- lua/frecency/state.lua | 21 ++++++++++++++ lua/frecency/tests/async_finder_spec.lua | 3 +- lua/frecency/types.lua | 17 +++++++++++ 6 files changed, 86 insertions(+), 9 deletions(-) create mode 100644 lua/frecency/state.lua diff --git a/lua/frecency/async_finder.lua b/lua/frecency/async_finder.lua index 12a4654..7aae334 100644 --- a/lua/frecency/async_finder.lua +++ b/lua/frecency/async_finder.lua @@ -3,17 +3,20 @@ local async = require "plenary.async" --[[@as PlenaryAsync]] ---@class FrecencyAsyncFinder ---@field closed boolean ---@field entries FrecencyEntry[] +---@field reflowed boolean ---@field rx PlenaryAsyncControlChannelRx +---@field state FrecencyState ---@overload fun(_: string, process_result: (fun(entry: FrecencyEntry): nil), process_complete: fun(): nil): nil local AsyncFinder = {} ---@param fs FrecencyFS +---@param state FrecencyState ---@param path string ---@param entry_maker fun(file: FrecencyFile): FrecencyEntry ---@param initial_results FrecencyFile[] ---@return FrecencyAsyncFinder -AsyncFinder.new = function(fs, path, entry_maker, initial_results) - local self = setmetatable({ closed = false, entries = {} }, { +AsyncFinder.new = function(state, fs, path, entry_maker, initial_results) + local self = setmetatable({ closed = false, entries = {}, reflowed = false, state = state }, { __index = AsyncFinder, ---@param self FrecencyAsyncFinder __call = function(self, ...) @@ -47,6 +50,7 @@ AsyncFinder.new = function(fs, path, entry_maker, initial_results) table.insert(self.entries, entry) tx.send(entry) if count % 1000 == 0 then + self:reflow_results() -- NOTE: This is needed not to lock text input. async.util.sleep(50) end @@ -88,4 +92,32 @@ function AsyncFinder:close() self.closed = true end +---@return nil +function AsyncFinder:reflow_results() + local picker = self.state:get() + if not picker then + return + end + local bufnr = picker.results_bufnr + local win = picker.results_win + if not bufnr or not win then + return + end + picker:clear_extra_rows(bufnr) + if picker.sorting_strategy == "descending" then + local manager = picker.manager + if not manager then + return + end + local worst_line = picker:get_row(manager:num_results()) + ---@type WinInfo + local wininfo = vim.fn.getwininfo(win)[1] + local bottom = vim.api.nvim_buf_line_count(bufnr) + if not self.reflowed or worst_line > wininfo.botline then + self.reflowed = true + vim.api.nvim_win_set_cursor(win, { bottom, 0 }) + end + end +end + return AsyncFinder diff --git a/lua/frecency/finder.lua b/lua/frecency/finder.lua index c824f2d..014ee88 100644 --- a/lua/frecency/finder.lua +++ b/lua/frecency/finder.lua @@ -27,11 +27,12 @@ end ---@field workspace string? ---@field workspace_tag string? +---@param state FrecencyState ---@param filepath_formatter FrecencyFilepathFormatter ---@param initial_results table ---@param opts FrecencyFinderOptions ---@return table -function Finder:start(filepath_formatter, initial_results, opts) +function Finder:start(state, filepath_formatter, initial_results, opts) local entry_maker = self.entry_maker:create(filepath_formatter, opts.workspace, opts.workspace_tag) if not opts.need_scandir then return finders.new_table { @@ -40,7 +41,7 @@ function Finder:start(filepath_formatter, initial_results, opts) } end log.debug { finder = opts } - return AsyncFinder.new(self.fs, opts.workspace, entry_maker, initial_results) + return AsyncFinder.new(state, self.fs, opts.workspace, entry_maker, initial_results) end return Finder diff --git a/lua/frecency/picker.lua b/lua/frecency/picker.lua index 2c13b83..0c55cfe 100644 --- a/lua/frecency/picker.lua +++ b/lua/frecency/picker.lua @@ -1,3 +1,4 @@ +local State = require "frecency.state" local log = require "plenary.log" local Path = require "plenary.path" --[[@as PlenaryPath]] local actions = require "telescope.actions" @@ -83,8 +84,10 @@ function Picker:start(opts) self.results = self:fetch_results(self.workspace) end + local state = State.new() + local filepath_formatter = self:filepath_formatter(opts) - local finder = self.finder:start(filepath_formatter, self.results, { + local finder = self.finder:start(state, filepath_formatter, self.results, { need_scandir = self.workspace and self.config.show_unindexed and true or false, workspace = self.workspace, workspace_tag = self.config.initial_workspace_tag, @@ -95,11 +98,12 @@ function Picker:start(opts) finder = finder, previewer = config_values.file_previewer(opts), sorter = sorters.get_substr_matcher(), - on_input_filter_cb = self:on_input_filter_cb(opts), + on_input_filter_cb = self:on_input_filter_cb(state, opts), attach_mappings = function(prompt_bufnr) return self:attach_mappings(prompt_bufnr) end, }) + state:set(picker) picker:find() self:set_prompt_options(picker.prompt_bufnr) end @@ -227,9 +231,10 @@ function Picker:get_lsp_workspace() end ---@private +---@param state FrecencyState ---@param picker_opts table ---@return fun(prompt: string): table -function Picker:on_input_filter_cb(picker_opts) +function Picker:on_input_filter_cb(state, picker_opts) local filepath_formatter = self:filepath_formatter(picker_opts) return function(prompt) local workspace @@ -243,7 +248,7 @@ function Picker:on_input_filter_cb(picker_opts) if self.workspace ~= workspace then self.workspace = workspace self.results = self:fetch_results(workspace) - opts.updated_finder = self.finder:start(filepath_formatter, self.results, { + opts.updated_finder = self.finder:start(state, filepath_formatter, self.results, { initial_results = self.results, need_scandir = self.workspace and self.config.show_unindexed and true or false, workspace = self.workspace, diff --git a/lua/frecency/state.lua b/lua/frecency/state.lua new file mode 100644 index 0000000..15b204c --- /dev/null +++ b/lua/frecency/state.lua @@ -0,0 +1,21 @@ +---@class FrecencyState +---@field picker TelescopePicker? +local State = {} + +---@return FrecencyState +State.new = function() + return setmetatable({}, { __index = State, __meta = "kv" }) +end + +---@param picker TelescopePicker? +---@return nil +function State:set(picker) + self.picker = picker +end + +---@return TelescopePicker? +function State:get() + return self.picker +end + +return State diff --git a/lua/frecency/tests/async_finder_spec.lua b/lua/frecency/tests/async_finder_spec.lua index 01ac29d..f3158e9 100644 --- a/lua/frecency/tests/async_finder_spec.lua +++ b/lua/frecency/tests/async_finder_spec.lua @@ -1,5 +1,6 @@ ---@diagnostic disable: invisible local AsyncFinder = require "frecency.async_finder" +local State = require "frecency.state" local FS = require "frecency.fs" local EntryMaker = require "frecency.entry_maker" local WebDevicons = require "frecency.web_devicons" @@ -22,7 +23,7 @@ local function with_files(files, initial_results, callback) local initials = vim.tbl_map(function(v) return { path = (dir / v):absolute() } end, initial_results) - local async_finder = AsyncFinder.new(fs, dir:absolute(), entry_maker, initials) + local async_finder = AsyncFinder.new(State.new(), fs, dir:absolute(), entry_maker, initials) callback(async_finder, dir) close() end diff --git a/lua/frecency/types.lua b/lua/frecency/types.lua index 232ccf5..7fb44fa 100644 --- a/lua/frecency/types.lua +++ b/lua/frecency/types.lua @@ -117,3 +117,20 @@ function PlenaryAsyncUtil.sleep(ms) end ---@field path_tail fun(path: string): string ---@field transform_path fun(opts: table, path: string): string ---@field buf_is_loaded fun(filename: string): boolean + +---@class TelescopePicker +---@field clear_extra_rows fun(self: TelescopePicker, results_bufnr: integer): nil +---@field get_row fun(self: TelescopePicker, index: integer): integer +---@field manager TelescopeEntryManager|false +---@field results_bufnr integer? +---@field results_win integer? +---@field sorting_strategy 'ascending'|'descending' + +---@class TelescopeEntryManager +---@field num_results fun(): integer + +-- NOTE: types for default functions + +---@class WinInfo +---@field topline integer +---@field botline integer