diff --git a/README.md b/README.md index 60ab7df..cf2c26c 100644 --- a/README.md +++ b/README.md @@ -500,13 +500,25 @@ $ nvim --headless "+Lazy! sync" +qa - **plugins**: a list of plugin names to run the operation on - **concurrency**: limit the `number` of concurrently running tasks -If you want to display the number of plugins on your dashboard, you can use -this simple API: +Stats API (`require("lazy").stats()`): + + ```lua -local plugins = require("lazy").stats().count +{ + -- startuptime in milliseconds till UIEnter + startuptime = 0, + -- when true, startuptime is the accurate cputime for the Neovim process. (Linux & Macos) + -- this is more accurate than `nvim --startuptime`, and as such will be slightly higher + -- when false, startuptime is calculated based on a delta with a timestamp when lazy started. + startuptime_cputime = false, + count = 0, -- total number of plugins + loaded = 0, -- number of loaded plugins +} ``` + + **lazy.nvim** provides a statusline component that you can use to show the number of pending updates. Make sure to enable `config.checker.enabled = true` to make this work. @@ -543,6 +555,8 @@ The following user events will be triggered: - **LazyLog**: after running log - **LazyReload**: triggered by change detection after reloading plugin specs - **VeryLazy**: triggered after `LazyDone` and processing `VimEnter` auto commands +- **LazyVimStarted**: triggered after `UIEnter` when `require("lazy").stats().startuptime` has been calculated. + Useful to update the startuptime on your dashboard. ## 🔒 Lockfile `lazy-lock.json` diff --git a/lua/lazy/core/config.lua b/lua/lazy/core/config.lua index 1da607b..41a6f65 100644 --- a/lua/lazy/core/config.lua +++ b/lua/lazy/core/config.lua @@ -200,6 +200,12 @@ function M.setup(spec, opts) if M.headless then require("lazy.view.commands").setup() else + vim.api.nvim_create_autocmd("UIEnter", { + callback = function() + require("lazy.stats").on_ui_enter() + end, + }) + vim.api.nvim_create_autocmd("User", { pattern = "VeryLazy", once = true, diff --git a/lua/lazy/docs.lua b/lua/lazy/docs.lua index 142b517..08a4b71 100644 --- a/lua/lazy/docs.lua +++ b/lua/lazy/docs.lua @@ -126,6 +126,7 @@ function M.update() config = config:gsub("%s*debug = false.\n", "\n") M.save({ bootstrap = M.extract("lua/lazy/init.lua", "function M%.bootstrap%(%)\n(.-)\nend"), + stats = M.extract("lua/lazy/stats.lua", "\nM%._stats = ({.-\n})"), config = config, spec = Util.read_file("lua/lazy/example.lua"), commands = M.commands(), diff --git a/lua/lazy/init.lua b/lua/lazy/init.lua index ae4309b..b1b6025 100644 --- a/lua/lazy/init.lua +++ b/lua/lazy/init.lua @@ -1,9 +1,11 @@ ---@type LazyCommands local M = {} +M._start = 0 ---@param spec LazySpec Should be a module name to load, or a plugin spec ---@param opts? LazyConfig function M.setup(spec, opts) + M._start = M._start == 0 and vim.loop.hrtime() or M._start if vim.g.lazy_did_setup then return vim.notify( "Re-sourcing your config is not supported with lazy.nvim", @@ -62,14 +64,7 @@ function M.setup(spec, opts) end function M.stats() - local ret = { count = 0, loaded = 0 } - for _, plugin in pairs(require("lazy.core.config").plugins) do - ret.count = ret.count + 1 - if plugin._.loaded then - ret.loaded = ret.loaded + 1 - end - end - return ret + return require("lazy.stats").stats() end function M.bootstrap() diff --git a/lua/lazy/stats.lua b/lua/lazy/stats.lua new file mode 100644 index 0000000..285b4e6 --- /dev/null +++ b/lua/lazy/stats.lua @@ -0,0 +1,56 @@ +local M = {} + +---@class LazyStats +M._stats = { + -- startuptime in milliseconds till UIEnter + startuptime = 0, + -- when true, startuptime is the accurate cputime for the Neovim process. (Linux & Macos) + -- this is more accurate than `nvim --startuptime`, and as such will be slightly higher + -- when false, startuptime is calculated based on a delta with a timestamp when lazy started. + startuptime_cputime = false, + count = 0, -- total number of plugins + loaded = 0, -- number of loaded plugins +} + +function M.on_ui_enter() + if not M.C then + pcall(function() end) + end + + local ok = pcall(function() + local ffi = require("ffi") + ffi.cdef([[ + typedef long time_t; + typedef int clockid_t; + + typedef struct timespec { + time_t tv_sec; /* seconds */ + long tv_nsec; /* nanoseconds */ + } nanotime; + int clock_gettime(clockid_t clk_id, struct timespec *tp); + ]]) + local pnano = assert(ffi.new("nanotime[?]", 1)) + local CLOCK_PROCESS_CPUTIME_ID = jit.os == "OSX" and 12 or 2 + ffi.C.clock_gettime(CLOCK_PROCESS_CPUTIME_ID, pnano) + M._stats.startuptime = tonumber(pnano[0].tv_sec) / 1e6 + tonumber(pnano[0].tv_nsec) / 1e6 + M._stats.startuptime_cputime = true + end) + if not ok then + M._stats.startuptime = (vim.loop.hrtime() - require("lazy")._start) / 1e6 + end + vim.cmd([[do User LazyVimStarted]]) +end + +function M.stats() + M._stats.count = 0 + M._stats.loaded = 0 + for _, plugin in pairs(require("lazy.core.config").plugins) do + M._stats.count = M._stats.count + 1 + if plugin._.loaded then + M._stats.loaded = M._stats.loaded + 1 + end + end + return M._stats +end + +return M diff --git a/lua/lazy/view/render.lua b/lua/lazy/view/render.lua index 34e4aaa..d3610e1 100644 --- a/lua/lazy/view/render.lua +++ b/lua/lazy/view/render.lua @@ -491,6 +491,24 @@ function M:details(plugin) end function M:profile() + local stats = require("lazy.stats").stats() + local ms = (math.floor(stats.startuptime * 100 + 0.5) / 100) + self:append("Startuptime: ", "LazyH2"):append(ms .. "ms", "Number"):nl():nl() + if stats.startuptime_cputime then + self:append("Based on the actual CPU time of the Neovim process till "):append("UIEnter", "LazySpecial") + self:append("."):nl() + self:append("This is more accurate than ") + self:append("`nvim --startuptime`", "@text.literal.markdown_inline") + self:append(".") + else + self:append("An accurate startuptime based on the actual CPU time of the Neovim process is not available."):nl() + self + :append("Startuptime is instead based on a delta with a timestamp when lazy started till ") + :append("UIEnter", "LazySpecial") + self:append(".") + end + self:nl():nl() + self:append("Profile", "LazyH2"):nl():nl() self :append("You can press ") @@ -505,6 +523,8 @@ function M:profile() self:nl() + self:nl():nl() + ---@param a LazyProfile ---@param b LazyProfile local function sort(a, b)