diff options
author | kylo252 <[email protected]> | 2022-10-17 17:29:15 +0200 |
---|---|---|
committer | kylo252 <[email protected]> | 2022-10-17 17:29:15 +0200 |
commit | 4ef07315003f723bb8e97d5a91b2bde3773ec1b8 (patch) | |
tree | e9889a492f76e3f9573228343aaba647dfd48136 /lua/lvim/impatient.lua | |
parent | e4a5fe97abe500bbbe78fb137d57a59f558da05a (diff) | |
parent | 6f6cbc394d2a7e64964b6067a2f42d2e6a07824e (diff) |
Merge remote-tracking branch 'origin/rolling'
Diffstat (limited to 'lua/lvim/impatient.lua')
-rw-r--r-- | lua/lvim/impatient.lua | 309 |
1 files changed, 211 insertions, 98 deletions
diff --git a/lua/lvim/impatient.lua b/lua/lvim/impatient.lua index fe26b940..beb3862a 100644 --- a/lua/lvim/impatient.lua +++ b/lua/lvim/impatient.lua @@ -1,5 +1,4 @@ -- modified version from https://github.com/lewis6991/impatient.nvim - local vim = vim local api = vim.api local uv = vim.loop @@ -7,66 +6,96 @@ local _loadfile = loadfile local get_runtime = api.nvim__get_runtime local fs_stat = uv.fs_stat local mpack = vim.mpack +local loadlib = package.loadlib + +local std_cache = vim.fn.stdpath "cache" + +local sep +if jit.os == "Windows" then + sep = "\\" +else + sep = "/" +end + +local std_dirs = { + ["<APPDIR>"] = os.getenv "APPDIR", + ["<VIMRUNTIME>"] = os.getenv "VIMRUNTIME", + ["<STD_DATA>"] = vim.fn.stdpath "data", + ["<STD_CONFIG>"] = vim.fn.stdpath "config", + ["<LVIM_BASE>"] = get_lvim_base_dir(), + ["<LVIM_RUNTIME>"] = get_runtime_dir(), + ["<LVIM_CONFIG>"] = get_config_dir(), +} -local appdir = os.getenv "APPDIR" +local function modpath_mangle(modpath) + for name, dir in pairs(std_dirs) do + modpath = modpath:gsub(dir, name) + end + return modpath +end -local M = { +local function modpath_unmangle(modpath) + for name, dir in pairs(std_dirs) do + modpath = modpath:gsub(name, dir) + end + return modpath +end + +-- Overridable by user +local default_config = { + chunks = { + enable = true, + path = std_cache .. sep .. "luacache_chunks", + }, + modpaths = { + enable = true, + path = std_cache .. sep .. "luacache_modpaths", + }, +} + +-- State used internally +local default_state = { chunks = { cache = {}, profile = nil, dirty = false, - path = vim.fn.stdpath "cache" .. "/luacache_chunks", + get = function(self, path) + return self.cache[modpath_mangle(path)] + end, + set = function(self, path, chunk) + self.cache[modpath_mangle(path)] = chunk + end, }, modpaths = { cache = {}, profile = nil, dirty = false, - path = vim.fn.stdpath "cache" .. "/luacache_modpaths", + get = function(self, mod) + if self.cache[mod] then + return modpath_unmangle(self.cache[mod]) + end + end, + set = function(self, mod, path) + self.cache[mod] = modpath_mangle(path) + end, }, log = {}, } +---@diagnostic disable-next-line: undefined-field +local M = vim.tbl_deep_extend("keep", _G.__luacache_config or {}, default_config, default_state) _G.__luacache = M -if not get_runtime then - -- nvim 0.5 compat - get_runtime = function(paths, all, _) - local r = {} - for _, path in ipairs(paths) do - local found = api.nvim_get_runtime_file(path, all) - for i = 1, #found do - r[#r + 1] = found[i] - end - end - return r - end -end - local function log(...) M.log[#M.log + 1] = table.concat({ string.format(...) }, " ") end -function M.print_log() +local function print_log() for _, l in ipairs(M.log) do print(l) end end -function M.enable_profile() - local P = require "lvim.impatient.profile" - - M.chunks.profile = {} - M.modpaths.profile = {} - - P.setup(M.modpaths.profile) - - M.print_profile = function() - P.print_profile(M) - end - - vim.cmd [[command! LuaCacheProfile lua _G.__luacache.print_profile()]] -end - local function hash(modpath) local stat = fs_stat(modpath) if stat then @@ -75,20 +104,6 @@ local function hash(modpath) error("Could not hash " .. modpath) end -local function modpath_mangle(modpath) - if appdir then - modpath = modpath:gsub(appdir, "/$APPDIR") - end - return modpath -end - -local function modpath_unmangle(modpath) - if appdir then - modpath = modpath:gsub("/$APPDIR", appdir) - end - return modpath -end - local function profile(m, entry, name, loader) if m.profile then local mp = m.profile @@ -107,18 +122,41 @@ local function mprofile(mod, name, loader) end local function cprofile(path, name, loader) + if M.chunks.profile then + path = modpath_mangle(path) + end profile(M.chunks, path, name, loader) end -local function get_runtime_file(basename, paths) +function M.enable_profile() + local P = require "lvim.impatient.profile" + + M.chunks.profile = {} + M.modpaths.profile = {} + + loadlib = function(path, fun) + cprofile(path, "load_start") + local f, err = package.loadlib(path, fun) + cprofile(path, "load_end", "standard") + return f, err + end + + P.setup(M.modpaths.profile) + + api.nvim_create_user_command("LuaCacheProfile", function() + P.print_profile(M, std_dirs) + end, {}) +end + +local function get_runtime_file_from_parent(basename, paths) -- Look in the cache to see if we have already loaded a parent module. -- If we have then try looking in the parents directory first. - local parents = vim.split(basename, "/") + local parents = vim.split(basename, sep) for i = #parents, 1, -1 do - local parent = table.concat(vim.list_slice(parents, 1, i), "/") - local ppath = M.modpaths.cache[parent] + local parent = table.concat(vim.list_slice(parents, 1, i), sep) + local ppath = M.modpaths:get(parent) if ppath then - if ppath:sub(-9) == "/init.lua" then + if ppath:sub(-9) == (sep .. "init.lua") then ppath = ppath:sub(1, -10) -- a/b/init.lua -> a/b else ppath = ppath:sub(1, -5) -- a/b.lua -> a/b @@ -126,38 +164,71 @@ local function get_runtime_file(basename, paths) for _, path in ipairs(paths) do -- path should be of form 'a/b/c.lua' or 'a/b/c/init.lua' - local modpath = ppath .. "/" .. path:sub(#("lua/" .. parent) + 2) + local modpath = ppath .. sep .. path:sub(#("lua" .. sep .. parent) + 2) if fs_stat(modpath) then return modpath, "cache(p)" end end end end +end + +local rtp = vim.split(vim.o.rtp, ",") - -- What Neovim does by default; slowest - local modpath = get_runtime(paths, false, { is_lua = true })[1] - return modpath, "standard" +-- Make sure modpath is in rtp and that modpath is in paths. +local function validate_modpath(modpath, paths) + local match = false + for _, p in ipairs(paths) do + if vim.endswith(modpath, p) then + match = true + break + end + end + if not match then + return false + end + for _, dir in ipairs(rtp) do + if vim.startswith(modpath, dir) then + return fs_stat(modpath) ~= nil + end + end + return false end local function get_runtime_file_cached(basename, paths) + local modpath, loader local mp = M.modpaths - if mp.cache[basename] then - local modpath = mp.cache[basename] - if fs_stat(modpath) then - mprofile(basename, "resolve_end", "cache") - return modpath + if mp.enable then + local modpath_cached = mp:get(basename) + if modpath_cached then + modpath, loader = modpath_cached, "cache" + else + modpath, loader = get_runtime_file_from_parent(basename, paths) + end + + if modpath and not validate_modpath(modpath, paths) then + modpath = nil + + -- Invalidate + mp.cache[basename] = nil + mp.dirty = true end - mp.cache[basename] = nil - mp.dirty = true end - local modpath, loader = get_runtime_file(basename, paths) + if not modpath then + -- What Neovim does by default; slowest + modpath, loader = get_runtime(paths, false, { is_lua = true })[1], "standard" + end + if modpath then mprofile(basename, "resolve_end", loader) - log("Creating cache for module %s", basename) - mp.cache[basename] = modpath_mangle(modpath) - mp.dirty = true + if mp.enable and loader ~= "cache" then + log("Creating cache for module %s", basename) + mp:set(basename, modpath) + mp.dirty = true + end end + return modpath end @@ -168,8 +239,12 @@ local function extract_basename(pats) for _, pat in ipairs(pats) do for i, npat in ipairs { -- Ordered by most specific - "lua/(.*)/init%.lua", - "lua/(.*)%.lua", + "lua" + .. sep + .. "(.*)" + .. sep + .. "init%.lua", + "lua" .. sep .. "(.*)%.lua", } do local m = pat:match(npat) if i == 2 and m and m:sub(-4) == "init" then @@ -211,8 +286,8 @@ end -- Copied from neovim/src/nvim/lua/vim.lua with two lines changed local function load_package(name) - local basename = name:gsub("%.", "/") - local paths = { "lua/" .. basename .. ".lua", "lua/" .. basename .. "/init.lua" } + local basename = name:gsub("%.", sep) + local paths = { "lua" .. sep .. basename .. ".lua", "lua" .. sep .. basename .. sep .. "init.lua" } -- Original line: -- local found = vim.api.nvim__get_runtime(paths, false, {is_lua=true}) @@ -239,7 +314,7 @@ local function load_package(name) -- So "foo-bar.baz" should result in "luaopen_bar_baz" local dash = name:find("-", 1, true) local modname = dash and name:sub(dash + 1) or name - local f, err = package.loadlib(found[1], "luaopen_" .. modname:gsub("%.", "_")) + local f, err = loadlib(found[1], "luaopen_" .. modname:gsub("%.", "_")) return f or error(err) end return nil @@ -248,14 +323,16 @@ end local function load_from_cache(path) local mc = M.chunks - if not mc.cache[path] then + local cache = mc:get(path) + + if not cache then return nil, string.format("No cache for path %s", path) end - local mhash, codes = unpack(mc.cache[path]) + local mhash, codes = unpack(cache) - if mhash ~= hash(modpath_unmangle(path)) then - mc.cache[path] = nil + if mhash ~= hash(path) then + mc:set(path) mc.dirty = true return nil, string.format("Stale cache for path %s", path) end @@ -263,7 +340,7 @@ local function load_from_cache(path) local chunk = loadstring(codes) if not chunk then - mc.cache[path] = nil + mc:set(path) mc.dirty = true return nil, string.format("Cache error for path %s", path) end @@ -274,19 +351,23 @@ end local function loadfile_cached(path) cprofile(path, "load_start") - local chunk, err = load_from_cache(path) - if chunk and not err then - log("Loaded cache for path %s", path) - cprofile(path, "load_end", "cache") - return chunk + local chunk, err + + if M.chunks.enable then + chunk, err = load_from_cache(path) + if chunk and not err then + log("Loaded cache for path %s", path) + cprofile(path, "load_end", "cache") + return chunk + end + log(err) end - log(err) chunk, err = _loadfile(path) - if not err then + if not err and M.chunks.enable then log("Creating cache for path %s", path) - M.chunks.cache[modpath_mangle(path)] = { hash(path), string.dump(chunk) } + M.chunks:set(path, { hash(path), string.dump(chunk) }) M.chunks.dirty = true end @@ -296,9 +377,12 @@ end function M.save_cache() local function _save_cache(t) + if not t.enable then + return + end if t.dirty then log("Updating chunk cache file: %s", t.path) - local f = io.open(t.path, "w+b") + local f = assert(io.open(t.path, "w+b")) f:write(mpack.encode(t.cache)) f:flush() t.dirty = false @@ -308,7 +392,7 @@ function M.save_cache() _save_cache(M.modpaths) end -function M.clear_cache() +local function clear_cache() local function _clear_cache(t) t.cache = {} os.remove(t.path) @@ -319,9 +403,12 @@ end local function init_cache() local function _init_cache(t) + if not t.enable then + return + end if fs_stat(t.path) then log("Loading cache file %s", t.path) - local f = io.open(t.path, "rb") + local f = assert(io.open(t.path, "rb")) local ok ok, t.cache = pcall(function() return mpack.decode(f:read "*a") @@ -336,6 +423,10 @@ local function init_cache() end end + if not uv.fs_stat(std_cache) then + vim.fn.mkdir(std_cache, "p") + end + _init_cache(M.chunks) _init_cache(M.modpaths) end @@ -343,20 +434,42 @@ end local function setup() init_cache() + -- Usual package loaders + -- 1. package.preload + -- 2. vim._load_package + -- 3. package.path + -- 4. package.cpath + -- 5. all-in-one + -- Override default functions + for i, loader in ipairs(package.loaders) do + if loader == vim._load_package then + package.loaders[i] = load_package + break + end + end vim._load_package = load_package + vim.api.nvim__get_runtime = get_runtime_cached - -- luacheck: ignore 121 loadfile = loadfile_cached - vim.cmd [[ - augroup impatient - autocmd VimEnter,VimLeave * lua _G.__luacache.save_cache() - augroup END + local augroup = api.nvim_create_augroup("impatient", {}) + + api.nvim_create_user_command("LuaCacheClear", clear_cache, {}) + api.nvim_create_user_command("LuaCacheLog", print_log, {}) + + api.nvim_create_autocmd({ "VimEnter", "VimLeave" }, { + group = augroup, + callback = M.save_cache, + }) - command! LuaCacheClear lua _G.__luacache.clear_cache() - command! LuaCacheLog lua _G.__luacache.print_log() - ]] + api.nvim_create_autocmd("OptionSet", { + group = augroup, + pattern = "runtimepath", + callback = function() + rtp = vim.split(vim.o.rtp, ",") + end, + }) end setup() |