diff options
| author | Evgeni Chasnovski <evgeni.chasnovski@gmail.com> | 2026-07-31 15:17:00 +0300 |
|---|---|---|
| committer | Evgeni Chasnovski <evgeni.chasnovski@gmail.com> | 2026-07-31 19:20:34 +0300 |
| commit | 9d12df0398499d22bb509ad5c209dd608095596d (patch) | |
| tree | c885120cf28814654dc92727a1c934f6164e22e1 /runtime/lua/vim/pack.lua | |
| parent | 4cdd6d76c5ad2714aa5c056c8b16fca408120e61 (diff) | |
| download | rneovim-9d12df0398499d22bb509ad5c209dd608095596d.tar.gz rneovim-9d12df0398499d22bb509ad5c209dd608095596d.tar.bz2 rneovim-9d12df0398499d22bb509ad5c209dd608095596d.zip | |
feat(pack): add support for sourcing manifest scripts
Problem: No way for plugins to define hooks that would be executed
during plugin lifecycle. Like after install/update or before
update/delete.
Solution: Automatically source scripts defined in plugin manifest file
(if any) after triggering corresponding `PackChanged{,Pre}` events.
During sourcing make some termporary adjustments:
- Current directory is set to plugin's root to simplify execution of
CLI calls like `make build`.
- Plugin's path is ensured to be inside 'runtimepath' to allow using
`require('plugin-module')` inside manifest scripts.
The reason to execute after triggering event is so that there is a
possibility for users to execute code both before the script (exactly
on event) and after the script (more-or-less via `vim.schedule` called
on the event).
Diffstat (limited to 'runtime/lua/vim/pack.lua')
| -rw-r--r-- | runtime/lua/vim/pack.lua | 53 |
1 files changed, 53 insertions, 0 deletions
diff --git a/runtime/lua/vim/pack.lua b/runtime/lua/vim/pack.lua index a69684de75..0e986d3196 100644 --- a/runtime/lua/vim/pack.lua +++ b/runtime/lua/vim/pack.lua @@ -236,6 +236,14 @@ --- ---Plugins can come with a special top level `pkg.json` manifest file with extra ---information. If present, `vim.pack` uses it for improved user experience: +---- Apply |:source| for scripts after triggering corresponding |vim.pack-events|. +--- This allows plugins to define hooks that will be executed during plugin's lifetime. +--- Sourcing is done with special context: +--- - The |current-directory| is temporarily set to plugin's root (to make +--- it easier to run |vim.system()| commands). +--- - Plugin's path is temporarily ensured to be inside |'runtimepath'| (so script +--- can use |require()| with plugin's module, possibly with explicit |package.loaded| +--- reset inside `"update"` scripts). ---- Running |:checkhealth| for `vim.pack` will perform extra checks to ensure --- healthy plugin installation. --- @@ -248,6 +256,12 @@ --- "engines": { --- "nvim": ">=0.13.0", --- "vim": ">=9.1.0" +--- }, +--- "scripts": { +--- "install": "scripts/install.lua", +--- "preupdate": "scripts/preupdate.vim", +--- "update": "scripts/update.vim", +--- "preuninstall": "scripts/preuninstall.lua" --- } ---} ---``` @@ -540,11 +554,21 @@ local n_active_plugins = 0 --- @field nvim? string Version range for Nvim. --- @field vim? string Version range for Vim. +--- @class vim.pack.ManifestScripts +--- @inlinedoc +--- @field install? string Post install script. +--- @field preupdate? string Pre update script. +--- @field update? string Post update script. +--- @field preuninstall? string Pre delete script. + --- @class vim.pack.Manifest --- @field name? string Plugin name --- @field description? string Plugin description --- Supported engine versions. Values should be |vim.version.range()| compatible specs. --- @field engines? vim.pack.ManifestEngines +--- Script locations (relative to plugin's root) to |:source| after triggering +--- corresponding |vim.pack-events|. +--- @field scripts? vim.pack.ManifestScripts --- @param path string --- @return vim.pack.Manifest? @@ -566,14 +590,43 @@ local function manifest_read(path) return (ok and type(res) == 'table') and res or {} end +--- @param p vim.pack.Plug +--- @param name string +local function source_manifest_script(p, name) + local manifest = manifest_read(p.path) + if not (type(manifest) == 'table' and (manifest.scripts or {})[name]) then + return + end + + local script_path = vim.fs.joinpath(p.path, (manifest.scripts or {})[name]) + vim._with({ cwd = p.path, o = { runtimepath = vim.o.runtimepath } }, function() + vim.cmd.packadd({ p.spec.name, bang = true }) + ---@diagnostic disable-next-line: no-unknown + local ok, err = pcall(vim.cmd.source, { script_path, magic = { file = false, bar = false } }) + if not ok then + notify(err, 'WARN') + end + end) +end + +local manifest_script_name_map = { + PackChangedPre = { update = 'preupdate', delete = 'preuninstall' }, + PackChanged = { install = 'install', update = 'update' }, +} + --- @param plugs vim.pack.Plug[] --- @param event_name 'PackChangedPre'|'PackChanged' --- @param kind 'install'|'update'|'delete' local function trigger_events(plugs, event_name, kind) + local manifest_script_name = manifest_script_name_map[event_name][kind] for _, p in ipairs(plugs) do local active = active_plugins[p.path] ~= nil local data = { active = active, kind = kind, spec = vim.deepcopy(p.spec), path = p.path } api.nvim_exec_autocmds(event_name, { pattern = p.path, data = data }) + + if manifest_script_name then + source_manifest_script(p, manifest_script_name) + end end end |