Skip to main content

🔥 Developers

To make it easier for users to install your plugin, you can include a package spec in your repo.

Best Practices

  • If your plugin needs setup(), then create a simple lazy.lua file like this:

      return { "me/my-plugin", opts = {} }
  • Plugins that are pure lua libraries should be lazy-loaded with lazy = true.

    { "nvim-lua/plenary.nvim", lazy = true }
  • Only use dependencies if a plugin needs the dep to be installed AND loaded. Lua plugins/libraries are automatically loaded when they are require()d, so they don't need to be in dependencies.

    GOOD
    { "folke/todo-comments.nvim", opts = {} },
    { "nvim-lua/plenary.nvim", lazy = true },
    BAD
    {
    "folke/todo-comments.nvim",
    opts = {},
    -- This will always load plenary as soon as todo-comments loads,
    -- even when todo-comments doesn't use it.
    dependencies = { "nvim-lua/plenary.nvim", lazy = true },
    },
  • Inside a build function or *.lua build file, use coroutine.yield(msg:string|LazyMsg) to show progress.

  • Don't change the cwd in your build function, since builds run in parallel and changing the cwd will affect other builds.

Building

The spec build property can be one of the following:

  • fun(plugin: LazyPlugin): a function that builds the plugin.
  • *.lua: a Lua file that builds the plugin (like build.lua)
  • ":Command": a Neovim command
  • "rockspec": this will run luarocks make in the plugin's directory This is automatically set by the rockspec package source.
  • any other string will be run as a shell command
  • a list of any of the above to run multiple build steps
  • if no build is specified, but a build.lua file exists, that will be used instead.

Build functions and *.lua files run asynchronously in a coroutine. Use coroutine.yield(msg:string|LazyMsg) to show progress.

Yielding will also schedule the next coroutine.resume() to run in the next tick, so you can do long-running tasks without blocking Neovim.

---@class LazyMsg
---@field msg string
---@field level? number vim.log.levels.XXX

Use vim.log.levels.TRACE to only show the message as a status message for the task.

tip

If you need to know the directory of your build lua file, you can use:

local dir = vim.fn.fnamemodify(debug.getinfo(1, "S").source:sub(2), ":p:h")

Minit (Minimal Init)

lazy.nvim comes with some built-in functionality to help you create a minimal init for your plugin.

I mainly use this for testing and for users to create a repro.lua.

When running in headless mode, lazy.nvim will log any messages to the terminal. See opts.headless for more info.

minit will install/load all your specs and will always run an update as well.

Bootstrap

-- setting this env will override all XDG paths
vim.env.LAZY_STDPATH = ".tests"
-- this will install lazy in your stdpath
load(vim.fn.system("curl -s https://raw.githubusercontent.com/folke/lazy.nvim/main/bootstrap.lua"))()

Testing with Busted

This will add "lunarmodules/busted", configure hererocks and run busted.

Below is an example of how I use minit to run tests with busted in LazyVim.

tests/busted.lua
#!/usr/bin/env -S nvim -l

vim.env.LAZY_STDPATH = ".tests"
load(vim.fn.system("curl -s https://raw.githubusercontent.com/folke/lazy.nvim/main/bootstrap.lua"))()

-- Setup lazy.nvim
require("lazy.minit").busted({
spec = {
"LazyVim/starter",
"williamboman/mason-lspconfig.nvim",
"williamboman/mason.nvim",
"nvim-treesitter/nvim-treesitter",
},
})

To use this, you can run:

nvim -l ./tests/busted.lua tests

If you want to inspect the test environment, run:

nvim -u ./tests/busted.lua

repro.lua

repro.lua
vim.env.LAZY_STDPATH = ".repro"
load(vim.fn.system("curl -s https://raw.githubusercontent.com/folke/lazy.nvim/main/bootstrap.lua"))()

require("lazy.minit").repro({
spec = {
"stevearc/conform.nvim",
"nvim-neotest/nvim-nio",
},
})

-- do anything else you need to do to reproduce the issue

Then run it with:

nvim -u repro.lua