feat(spec): event, keys, ft and cmd can now also be a function that returns the values to be used

This commit is contained in:
Folke Lemaitre 2023-01-12 13:07:51 +01:00
parent bcd87a0215
commit 2128ca90fb
No known key found for this signature in database
GPG Key ID: 41F8B1FBACAE2040
6 changed files with 76 additions and 75 deletions

View File

@ -79,32 +79,32 @@ require("lazy").setup({
## 🔌 Plugin Spec ## 🔌 Plugin Spec
| Property | Type | Description | | Property | Type | Description |
| ---------------- | --------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | ---------------- | ------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `[1]` | `string?` | Short plugin url. Will be expanded using `config.git.url_format` | | `[1]` | `string?` | Short plugin url. Will be expanded using `config.git.url_format` |
| **dir** | `string?` | A directory pointing to a local plugin | | **dir** | `string?` | A directory pointing to a local plugin |
| **url** | `string?` | A custom git url where the plugin is hosted | | **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 | | **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` | | **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 | | **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 | | **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. | | **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. | | **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 | | **init** | `fun(LazyPlugin)` | `init` functions are always executed during startup |
| **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()` | | **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()` |
| **config** | `fun(LazyPlugin, opts:table)` or `true` | `config` is executed when the plugin loads. The default implementation will automatically run `require("plugin").setup(opts)`. See also `opts`. | | **config** | `fun(LazyPlugin, opts:table)` or `true` | `config` is executed when the plugin loads. The default implementation will automatically run `require("plugin").setup(opts)`. See also `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 | | **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 | | **branch** | `string?` | Branch of the repository |
| **tag** | `string?` | Tag of the repository | | **tag** | `string?` | Tag of the repository |
| **commit** | `string?` | Commit 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 | | **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 | | **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` | | **event** | `string?` or `string[]` or `fun(self:LazyPlugin, event:string[]):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 | | **cmd** | `string?` or `string[]` or `fun(self:LazyPlugin, cmd:string[]):string[]` | Lazy-load on command |
| **ft** | `string?` or `string[]` | Lazy-load on filetype | | **ft** | `string?` or `string[]` or `fun(self:LazyPlugin, ft:string[]):string[]` | Lazy-load on filetype |
| **keys** | `string?` or `string[]` or `LazyKeys[]` | Lazy-load on key mapping | | **keys** | `string?` or `string[]` or `LazyKeys[]` or `fun(self:LazyPlugin, keys:string[]):(string \| LazyKeys)[]` | Lazy-load on key mapping |
| **module** | `false?` | Do not automatically load this Lua module when it's required somewhere | | **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. | | **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 ### Lazy Loading

View File

@ -346,7 +346,7 @@ function M.find(modname, opts)
local updated = false local updated = false
---@type LazyCoreConfig ---@type LazyCoreConfig
local Config = package.loaded["lazy.core.config"] local Config = package.loaded["lazy.core.config"]
if Config then if Config and Config.spec then
for _, plugin in pairs(Config.spec.plugins) do for _, plugin in pairs(Config.spec.plugins) do
if not (M.indexed[plugin.dir] or plugin._.loaded or plugin.module == false) then if not (M.indexed[plugin.dir] or plugin._.loaded or plugin.module == false) then
updated = M._index(plugin.dir) or updated updated = M._index(plugin.dir) or updated

View File

@ -231,33 +231,14 @@ function M._load(plugin, reason, opts)
end) end)
end end
-- Merges super opts or runs the opts function to override opts or return new ones
---@param plugin LazyPlugin
function M.opts(plugin)
---@type table
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 --- runs plugin config
---@param plugin LazyPlugin ---@param plugin LazyPlugin
function M.config(plugin) function M.config(plugin)
local opts = Plugin.values(plugin, "opts", false)
local fn local fn
if type(plugin.config) == "function" then if type(plugin.config) == "function" then
fn = function() fn = function()
plugin.config(plugin, M.opts(plugin)) plugin.config(plugin, opts)
end end
else else
local normname = Util.normname(plugin.name) local normname = Util.normname(plugin.name)
@ -274,7 +255,7 @@ function M.config(plugin)
end end
if #mods == 1 then if #mods == 1 then
fn = function() fn = function()
require(mods[1]).setup(M.opts(plugin)) require(mods[1]).setup(opts)
end end
else else
return Util.error( return Util.error(

View File

@ -7,9 +7,6 @@ local Cache = require("lazy.core.cache")
local M = {} local M = {}
M.loading = false M.loading = false
local list_merge = { "dependencies" }
vim.list_extend(list_merge, vim.tbl_values(Handler.types))
---@class LazySpecLoader ---@class LazySpecLoader
---@field plugins table<string, LazyPlugin> ---@field plugins table<string, LazyPlugin>
---@field disabled table<string, LazyPlugin> ---@field disabled table<string, LazyPlugin>
@ -27,11 +24,26 @@ function Spec.new(spec)
self.modules = {} self.modules = {}
self.notifs = {} self.notifs = {}
if spec then if spec then
self:normalize(spec) self:parse(spec)
end end
return self return self
end end
function Spec:parse(spec)
self:normalize(spec)
-- calculate handlers
for _, plugin in pairs(self.plugins) do
for _, handler in pairs(Handler.types) do
if plugin[handler] then
plugin[handler] = M.values(plugin, handler, true)
end
end
end
self:fix_disabled()
end
-- PERF: optimized code to get package name without using lua patterns -- PERF: optimized code to get package name without using lua patterns
function Spec.get_name(pkg) function Spec.get_name(pkg)
local name = pkg:sub(-4) == ".git" and pkg:sub(1, -5) or pkg local name = pkg:sub(-4) == ".git" and pkg:sub(1, -5) or pkg
@ -88,11 +100,6 @@ function Spec:add(plugin, results, is_dep)
return return
end end
plugin.event = type(plugin.event) == "string" and { plugin.event } or plugin.event
plugin.keys = type(plugin.keys) == "string" and { plugin.keys } or plugin.keys
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 if type(plugin.config) == "table" then
self:warn( self:warn(
"{" .. plugin.name .. "}: setting a table to `Plugin.config` is deprecated. Please use `Plugin.opts` instead" "{" .. plugin.name .. "}: setting a table to `Plugin.config` is deprecated. Please use `Plugin.opts` instead"
@ -272,14 +279,10 @@ function Spec:merge(old, new)
self:error("Two plugins with the same name and different url:\n" .. vim.inspect({ old = old, new = new })) self:error("Two plugins with the same name and different url:\n" .. vim.inspect({ old = old, new = new }))
end end
for _, prop in ipairs(list_merge) do if new.dependencies and old.dependencies then
if new[prop] and old[prop] then vim.list_extend(new.dependencies, old.dependencies)
if new[prop].__merge == nil then
new[prop].__merge = true
end
new[prop] = Util.merge(old[prop], new[prop])
end
end end
new._.super = old new._.super = old
setmetatable(new, { __index = old }) setmetatable(new, { __index = old })
@ -335,10 +338,8 @@ function M.load()
-- load specs -- load specs
Util.track("spec") Util.track("spec")
Config.spec = Spec.new() Config.spec = Spec.new()
Config.spec:normalize(vim.deepcopy(Config.options.spec)) Config.spec:parse({ vim.deepcopy(Config.options.spec), "folke/lazy.nvim" })
-- add ourselves
Config.spec:add({ "folke/lazy.nvim" })
-- override some lazy props -- override some lazy props
local lazy = Config.spec.plugins["lazy.nvim"] local lazy = Config.spec.plugins["lazy.nvim"]
if lazy then if lazy then
@ -349,7 +350,6 @@ function M.load()
end end
lazy._.loaded = {} lazy._.loaded = {}
end end
Config.spec:fix_disabled()
local existing = Config.plugins local existing = Config.plugins
Config.plugins = Config.spec.plugins Config.plugins = Config.spec.plugins
@ -395,4 +395,25 @@ function M.has_errors(plugin)
return false return false
end end
-- Merges super values or runs the values function to override values or return new ones
-- Used for opts, cmd, event, ft and keys
---@param plugin LazyPlugin
---@param prop string
---@param is_list? boolean
function M.values(plugin, prop, is_list)
---@type table
local ret = plugin._.super and M.values(plugin._.super, prop) or {}
local values = rawget(plugin, prop)
if not values then
return ret
elseif type(values) == "function" then
ret = values(plugin, ret) or ret
return type(ret) == "table" and ret or { ret }
end
values = type(values) == "table" and values or { values }
return is_list and vim.list_extend(ret, values) or Util.merge(ret, values)
end
return M return M

View File

@ -27,7 +27,7 @@
---@field event? string[] ---@field event? string[]
---@field cmd? string[] ---@field cmd? string[]
---@field ft? string[] ---@field ft? string[]
---@field keys? string[] ---@field keys? (string|LazyKeys)[]
---@field module? false ---@field module? false
---@class LazyPluginRef ---@class LazyPluginRef
@ -53,10 +53,10 @@
---@field _ LazyPluginState ---@field _ LazyPluginState
---@class LazyPluginSpecHandlers ---@class LazyPluginSpecHandlers
---@field event? string[]|string ---@field event? string[]|string|fun(self:LazyPlugin, event:string[]):string[]
---@field cmd? string[]|string ---@field cmd? string[]|string|fun(self:LazyPlugin, cmd:string[]):string[]
---@field ft? string[]|string ---@field ft? string[]|string|fun(self:LazyPlugin, ft:string[]):string[]
---@field keys? string|string[]|LazyKeys[] ---@field keys? string|string[]|LazyKeys[]|fun(self:LazyPlugin, keys:string[]):(string|LazyKeys)[]
---@field module? false ---@field module? false
---@class LazyPluginSpec: LazyPluginBase,LazyPluginSpecHandlers,LazyPluginHooks,LazyPluginRef ---@class LazyPluginSpec: LazyPluginBase,LazyPluginSpecHandlers,LazyPluginHooks,LazyPluginRef

View File

@ -262,7 +262,6 @@ describe("plugin spec opt", function()
} }
for test, ret in pairs(tests) do for test, ret in pairs(tests) do
local spec = Plugin.Spec.new(test) local spec = Plugin.Spec.new(test)
spec:fix_disabled()
assert(#spec.notifs == 0) assert(#spec.notifs == 0)
if ret then if ret then
assert(spec.plugins.bar) assert(spec.plugins.bar)