diff options
author | Lewis Russell <lewis6991@gmail.com> | 2023-03-31 13:05:22 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-03-31 13:05:22 +0100 |
commit | 83bfd94d1df5eecb8e4069a227c7d24598636d63 (patch) | |
tree | 14ef711cf4f158f48bfc83473e024b62f6f74da7 | |
parent | b34097fe6d2ea5c84bcec65a834a430d9f58eb64 (diff) | |
download | rneovim-83bfd94d1df5eecb8e4069a227c7d24598636d63.tar.gz rneovim-83bfd94d1df5eecb8e4069a227c7d24598636d63.tar.bz2 rneovim-83bfd94d1df5eecb8e4069a227c7d24598636d63.zip |
refactor(loader): cache hash information
Whenever we run fs_stat() on a path, save this information in the loader
so it can be re-used.
- Loader.loadfile: Remove arguments `hash` as it is no longer needed.
- Loader.loader: Use _G.loadstring instead of Loader.load
This allows plugins to wrap loadstring to inspection and profiling
- factor out read file logic
-rw-r--r-- | runtime/doc/lua.txt | 2 | ||||
-rw-r--r-- | runtime/lua/vim/loader.lua | 62 |
2 files changed, 46 insertions, 18 deletions
diff --git a/runtime/doc/lua.txt b/runtime/doc/lua.txt index 33833fa83e..14f2ba2b04 100644 --- a/runtime/doc/lua.txt +++ b/runtime/doc/lua.txt @@ -2065,7 +2065,7 @@ find({modname}, {opts}) *vim.loader.find()* for `modname="*"` reset({path}) *vim.loader.reset()* - Resets the topmods cache for the path, or all the paths if path is nil. + Resets the cache for the path, or all the paths if path is nil. Parameters: ~ • {path} string? path to reset diff --git a/runtime/lua/vim/loader.lua b/runtime/lua/vim/loader.lua index 7ac7f44338..201de18497 100644 --- a/runtime/lua/vim/loader.lua +++ b/runtime/lua/vim/loader.lua @@ -5,7 +5,7 @@ local loaders = package.loaders local M = {} ----@alias CacheHash {mtime: {sec:number, nsec:number}, size:number} +---@alias CacheHash {mtime: {sec:number, nsec:number}, size:number, type: string} ---@alias CacheEntry {hash:CacheHash, chunk:string} ---@class ModuleFindOpts @@ -32,6 +32,8 @@ local Loader = { VERSION = 3, ---@type table<string, table<string,ModuleInfo>> _indexed = {}, + ---@type table<string, CacheHash> + _hashes = {}, ---@type table<string, string[]> _topmods = {}, _loadfile = loadfile, @@ -41,6 +43,18 @@ local Loader = { }, } +--- @param path string +--- @return uv.fs_stat.result +--- @private +function Loader.get_hash(path) + if not Loader._hashes[path] then + -- Note we must never save a stat for a non-existent path. + -- For non-existent paths fs_stat() will return nil. + Loader._hashes[path] = uv.fs_stat(path) + end + return Loader._hashes[path] +end + ---@private local function normalize(path) return vim.fs.normalize(path, { expand_env = false }) @@ -102,18 +116,28 @@ function Loader.write(name, entry) uv.fs_close(f) end +--- @param path string +--- @param mode integer +--- @return string? data +--- @private +local function readfile(path, mode) + local f = uv.fs_open(path, 'r', mode) + if f then + local hash = assert(uv.fs_fstat(f)) + local data = uv.fs_read(f, hash.size, 0) --[[@as string?]] + uv.fs_close(f) + return data + end +end + --- Loads the cache entry for a given module or file ---@param name string module name or filename ---@return CacheEntry? ---@private function Loader.read(name) local cname = Loader.cache_file(name) - local f = uv.fs_open(cname, 'r', 438) - if f then - local hash = uv.fs_fstat(f) --[[@as CacheHash]] - local data = uv.fs_read(f, hash.size, 0) --[[@as string]] - uv.fs_close(f) - + local data = readfile(cname, 438) + if data then local zero = data:find('\0', 1, true) if not zero then return @@ -141,7 +165,9 @@ end function Loader.loader(modname) local ret = M.find(modname)[1] if ret then - local chunk, err = Loader.load(ret.modpath, { hash = ret.stat }) + -- Make sure to call the global loadfile so we respect any augmentations done elsewhere. + -- E.g. profiling + local chunk, err = loadfile(ret.modpath) return chunk or error(err) end return '\ncache_loader: module ' .. modname .. ' not found' @@ -171,17 +197,17 @@ function Loader.loader_lib(modname) end --- `loadfile` using the cache +--- Note this has the mode and env arguments which is supported by LuaJIT and is 5.1 compatible. ---@param filename? string ---@param mode? "b"|"t"|"bt" ---@param env? table ----@param hash? CacheHash ---@return function?, string? error_message ---@private -- luacheck: ignore 312 -function Loader.loadfile(filename, mode, env, hash) +function Loader.loadfile(filename, mode, env) -- ignore mode, since we byte-compile the lua source files mode = nil - return Loader.load(normalize(filename), { mode = mode, env = env, hash = hash }) + return Loader.load(normalize(filename), { mode = mode, env = env }) end --- Checks whether two cache hashes are the same based on: @@ -201,8 +227,7 @@ end --- Loads the given module path using the cache ---@param modpath string ----@param opts? {hash?: CacheHash, mode?: "b"|"t"|"bt", env?:table} (table|nil) Options for loading the module: ---- - hash: (table) the hash of the file to load if it is already known. (defaults to `vim.loop.fs_stat({modpath})`) +---@param opts? {mode?: "b"|"t"|"bt", env?:table} (table|nil) Options for loading the module: --- - mode: (string) the mode to load the module with. "b"|"t"|"bt" (defaults to `nil`) --- - env: (table) the environment to load the module in. (defaults to `nil`) ---@see |luaL_loadfile()| @@ -210,7 +235,7 @@ end ---@private function Loader.load(modpath, opts) opts = opts or {} - local hash = opts.hash or uv.fs_stat(modpath) + local hash = Loader.get_hash(modpath) ---@type function?, string? local chunk, err @@ -301,7 +326,7 @@ function M.find(modname, opts) for _, pattern in ipairs(patterns) do local modpath = path .. pattern Loader._stats.find.stat = (Loader._stats.find.stat or 0) + 1 - local hash = uv.fs_stat(modpath) + local hash = Loader.get_hash(modpath) if hash then results[#results + 1] = { modpath = modpath, stat = hash, modname = modname } if not continue() then @@ -337,7 +362,7 @@ function M.find(modname, opts) return results end ---- Resets the topmods cache for the path, or all the paths +--- Resets the cache for the path, or all the paths --- if path is nil. ---@param path string? path to reset function M.reset(path) @@ -346,6 +371,9 @@ function M.reset(path) else Loader._indexed = {} end + + -- Path could be a directory so just clear all the hashes. + Loader._hashes = {} end --- Enables the experimental Lua module loader: @@ -399,7 +427,7 @@ function Loader.lsmod(path) for name, t in vim.fs.dir(path .. '/lua') do local modpath = path .. '/lua/' .. name -- HACK: type is not always returned due to a bug in luv - t = t or uv.fs_stat(modpath).type + t = t or Loader.get_hash(modpath).type ---@type string local topname local ext = name:sub(-4) |