telescope-frecency.nvim/lua/frecency/fs.lua
JINNOUCHI Yasushi 1f32091e2b
refactor!: use OO & add tests (#100)
* I did an overhall for all codes and added typing by Lua-language-server and tests. It also works on CI.
* Now it searches files on the workspace completely asynchronously. It does not block your text input. (Fix #106)
Make count = 1 when you open a file you've never opened (Fix #107)
2023-08-06 16:02:37 +09:00

90 lines
2.3 KiB
Lua

local Path = require "plenary.path" --[[@as PlenaryPath]]
local scandir = require "plenary.scandir"
local log = require "plenary.log"
local uv = vim.uv or vim.loop
---@class FrecencyFS
---@field os_homedir string
---@field private config FrecencyFSConfig
---@field private ignore_regexes string[]
local FS = {}
---@class FrecencyFSConfig
---@field scan_depth integer?
---@field ignore_patterns string[]
---@param config FrecencyFSConfig
---@return FrecencyFS
FS.new = function(config)
local self = setmetatable(
{ config = vim.tbl_extend("force", { scan_depth = 100 }, config), os_homedir = assert(uv.os_homedir()) },
{ __index = FS }
)
---@param pattern string
self.ignore_regexes = vim.tbl_map(function(pattern)
local escaped = pattern:gsub("[%-%.%+%[%]%(%)%$%^%%%?%*]", "%%%1")
local regex = escaped:gsub("%%%*", ".*"):gsub("%%%?", ".")
return "^" .. regex .. "$"
end, self.config.ignore_patterns)
return self
end
---@param path string
---@return boolean
function FS:is_valid_path(path)
return Path:new(path):is_file() and not self:is_ignored(path)
end
---@param path string
---@return function
function FS:scan_dir(path)
log.debug { path = path }
local gitignore = self:make_gitignore(path)
return coroutine.wrap(function()
for name, type in
vim.fs.dir(path, {
depth = self.config.scan_depth,
skip = function(dirname)
if self:is_ignored(vim.fs.joinpath(path, dirname)) then
return false
end
end,
})
do
local fullpath = vim.fs.joinpath(path, name)
if type == "file" and not self:is_ignored(fullpath) and gitignore({ path }, fullpath) then
coroutine.yield(name)
end
end
end)
end
---@param path string
---@return string
function FS:relative_from_home(path)
return Path:new(path):make_relative(self.os_homedir)
end
---@private
---@param path string
---@return boolean
function FS:is_ignored(path)
for _, regex in ipairs(self.ignore_regexes) do
if path:find(regex) then
return true
end
end
return false
end
---@private
---@param basepath string
---@return fun(base_paths: string[], entry: string): boolean
function FS:make_gitignore(basepath)
return scandir.__make_gitignore { basepath } or function(_, _)
return true
end
end
return FS