Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature: load handlers lazily #1442

Open
1 task done
hoofcushion opened this issue May 11, 2024 · 3 comments
Open
1 task done

feature: load handlers lazily #1442

hoofcushion opened this issue May 11, 2024 · 3 comments
Assignees
Labels
enhancement New feature or request

Comments

@hoofcushion
Copy link

hoofcushion commented May 11, 2024

Did you check the docs?

  • I have read all the lazy.nvim docs

Is your feature request related to a problem? Please describe.

When too many lazy handlers are added, lazy.nvim will take too long to enable all of them.
I had hundreds of keys handlers and that slow startup time a lot.
But these handlers are not possible to trigger before some specific events, and it's a waste of time for starting these handlers in the startup.

e.g:
keys handler is always after "SafeState", because user can't type any command before that.
cmd handler is always after "CmdlineChange" since user needs to type one character for run a command or do a completion (or "CmdlineEnter" if user uses other way to do that).
ft handler is always after "BufEnter" ("BufNewFile" or "BufReadPost" when starting Neovim with a file parameter).
And most of event handlers are after "SafeState" too.

---@param plugin LazyPlugin
function M.resolve(plugin)
local Plugin = require("lazy.core.plugin")
plugin._.handlers = {}
for type, handler in pairs(M.handlers) do
if plugin[type] then
plugin._.handlers[type] = handler:_values(Plugin.values(plugin, type, true), plugin)
end
end
end

---@param plugin LazyPlugin
function M.enable(plugin)
if not plugin._.loaded then
if not plugin._.handlers then
M.resolve(plugin)
end
for type in pairs(plugin._.handlers or {}) do
M.handlers[type]:add(plugin)
end
end
end

Describe the solution you'd like

I write a demo outside the lazy.nvim in my configuration.
You can try this code before lazy.setup(), it will override the original handler.

local Handler=require("lazy.core.handler")
local Plugin=require("lazy.core.plugin")
local delay_handlers={
 keys="SafeState",
 cmd="CmdlineEnter",
 ft={"BufEnter","BufReadPost","BufNewFile"},
}
local delay={}
function Handler.resolve(plugin)
 plugin._.handlers={}
 local handlers=plugin._.handlers
 for type in pairs(Handler.handlers) do
  if plugin[type] then
   handlers[type]=delay
  end
 end
end
local startup_events={
 ["user"]=true,
 ["sourcecmd"]=true,
 ["sourcepost"]=true,
 ["sourcepre"]=true,
 ["bufenter"]=true,
 ["bufreadpost"]=true,
 ["bufnewfile"]=true,
 ["vimenter"]=true,
 ["uienter"]=true,
 ["safestate"]=true,
}
local function search_fn(v)
 return startup_events[v.event:lower()]
end
---@param plugin LazyPlugin
function Handler.enable(plugin)
 if plugin._.loaded then
  return
 end
 if not plugin._.handlers then
  Handler.resolve(plugin)
 end
 for type in pairs(plugin._.handlers or {}) do
  local handlers=plugin._.handlers
  if type=="event" then
   local handler=Handler.handlers[type]
   local events=handler:_values(Plugin.values(plugin,type,true),plugin)
   handlers[type]=events
   if vim.tbl_contains(events,search_fn,{predicate=true}) then
    handler:add(plugin)
   else
    vim.api.nvim_create_autocmd("SafeState",{
     once=true,
     callback=function()
      handler:add(plugin)
     end,
    })
   end
   goto next
  end
  vim.api.nvim_create_autocmd(delay_handlers[type],{
   once=true,
   callback=function()
    local handler=Handler.handlers[type]
    handlers[type]=handler:_values(Plugin.values(plugin,type,true),plugin)
    handler:add(plugin)
   end,
  })
  ::next::
 end
end

Describe alternatives you've considered

My solution is a bit extreme, ft and event part may not very stable, but the keys and cmd handler is very stable for SafeState and CmdlineEnter event. Simply loading these two events lazily can reduce most of the load time.

Additional context

I installed 109 plugins in total, here's how this optimization improves startup time:

before

image

after

image

Note the handlers is taking 18.53ms before, and now it only takes 1.44ms, it's about 10 time faster (the improvement base on how many handlers you had in total)

@hoofcushion hoofcushion added the enhancement New feature or request label May 11, 2024
@Shougo
Copy link

Shougo commented May 12, 2024

I think cmd should not be loaded on CmdlineEnter or CmdlineChanged.
Because the commands may be called from cmd-mappings. It does not call command line events.

@hoofcushion
Copy link
Author

You are right. User command can be called before CmdlineEnter, but I think most User command will be called after CmdlineEnter, There could be a way to balance all this.

@hoofcushion
Copy link
Author

The keys is always called after SafeState, I think that should be fine.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

3 participants