mirror of
https://github.com/kristoferssolo/telescope-frecency.nvim.git
synced 2025-10-21 20:10:38 +00:00
feat: separate bootstrap logic to launch faster (#250)
* chore: track setup() time * feat: avoid setup() to be called twice * chore: track time between Database:start() * feat: add bootstrap option to load DB in advance * feat: initialize DB before frecency class starts * chore: add more logging * feat!: load DB in Neovim starting only if the plugin is loaded non-lazily. * fix: simplify logic for timer * fix: detect error and safely finish * chore: remove unnecessary method
This commit is contained in:
parent
38f2a2207e
commit
a6482c2fbf
@ -4,6 +4,7 @@ local os_util = require "frecency.os_util"
|
||||
---@class FrecencyOpts
|
||||
---@field recency_values? { age: integer, value: integer }[] default: see lua/frecency/config.lua
|
||||
---@field auto_validate? boolean default: true
|
||||
---@field bootstrap? boolean default: true
|
||||
---@field db_root? string default: vim.fn.stdpath "state"
|
||||
---@field db_safe_mode? boolean default: true
|
||||
---@field db_validate_threshold? integer default: 10
|
||||
@ -33,6 +34,7 @@ local Config = {}
|
||||
---@class FrecencyRawConfig
|
||||
---@field recency_values { age: integer, value: integer }[] default: see lua/frecency/config.lua
|
||||
---@field auto_validate boolean default: true
|
||||
---@field bootstrap boolean default: true
|
||||
---@field db_root string default: vim.fn.stdpath "state"
|
||||
---@field db_safe_mode boolean default: true
|
||||
---@field db_validate_threshold integer default: 10
|
||||
@ -59,6 +61,7 @@ Config.new = function()
|
||||
local keys = {
|
||||
recency_values = true,
|
||||
auto_validate = true,
|
||||
bootstrap = true,
|
||||
db_root = true,
|
||||
db_safe_mode = true,
|
||||
db_validate_threshold = true,
|
||||
@ -98,6 +101,7 @@ end
|
||||
---@type FrecencyRawConfig
|
||||
Config.default_values = {
|
||||
auto_validate = true,
|
||||
bootstrap = true,
|
||||
db_root = vim.fn.stdpath "state" --[[@as string]],
|
||||
db_safe_mode = true,
|
||||
db_validate_threshold = 10,
|
||||
@ -158,6 +162,7 @@ Config.setup = function(ext_config)
|
||||
vim.validate {
|
||||
recency_values = { opts.recency_values, "t" },
|
||||
auto_validate = { opts.auto_validate, "b" },
|
||||
bootstrap = { opts.bootstrap, "b" },
|
||||
db_root = { opts.db_root, "s" },
|
||||
db_safe_mode = { opts.db_safe_mode, "b" },
|
||||
db_validate_threshold = { opts.db_validate_threshold, "n" },
|
||||
|
||||
@ -22,6 +22,7 @@ local Path = lazy_require "plenary.path" --[[@as FrecencyPlenaryPath]]
|
||||
---@field private _file_lock FrecencyFileLock
|
||||
---@field private file_lock_rx async fun(): ...
|
||||
---@field private file_lock_tx fun(...): nil
|
||||
---@field private is_started boolean
|
||||
---@field private tbl FrecencyDatabaseTable
|
||||
---@field private version FrecencyDatabaseVersion
|
||||
---@field private watcher_rx FrecencyPlenaryAsyncControlChannelRx
|
||||
@ -36,6 +37,7 @@ Database.new = function()
|
||||
return setmetatable({
|
||||
file_lock_rx = file_lock_rx,
|
||||
file_lock_tx = file_lock_tx,
|
||||
is_started = false,
|
||||
tbl = Table.new(version),
|
||||
version = version,
|
||||
watcher_rx = watcher_rx,
|
||||
@ -74,6 +76,11 @@ end
|
||||
---@async
|
||||
---@return nil
|
||||
function Database:start()
|
||||
timer.track "Database:start() start"
|
||||
if self.is_started then
|
||||
return
|
||||
end
|
||||
self.is_started = true
|
||||
local target = self:filename()
|
||||
self.file_lock_tx(FileLock.new(target))
|
||||
self.watcher_tx.send "load"
|
||||
@ -94,6 +101,7 @@ function Database:start()
|
||||
log.debug("DB coroutine end:", mode)
|
||||
end
|
||||
end)()
|
||||
timer.track "Database:start() finish"
|
||||
end
|
||||
|
||||
---@async
|
||||
|
||||
@ -1,7 +1,11 @@
|
||||
---@type FrecencyDatabase?
|
||||
local database
|
||||
|
||||
---This object is intended to be used as a singleton, and is lazily loaded.
|
||||
---When methods are called at the first time, it calls the constructor and
|
||||
---setup() to be initialized.
|
||||
---@class FrecencyInstance
|
||||
---@field bootstrap async fun(): nil
|
||||
---@field complete fun(findstart: 1|0, base: string): integer|''|string[]
|
||||
---@field delete async fun(path: string): nil
|
||||
---@field query fun(opts?: FrecencyQueryOpts): FrecencyQueryEntry[]|string[]
|
||||
@ -20,7 +24,7 @@ local frecency = setmetatable({}, {
|
||||
|
||||
return function(...)
|
||||
if not instance() then
|
||||
rawset(self, "instance", require("frecency.klass").new())
|
||||
rawset(self, "instance", require("frecency.klass").new(database))
|
||||
local is_async = key == "delete" or key == "validate_database" or key == "register"
|
||||
instance():setup(is_async)
|
||||
end
|
||||
@ -45,8 +49,9 @@ local function setup(ext_config)
|
||||
end
|
||||
|
||||
local config = require "frecency.config"
|
||||
|
||||
config.setup(ext_config)
|
||||
local timer = require "frecency.timer"
|
||||
timer.track "setup() start"
|
||||
|
||||
vim.api.nvim_set_hl(0, "TelescopeBufferLoaded", { link = "String", default = true })
|
||||
vim.api.nvim_set_hl(0, "TelescopePathSeparator", { link = "Directory", default = true })
|
||||
@ -86,7 +91,15 @@ local function setup(ext_config)
|
||||
end,
|
||||
})
|
||||
|
||||
if config.bootstrap and vim.v.vim_did_enter == 0 then
|
||||
database = require("frecency.database").new()
|
||||
async_call(function()
|
||||
database:start()
|
||||
end)
|
||||
end
|
||||
|
||||
setup_done = true
|
||||
timer.track "setup() finish"
|
||||
end
|
||||
|
||||
return {
|
||||
|
||||
@ -9,16 +9,25 @@ local wait = require "frecency.wait"
|
||||
local lazy_require = require "frecency.lazy_require"
|
||||
local async = lazy_require "plenary.async" --[[@as FrecencyPlenaryAsync]]
|
||||
|
||||
---@enum FrecencyStatus
|
||||
local STATUS = {
|
||||
NEW = 0,
|
||||
SETUP_CALLED = 1,
|
||||
SETUP_FINISHED = 2,
|
||||
}
|
||||
|
||||
---@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 picker FrecencyPicker
|
||||
---@field private status FrecencyStatus
|
||||
local Frecency = {}
|
||||
|
||||
---@param database? FrecencyDatabase
|
||||
---@return Frecency
|
||||
Frecency.new = function()
|
||||
local self = setmetatable({ buf_registered = {} }, { __index = Frecency }) --[[@as Frecency]]
|
||||
self.database = Database.new()
|
||||
Frecency.new = function(database)
|
||||
local self = setmetatable({ buf_registered = {}, status = STATUS.NEW }, { __index = Frecency }) --[[@as Frecency]]
|
||||
self.database = database or Database.new()
|
||||
return self
|
||||
end
|
||||
|
||||
@ -26,22 +35,35 @@ end
|
||||
---@param is_async boolean
|
||||
---@return nil
|
||||
function Frecency:setup(is_async)
|
||||
if self.status >= STATUS.SETUP_CALLED then
|
||||
return
|
||||
end
|
||||
self.status = STATUS.SETUP_CALLED
|
||||
timer.track "frecency.setup() start"
|
||||
|
||||
---@async
|
||||
local function init()
|
||||
timer.track "init() start"
|
||||
self.database:start()
|
||||
self:assert_db_entries()
|
||||
if config.auto_validate then
|
||||
self:validate_database()
|
||||
end
|
||||
timer.track "init() finish"
|
||||
timer.track "frecency.setup() finish"
|
||||
self.status = STATUS.SETUP_FINISHED
|
||||
end
|
||||
|
||||
if is_async then
|
||||
init()
|
||||
else
|
||||
wait(init)
|
||||
return
|
||||
end
|
||||
|
||||
local ok, status = wait(init)
|
||||
if ok then
|
||||
return
|
||||
end
|
||||
-- NOTE: This means init() has failed. Try again.
|
||||
self.status = STATUS.NEW
|
||||
self:error(status == -1 and "init() never returns during the time" or "init() is interrupted during the time")
|
||||
end
|
||||
|
||||
---This can be calledBy `require("telescope").extensions.frecency.frecency`.
|
||||
@ -79,8 +101,11 @@ end
|
||||
---@param force? boolean
|
||||
---@return nil
|
||||
function Frecency:validate_database(force)
|
||||
timer.track "validate_database() start"
|
||||
local unlinked = self.database:unlinked_entries()
|
||||
timer.track "validate_database() calculate unlinked"
|
||||
if #unlinked == 0 or (not force and #unlinked < config.db_validate_threshold) then
|
||||
timer.track "validate_database() finish: no unlinked"
|
||||
return
|
||||
end
|
||||
local function remove_entries()
|
||||
@ -89,6 +114,7 @@ function Frecency:validate_database(force)
|
||||
end
|
||||
if not config.db_safe_mode then
|
||||
remove_entries()
|
||||
timer.track "validate_database() finish: removed"
|
||||
return
|
||||
end
|
||||
-- HACK: This is needed because the default implementaion of vim.ui.select()
|
||||
|
||||
@ -18,21 +18,11 @@ function M.track(event)
|
||||
end
|
||||
if M.has_lazy then
|
||||
local stats = require "lazy.stats"
|
||||
---@param n integer
|
||||
---@return string
|
||||
local function make_key(n)
|
||||
return ("[telescope-frecency] %s: %d"):format(event, n)
|
||||
local function make_key(num)
|
||||
local key = num and ("[telescope-frecency] %s: %d"):format(event, num) or "[telescope-frecency] " .. event
|
||||
return stats._stats.times[key] and make_key((num or 1) + 1) or key
|
||||
end
|
||||
local key
|
||||
local num = 0
|
||||
while true do
|
||||
key = make_key(num)
|
||||
if not stats._stats.times[key] then
|
||||
break
|
||||
end
|
||||
num = num + 1
|
||||
end
|
||||
stats.track(key)
|
||||
stats.track(make_key())
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@ -40,15 +40,8 @@ end
|
||||
|
||||
---@param f FrecencyWaitCallback
|
||||
---@param opts FrecencyWaitConfig?
|
||||
---@return nil
|
||||
---@return boolean ok
|
||||
---@return nil|-1|-2 status
|
||||
return function(f, opts)
|
||||
local wait = Wait.new(f, opts)
|
||||
local ok, status = wait:run()
|
||||
if ok then
|
||||
return
|
||||
elseif status == -1 then
|
||||
error "callback never returnes during the time"
|
||||
elseif status == -2 then
|
||||
error "callback is interrupted during the time"
|
||||
end
|
||||
return Wait.new(f, opts):run()
|
||||
end
|
||||
|
||||
Loading…
Reference in New Issue
Block a user