mirror of
https://github.com/kristoferssolo/telescope-frecency.nvim.git
synced 2025-10-21 20:10:38 +00:00
* 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
109 lines
3.1 KiB
Lua
109 lines
3.1 KiB
Lua
local log = require "frecency.log"
|
|
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
|
|
---@field config FrecencyFileLockRawConfig
|
|
---@field lock string
|
|
---@field target string
|
|
local FileLock = {}
|
|
|
|
---@class FrecencyFileLockConfig
|
|
---@field retry? integer default: 5
|
|
---@field unlink_retry? integer default: 5
|
|
---@field interval? integer default: 500
|
|
|
|
---@class FrecencyFileLockRawConfig
|
|
---@field retry integer default: 5
|
|
---@field unlink_retry integer default: 5
|
|
---@field interval integer default: 500
|
|
|
|
---@param target string
|
|
---@param file_lock_config? FrecencyFileLockConfig
|
|
---@return FrecencyFileLock
|
|
FileLock.new = function(target, file_lock_config)
|
|
log.debug(("file_lock new(): %s"):format(target))
|
|
local config = vim.tbl_extend("force", { retry = 5, unlink_retry = 5, interval = 500 }, file_lock_config or {})
|
|
return setmetatable({ config = config, lock = target .. ".lock", target = target }, { __index = FileLock })
|
|
end
|
|
|
|
---@async
|
|
---@return string? err
|
|
function FileLock:get()
|
|
local count = 0
|
|
local unlink_count = 0
|
|
local err, fd
|
|
while true do
|
|
count = count + 1
|
|
local dir = Path.new(self.lock):parent()
|
|
if not dir:exists() then
|
|
-- TODO: make this call be async
|
|
log.debug(("file_lock get(): mkdir parent: %s"):format(dir.filename))
|
|
---@diagnostic disable-next-line: undefined-field
|
|
dir:mkdir { parents = true }
|
|
end
|
|
err, fd = async.uv.fs_open(self.lock, "wx", tonumber("600", 8))
|
|
if not err then
|
|
break
|
|
end
|
|
async.util.sleep(self.config.interval)
|
|
if count >= self.config.retry then
|
|
log.debug(("file_lock get(): retry count reached. try to delete the lock file: %d"):format(count))
|
|
err = async.uv.fs_unlink(self.lock)
|
|
if err then
|
|
log.debug("file_lock get() failed: " .. err)
|
|
unlink_count = unlink_count + 1
|
|
if unlink_count >= self.config.unlink_retry then
|
|
log.error("file_lock get(): failed to unlink the lock file: " .. err)
|
|
return "failed to get lock"
|
|
end
|
|
end
|
|
end
|
|
log.debug(("file_lock get() retry: %d"):format(count))
|
|
end
|
|
err = async.uv.fs_close(fd)
|
|
if err then
|
|
log.debug("file_lock get() failed: " .. err)
|
|
return err
|
|
end
|
|
end
|
|
|
|
---@async
|
|
---@return string? err
|
|
function FileLock:release()
|
|
local err = async.uv.fs_stat(self.lock)
|
|
if err then
|
|
log.debug("file_lock release() not found: " .. err)
|
|
return "lock not found"
|
|
end
|
|
err = async.uv.fs_unlink(self.lock)
|
|
if err then
|
|
log.debug("file_lock release() unlink failed: " .. err)
|
|
return err
|
|
end
|
|
end
|
|
|
|
---@async
|
|
---@generic T
|
|
---@param f fun(target: string): T
|
|
---@return string? err
|
|
---@return T
|
|
function FileLock:with(f)
|
|
local err = self:get()
|
|
if err then
|
|
return err, nil
|
|
end
|
|
local ok, result_or_err = pcall(f, self.target)
|
|
err = self:release()
|
|
if err then
|
|
return err, nil
|
|
elseif ok then
|
|
return nil, result_or_err
|
|
end
|
|
return result_or_err, nil
|
|
end
|
|
|
|
return FileLock
|