lazy.nvim optimization
npx skills add https://github.com/kriscard/kriscard-claude-plugins --skill Lazy.nvim Optimization
Skill 文档
Lazy.nvim Optimization
Optimize Neovim startup time and runtime performance through effective lazy-loading strategies and plugin configuration with lazy.nvim.
Understanding Startup Performance
Neovim startup involves several phases:
- Initialization – Load init.lua, set options
- Plugin loading – Load plugin managers and plugins
- Plugin configuration – Run setup() functions
- UI render – Display first buffer
Target startup times:
- Excellent: < 30ms
- Good: 30-50ms
- Acceptable: 50-100ms
- Needs optimization: > 100ms
Measure with:
nvim --startuptime startup.log
Lazy-Loading Strategies
When to Lazy-Load
Always lazy-load:
- File explorers (cmd: “NvimTreeToggle”)
- Git interfaces (cmd: “Neogit”, “LazyGit”)
- Debuggers (keys for debug actions)
- Language-specific plugins (ft: “rust”, “go”)
- Tools used occasionally (cmd or keys)
Don’t lazy-load:
- Colorschemes (priority: 1000)
- nvim-treesitter (needed for syntax immediately)
- Core UI (statusline, which-key if showing on startup)
- LSP base setup (though servers can lazy-load by filetype)
Conditionally lazy-load:
- Completion (event: “InsertEnter”)
- Git decorations (event: “BufReadPost”)
- Auto-pairs (event: “InsertEnter”)
- Telescope (cmd: “Telescope” or keys)
Loading Triggers
By Command
Load when command is first used:
{
"nvim-telescope/telescope.nvim",
cmd = "Telescope",
}
Use for: Plugins with clear command interfaces
Multiple commands:
cmd = { "Telescope", "Tele" }
By Keymap
Load when keymap is pressed:
{
"nvim-tree/nvim-tree.lua",
keys = {
{ "<leader>e", "<cmd>NvimTreeToggle<cr>", desc = "Toggle file tree" },
},
}
Use for: Plugins accessed via keybindings
Complex keymaps:
keys = {
{ "<leader>ff", function() require("telescope.builtin").find_files() end, desc = "Find files" },
{ "<leader>fg", "<cmd>Telescope live_grep<cr>", desc = "Live grep", mode = "n" },
}
By Filetype
Load for specific file types:
{
"rust-lang/rust.vim",
ft = { "rust" },
}
Use for: Language-specific tooling
Multiple filetypes:
ft = { "javascript", "typescript", "javascriptreact", "typescriptreact" }
By Event
Load on Neovim events:
{
"lewis6991/gitsigns.nvim",
event = "BufReadPost",
}
Common events:
VeryLazy– After startup complete (defer non-critical plugins)BufReadPost– After opening a bufferBufNewFile– When creating new fileInsertEnter– Entering insert modeCmdlineEnter– Opening command lineLspAttach– When LSP attaches
Multiple events:
event = { "BufReadPost", "BufNewFile" }
Lazy Flag
Prevent automatic loading:
{
"nvim-lua/plenary.nvim",
lazy = true, -- Only load when required as dependency
}
Use for: Dependency-only plugins
Optimization Patterns
Pattern 1: Telescope with FZF Native
{
"nvim-telescope/telescope.nvim",
cmd = "Telescope",
keys = {
{ "<leader>ff", "<cmd>Telescope find_files<cr>" },
{ "<leader>fg", "<cmd>Telescope live_grep<cr>" },
},
dependencies = {
"nvim-lua/plenary.nvim",
{
"nvim-telescope/telescope-fzf-native.nvim",
build = "make",
},
},
config = function()
local telescope = require("telescope")
telescope.setup({})
telescope.load_extension("fzf")
end,
}
Key optimizations:
- Lazy-load on cmd and keys
- FZF native for faster sorting
- plenary.nvim loads automatically as dependency
Pattern 2: LSP with Filetype Loading
{
"neovim/nvim-lspconfig",
ft = { "lua", "python", "typescript", "rust" }, -- Load only for these filetypes
dependencies = {
"williamboman/mason.nvim",
"williamboman/mason-lspconfig.nvim",
},
config = function()
-- Configure LSP servers
end,
}
Alternative: Load on LspAttach event
Pattern 3: Completion on Insert
{
"hrsh7th/nvim-cmp",
event = "InsertEnter", -- Load when entering insert mode
dependencies = {
"hrsh7th/cmp-nvim-lsp",
"hrsh7th/cmp-buffer",
"L3MON4D3/LuaSnip",
},
config = function()
require("cmp").setup({})
end,
}
Optimization: No startup cost, only loads when actually needed
Pattern 4: Deferred UI Enhancement
{
"folke/todo-comments.nvim",
event = "VeryLazy", -- Defer until after startup
config = function()
require("todo-comments").setup({})
end,
}
Use for: Nice-to-have features that aren’t immediately visible
Pattern 5: Conditional Loading
{
"iamcco/markdown-preview.nvim",
ft = "markdown",
build = function()
vim.fn["mkdp#util#install"]()
end,
cond = function()
return vim.fn.executable("node") == 1 -- Only if Node.js available
end,
}
Use for: Plugins with external dependencies
Profiling Tools
Built-in Profiling
Startup time:
nvim --startuptime startup.log
# View with: nvim startup.log
Lazy.nvim profile:
:Lazy profile
Shows:
- Load time per plugin
- Total time
- Event triggers
Analyzing Startup Log
Look for:
- Slow sourcing – Files taking > 10ms
- Plugin init – Plugins loading at startup
- Heavy configs – setup() taking > 5ms
Example analysis:
020.123 000.050: sourcing ~/.config/nvim/init.lua
025.456 005.333: require('plugins.telescope') â Too slow!
030.789 005.333: setup telescope â Should be deferred
Fix: Lazy-load telescope with cmd or keys
Common Bottlenecks
Problem 1: Loading all plugins at startup
-- Bad
return {
"plugin/name",
}
-- Good
return {
"plugin/name",
cmd = "PluginCommand",
}
Problem 2: Heavy setup() at startup
-- Bad
return {
"plugin/name",
config = function()
-- Runs at startup!
require("plugin").setup({
-- Heavy configuration
})
end,
}
-- Good
return {
"plugin/name",
event = "VeryLazy", -- Defer setup
config = function()
require("plugin").setup({})
end,
}
Problem 3: Synchronous operations
-- Bad
vim.fn.system("git status") -- Blocks startup
-- Good
vim.defer_fn(function()
vim.fn.system("git status")
end, 100) -- Defer 100ms
Performance Checklist
Startup optimization:
- Colorscheme has
priority = 1000 - Only essential plugins load at startup
- File explorers load on cmd/keys
- Git interfaces load on cmd/keys
- Completion loads on
InsertEnter - Language plugins load on filetype
- UI enhancements load on
VeryLazy
Runtime optimization:
- Telescope uses fzf-native extension
- Treesitter parsers installed only for used languages
- LSP servers only for filetypes you use
- Heavy plugins have lazy-loading
- No plugins duplicating built-in 0.10+ features
Config optimization:
- No synchronous system calls at startup
- setup() functions only run when plugins load
- No heavy computation in init.lua
- Keymaps defined with plugin lazy-loading
- Auto-commands use specific events, not “*”
Lazy.nvim Features
Lockfile
Lazy.nvim maintains lazy-lock.json:
{
"telescope.nvim": {
"branch": "master",
"commit": "abc123"
}
}
Benefits:
- Reproducible plugin versions
- Controlled updates
- Easy rollback
Workflow:
:Lazy update " Update all plugins
:Lazy restore " Restore to lockfile versions
Commit lockfile to version control
Profile View
Access with :Lazy profile:
Shows:
- Load time per plugin
- Load reason (cmd, keys, event)
- Total startup time
Use to identify:
- Plugins loading unnecessarily
- Slow plugin configurations
- Missing lazy-loading opportunities
Lazy Commands
:Lazy " Open UI
:Lazy update " Update all plugins
:Lazy sync " Install missing + update + clean
:Lazy clean " Remove unused plugins
:Lazy profile " Show startup profile
:Lazy log " Show recent changes
:Lazy restore " Restore to lockfile
Advanced Techniques
Lazy-Load LSP Servers by Filetype
Instead of loading all LSP at once:
-- Create filetype-specific LSP files
-- lua/plugins/lsp-lua.lua
return {
"neovim/nvim-lspconfig",
ft = "lua",
config = function()
require("lspconfig").lua_ls.setup({})
end,
}
-- lua/plugins/lsp-typescript.lua
return {
"neovim/nvim-lspconfig",
ft = { "typescript", "javascript" },
config = function()
require("lspconfig").tsserver.setup({})
end,
}
Benefit: LSP only loads for languages you actually edit
Smart Event Combinations
{
"plugin/name",
event = { "BufReadPost", "BufNewFile" },
cond = function()
-- Additional condition
return vim.fn.argc() > 0 -- Only if files provided
end,
}
Lazy-Load UI Enhancements
{
"folke/noice.nvim",
event = "VeryLazy",
init = function()
-- Set options before plugin loads
vim.o.cmdheight = 0
end,
config = function()
require("noice").setup({})
end,
}
Troubleshooting Slow Startups
Step 1: Measure Baseline
nvim --startuptime startup.log
Check total time at bottom of log.
Step 2: Identify Slow Plugins
Look for entries > 10ms:
050.123 015.000: require('plugins.slow-plugin')
Step 3: Add Lazy-Loading
For each slow plugin, add appropriate trigger:
- cmd for command-based plugins
- keys for keymap-based plugins
- ft for language-specific plugins
- event for everything else
Step 4: Verify Improvement
nvim --startuptime startup-after.log
Compare total times.
Step 5: Use Lazy Profile
:Lazy profile
Verify plugins only load when expected.
Additional Resources
Reference Files
For detailed information, consult:
references/lazy-loading-decision-tree.md– Decision tree for choosing lazy-loading strategyreferences/profiling-guide.md– Advanced profiling techniques
Example Files
Working examples in examples/:
optimized-config.lua– Complete optimized lazy.nvim configuration
Quick Reference
Loading triggers:
cmd– Load on commandkeys– Load on keymapft– Load on filetypeevent– Load on Neovim eventlazy = true– Never auto-load
Events for deferred loading:
VeryLazy– After startup (defer non-critical plugins)BufReadPost– After opening bufferInsertEnter– Entering insert modeLspAttach– LSP attached
Profiling commands:
nvim --startuptime startup.log # Startup profiling
:Lazy profile # Plugin load times
Target startup time: < 50ms
Apply these strategies to achieve fast, responsive Neovim with full plugin functionality.