Lazy neovim plugins loading
A few months ago I changed my neovim configuration and started using the Lazy package manager. The thing is that Lazy is fantastic at installing plugins. LSP, neotest, lazydev, telescope and all the goodies from the wonderful neovim community are easy to install and add.
So it is not surprising that I ended up with 37 plugins installed and loaded every time I run nvim
. While none of the plugins are particularly heavy and the
startup time is below 100 ms. It is still interesting to find out why is the
package manager is named Lazy.
Make Lazy.nvim lazy again
folke/lazy.nvim already advertises the lazy loading feature
- π Automatic lazy-loading of Lua modules and lazy-loading on events, commands, filetypes, and key mappings
The problem is that most of the configuration examples do not care. After all, loading on startup doesn’t break anything, so it is a good default after all. This leaves the exercise of lazily loading stuff to the curious user.
Lazy itself provides all the necessary hints. Just type :Lazy
and check the
output. In my case nvim
loads 5 mandatory plugins, tht can’t be postponed without breaking the editor.
Total: 37 plugins
Loaded (5)
β gruvbox 3.57ms ξ« start
β lazy.nvim 5.56ms ο‘ init.lua
β lualine.nvim 6.19ms ξͺ VeryLazy
β nvim-lastplace 0.57ms ξ« start
β vim-sleuth 0.47ms ξ« start
VeryLazy
That is actually a cool name. In short, lazy allows you to start a plugin in a case of an event. VeryLazy is specific one. Documentation says
VeryLazy: triggered after LazyDone and processing VimEnter auto commands
VeryLazy plugins are not
counted in the startup time. Just press P
in :Lazy
view to compare
lualine.nvim 3.82ms ξͺ VeryLazy
Startuptime: 36.28ms
Based on the actual CPU time of the Neovim process till UIEnter.
This is more accurate than `nvim --startuptime`.
LazyStart 14.69ms
LazyDone 28.89ms (+14.2ms)
UIEnter 36.28ms (+7.39ms)
β ξ« startuptime 36.28ms
β ξͺ VeryLazy 5.33ms
β ο lualine.nvim 4.06ms
And as lualine.nvim 4.11ms ξ« start
Startuptime: 47.56ms
Based on the actual CPU time of the Neovim process till UIEnter.
This is more accurate than `nvim --startuptime`.
LazyStart 12.86ms
LazyDone 37.18ms (+24.32ms)
UIEnter 47.56ms (+10.38ms)
β ξ« startuptime 47.56ms
β ξͺ VeryLazy 1.56ms
VeryLazy
is then the simplest optimization. But the plugin will still load on every startup. So it is indeed a simple but lazy optimization. With a cool name.
Events
Lazy Loading offers an excellent place to start. Unfortunately a bit short on examples.
event: Lazy-load on event. Events can be specified as BufEnter or with a pattern like BufEnter *.lua
The value can be a string, an array of strings, or a function or a patterns that allows you to start the plugin after some action. Here are gitsigns
and nvim-lint
plugins configured to get loaded after a buffer is read. As neither is language
dependent, there isn’t much to do.
β gitsigns.nvim ξͺ BufReadPost
β nvim-lint ξͺ BufReadPost ξͺ InsertLeave ξͺ BufWritePost
~/.config/nvim/lua/plugins/gitsigns.lua
contains
return {
"lewis6991/gitsigns.nvim",
event = { "BufReadPost" },
}
File types
I tend to use a language agnostic tools, so have a lspconfig/neotest/conform
combo instead of go.nvim
. Nothing against go.nvim
, it is a great plugin I use as a base for my configuration. This means that most of the plugins I have can’t be restricted to a file type. There are notable exceptions though.
β kulala.nvim ο http
β lazydev.nvim ο lua
β luvit-meta
Lazydev is for Lua files, and since it’s a plugin from folke himself, it would be odd to not to suggest a lazy loading by default.
return {
{
"folke/lazydev.nvim",
-- only load on lua files
ft = "lua",
},
{ -- optional `vim.uv` typings
"Bilal2453/luvit-meta",
lazy = true
},
}
lazy=true
options is handy for library plugins. Such plugin will be loaded when needed by other plugin.
Command
Since I use some commands only via neovim’s command line, there’s nothing easier
than loading the plugin after typing it on a command line. This is a case of
vim-fugitive
and :G
command. Lazy registers the command and loads the
plugin when it is really needed.
β vim-fugitive ξ― G
return {
"tpope/vim-fugitive",
cmd = "G",
}
Keys
Some other plugins have a defined shortcut(s) to activate. In my case it’s the
powerful Telescope plugin. Lazy can also handle this scenario too. The plugin
can be activated with a command or a shortcut. This form expects the actual
keymap to be activated inside config
function of a plugin.
β neotest ο <leader>t
β telescope.nvim ξ― Telescope ο <leader>sr ο <leader>s.
return {
{
"nvim-telescope/telescope.nvim",
cmd = "Telescope",
keys = {
{"<leader><leader>"},
{"<leader>sh"},
},
config = function()
-- set <leader>sh here
vim.keymap.set("n", "<leader>sh", ...)
end
},
}
Conclusion
Was it worth it? I reduced the startup time of an empty Neovim from 90ms to ~40ms, where opening a Go file increases the time to 80ms. So in terms of numbers it was not. On the other hand, it was cool and fun, and I hope you’ve enjoyed reading this.
Fonts
FiraCode Nerd Font under SIL OPEN FONT LICENSE Version 1.1, converted to woff2 format by cloudconvert.com