diff --git a/lua/frecency/async_finder.lua b/lua/frecency/async_finder.lua new file mode 100644 index 00000000..2bd2a5d9 --- /dev/null +++ b/lua/frecency/async_finder.lua @@ -0,0 +1,91 @@ +local a = require "plenary.async" +local log = require "plenary.log" + +---@class AsyncFinder: TelescopeFinder +---@field closed boolean +---@field entries FrecencyEntry[] +---@field rx { recv: fun(): table } +---@operator call():nil +local AsyncFinder = {} + +---@param fs FrecencyFS +---@param path string +---@param entry_maker fun(file: FrecencyFile): FrecencyEntry +---@param initial_results FrecencyFile[] +---@return AsyncFinder +AsyncFinder.new = function(fs, path, entry_maker, initial_results) + local self = setmetatable({ closed = false, entries = {} }, { + __index = AsyncFinder, + __call = function(self, ...) + return self:_find(...) + end, + }) + for i, file in ipairs(initial_results) do + local entry = entry_maker(file) + entry.index = i + table.insert(self.entries, entry) + end + local it = vim.F.nil_wrap(fs:scan_dir(path)) + local index = #initial_results + local count = 0 + local tx, rx = a.control.channel.mpsc() + self.rx = rx + a.run(function() + for name in it do + if self.closed then + break + end + index = index + 1 + count = count + 1 + ---@diagnostic disable-next-line: missing-fields + local entry = entry_maker { path = vim.fs.joinpath(path, name), score = 0 } + if entry then + entry.index = index + table.insert(self.entries, entry) + tx.send(entry) + if count % 1000 == 0 then + a.util.sleep(0) + end + end + end + self:close() + tx.send(nil) + end) + return self +end + +---@param prompt string +---@param process_result fun(entry: FrecencyEntry): nil +---@param process_complete fun(): nil +function AsyncFinder:_find(prompt, process_result, process_complete) + for _, entry in ipairs(self.entries) do + if process_result(entry) then + return + end + end + local count = 0 + local last_index = self.entries[#self.entries].index + while true do + if self.closed then + break + end + local entry = self.rx.recv() + if entry then + if entry.index > last_index then + if process_result(entry) then + return + end + count = count + 1 + end + else + break + end + end + process_complete() +end + +function AsyncFinder:close() + self.closed = true +end + +return AsyncFinder diff --git a/lua/frecency/finder.lua b/lua/frecency/finder.lua index 6f207acd..c824f2d5 100644 --- a/lua/frecency/finder.lua +++ b/lua/frecency/finder.lua @@ -1,3 +1,4 @@ +local AsyncFinder = require "frecency.async_finder" local finders = require "telescope.finders" local log = require "plenary.log" @@ -39,33 +40,7 @@ function Finder:start(filepath_formatter, initial_results, opts) } end log.debug { finder = opts } - return finders.new_dynamic { entry_maker = entry_maker, fn = self:create_fn(initial_results, opts.workspace) } -end - ----@private ----@param initial_results table ----@param path string ----@return fun(prompt: string?): table[] -function Finder:create_fn(initial_results, path) - local it = vim.F.nil_wrap(self.fs:scan_dir(path)) - local results = vim.deepcopy(initial_results) - local called = 0 - ---@param _ string? - ---@return table[] - return function(_) - called = called + 1 - log.debug { called = called } - local count = 0 - for name in it do - table.insert(results, { path = vim.fs.joinpath(path, name), score = 0 }) - count = count + 1 - if count >= self.config.chunk_size then - break - end - end - log.debug(("dynamic results: %d"):format(#results)) - return results - end + return AsyncFinder.new(self.fs, opts.workspace, entry_maker, initial_results) end return Finder