From 7260a2b28be807c4bdc1caf23fa35c2aa33aa6ac Mon Sep 17 00:00:00 2001 From: Folke Lemaitre Date: Sun, 8 Jan 2023 15:01:49 +0100 Subject: [PATCH] feat(spec)!: setting a table to `Plugin.config` is now deprecated. Please use `Plugin.opts` instead. (backward compatible for now) --- README.md | 73 ++++++++++++++++++++++++-------------- lua/lazy/core/loader.lua | 27 +++++++++++--- lua/lazy/core/plugin.lua | 9 +++++ lua/lazy/example.lua | 4 +-- lua/lazy/health.lua | 44 +++++++++++++---------- lua/lazy/types.lua | 9 +++-- tests/core/plugin_spec.lua | 35 ++++++++++++++++++ 7 files changed, 146 insertions(+), 55 deletions(-) diff --git a/README.md b/README.md index a2377436..594b1aee 100644 --- a/README.md +++ b/README.md @@ -79,31 +79,32 @@ require("lazy").setup({ ## 🔌 Plugin Spec -| Property | Type | Description | -| ---------------- | --------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `[1]` | `string?` | Short plugin url. Will be expanded using `config.git.url_format` | -| **dir** | `string?` | A directory pointing to a local plugin | -| **url** | `string?` | A custom git url where the plugin is hosted | -| **name** | `string?` | A custom name for the plugin used for the local plugin directory and as the display name | -| **dev** | `boolean?` | When `true`, a local plugin directory will be used instead. See `config.dev` | -| **lazy** | `boolean?` | When `true`, the plugin will only be loaded when needed. Lazy-loaded plugins are automatically loaded when their Lua modules are `required`, or when one of the lazy-loading handlers triggers | -| **enabled** | `boolean?` or `fun():boolean` | When `false`, or if the `function` returns false, then this plugin will not be included in the spec | -| **cond** | `boolean?` or `fun():boolean` | When `false`, or if the `function` returns false, then this plugin will not be loaded. Useful to disable some plugins in vscode, or firenvim for example. | -| **dependencies** | `LazySpec[]` | A list of plugin names or plugin specs that should be loaded when the plugin loads. Dependencies are always lazy-loaded unless specified otherwise. When specifying a name, make sure the plugin spec has been defined somewhere else. | -| **init** | `fun(LazyPlugin)` | `init` functions are always executed during startup | -| **config** | `fun(LazyPlugin)` or `true` or `table` | `config` is executed when the plugin loads. You can also set to `true` or pass a `table`, that will be passed to `require("plugin").setup(opts)` | -| **build** | `fun(LazyPlugin)` or `string` or a list of build commands | `build` is executed when a plugin is installed or updated. If it's a string it will be ran as a shell command. When prefixed with `:` it is a Neovim command. You can also specify a list to executed multiple build commands | -| **branch** | `string?` | Branch of the repository | -| **tag** | `string?` | Tag of the repository | -| **commit** | `string?` | Commit of the repository | -| **version** | `string?` | Version to use from the repository. Full [Semver](https://devhints.io/semver) ranges are supported | -| **pin** | `boolean?` | When `true`, this plugin will not be included in updates | -| **event** | `string?` or `string[]` | Lazy-load on event. Events can be specified as `BufEnter` or with a pattern like `BufEnter *.lua` | -| **cmd** | `string?` or `string[]` | Lazy-load on command | -| **ft** | `string?` or `string[]` | Lazy-load on filetype | -| **keys** | `string?` or `string[]` or `LazyKeys[]` | Lazy-load on key mapping | -| **module** | `false?` | Do not automatically load this Lua module when it's required somewhere | -| **priority** | `number?` | Only useful for **start** plugins (`lazy=false`) to force loading certain plugins first. Default priority is `50`. It's recommended to set this to a high number for colorschemes. | +| Property | Type | Description | +| ---------------- | --------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `[1]` | `string?` | Short plugin url. Will be expanded using `config.git.url_format` | +| **dir** | `string?` | A directory pointing to a local plugin | +| **url** | `string?` | A custom git url where the plugin is hosted | +| **name** | `string?` | A custom name for the plugin used for the local plugin directory and as the display name | +| **dev** | `boolean?` | When `true`, a local plugin directory will be used instead. See `config.dev` | +| **lazy** | `boolean?` | When `true`, the plugin will only be loaded when needed. Lazy-loaded plugins are automatically loaded when their Lua modules are `required`, or when one of the lazy-loading handlers triggers | +| **enabled** | `boolean?` or `fun():boolean` | When `false`, or if the `function` returns false, then this plugin will not be included in the spec | +| **cond** | `boolean?` or `fun():boolean` | When `false`, or if the `function` returns false, then this plugin will not be loaded. Useful to disable some plugins in vscode, or firenvim for example. | +| **dependencies** | `LazySpec[]` | A list of plugin names or plugin specs that should be loaded when the plugin loads. Dependencies are always lazy-loaded unless specified otherwise. When specifying a name, make sure the plugin spec has been defined somewhere else. | +| **init** | `fun(LazyPlugin)` | `init` functions are always executed during startup | +| **config** | `fun(LazyPlugin, opts:table)` or `true` | `config` is executed when the plugin loads. You can also set it to `true`, to automatically run `require("plugin").setup(opts)`. See also `opts`. | +| **opts** | `table` or `fun(LazyPlugin, opts:table)` | `opts` should be a table (will be merged with parent specs), return a table (replaces parent specs) or should change a table. The table will be passed to the `Plugin.config()` function. Setting this value will imply `Plugin.config()` | +| **build** | `fun(LazyPlugin)` or `string` or a list of build commands | `build` is executed when a plugin is installed or updated. If it's a string it will be ran as a shell command. When prefixed with `:` it is a Neovim command. You can also specify a list to executed multiple build commands | +| **branch** | `string?` | Branch of the repository | +| **tag** | `string?` | Tag of the repository | +| **commit** | `string?` | Commit of the repository | +| **version** | `string?` | Version to use from the repository. Full [Semver](https://devhints.io/semver) ranges are supported | +| **pin** | `boolean?` | When `true`, this plugin will not be included in updates | +| **event** | `string?` or `string[]` | Lazy-load on event. Events can be specified as `BufEnter` or with a pattern like `BufEnter *.lua` | +| **cmd** | `string?` or `string[]` | Lazy-load on command | +| **ft** | `string?` or `string[]` | Lazy-load on filetype | +| **keys** | `string?` or `string[]` or `LazyKeys[]` | Lazy-load on key mapping | +| **module** | `false?` | Do not automatically load this Lua module when it's required somewhere | +| **priority** | `number?` | Only useful for **start** plugins (`lazy=false`) to force loading certain plugins first. Default priority is `50`. It's recommended to set this to a high number for colorschemes. | ### Lazy Loading @@ -224,11 +225,11 @@ return { config = true, -- run require("neorg").setup() }, - -- or set a custom config: + -- or set custom options: { "nvim-neorg/neorg", ft = "norg", - config = { foo = "bar" }, -- run require("neorg").setup({foo = "bar"}) + opts = { foo = "bar" }, -- run require("neorg").setup({foo = "bar"}) }, { @@ -660,6 +661,24 @@ For a real-life example, you can check [LazyVim](https://github.com/LazyVim/Lazy - [lazyvim.plugins](https://github.com/LazyVim/LazyVim/tree/main/lua/lazyvim/plugins) contains all the plugin specs that will be loaded +### ↩ī¸ Importing Specs, `config` & `opts` + +As part of a spec, you can add `import` statements to import additional plugin modules. +Both of the `setup()` calls are equivalent: + +```lua +require("lazy").setup("plugins") + +-- same as: +require("lazy").setup({{import = "plugins"}}) +``` + +When you import specs, you can override them by simply adding a spec for the same plugin to your local +specs, adding any keys you want to override / merge. + +`opts`, `dependencies`, `cmd`, `event`, `ft` and `keys` are always merged with the parent spec. +Any other property will override the property from the parent spec. + ## đŸ“Ļ Migration Guide ### [packer.nvim](https://github.com/wbthomason/packer.nvim) diff --git a/lua/lazy/core/loader.lua b/lua/lazy/core/loader.lua index 8ef6ee56..9054d32e 100644 --- a/lua/lazy/core/loader.lua +++ b/lua/lazy/core/loader.lua @@ -220,7 +220,7 @@ function M._load(plugin, reason, opts) end M.packadd(plugin.dir) - if plugin.config then + if plugin.config or plugin.opts then M.config(plugin) end @@ -231,13 +231,32 @@ function M._load(plugin, reason, opts) end) end +-- Merges super opts or runs the opts function to override opts or return new ones +---@param plugin LazyPlugin +function M.opts(plugin) + local opts = plugin._.super and M.opts(plugin._.super) or {} + ---@type PluginOpts? + local plugin_opts = rawget(plugin, "opts") + + if type(plugin_opts) == "table" then + opts = Util.merge(opts, plugin_opts) + elseif type(plugin_opts) == "function" then + local new_opts = plugin_opts(plugin, opts) + if new_opts then + opts = new_opts + end + end + + return opts +end + --- runs plugin config ---@param plugin LazyPlugin function M.config(plugin) local fn if type(plugin.config) == "function" then fn = function() - plugin.config(plugin) + plugin.config(plugin, M.opts(plugin)) end else local normname = Util.normname(plugin.name) @@ -254,8 +273,8 @@ function M.config(plugin) end if #mods == 1 then fn = function() - local opts = plugin.config - if opts == true then + local opts = M.opts(plugin) + if next(opts) == nil then opts = nil end require(mods[1]).setup(opts) diff --git a/lua/lazy/core/plugin.lua b/lua/lazy/core/plugin.lua index fe3a34b4..d955b49b 100644 --- a/lua/lazy/core/plugin.lua +++ b/lua/lazy/core/plugin.lua @@ -87,6 +87,15 @@ function Spec:add(plugin, results, is_dep) plugin.cmd = type(plugin.cmd) == "string" and { plugin.cmd } or plugin.cmd plugin.ft = type(plugin.ft) == "string" and { plugin.ft } or plugin.ft + if type(plugin.config) == "table" then + self:warn( + "{" .. plugin.name .. "}: setting a table to `Plugin.config` is deprecated. Please use `Plugin.opts` instead" + ) + ---@diagnostic disable-next-line: assign-type-mismatch + plugin.opts = plugin.config + plugin.config = nil + end + plugin._ = {} plugin._.dep = is_dep diff --git a/lua/lazy/example.lua b/lua/lazy/example.lua index 93f87d4e..18c1ca93 100644 --- a/lua/lazy/example.lua +++ b/lua/lazy/example.lua @@ -31,11 +31,11 @@ return { config = true, -- run require("neorg").setup() }, - -- or set a custom config: + -- or set custom options: { "nvim-neorg/neorg", ft = "norg", - config = { foo = "bar" }, -- run require("neorg").setup({foo = "bar"}) + opts = { foo = "bar" }, -- run require("neorg").setup({foo = "bar"}) }, { diff --git a/lua/lazy/health.lua b/lua/lazy/health.lua index 24b9de21..59fa1391 100644 --- a/lua/lazy/health.lua +++ b/lua/lazy/health.lua @@ -68,8 +68,12 @@ function M.check_override(plugin) return end + local Handler = require("lazy.core.handler") + local skip = { "dependencies", "_", "opts" } + vim.list_extend(skip, vim.tbl_values(Handler.types)) + for key, value in pairs(plugin._.super) do - if key ~= "_" and plugin[key] and plugin[key] ~= value then + if not vim.tbl_contains(skip, key) and plugin[key] and plugin[key] ~= value then vim.health.report_warn("{" .. plugin.name .. "}: overriding <" .. key .. ">") end end @@ -77,29 +81,31 @@ end M.valid = { 1, - "name", - "url", - "enabled", - "lazy", - "dev", - "dependencies", - "init", - "config", - "build", + "_", "branch", - "tag", - "commit", - "version", - "module", - "pin", + "build", "cmd", + "commit", + "cond", + "config", + "dependencies", + "dev", + "dir", + "enabled", "event", - "keys", "ft", - "dir", + "import", + "init", + "keys", + "lazy", + "module", + "name", + "opts", + "pin", "priority", - "cond", - "_", + "tag", + "url", + "version", } return M diff --git a/lua/lazy/types.lua b/lua/lazy/types.lua index ab3832f3..557a2f44 100644 --- a/lua/lazy/types.lua +++ b/lua/lazy/types.lua @@ -15,10 +15,13 @@ ---@field cond? boolean ---@field super? LazyPlugin +---@alias PluginOpts table|fun(self:LazyPlugin, opts:table):table? + ---@class LazyPluginHooks ----@field init? fun(LazyPlugin) Will always be run ----@field config? fun(LazyPlugin)|true|table Will be executed when loading the plugin ----@field build? string|fun(LazyPlugin)|(string|fun(LazyPlugin))[] +---@field init? fun(self:LazyPlugin) Will always be run +---@field config? fun(self:LazyPlugin, opts:table)|true Will be executed when loading the plugin +---@field build? string|fun(self:LazyPlugin)|(string|fun(self:LazyPlugin))[] +---@field opts? PluginOpts ---@class LazyPluginHandlers ---@field event? string[] diff --git a/tests/core/plugin_spec.lua b/tests/core/plugin_spec.lua index 79953811..d3b89022 100644 --- a/tests/core/plugin_spec.lua +++ b/tests/core/plugin_spec.lua @@ -1,5 +1,6 @@ local Config = require("lazy.core.config") local Plugin = require("lazy.core.plugin") +local Loader = require("lazy.core.loader") local assert = require("luassert") @@ -272,3 +273,37 @@ describe("plugin spec opt", function() end end) end) + +describe("plugin opts", function() + it("correctly parses opts", function() + ---@type {spec:LazySpec, opts:table}[] + local tests = { + { + spec = { { "foo/foo", opts = { a = 1, b = 1 } }, { "foo/foo", opts = { a = 2 } } }, + opts = { a = 2, b = 1 }, + }, + { + spec = { { "foo/foo", config = { a = 1, b = 1 } }, { "foo/foo", opts = { a = 2 } } }, + opts = { a = 2, b = 1 }, + }, + { + spec = { { "foo/foo", opts = { a = 1, b = 1 } }, { "foo/foo", config = { a = 2 } } }, + opts = { a = 2, b = 1 }, + }, + { + spec = { { "foo/foo", config = { a = 1, b = 1 } }, { "foo/foo", config = { a = 2 } } }, + opts = { a = 2, b = 1 }, + }, + { + spec = { { "foo/foo", config = { a = 1, b = 1 } }, { "foo/foo", config = { a = vim.NIL } } }, + opts = { b = 1 }, + }, + } + + for _, test in ipairs(tests) do + local spec = Plugin.Spec.new(test.spec) + assert(spec.plugins.foo) + assert.same(test.opts, Loader.opts(spec.plugins.foo)) + end + end) +end)