diff --git a/lua/lazy/core/cache.lua b/lua/lazy/core/cache.lua index df0c315..5e34cf8 100644 --- a/lua/lazy/core/cache.lua +++ b/lua/lazy/core/cache.lua @@ -62,11 +62,11 @@ function M.check_path(modname, modpath) -- the correct lazy path should be part of rtp. -- so if we get here, this is folke using the local dev instance ;) - if modname:sub(1, 4) == "lazy" then + if modname and modname:sub(1, 4) == "lazy" then return false end - return M.check_autoload(modname, modpath) + return modname and M.check_autoload(modname, modpath) end function M.check_autoload(modname, modpath) @@ -246,8 +246,43 @@ function M.get_topmods(path) end ---@param modname string +---@return string?, string? +function M.find_dir(modname) + if M.cache[modname] then + -- check if modname is in cache + local modpath = M.cache[modname].modpath + if M.check_path(modname, modpath) then + local root = modpath:gsub("/init%.lua$", ""):gsub("%.lua$", "") + return root, modpath + end + else + -- in case modname is just a directory and not a real mod, + -- check for any children in the cache + for child, entry in pairs(M.cache) do + if child:find(modname, 1, true) == 1 and M.check_path(nil, entry.modpath) then + local basename = modname:gsub("%.", "/") + local childbase = child:gsub("%.", "/") + local ret = entry.modpath:gsub("/init%.lua$", ""):gsub("%.lua$", "") + local idx = assert(ret:find(childbase, 1, true)) + return ret:sub(1, idx - 1) .. basename + end + end + end + + -- not found in cache, so find the root with the special pattern + local modpath = M.find(modname, { patterns = { "" } }) + if modpath then + local root = modpath:gsub("/init%.lua$", ""):gsub("%.lua$", "") + return root, root ~= modpath and modpath or nil + end +end + +---@param modname string +---@param opts? {patterns?:string[]} ---@return string? -function M.find(modname) +function M.find(modname, opts) + opts = opts or {} + M.stats.find.total = M.stats.find.total + 1 local start = uv.hrtime() local basename = modname:gsub("%.", "/") @@ -257,6 +292,10 @@ function M.find(modname) -- search for a directory first when topmod == modname local patterns = topmod == modname and { "/init.lua", ".lua" } or { ".lua", "/init.lua" } + if opts.patterns then + vim.list_extend(patterns, opts.patterns) + end + -- check top-level mods to find the module local function _find() for _, toppath in ipairs(M.topmods[topmod] or {}) do diff --git a/lua/lazy/core/util.lua b/lua/lazy/core/util.lua index 123f762..cd16edd 100644 --- a/lua/lazy/core/util.lua +++ b/lua/lazy/core/util.lua @@ -176,25 +176,21 @@ function M.walk(path, fn) end ---@param modname string ----@param root string ---@param fn fun(modname:string, modpath:string) ----@overload fun(modname:string, fn: fun(modname:string, modpath:string)) -function M.lsmod(modname, root, fn) - if type(root) == "function" then - fn = root - root = vim.fn.stdpath("config") .. "/lua" +function M.lsmod(modname, fn) + local Cache = require("lazy.core.cache") + local root, modpath = Cache.find_dir(modname) + if not root then + return end - root = root .. "/" .. modname:gsub("%.", "/") - if vim.loop.fs_stat(root .. ".lua") then - fn(modname, root .. ".lua") + + if modpath and vim.loop.fs_stat(modpath) then + fn(modname, modpath) end + M.ls(root, function(path, name, type) - if (type == "file" or type == "link") and name:sub(-4) == ".lua" then - if name == "init.lua" then - fn(modname, path) - else - fn(modname .. "." .. name:sub(1, -5), path) - end + if name ~= "init.lua" and (type == "file" or type == "link") and name:sub(-4) == ".lua" then + fn(modname .. "." .. name:sub(1, -5), path) elseif type == "directory" and vim.loop.fs_stat(path .. "/init.lua") then fn(modname .. "." .. name, path .. "/init.lua") end diff --git a/lua/lazy/manage/reloader.lua b/lua/lazy/manage/reloader.lua index 3e233d0..762cfbc 100644 --- a/lua/lazy/manage/reloader.lua +++ b/lua/lazy/manage/reloader.lua @@ -55,7 +55,7 @@ function M.check(start) end end - Util.lsmod(Config.spec --[[@as string]], M.root, check) + Util.lsmod(Config.spec --[[@as string]], check) for file in pairs(M.files) do if not checked[file] then diff --git a/tests/core/util_spec.lua b/tests/core/util_spec.lua new file mode 100644 index 0000000..3dee0a8 --- /dev/null +++ b/tests/core/util_spec.lua @@ -0,0 +1,40 @@ +local Util = require("lazy.util") +local Cache = require("lazy.core.cache") +local Helpers = require("tests.helpers") + +describe("util", function() + before_each(function() + Helpers.fs_rm("") + end) + + it("lsmod lists all mods in dir", function() + local tests = { + { + files = { "lua/foo/one.lua", "lua/foo/two.lua", "lua/foo/init.lua" }, + mods = { "foo", "foo.one", "foo.two" }, + }, + { + files = { "lua/foo/one.lua", "lua/foo/two.lua", "lua/foo.lua" }, + mods = { "foo", "foo.one", "foo.two" }, + }, + { + files = { "lua/foo/one.lua", "lua/foo/two.lua" }, + mods = { "foo.one", "foo.two" }, + }, + } + + vim.opt.rtp:append(Helpers.path("")) + for _, test in ipairs(tests) do + Cache.cache = {} + table.sort(test.mods) + Helpers.fs_rm("") + Helpers.fs_create(test.files) + local mods = {} + Util.lsmod("foo", function(modname, modpath) + mods[#mods + 1] = modname + end) + table.sort(mods) + assert.same(test.mods, mods) + end + end) +end) diff --git a/tests/helpers.lua b/tests/helpers.lua new file mode 100644 index 0000000..c6174a8 --- /dev/null +++ b/tests/helpers.lua @@ -0,0 +1,37 @@ +local Util = require("lazy.util") + +local M = {} + +M.fs_root = vim.fn.fnamemodify("./.tests/fs", ":p") + +function M.path(path) + return Util.norm(M.fs_root .. "/" .. path) +end + +---@param files string[] +function M.fs_create(files) + ---@type string[] + local ret = {} + + for _, file in ipairs(files) do + ret[#ret + 1] = Util.norm(M.fs_root .. "/" .. file) + local parent = vim.fn.fnamemodify(ret[#ret], ":h:p") + vim.fn.mkdir(parent, "p") + Util.write_file(ret[#ret], "") + end + return ret +end + +function M.fs_rm(dir) + dir = Util.norm(M.fs_root .. dir) + Util.walk(dir, function(path, _, type) + if type == "directory" then + vim.loop.fs_rmdir(path) + else + vim.loop.fs_unlink(path) + end + end) + vim.loop.fs_rmdir(dir) +end + +return M