| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477 | 
							- -- modified version from https://github.com/lewis6991/impatient.nvim
 
- local vim = vim
 
- local api = vim.api
 
- local uv = vim.loop
 
- 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 function modpath_mangle(modpath)
 
-   for name, dir in pairs(std_dirs) do
 
-     modpath = modpath:gsub(dir, name)
 
-   end
 
-   return modpath
 
- end
 
- 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,
 
-     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,
 
-     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
 
- local function log(...)
 
-   M.log[#M.log + 1] = table.concat({ string.format(...) }, " ")
 
- end
 
- local function print_log()
 
-   for _, l in ipairs(M.log) do
 
-     print(l)
 
-   end
 
- end
 
- local function hash(modpath)
 
-   local stat = fs_stat(modpath)
 
-   if stat then
 
-     return stat.mtime.sec .. stat.mtime.nsec .. stat.size
 
-   end
 
-   error("Could not hash " .. modpath)
 
- end
 
- local function profile(m, entry, name, loader)
 
-   if m.profile then
 
-     local mp = m.profile
 
-     mp[entry] = mp[entry] or {}
 
-     if not mp[entry].loader and loader then
 
-       mp[entry].loader = loader
 
-     end
 
-     if not mp[entry][name] then
 
-       mp[entry][name] = uv.hrtime()
 
-     end
 
-   end
 
- end
 
- local function mprofile(mod, name, loader)
 
-   profile(M.modpaths, 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
 
- 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, sep)
 
-   for i = #parents, 1, -1 do
 
-     local parent = table.concat(vim.list_slice(parents, 1, i), sep)
 
-     local ppath = M.modpaths:get(parent)
 
-     if ppath 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
 
-       end
 
-       for _, path in ipairs(paths) do
 
-         -- path should be of form 'a/b/c.lua' or 'a/b/c/init.lua'
 
-         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, ",")
 
- -- 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.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
 
-   end
 
-   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)
 
-     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
 
- local function extract_basename(pats)
 
-   local basename
 
-   -- Deconstruct basename from pats
 
-   for _, pat in ipairs(pats) do
 
-     for i, npat in ipairs {
 
-       -- Ordered by most specific
 
-       "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
 
-         m = m:sub(0, -6)
 
-       end
 
-       if not basename then
 
-         if m then
 
-           basename = m
 
-         end
 
-       elseif m and m ~= basename then
 
-         -- matches are inconsistent
 
-         return
 
-       end
 
-     end
 
-   end
 
-   return basename
 
- end
 
- local function get_runtime_cached(pats, all, opts)
 
-   local fallback = false
 
-   if all or not opts or not opts.is_lua then
 
-     -- Fallback
 
-     fallback = true
 
-   end
 
-   local basename
 
-   if not fallback then
 
-     basename = extract_basename(pats)
 
-   end
 
-   if fallback or not basename then
 
-     return get_runtime(pats, all, opts)
 
-   end
 
-   return { get_runtime_file_cached(basename, pats) }
 
- end
 
- -- Copied from neovim/src/nvim/lua/vim.lua with two lines changed
 
- local function load_package(name)
 
-   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})
 
-   local found = { get_runtime_file_cached(basename, paths) }
 
-   if #found > 0 then
 
-     local f, err = loadfile(found[1])
 
-     return f or error(err)
 
-   end
 
-   local so_paths = {}
 
-   for _, trail in ipairs(vim._so_trails) do
 
-     local path = "lua" .. trail:gsub("?", basename) -- so_trails contains a leading slash
 
-     table.insert(so_paths, path)
 
-   end
 
-   -- Original line:
 
-   -- found = vim.api.nvim__get_runtime(so_paths, false, {is_lua=true})
 
-   found = { get_runtime_file_cached(basename, so_paths) }
 
-   if #found > 0 then
 
-     -- Making function name in Lua 5.1 (see src/loadlib.c:mkfuncname) is
 
-     -- a) strip prefix up to and including the first dash, if any
 
-     -- b) replace all dots by underscores
 
-     -- c) prepend "luaopen_"
 
-     -- 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 = loadlib(found[1], "luaopen_" .. modname:gsub("%.", "_"))
 
-     return f or error(err)
 
-   end
 
-   return nil
 
- end
 
- local function load_from_cache(path)
 
-   local mc = M.chunks
 
-   local cache = mc:get(path)
 
-   if not cache then
 
-     return nil, string.format("No cache for path %s", path)
 
-   end
 
-   local mhash, codes = unpack(cache)
 
-   if mhash ~= hash(path) then
 
-     mc:set(path)
 
-     mc.dirty = true
 
-     return nil, string.format("Stale cache for path %s", path)
 
-   end
 
-   local chunk = loadstring(codes)
 
-   if not chunk then
 
-     mc:set(path)
 
-     mc.dirty = true
 
-     return nil, string.format("Cache error for path %s", path)
 
-   end
 
-   return chunk
 
- end
 
- local function loadfile_cached(path)
 
-   cprofile(path, "load_start")
 
-   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
 
-   chunk, err = _loadfile(path)
 
-   if not err and M.chunks.enable then
 
-     log("Creating cache for path %s", path)
 
-     M.chunks:set(path, { hash(path), string.dump(chunk) })
 
-     M.chunks.dirty = true
 
-   end
 
-   cprofile(path, "load_end", "standard")
 
-   return chunk, err
 
- 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 = assert(io.open(t.path, "w+b"))
 
-       f:write(mpack.encode(t.cache))
 
-       f:flush()
 
-       t.dirty = false
 
-     end
 
-   end
 
-   _save_cache(M.chunks)
 
-   _save_cache(M.modpaths)
 
- end
 
- local function clear_cache()
 
-   local function _clear_cache(t)
 
-     t.cache = {}
 
-     os.remove(t.path)
 
-   end
 
-   _clear_cache(M.chunks)
 
-   _clear_cache(M.modpaths)
 
- 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 = assert(io.open(t.path, "rb"))
 
-       local ok
 
-       ok, t.cache = pcall(function()
 
-         return mpack.decode(f:read "*a")
 
-       end)
 
-       if not ok then
 
-         log("Corrupted cache file, %s. Invalidating...", t.path)
 
-         os.remove(t.path)
 
-         t.cache = {}
 
-       end
 
-       t.dirty = not ok
 
-     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
 
- 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
 
-   loadfile = loadfile_cached
 
-   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,
 
-   })
 
-   api.nvim_create_autocmd("OptionSet", {
 
-     group = augroup,
 
-     pattern = "runtimepath",
 
-     callback = function()
 
-       rtp = vim.split(vim.o.rtp, ",")
 
-     end,
 
-   })
 
- end
 
- setup()
 
- return M
 
 
  |