Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: check DB file has been changed #143

Merged
merged 25 commits into from
Sep 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
32b4909
refactor: unite logic for finder & async_finder
delphinus Sep 2, 2023
a165b57
chore: fix types
delphinus Sep 10, 2023
4e6c81f
chore: add sleep to show results at first
delphinus Sep 10, 2023
613bcf8
refactor: fix to find results separatedly
delphinus Sep 10, 2023
30fe451
test: remove unnecessary ones and fix others
delphinus Sep 10, 2023
5d7b2fe
test: add matrix for 0.9.x & Windows
delphinus Sep 10, 2023
041c2a4
test: use forked plenary.log for Windows
delphinus Sep 10, 2023
1108b0d
test: fix to use strptime in Windows
delphinus Sep 10, 2023
98c4620
test: run again if segmentation fault in Windows
delphinus Sep 10, 2023
e441aa1
test: loosen timeout for Perl
delphinus Sep 10, 2023
8eab23a
test: use the latest plenary.nvim again
delphinus Sep 10, 2023
7c14d15
chore: fix types
delphinus Sep 14, 2023
0c992e1
chore: change variable name
delphinus Sep 16, 2023
0ad2ec2
feat: watch changes of DB to reload
delphinus Sep 16, 2023
d69c430
chore: add comments to steps
delphinus Sep 16, 2023
80074d1
test: copy whole modules for testing in Windows
delphinus Sep 16, 2023
9f8fd83
fix: make valid paths for Windows
delphinus Sep 17, 2023
aec1d1c
test: add tests for Native
delphinus Sep 17, 2023
94dbe5a
test: use robust way to calculate time
delphinus Sep 17, 2023
bad03a1
chore: fix comments
delphinus Sep 17, 2023
8a0243d
refactor: simplify the code
delphinus Sep 17, 2023
66543f6
test: loosen condition to detect failures
delphinus Sep 17, 2023
fd58c0f
test: disable some logging
delphinus Sep 17, 2023
f34d315
test: run tests sequentially in Windows
delphinus Sep 17, 2023
75ae450
test: loosen timeout not to fail on Windows
delphinus Sep 17, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 26 additions & 5 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@ jobs:
matrix:
os:
- ubuntu-latest
# TODO: nix seems not to work with SIP
# - macos-latest
# TODO: PlenaryBustedDirectory seems not to run on Windows
# - windows-latest
- macos-latest
- windows-latest
version:
- v0.9.2
- v0.9.1
- v0.9.0
- nightly
runs-on: ${{ matrix.os }}
Expand Down Expand Up @@ -41,7 +41,7 @@ jobs:
with:
neovim: true
version: ${{ matrix.version }}
- name: Run tests
- name: Run tests (not for Windows)
env:
PLENARY_PATH: plenary.nvim
TELESCOPE_PATH: telescope.nvim
Expand All @@ -53,8 +53,29 @@ jobs:
MINIMAL_LUA=${TEST_DIR}minimal.lua
NVIM=$(perl -e '$_ = $ENV{EXE}; s,\\,/,g; print')
$NVIM --headless --clean -u $MINIMAL_LUA -c "PlenaryBustedDirectory $TEST_DIR {minimal_init = '$MINIMAL_LUA'}"
if: matrix.os != 'windows-latest'
- name: Run tests (for Windows)
shell: bash
env:
PLENARY_PATH: plenary.nvim
TELESCOPE_PATH: telescope.nvim
SQLITE_PATH: sqlite.lua
DEBUG_PLENARY: 1
EXE: ${{ steps.nvim.outputs.executable }}
run: |-
# HACK: This is needed because it fails to add runtimepath's.
cp -af $PLENARY_PATH/lua/plenary/ lua/
cp -af $TELESCOPE_PATH/lua/telescope/ lua/
cp -af $SQLITE_PATH/lua/sqlite/ lua/
TEST_DIR=lua/frecency/tests/
MINIMAL_LUA=${TEST_DIR}minimal.lua
NVIM=$(perl -e '$_ = $ENV{EXE}; s,\\,/,g; print')
$NVIM --headless --clean -u $MINIMAL_LUA -c "PlenaryBustedDirectory $TEST_DIR {minimal_init = '$MINIMAL_LUA', timeout = 180000, sequential = true}"
if: matrix.os == 'windows-latest'
- name: Type Check Code Base
uses: mrcjkb/lua-typecheck-action@v0.2.0
with:
checkLevel: Hint
configpath: .luarc.json
# NOTE: This step needs nix that seems not to work with SIP (macOS)
if: matrix.os == 'ubuntu-latest'
123 changes: 0 additions & 123 deletions lua/frecency/async_finder.lua

This file was deleted.

55 changes: 41 additions & 14 deletions lua/frecency/database/native.lua
Original file line number Diff line number Diff line change
@@ -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"
Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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
Expand All @@ -120,29 +129,40 @@ 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
---@return nil
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)
Expand All @@ -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 changes: 87 additions & 0 deletions lua/frecency/database/native/watcher.lua
Original file line number Diff line number Diff line change
@@ -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,
}
4 changes: 3 additions & 1 deletion lua/frecency/entry_maker.lua
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,12 @@ end
---@field score number
---@field display fun(entry: FrecencyEntry): string, table

---@alias FrecencyEntryMakerInstance fun(file: FrecencyFile): FrecencyEntry

---@param filepath_formatter FrecencyFilepathFormatter
---@param workspace string?
---@param workspace_tag string?
---@return fun(file: FrecencyFile): FrecencyEntry
---@return FrecencyEntryMakerInstance
function EntryMaker:create(filepath_formatter, workspace, workspace_tag)
local displayer = entry_display.create {
separator = "",
Expand Down
Loading