mirror of
https://github.com/kristoferssolo/telescope-frecency.nvim.git
synced 2026-02-04 23:02:06 +00:00
feat: check DB file has been changed (#143)
* refactor: unite logic for finder & async_finder * chore: fix types * chore: add sleep to show results at first * refactor: fix to find results separatedly * test: remove unnecessary ones and fix others * test: add matrix for 0.9.x & Windows * test: use forked plenary.log for Windows * test: fix to use strptime in Windows * test: run again if segmentation fault in Windows * test: loosen timeout for Perl * test: use the latest plenary.nvim again * chore: fix types * chore: change variable name * feat: watch changes of DB to reload * chore: add comments to steps * test: copy whole modules for testing in Windows * fix: make valid paths for Windows * test: add tests for Native * test: use robust way to calculate time vim.fn.strptime cannot be used in Lua loop * chore: fix comments * refactor: simplify the code * test: loosen condition to detect failures * test: disable some logging Many loggings make the test fail. * test: run tests sequentially in Windows * test: loosen timeout not to fail on Windows
This commit is contained in:
committed by
GitHub
parent
fbda5d91d6
commit
767fbf074f
@@ -1,7 +1,9 @@
|
||||
local FileLock = require "frecency.file_lock"
|
||||
local wait = require "frecency.wait"
|
||||
local watcher = require "frecency.database.native.watcher"
|
||||
local log = require "plenary.log"
|
||||
local async = require "plenary.async" --[[@as PlenaryAsync]]
|
||||
local Path = require "plenary.path" --[[@as PlenaryPath]]
|
||||
|
||||
---@class FrecencyDatabaseNative: FrecencyDatabase
|
||||
---@field version "v1"
|
||||
@@ -29,11 +31,20 @@ Native.new = function(fs, config)
|
||||
table = { version = version, records = {} },
|
||||
version = version,
|
||||
}, { __index = Native })
|
||||
self.filename = self.config.root .. "/file_frecency.bin"
|
||||
self.filename = Path.new(self.config.root, "file_frecency.bin").filename
|
||||
self.file_lock = FileLock.new(self.filename)
|
||||
local tx, rx = async.control.channel.counter()
|
||||
watcher.watch(self.filename, tx)
|
||||
wait(function()
|
||||
self:load()
|
||||
end)
|
||||
async.void(function()
|
||||
while true do
|
||||
rx.last()
|
||||
log.debug "file changed. loading..."
|
||||
self:load()
|
||||
end
|
||||
end)()
|
||||
return self
|
||||
end
|
||||
|
||||
@@ -102,8 +113,6 @@ end
|
||||
---@param datetime string?
|
||||
---@return FrecencyDatabaseEntry[]
|
||||
function Native:get_entries(workspace, datetime)
|
||||
-- TODO: check mtime of DB and reload it
|
||||
-- self:load()
|
||||
local now = self:now(datetime)
|
||||
local items = {}
|
||||
for path, record in pairs(self.table.records) do
|
||||
@@ -120,11 +129,21 @@ function Native:get_entries(workspace, datetime)
|
||||
return items
|
||||
end
|
||||
|
||||
-- TODO: remove this func
|
||||
-- This is a func for testing
|
||||
---@private
|
||||
---@param datetime string?
|
||||
---@return integer
|
||||
function Native:now(datetime)
|
||||
return datetime and vim.fn.strptime("%FT%T%z", datetime) or os.time()
|
||||
if not datetime then
|
||||
return os.time()
|
||||
end
|
||||
local epoch
|
||||
wait(function()
|
||||
local tz_fix = datetime:gsub("+(%d%d):(%d%d)$", "+%1%2")
|
||||
epoch = require("frecency.tests.util").time_piece(tz_fix)
|
||||
end)
|
||||
return epoch
|
||||
end
|
||||
|
||||
---@async
|
||||
@@ -132,17 +151,18 @@ end
|
||||
function Native:load()
|
||||
local start = os.clock()
|
||||
local err, data = self.file_lock:with(function()
|
||||
local err, st = async.uv.fs_stat(self.filename)
|
||||
local err, stat = async.uv.fs_stat(self.filename)
|
||||
if err then
|
||||
return nil
|
||||
end
|
||||
local fd
|
||||
err, fd = async.uv.fs_open(self.filename, "r", tonumber("644", 8))
|
||||
assert(not err)
|
||||
assert(not err, err)
|
||||
local data
|
||||
err, data = async.uv.fs_read(fd, st.size)
|
||||
assert(not err)
|
||||
err, data = async.uv.fs_read(fd, stat.size)
|
||||
assert(not err, err)
|
||||
assert(not async.uv.fs_close(fd))
|
||||
watcher.update(stat)
|
||||
return data
|
||||
end)
|
||||
assert(not err, err)
|
||||
@@ -158,16 +178,23 @@ end
|
||||
function Native:save()
|
||||
local start = os.clock()
|
||||
local err = self.file_lock:with(function()
|
||||
local f = assert(load("return " .. vim.inspect(self.table)))
|
||||
local data = string.dump(f)
|
||||
local err, fd = async.uv.fs_open(self.filename, "w", tonumber("644", 8))
|
||||
assert(not err)
|
||||
assert(not async.uv.fs_write(fd, data))
|
||||
assert(not async.uv.fs_close(fd))
|
||||
self:raw_save(self.table)
|
||||
local err, stat = async.uv.fs_stat(self.filename)
|
||||
assert(not err, err)
|
||||
watcher.update(stat)
|
||||
return nil
|
||||
end)
|
||||
assert(not err, err)
|
||||
log.debug(("save() takes %f seconds"):format(os.clock() - start))
|
||||
end
|
||||
|
||||
function Native:raw_save(tbl)
|
||||
local f = assert(load("return " .. vim.inspect(tbl)))
|
||||
local data = string.dump(f)
|
||||
local err, fd = async.uv.fs_open(self.filename, "w", tonumber("644", 8))
|
||||
assert(not err, err)
|
||||
assert(not async.uv.fs_write(fd, data))
|
||||
assert(not async.uv.fs_close(fd))
|
||||
end
|
||||
|
||||
return Native
|
||||
|
||||
87
lua/frecency/database/native/watcher.lua
Normal file
87
lua/frecency/database/native/watcher.lua
Normal file
@@ -0,0 +1,87 @@
|
||||
local async = require "plenary.async" --[[@as PlenaryAsync]]
|
||||
local log = require "plenary.log"
|
||||
local uv = vim.loop or vim.uv
|
||||
|
||||
---@class FrecencyNativeWatcherMtime
|
||||
---@field sec integer
|
||||
---@field nsec integer
|
||||
local Mtime = {}
|
||||
|
||||
---@param mtime FsStatMtime
|
||||
---@return FrecencyNativeWatcherMtime
|
||||
Mtime.new = function(mtime)
|
||||
return setmetatable({ sec = mtime.sec, nsec = mtime.nsec }, Mtime)
|
||||
end
|
||||
|
||||
---@param other FrecencyNativeWatcherMtime
|
||||
---@return boolean
|
||||
function Mtime:__eq(other)
|
||||
return self.sec == other.sec and self.nsec == other.nsec
|
||||
end
|
||||
|
||||
---@return string
|
||||
function Mtime:__tostring()
|
||||
return string.format("%d.%d", self.sec, self.nsec)
|
||||
end
|
||||
|
||||
---@class FrecencyNativeWatcher
|
||||
---@field handler UvFsEventHandle
|
||||
---@field path string
|
||||
---@field mtime FrecencyNativeWatcherMtime
|
||||
local Watcher = {}
|
||||
|
||||
---@return FrecencyNativeWatcher
|
||||
Watcher.new = function()
|
||||
return setmetatable({ path = "", mtime = Mtime.new { sec = 0, nsec = 0 } }, { __index = Watcher })
|
||||
end
|
||||
|
||||
---@param path string
|
||||
---@param tx PlenaryAsyncControlChannelTx
|
||||
function Watcher:watch(path, tx)
|
||||
if self.handler then
|
||||
self.handler:stop()
|
||||
end
|
||||
self.handler = assert(uv.new_fs_event()) --[[@as UvFsEventHandle]]
|
||||
self.handler:start(path, { recursive = true }, function(err, _, _)
|
||||
if err then
|
||||
log.debug("failed to watch path: " .. err)
|
||||
return
|
||||
end
|
||||
async.void(function()
|
||||
-- NOTE: wait for updating mtime
|
||||
async.util.sleep(50)
|
||||
local stat
|
||||
err, stat = async.uv.fs_stat(path)
|
||||
if err then
|
||||
log.debug("failed to stat path: " .. err)
|
||||
return
|
||||
end
|
||||
local mtime = Mtime.new(stat.mtime)
|
||||
if self.mtime ~= mtime then
|
||||
log.debug(("mtime changed: %s -> %s"):format(self.mtime, mtime))
|
||||
self.mtime = mtime
|
||||
tx.send()
|
||||
end
|
||||
end)()
|
||||
end)
|
||||
end
|
||||
|
||||
local watcher = Watcher.new()
|
||||
|
||||
return {
|
||||
---@param path string
|
||||
---@param tx PlenaryAsyncControlChannelTx
|
||||
---@return nil
|
||||
watch = function(path, tx)
|
||||
log.debug("watch path: " .. path)
|
||||
watcher:watch(path, tx)
|
||||
end,
|
||||
|
||||
---@param stat FsStat
|
||||
---@return nil
|
||||
update = function(stat)
|
||||
local mtime = Mtime.new(stat.mtime)
|
||||
log.debug(("update mtime: %s -> %s"):format(watcher.mtime, mtime))
|
||||
watcher.mtime = mtime
|
||||
end,
|
||||
}
|
||||
Reference in New Issue
Block a user