Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/nvim-neorocks/lz.n
🦥 A dead simple lazy-loading Lua library for Neovim plugins.
https://github.com/nvim-neorocks/lz.n
lazy-loading neovim neovim-plugin nvim nvim-plugin plugin
Last synced: 2 days ago
JSON representation
🦥 A dead simple lazy-loading Lua library for Neovim plugins.
- Host: GitHub
- URL: https://github.com/nvim-neorocks/lz.n
- Owner: nvim-neorocks
- License: gpl-2.0
- Created: 2024-01-05T13:38:49.000Z (11 months ago)
- Default Branch: master
- Last Pushed: 2024-11-03T01:54:11.000Z (13 days ago)
- Last Synced: 2024-11-06T19:14:56.462Z (10 days ago)
- Topics: lazy-loading, neovim, neovim-plugin, nvim, nvim-plugin, plugin
- Language: Lua
- Homepage:
- Size: 306 KB
- Stars: 101
- Watchers: 1
- Forks: 7
- Open Issues: 1
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- Contributing: CONTRIBUTING.md
- License: LICENSE
Awesome Lists containing this project
README
# :sloth: lz.n
[![Neovim][neovim-shield]][neovim-url]
[![Lua][lua-shield]][lua-url]
[![LuaRocks][luarocks-shield]][luarocks-url]A dead simple lazy-loading Lua library for Neovim plugins.
It is intended to be used
- by users of plugin managers that don't provide a convenient API for lazy-loading.
- by plugin managers, to provide a convenient API for lazy-loading.> [!NOTE]
>
> **Should I lazy-load plugins?**
>
> It should be a plugin author's responsibility to ensure their plugin doesn't
> unnecessarily impact startup time, not yours!
>
> See [our "DO's and DONT's" guide for plugin developers](https://github.com/nvim-neorocks/nvim-best-practices?tab=readme-ov-file#sleeping_bed-lazy-loading).
>
> Regardless, the current status quo is horrible, and some authors may
> not have the will or capacity to improve their plugins' startup impact.
>
> If you find a plugin that takes too long to load,
> or worse, forces you to load it manually at startup with a
> call to a heavy `setup` function,
> consider opening an issue on the plugin's issue tracker.## :star2: Features
- API for lazy-loading plugins on:
- Events (`:h autocmd-events`)
- `FileType` events
- Key mappings
- User commands
- Colorscheme events
- Works with:
- Neovim's built-in `:h packpath` (`:h packadd`)
- Any plugin manager that supports manually lazy-loading
plugins by name
- Configurable in multiple files## :moon: Introduction
`lz.n` provides abstractions for lazy-loading Neovim plugins,
with an API that is loosely based on [`lazy.nvim`](https://github.com/folke/lazy.nvim),
but reduced down to the very basics required for lazy-loading only.### :milky_way: Philosophy
`lz.n` is designed based on the UNIX philosophy: Do one thing well.
### :zzz: Comparison with `lazy.nvim`
- `lz.n` is **not a plugin manager**, but focuses **on lazy-loading only**.
It is intended to be used with (or by) a plugin manager.
- The feature set is minimal, to [reduce code complexity](https://grugbrain.dev/)
and simplify the API.
For example, the following `lazy.nvim` features are **out of scope**:
- Merging multiple plugin specs for a single plugin
(primarily intended for use by Neovim distributions).
- `lazy.vim` completely disables and takes over Neovim's
built-in loading mechanisms, including
adding a plugin's API (`lua`, `autoload`, ...)
to the runtimepath.
`lz.n` doesn't.
Its only concern is plugin initialization, which is
the bulk of the startup overhead.
- Automatic lazy-loading of Lua modules on `require`.
- Automatic lazy-loading of colorschemes.
`lz.n` provides a `colorscheme` handler in the plugin spec.
- Heuristics for determining a `main` module and automatically calling
a `setup()` function.
- Heuristics for loading plugins on `require`.
You can use [`lzn-auto-require`](https://github.com/horriblename/lzn-auto-require)
for that.
- Plugin spec fields (like `lazy.nvim`'s `dependencies`)
for influencing the order in which plugins are loaded.
See also: [Plugin dependencies](#plugin-dependencies).
- Abstractions for plugin configuration with an `opts` table.
`lz.n` provides simple hooks that you can use to specify
when to load configurations.
- Features related to plugin management.
- Profiling tools.
- UI.
- Some configuration options are different.## :pencil: Requirements
- `Neovim >= 0.10.0`
## :wrench: Configuration
You can override the function used to load plugins.
`lz.n` has the following default:```lua
vim.g.lz_n = {
---@type fun(name: string)
load = vim.cmd.packadd,
}
```## :books: Usage
```lua
require("lz.n").load(plugins)
```- **plugins**: this should be a `table` or a `string`
- `table`:
- A list with your [Plugin Specs](#plugin-spec)
- Or a single plugin spec.
- `string`: a Lua module name that contains your [Plugin Spec](#plugin-spec).
See [Structuring Your Plugins](#structuring-your-plugins)> [!TIP]
>
> - You can call `load()` as you would call `lazy.nvim`'s `setup()`.
> Or, you can also use it to register individual plugin specs for lazy
> loading.
>
> - See also: [`:h lz.n`](./doc/lz.n.txt)> [!IMPORTANT]
>
> Since merging configs is out of scope, calling `load()` with conflicting
> plugin specs is not supported.### Plugin spec
| Property | Type | Description | `lazy.nvim` equivalent |
|------------------|------|-------------|-----------------------|
| **[1]** | `string` | The plugin's name (not the module name). This is what is passed to the `load(name)` function. | `name`[^1] |
| **enabled** | `boolean?` or `fun():boolean` | When `false`, or if the `function` returns false, then this plugin will not be included in the spec. | `enabled` |
| **beforeAll** | `fun(lz.n.Plugin)?` | Always executed before any plugins are loaded. | `init` |
| **before** | `fun(lz.n.Plugin)?` | Executed before a plugin is loaded. | None |
| **after** | `fun(lz.n.Plugin)?` | Executed after a plugin is loaded. | `config` |
| **event** | `string?` or `{event?:string\|string[], pattern?:string\|string[]}\` or `string[]` | Lazy-load on event. Events can be specified as `BufEnter` or with a pattern like `BufEnter *.lua`. | `event` |
| **cmd** | `string?` or `string[]` | Lazy-load on command. | `cmd` |
| **ft** | `string?` or `string[]` | Lazy-load on filetype. | `ft` |
| **keys** | `string?` or `string[]` or `lz.n.KeysSpec[]` | Lazy-load on key mapping. | `keys` |
| **colorscheme** | `string?` or `string[]` | Lazy-load on colorscheme. | None. `lazy.nvim` lazy-loads colorschemes automatically[^2]. |
| **lazy** | `boolean?` | Lazy-load manually, e.g. using `trigger_load`. | `lazy` |
| **priority** | `number?` | Only useful for **start** plugins (not lazy-loaded) to force loading certain plugins first. Default priority is `50`. | `priority` |
| **load** | `fun(string)?` | Can be used to override the `vim.g.lz_n.load()` function for an individual plugin. | None. |[^1]: In contrast to `lazy.nvim`'s `name` field, a `lz.n.PluginSpec`'s `name` *is not optional*.
This is because `lz.n` is not a plugin manager and needs to be told which
plugins to load.
[^2]: The reason this library doesn't lazy-load colorschemes automatically is that
it would have to know where the plugin is installed in order to determine
which plugin to load.### User events
- `DeferredUIEnter`: Triggered when `load()` is done and after `UIEnter`.
Can be used as an `event` to lazy-load plugins that are not immediately needed
for the initial UI[^3].[^3]: This is equivalent to `lazy.nvim`'s `VeryLazy` event.
### Plugin dependencies
This library does not provide a `lz.n.PluginSpec` field like `lazy.nvim`'s `dependencies`.
The rationale behind this is that you shouldn't need it.
Instead, you can utilise the [`trigger_load`](#trigger_load) function
in a `before` or `after` hook.However, we generally do not recommend this approach.
Most plugins primarily rely on the Lua libraries of other plugins,
which can be added to the `:h package.path` without any noticeable
impact on startup time.Relying on another plugin's `plugin` or `after/plugin` scripts is considered a bug,
as Neovim's built-in loading mechanism does not guarantee initialisation order.
Requiring users to manually call a `setup` function [is an anti pattern](https://github.com/nvim-neorocks/nvim-best-practices?tab=readme-ov-file#zap-initialization).
Forcing users to think about the order in which they load plugins that
extend or depend on each other is even worse. We strongly suggest opening
an issue or submitting a PR to fix this upstream.
However, if you're looking for a temporary workaround, you can use
`trigger_load` in a `before` or `after` hook, or bundle the relevant plugin configurations.> [!NOTE]
>
> - This does not work with plugins that rely on `after/plugin`, such as many
> nvim-cmp sources, because Neovim's `:h packadd` does not source
> `after/plugin` scripts after startup has completed.
> We recommend bundling such plugins with their extensions, or sourcing
> the `after` scripts manually.
> In the spirit of the UNIX philosophy, `lz.n` does not provide any functions
> for sourcing plugin scripts. For sourcing `after/plugin` directories
> manually, you can use [`rtp.nvim`](https://github.com/nvim-neorocks/rtp.nvim).
> [Here is an example](https://github.com/nvim-neorocks/lz.n/wiki/lazy%E2%80%90loading-nvim%E2%80%90cmp-and-its-extensions).
>
> - Why not provide a `dependencies` field for plugins that don't adhere
> to best practices?
> Because it's unnecessary. By using the `before` and `after` hooks,
> you gain full control over when to load another plugin, without cluttering
> the API.> [!TIP]
>
> We recommend [care.nvim](https://max397574.github.io/care.nvim/)
> as a modern alternative to nvim-cmp.### Examples
```lua
require("lz.n").load {
{
"neo-tree.nvim",
keys = {
-- Create a key mapping and lazy-load when it is used
{ "ft", "Neotree toggle", desc = "NeoTree toggle" },
},
after = function()
require("neo-tree").setup()
end,
},
{
"crates.nvim",
-- lazy-load when opening a toml file
ft = "toml",
},
{
"sweetie.nvim",
-- lazy-load when setting the `sweetie` colorscheme
colorscheme = "sweetie",
},
{
"vim-startuptime",
cmd = "StartupTime",
before = function()
-- Configuration for plugins that don't force you to call a `setup` function
-- for initialization should typically go in a `before`
--- or `beforeAll` function.
vim.g.startuptime_tries = 10
end,
},
{
"care.nvim",
-- load care.nvim on InsertEnter
event = "InsertEnter",
},
{
"dial.nvim",
-- lazy-load on keys. -- Mode is `n` by default.
keys = { "", { "", mode = "n" } },
},
}
```
paq-nvim example
```lua
require "paq" {
{ "nvim-telescope/telescope.nvim", opt = true }
{ "NTBBloodBatch/sweetie.nvim", opt = true }
}require("lz.n").load {
{
"telescope.nvim",
cmd = "Telescope",
},
{
"sweetie.nvim",
colorscheme = "sweetie",
},
}
``````nix
programs.neovim = {
enable = true;
plugins = with pkgs.vimPlugins [
lz-n
{
plugin = pkgs.vimPlugins.telescope-nvim;
config = ''
require("lz.n").load {
"telescope.nvim",
cmd = "Telescope",
}
'';
type = "lua";
optional = true;
}
{
plugin = pkgs.vimPlugins.sweetie-nvim;
config = ''
require("lz.n").load {
"sweetie.nvim",
colorscheme = "sweetie",
}
'';
type = "lua";
optional = true;
}
];
};
```### Structuring Your Plugins
As is the case with `lazy.nvim`, you can also split your plugin specs
into multiple files.
Instead of passing a spec table to `load()`, you can use a Lua module.
The function will merge specs from the **module** and any top-level **sub-modules**
together in the final spec, so it is not needed to add `require` calls
in your main plugin file to the other files.Example:
- `~/.config/nvim/init.lua`
```lua
require("lz.n").load("plugins")
```- `~/.config/nvim/lua/plugins.lua` or `~/.config/nvim/lua/plugins/init.lua`
**(this file is optional)**```lua
return {
{ "sweetie.nvim" },
{ "telescope.nvim", cmd = "Telescope" },
}
```- `lz.n` will automatically merge any Lua file in `~/.config/nvim/lua/plugins/*.lua`
with the main plugin spec[^4].[^4]: It *does not* merge multiple specs for the same plugin from different files.
Example structure:
```sh
── nvim
├── lua
│ └── plugins # Your plugin specs go here.
│ └── init.lua # Optional top-level module returning a list of specs
│ └── neorg.lua # Single spec
│ └── telescope/init.lua # Single spec
├── init.lua
```Or
```sh
── nvim
├── lua
│ └── plugins.lua # Optional top-level module returning a list of specs
├── init.lua
```## :electric_plug: API
### Custom handlers
You may register your own handlers to lazy-load plugins via
other triggers not already covered by the plugin spec.You should register all handlers before calling `require('lz.n').load`,
because they will not be retroactively applied to
the `load` calls that occur before they are registered.The `register_handler` function returns a `boolean` that indicates success.
```lua
---@param handler lz.n.Handler
---@return boolean success
require("lz.n").register_handler(handler)
```#### `lz.n.Handler`
| Property | Type | Description |
| --- | --- | --- |
| spec_field | `string` | The `lz.n.PluginSpec` field used to configure the handler |
| parse | `fun(plugin: lz.n.Plugin, spec: unknown)?` | Parse a spec and add it to the passed in plugin |
| add | `fun(plugin: lz.n.Plugin)` | Adds a plugin to the handler |
| del | `fun(name: string)` | Removes a plugin from the handler by name |
| lookup | `fun(name: string): lz.n.Plugin?` | Lookup a plugin managed by this handler by name |
| post_load | `fun()?` | Ran once after each `require('lz.n').load` call, for handlers to create custom triggers such as the event handler's `DeferredUIEnter` event |To manage handler state safely, ensuring `trigger_load` can be invoked from
within a plugin's hooks, it is recommended to use
the [`:h lz.n.handler.state`](./doc/lz.n.txt) module.> [!TIP]
>
> For some examples, look at
>
> - [The built-in handlers](./lua/lz/n/handler)
> - [The Wiki](https://github.com/nvim-neorocks/lz.n/wiki/Custom-handler-examples)### Lua API
The following Lua functions are part of the public API.
> [!WARNING]
>
> If you use internal functions or modules that are not listed here,
> things may break without a major version bump.#### `trigger_load`
You can manually load a plugin and run its associated hooks
using the `trigger_load` function:```lua
---@overload fun(plugin: lz.n.Plugin | lz.n.Plugin[])
---@overload fun(plugin_name: string | string[], opts: lz.n.lookup.Opts): string[]
require('lz.n').trigger_load
```The function provides two overloads, each suited for different use cases:
1. **Stateless version:**
- *Usage:* `trigger_load(plugin: lz.n.Plugin)`
- *Intended for:* Use by a `lz.n.Handler`
- *Description:* This version should be used when working with `lz.n.Handler`
instances to maintain referential transparency.
Each handler has full authority over its internal state, ensuring it
remains isolated and unaffected by external influences[^5],
thereby preventing multiple sources of truth.
2. **Stateful version:**
- *Usage:* `trigger_load(plugin_name: string | string[], opts?: lz.n.lookup.Opts)`
- *Returns:* A list of plugin names that were skipped
(empty if all plugins were loaded).
- *Intended for:* Scenarios where handler state is unknown or inaccessible,
such as in `before` or `after` hooks.
- *Description:* This version allows you to load plugins by name.
It searches through the handlers, querying their `lookup` functions
to identify an appropriate plugin, and returns the first match.
You can fine-tune the search process by providing a [`lz.n.lookup.Opts` table](#lookup).[^5]: Until the handler is instructed to stop tracking a loaded plugin via its `del` function.
#### `lookup`
To lookup a plugin that is pending to be loaded by name, use:
```lua
---@type fun(name: string, opts: lz.n.lookup.Opts):lz.n.Plugin?
require('lz.n').lookup
```The lookup, as well as `trigger_load(string|string[])` can be
fine-tuned with a `lz.n.lookup.Opts` table:```lua
---@class lz.n.lookup.Opts
---
--- The handlers to include in the search (filtered by `spec_field`)
--- In case of multiple filters, the order of the filter list
--- determines the order in which handlers' `lookup` functions are called.
---@field filter string | string[]
```## :green_heart: Contributing
All contributions are welcome!
See [CONTRIBUTING.md](./CONTRIBUTING.md).## :book: License
This library is [licensed](./LICENSE) according to GPL version 2
or (at your option) any later version.[neovim-shield]: https://img.shields.io/badge/NeoVim-%2357A143.svg?&style=for-the-badge&logo=neovim&logoColor=white
[neovim-url]: https://neovim.io/
[lua-shield]: https://img.shields.io/badge/lua-%232C2D72.svg?style=for-the-badge&logo=lua&logoColor=white
[lua-url]: https://www.lua.org/
[luarocks-shield]:
https://img.shields.io/luarocks/v/neorocks/lz.n?logo=lua&color=purple&style=for-the-badge
[luarocks-url]: https://luarocks.org/modules/neorocks/lz.n