feat: make query() faster and more lazier (#241)

* refactor: simplify logic to load web_devicons

* refactor: make register() asynchronous

* fix: load lazily modules outside this plugin

* refactor: simplify logic to wait initialization

* refactor: use uv.hrtime() instead of os.clock()

* fix: avoid errors in calling plenary.log in async

* test: store elapsed time to check in tests

* test: fix module names

This becomes a problem only in Ubuntu because macOS and Windows does not
care cases in filenames.

* test: fix types and unused modules

* style: fix by stylua

* refactor: make recency / entry_maker loaded lazily
This commit is contained in:
JINNOUCHI Yasushi 2024-08-25 19:28:52 +09:00 committed by GitHub
parent 673585ee99
commit c140e6ff9c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
23 changed files with 199 additions and 166 deletions

View File

@ -1,11 +1,13 @@
local Table = require "frecency.database.table"
local FileLock = require "frecency.file_lock"
local Timer = require "frecency.timer"
local config = require "frecency.config"
local fs = require "frecency.fs"
local watcher = require "frecency.watcher"
local log = require "frecency.log"
local async = require "plenary.async" --[[@as FrecencyPlenaryAsync]]
local Path = require "plenary.path" --[[@as FrecencyPlenaryPath]]
local lazy_require = require "frecency.lazy_require"
local async = lazy_require "plenary.async" --[[@as FrecencyPlenaryAsync]]
local Path = lazy_require "plenary.path" --[[@as FrecencyPlenaryPath]]
---@class FrecencyDatabaseEntry
---@field ages number[]
@ -184,7 +186,7 @@ end
---@async
---@return nil
function Database:load()
local start = os.clock()
local timer = Timer.new "load()"
local err, data = self:file_lock():with(function(target)
local err, stat = async.uv.fs_stat(target)
if err then
@ -203,13 +205,13 @@ function Database:load()
assert(not err, err)
local tbl = vim.F.npcall(loadstring(data or ""))
self.tbl:set(tbl)
log.debug(("load() takes %f seconds"):format(os.clock() - start))
timer:finish()
end
---@async
---@return nil
function Database:save()
local start = os.clock()
local timer = Timer.new "save()"
local err = self:file_lock():with(function(target)
self:raw_save(self.tbl:raw(), target)
local err, stat = async.uv.fs_stat(target)
@ -218,7 +220,7 @@ function Database:save()
return nil
end)
assert(not err, err)
log.debug(("save() takes %f seconds"):format(os.clock() - start))
timer:finish()
end
---@async

View File

@ -1,5 +1,6 @@
local log = require "frecency.log"
local async = require "plenary.async"
local Timer = require "frecency.timer"
local lazy_require = require "frecency.lazy_require"
local async = lazy_require "plenary.async" --[[@as FrecencyPlenaryAsync]]
---@class FrecencyDatabaseRecordValue
---@field count integer
@ -47,13 +48,13 @@ end
---@async
---@return nil
function Table:wait_ready()
local start = os.clock()
local timer = Timer.new "wait_ready()"
local t = 0.2
while not rawget(self, "is_ready") do
async.util.sleep(t)
t = t * 2
end
log.debug(("wait_ready() takes %f seconds"):format(os.clock() - start))
timer:finish()
end
return Table

View File

@ -1,18 +1,18 @@
local WebDevicons = require "frecency.web_devicons"
local web_devicons = require "frecency.web_devicons"
local config = require "frecency.config"
local fs = require "frecency.fs"
local Path = require "plenary.path" --[[@as FrecencyPlenaryPath]]
local entry_display = require "telescope.pickers.entry_display" --[[@as FrecencyTelescopeEntryDisplay]]
local utils = require "telescope.utils" --[[@as FrecencyTelescopeUtils]]
local Path = require "plenary.path"
local lazy_require = require "frecency.lazy_require"
local entry_display = lazy_require "telescope.pickers.entry_display" --[[@as FrecencyTelescopeEntryDisplay]]
local utils = lazy_require "telescope.utils" --[[@as FrecencyTelescopeUtils]]
---@class FrecencyEntryMaker
---@field loaded table<string,boolean>
---@field web_devicons WebDevicons
local EntryMaker = {}
---@return FrecencyEntryMaker
EntryMaker.new = function()
return setmetatable({ web_devicons = WebDevicons.new() }, { __index = EntryMaker })
return setmetatable({}, { __index = EntryMaker })
end
---@class FrecencyEntry
@ -86,7 +86,7 @@ function EntryMaker:width_items(workspace, workspace_tag)
table.insert(width_items, { width = 6 }) -- fuzzy score
end
end
if self.web_devicons.is_enabled then
if not config.disable_devicons then
table.insert(width_items, { width = 2 })
end
if config.show_filter_column and workspace and workspace_tag then
@ -116,12 +116,11 @@ function EntryMaker:items(entry, workspace, workspace_tag, formatter)
table.insert(items, { score, "TelescopeFrecencyScores" })
end
end
if self.web_devicons.is_enabled then
if not config.disable_devicons then
local basename = utils.path_tail(entry.name)
local icon, icon_highlight =
self.web_devicons:get_icon(basename, utils.file_extension(basename), { default = false })
local icon, icon_highlight = web_devicons.get_icon(basename, utils.file_extension(basename), { default = false })
if not icon then
icon, icon_highlight = self.web_devicons:get_icon(basename, nil, { default = true })
icon, icon_highlight = web_devicons.get_icon(basename, nil, { default = true })
end
table.insert(items, { icon, icon_highlight })
end

View File

@ -1,6 +1,7 @@
local log = require "frecency.log"
local Path = require "plenary.path" --[[@as FrecencyPlenaryPath]]
local async = require "plenary.async" --[[@as FrecencyPlenaryAsync]]
local lazy_require = require "frecency.lazy_require"
local Path = lazy_require "plenary.path" --[[@as FrecencyPlenaryPath]]
local async = lazy_require "plenary.async" --[[@as FrecencyPlenaryAsync]]
---@class FrecencyFileLock
---@field base string

View File

@ -1,9 +1,12 @@
local Timer = require "frecency.timer"
local config = require "frecency.config"
local fs = require "frecency.fs"
local os_util = require "frecency.os_util"
local recency = require "frecency.recency"
local log = require "frecency.log"
local Job = require "plenary.job"
local async = require "plenary.async" --[[@as FrecencyPlenaryAsync]]
local lazy_require = require "frecency.lazy_require"
local Job = lazy_require "plenary.job" --[[@as FrecencyPlenaryJob]]
local async = lazy_require "plenary.async" --[[@as FrecencyPlenaryAsync]]
---@class FrecencyFinder
---@field config FrecencyFinderConfig
@ -21,7 +24,6 @@ local async = require "plenary.async" --[[@as FrecencyPlenaryAsync]]
---@field private need_scan_dir boolean
---@field private seen table<string, boolean>
---@field private process VimSystemObj?
---@field private recency FrecencyRecency
---@field private state FrecencyState
local Finder = {}
@ -34,11 +36,10 @@ local Finder = {}
---@param entry_maker FrecencyEntryMakerInstance
---@param need_scandir boolean
---@param path string?
---@param recency FrecencyRecency
---@param state FrecencyState
---@param finder_config? FrecencyFinderConfig
---@return FrecencyFinder
Finder.new = function(database, entry_maker, need_scandir, path, recency, state, finder_config)
Finder.new = function(database, entry_maker, need_scandir, path, state, finder_config)
local tx, rx = async.control.channel.mpsc()
local scan_tx, scan_rx = async.control.channel.mpsc()
local self = setmetatable({
@ -47,7 +48,6 @@ Finder.new = function(database, entry_maker, need_scandir, path, recency, state,
database = database,
entry_maker = entry_maker,
path = path,
recency = recency,
state = state,
seen = {},
@ -257,25 +257,21 @@ end
---@return FrecencyFile[]
function Finder:get_results(workspace, epoch)
log.debug { workspace = workspace or "NONE" }
local start_fetch = os.clock()
local timer_fetch = Timer.new "fetching entries"
local files = self.database:get_entries(workspace, epoch)
log.debug(("it takes %f seconds in fetching entries"):format(os.clock() - start_fetch))
local start_results = os.clock()
local elapsed_recency = 0
timer_fetch:finish()
local timer_results = Timer.new "making results"
for _, file in ipairs(files) do
local start_recency = os.clock()
file.score = file.ages and self.recency:calculate(file.count, file.ages) or 0
file.score = file.ages and recency.calculate(file.count, file.ages) or 0
file.ages = nil
elapsed_recency = elapsed_recency + (os.clock() - start_recency)
end
log.debug(("it takes %f seconds in calculating recency"):format(elapsed_recency))
log.debug(("it takes %f seconds in making results"):format(os.clock() - start_results))
timer_results:finish()
local start_sort = os.clock()
local timer_sort = Timer.new "sorting"
table.sort(files, function(a, b)
return a.score > b.score or (a.score == b.score and a.path > b.path)
end)
log.debug(("it takes %f seconds in sorting"):format(os.clock() - start_sort))
timer_sort:finish()
return files
end

View File

@ -1,11 +1,13 @@
local config = require "frecency.config"
local os_util = require "frecency.os_util"
local log = require "frecency.log"
local Path = require "plenary.path" --[[@as FrecencyPlenaryPath]]
local async = require "plenary.async" --[[@as FrecencyPlenaryAsync]]
local scandir = require "plenary.scandir"
local lazy_require = require "frecency.lazy_require"
local Path = lazy_require "plenary.path" --[[@as FrecencyPlenaryPath]]
local async = lazy_require "plenary.async" --[[@as FrecencyPlenaryAsync]]
local scandir = lazy_require "plenary.scandir"
local uv = vim.uv or vim.loop
---@class FrecencyFS
local M = {
os_homedir = assert(uv.os_homedir()),
}

View File

@ -1,5 +1,6 @@
local config = require "frecency.config"
local sorters = require "telescope.sorters"
local lazy_require = require "frecency.lazy_require"
local sorters = lazy_require "telescope.sorters"
---@param opts any options for get_fzy_sorter()
return function(opts)

View File

@ -5,7 +5,7 @@
---@field complete fun(findstart: 1|0, base: string): integer|''|string[]
---@field delete async fun(path: string): nil
---@field query fun(opts?: FrecencyQueryOpts): FrecencyQueryEntry[]|string[]
---@field register fun(bufnr: integer, datetime: string?): nil
---@field register async fun(bufnr: integer, datetime: string?): nil
---@field start fun(opts: FrecencyPickerOptions?): nil
---@field validate_database async fun(force: boolean?): nil
local frecency = setmetatable({}, {
@ -43,7 +43,9 @@ local function setup(ext_config)
return
end
require("frecency.config").setup(ext_config)
local config = require "frecency.config"
config.setup(ext_config)
vim.api.nvim_set_hl(0, "TelescopeBufferLoaded", { link = "String", default = true })
vim.api.nvim_set_hl(0, "TelescopePathSeparator", { link = "Directory", default = true })
@ -76,9 +78,10 @@ local function setup(ext_config)
return
end
local is_floatwin = vim.api.nvim_win_get_config(0).relative ~= ""
if not is_floatwin then
frecency.register(args.buf)
if is_floatwin or (config.ignore_register and config.ignore_register(args.buf)) then
return
end
async_call(frecency.register, args.buf, vim.api.nvim_buf_get_name(args.buf))
end,
})

View File

@ -1,54 +1,46 @@
local Database = require "frecency.database"
local EntryMaker = require "frecency.entry_maker"
local Picker = require "frecency.picker"
local Recency = require "frecency.recency"
local Timer = require "frecency.timer"
local config = require "frecency.config"
local fs = require "frecency.fs"
local log = require "frecency.log"
local async = require "plenary.async" --[[@as FrecencyPlenaryAsync]]
local recency = require "frecency.recency"
local wait = require "frecency.wait"
local lazy_require = require "frecency.lazy_require"
local async = lazy_require "plenary.async" --[[@as FrecencyPlenaryAsync]]
---@class Frecency
---@field private buf_registered table<integer, boolean> flag to indicate the buffer is registered to the database.
---@field private database FrecencyDatabase
---@field private entry_maker FrecencyEntryMaker
---@field private picker FrecencyPicker
---@field private recency FrecencyRecency
local Frecency = {}
---@return Frecency
Frecency.new = function()
local self = setmetatable({ buf_registered = {} }, { __index = Frecency }) --[[@as Frecency]]
self.database = Database.new()
self.entry_maker = EntryMaker.new()
self.recency = Recency.new()
return self
end
---This is called when `:Telescope frecency` is called at the first time.
---@return nil
function Frecency:setup()
local done = false
---@async
local function init()
local timer = Timer.new "init()"
self.database:start()
self:assert_db_entries()
if config.auto_validate then
self:validate_database()
end
done = true
timer:finish()
end
local is_async = not not coroutine.running()
if is_async then
init()
else
async.void(init)()
local ok, status = vim.wait(10000, function()
return done
end)
if not ok then
error("failed to setup:" .. (status == -1 and "timed out" or "interrupted"))
end
wait(init)
end
end
@ -56,7 +48,7 @@ end
---@param opts? FrecencyPickerOptions
---@return nil
function Frecency:start(opts)
local start = os.clock()
local timer = Timer.new "start()"
log.debug "Frecency:start"
opts = opts or {}
if opts.cwd then
@ -66,13 +58,13 @@ function Frecency:start(opts)
if opts.hide_current_buffer or config.hide_current_buffer then
ignore_filenames = { vim.api.nvim_buf_get_name(0) }
end
self.picker = Picker.new(self.database, self.entry_maker, self.recency, {
self.picker = Picker.new(self.database, {
editing_bufnr = vim.api.nvim_get_current_buf(),
ignore_filenames = ignore_filenames,
initial_workspace_tag = opts.workspace,
})
self.picker:start(vim.tbl_extend("force", config.get(), opts))
log.debug(("Frecency:start picker:start takes %f seconds"):format(os.clock() - start))
timer:finish()
end
---This can be calledBy `require("telescope").extensions.frecency.complete`.
@ -128,15 +120,12 @@ function Frecency:assert_db_entries()
end
end
---@async
---@param bufnr integer
---@param path string
---@param epoch? integer
function Frecency:register(bufnr, epoch)
if (config.ignore_register and config.ignore_register(bufnr)) or self.buf_registered[bufnr] then
return
end
local path = vim.api.nvim_buf_get_name(bufnr)
async.void(function()
if not fs.is_valid_path(path) then
function Frecency:register(bufnr, path, epoch)
if self.buf_registered[bufnr] or not fs.is_valid_path(path) then
return
end
local err, realpath = async.uv.fs_realpath(path)
@ -146,7 +135,6 @@ function Frecency:register(bufnr, epoch)
self.database:update(realpath, epoch)
self.buf_registered[bufnr] = true
log.debug("registered:", bufnr, path)
end)()
end
---@async
@ -191,7 +179,7 @@ function Frecency:query(opts, epoch)
return {
count = entry.count,
path = entry.path,
score = entry.ages and self.recency:calculate(entry.count, entry.ages) or 0,
score = entry.ages and recency.calculate(entry.count, entry.ages) or 0,
timestamps = entry.timestamps,
}
end, self.database:get_entries(opts.workspace, epoch))

View File

@ -0,0 +1,11 @@
---@param module string
return function(module)
return setmetatable({}, {
__index = function(_, key)
return require(module)[key]
end,
__call = function(_, ...)
return require(module)(...)
end,
})
end

View File

@ -1,8 +1,11 @@
local config = require "frecency.config"
local log = require "plenary.log"
local lazy_require = require "frecency.lazy_require"
local log = lazy_require "plenary.log"
return setmetatable({}, {
__index = function(_, key)
return config.debug and log[key] or function() end
return config.debug and vim.schedule_wrap(function(...)
log[key](...)
end) or function() end
end,
})

View File

@ -1,6 +1,8 @@
local Path = require "plenary.path"
local lazy_require = require "frecency.lazy_require"
local Path = lazy_require "plenary.path" --[[@as FrecencyPlenaryPath]]
local uv = vim.uv or vim.loop
---@class FrecencyOSUtil
local M = {
is_windows = uv.os_uname().sysname == "Windows_NT",
}

View File

@ -1,3 +1,4 @@
local EntryMaker = require "frecency.entry_maker"
local State = require "frecency.state"
local Finder = require "frecency.finder"
local config = require "frecency.config"
@ -5,11 +6,12 @@ local fs = require "frecency.fs"
local fuzzy_sorter = require "frecency.fuzzy_sorter"
local substr_sorter = require "frecency.substr_sorter"
local log = require "frecency.log"
local Path = require "plenary.path" --[[@as FrecencyPlenaryPath]]
local actions = require "telescope.actions"
local config_values = require("telescope.config").values
local pickers = require "telescope.pickers"
local utils = require "telescope.utils" --[[@as FrecencyTelescopeUtils]]
local lazy_require = require "frecency.lazy_require"
local Path = lazy_require "plenary.path" --[[@as FrecencyPlenaryPath]]
local actions = lazy_require "telescope.actions"
local telescope_config = lazy_require "telescope.config"
local pickers = lazy_require "telescope.pickers"
local utils = lazy_require "telescope.utils" --[[@as FrecencyTelescopeUtils]]
local uv = vim.loop or vim.uv
---@class FrecencyPicker
@ -18,7 +20,6 @@ local uv = vim.loop or vim.uv
---@field private entry_maker FrecencyEntryMaker
---@field private lsp_workspaces string[]
---@field private namespace integer
---@field private recency FrecencyRecency
---@field private state FrecencyState
---@field private workspace string?
---@field private workspace_tag_regex string
@ -37,18 +38,15 @@ local Picker = {}
---@field score number
---@param database FrecencyDatabase
---@param entry_maker FrecencyEntryMaker
---@param recency FrecencyRecency
---@param picker_config FrecencyPickerConfig
---@return FrecencyPicker
Picker.new = function(database, entry_maker, recency, picker_config)
Picker.new = function(database, picker_config)
local self = setmetatable({
config = picker_config,
database = database,
entry_maker = entry_maker,
entry_maker = EntryMaker.new(),
lsp_workspaces = {},
namespace = vim.api.nvim_create_namespace "frecency",
recency = recency,
}, { __index = Picker })
local d = config.filter_delimiter
self.workspace_tag_regex = "^%s*" .. d .. "(%S+)" .. d
@ -80,7 +78,6 @@ function Picker:finder(opts, workspace, workspace_tag)
entry_maker,
need_scandir,
workspace,
self.recency,
self.state,
{ ignore_filenames = self.config.ignore_filenames }
)
@ -93,7 +90,7 @@ function Picker:start(opts)
path_display = function(picker_opts, path)
return self:default_path_display(picker_opts, path)
end,
}, config_values, opts or {}) --[[@as FrecencyPickerOptions]]
}, telescope_config.values, opts or {}) --[[@as FrecencyPickerOptions]]
self.workspace = self:get_workspace(opts.cwd, self.config.initial_workspace_tag or config.default_workspace)
log.debug { workspace = self.workspace }
@ -102,7 +99,7 @@ function Picker:start(opts)
local picker = pickers.new(opts, {
prompt_title = "Frecency",
finder = finder,
previewer = config_values.file_previewer(opts),
previewer = telescope_config.values.file_previewer(opts),
sorter = config.matcher == "default" and substr_sorter() or fuzzy_sorter(opts),
on_input_filter_cb = self:on_input_filter_cb(opts),
attach_mappings = function(prompt_bufnr)

View File

@ -1,23 +1,15 @@
local config = require "frecency.config"
---@class FrecencyRecency
---@field private modifier table<integer, { age: integer, value: integer }>
local Recency = {}
---@return FrecencyRecency
Recency.new = function()
return setmetatable({
modifier = config.recency_values,
}, { __index = Recency })
end
local M = {}
---@param count integer
---@param ages number[]
---@return number
function Recency:calculate(count, ages)
function M.calculate(count, ages)
local score = 0
for _, age in ipairs(ages) do
for _, rank in ipairs(self.modifier) do
for _, rank in ipairs(config.recency_values) do
if age <= rank.age then
score = score + rank.value
goto continue
@ -28,4 +20,4 @@ function Recency:calculate(count, ages)
return count * score / config.max_timestamps
end
return Recency
return M

View File

@ -1,8 +1,9 @@
-- TODO: use this module until telescope's release include this below.
-- https://github.com/nvim-telescope/telescope.nvim/pull/2950
local sorters = require "telescope.sorters"
local util = require "telescope.utils"
local lazy_require = require "frecency.lazy_require"
local sorters = lazy_require "telescope.sorters"
local util = lazy_require "telescope.utils"
local substr_highlighter = function(make_display)
return function(_, prompt, display)

View File

@ -2,6 +2,7 @@
-- https://github.com/nvim-lua/plenary.nvim/blob/663246936325062427597964d81d30eaa42ab1e4/lua/plenary/test_harness.lua#L86-L86
vim.opt.runtimepath:append(vim.env.TELESCOPE_PATH)
local Timer = require "frecency.timer"
local util = require "frecency.tests.util"
local log = require "plenary.log"
@ -183,7 +184,7 @@ describe("frecency", function()
register(file, make_epoch "2023-07-29T00:00:00+09:00")
log.new({}, true)
end
local start = os.clock()
local timer = Timer.new "all results"
local results = vim.tbl_map(function(result)
result.timestamps = nil
return result
@ -191,11 +192,10 @@ describe("frecency", function()
table.sort(results, function(a, b)
return a.path < b.path
end)
local elapsed = os.clock() - start
log.info(("it takes %f seconds in fetching all results"):format(elapsed))
timer:finish()
it("returns appropriate latency (<1.0 second)", function()
assert.are.is_true(elapsed < 1.0)
assert.are.is_true(timer.elapsed < 1.0)
end)
it("returns valid response", function()

View File

@ -1,5 +1,4 @@
local Recency = require "frecency.recency"
local recency = Recency.new()
local recency = require "frecency.recency"
describe("frecency.recency", function()
for _, c in ipairs {
@ -13,7 +12,7 @@ describe("frecency.recency", function()
} do
local dumped = vim.inspect(c.ages, { indent = "", newline = "" })
it(("%d, %s => %d"):format(c.count, dumped, c.score), function()
assert.are.same(c.score, recency:calculate(c.count, c.ages))
assert.are.same(c.score, recency.calculate(c.count, c.ages))
end)
end
end)

View File

@ -7,7 +7,7 @@ local log = require "plenary.log"
local async = require "plenary.async" --[[@as FrecencyPlenaryAsync]]
local Path = require "plenary.path"
local Job = require "plenary.job"
local wait = require "frecency.tests.wait"
local wait = require "frecency.wait"
---@return FrecencyPlenaryPath
---@return fun(): nil close swwp all entries
@ -101,7 +101,7 @@ local function with_files(files, cb_or_config, callback)
frecency.database:start()
frecency.database.tbl:wait_ready()
end)
frecency.picker = Picker.new(frecency.database, frecency.entry_maker, frecency.recency, { editing_bufnr = 0 })
frecency.picker = Picker.new(frecency.database, { editing_bufnr = 0 })
local finder = frecency.picker:finder {}
callback(frecency, finder, dir)
close()
@ -116,16 +116,28 @@ end
---@return fun(file: string, epoch: integer, reset: boolean?, wipeout?: boolean): nil reset: boolean?): nil
local function make_register(frecency, dir)
return function(file, epoch, reset, wipeout)
-- NOTE: this function does the same thing as BufWinEnter autocmd.
---@param bufnr integer
local function register(bufnr)
if vim.api.nvim_buf_get_name(bufnr) == "" then
return
end
local is_floatwin = vim.api.nvim_win_get_config(0).relative ~= ""
if is_floatwin or (config.ignore_register and config.ignore_register(bufnr)) then
return
end
async.util.block_on(function()
frecency:register(bufnr, vim.api.nvim_buf_get_name(bufnr), epoch)
end)
end
local path = filepath(dir, file)
vim.cmd.edit(path)
local bufnr = assert(vim.fn.bufnr(path))
if reset then
frecency.buf_registered[bufnr] = nil
end
frecency:register(bufnr, epoch)
vim.wait(1000, function()
return not not frecency.buf_registered[bufnr]
end)
register(bufnr)
-- HACK: This is needed because almost the same filenames use the same
-- buffer.
if wipeout then
@ -154,7 +166,7 @@ local function with_fake_register(frecency, dir, callback)
bufnr = bufnr + 1
buffers[bufnr] = path
async.util.block_on(function()
frecency:register(bufnr, epoch)
frecency:register(bufnr, path, epoch)
end)
end
callback(register)

26
lua/frecency/timer.lua Normal file
View File

@ -0,0 +1,26 @@
local config = require "frecency.config"
local log = require "frecency.log"
local uv = vim.uv or vim.loop
---@class FrecencyTimer
---@field elapsed number
---@field start integer
---@field title string
local Timer = {}
---@param title string
---@return FrecencyTimer
Timer.new = function(title)
return setmetatable({ start = uv.hrtime(), title = title }, { __index = Timer })
end
---@return nil
function Timer:finish()
if not config.debug then
return
end
self.elapsed = (uv.hrtime() - self.start) / 1000000000
log.debug(("[%s] takes %.3f seconds"):format(self.title, self.elapsed))
end
return Timer

View File

@ -2,6 +2,20 @@
-- NOTE: types are borrowed from plenary.nvim
---@class FrecencyPlenaryJob
---@field new fun(self: FrecencyPlenaryJob, opts: FrecencyPlenaryJobOpts): FrecencyPlenaryJob
---@field start fun(self: FrecencyPlenaryJob): nil
---@field handle VimSystemObj uv_process_t
---@class FrecencyPlenaryJobOpts
---@field cwd? string
---@field command? string
---@field args? string[]
---@field on_stdout? FrecencyPlenaryJobCallback
---@field on_stderr? FrecencyPlenaryJobCallback
---@alias FrecencyPlenaryJobCallback fun(error: string, data: string, self?: FrecencyPlenaryJob)
---@class FrecencyPlenaryPath
---@field new fun(self: FrecencyPlenaryPath|string, path?: string): FrecencyPlenaryPath
---@field absolute fun(): string

View File

@ -1,19 +1,20 @@
local log = require "frecency.log"
local async = require "plenary.async" --[[@as FrecencyPlenaryAsync]]
local lazy_require = require "frecency.lazy_require"
local async = lazy_require "plenary.async" --[[@as FrecencyPlenaryAsync]]
local uv = vim.loop or vim.uv
---@class FrecencyNativeWatcherMtime
---@class FrecencyWatcherMtime
---@field sec integer
---@field nsec integer
local Mtime = {}
---@param mtime FsStatMtime
---@return FrecencyNativeWatcherMtime
---@return FrecencyWatcherMtime
Mtime.new = function(mtime)
return setmetatable({ sec = mtime.sec, nsec = mtime.nsec }, Mtime)
end
---@param other FrecencyNativeWatcherMtime
---@param other FrecencyWatcherMtime
---@return boolean
function Mtime:__eq(other)
return self.sec == other.sec and self.nsec == other.nsec
@ -24,13 +25,13 @@ function Mtime:__tostring()
return string.format("%d.%d", self.sec, self.nsec)
end
---@class FrecencyNativeWatcher
---@class FrecencyWatcher
---@field handler UvFsEventHandle
---@field path string
---@field mtime FrecencyNativeWatcherMtime
---@field mtime FrecencyWatcherMtime
local Watcher = {}
---@return FrecencyNativeWatcher
---@return FrecencyWatcher
Watcher.new = function()
return setmetatable({ path = "", mtime = Mtime.new { sec = 0, nsec = 0 } }, { __index = Watcher })
end

View File

@ -1,32 +1,14 @@
local config = require "frecency.config"
---@class WebDeviconsModule
---@field get_icon fun(name?: string, ext?: string, opts?: table): string, string
---@class WebDevicons
---@field is_enabled boolean
---@field private web_devicons WebDeviconsModule
local WebDevicons = {}
---@return WebDevicons
WebDevicons.new = function()
local ok, web_devicons = pcall(require, "nvim-web-devicons")
return setmetatable(
{ is_enabled = not config.disable_devicons and ok, web_devicons = web_devicons },
{ __index = WebDevicons }
)
end
---@class FrecencyWebDevicons
local M = {
---@param name string?
---@param ext string?
---@param opts table?
---@return string
---@return string
function WebDevicons:get_icon(name, ext, opts)
if self.is_enabled then
return self.web_devicons.get_icon(name, ext, opts)
end
return "", ""
end
get_icon = function(name, ext, opts)
local ok, web_devicons = pcall(require, "nvim-web-devicons")
return ok and web_devicons.get_icon(name, ext, opts) or "", ""
end,
}
return WebDevicons
return M