summaryrefslogtreecommitdiffstatshomepage
path: root/test/functional/plugin/pack_spec.lua
AgeCommit message (Collapse)AuthorFiles
2026-04-23fix(pack): only use tags that strictly comply with semver spec #39342Evgeni Chasnovski1
Problem: Using `version=vim.version.range(...)` in plugin specification is meant to use semver-like tags. Whether a tag is semver-like was decided by a plain `vim.version.parse` which is not strict by default. This allowed treating tags like `nvim-0.6` (which is usually reserved for the latest revision compatible with Nvim<=0.6 version) like semver tags and resulted in confusing behavior (preferring `nvim-0.6` tag over `v0.2.2`, for example). Solution: Use `vim.version.range(x, { strict = true })` to decide if the tag name is semver-like or not. This allows tags like both `v1.2.3` and `1.2.3` while being consistent in what Nvim thinks is a semver string. This is technically not a breaking change since it was documented that only tags like `v<major>.<minor>.<patch>` will be recognized as semver.
2026-04-22fix(pack): GIT_DIR/GIT_WORK_TREE env vars may interfere #39279fleesk1
Problem: With GIT_DIR/GIT_WORK_TREE set, the LSP on the vim.pack.update() confirmation buffer does not show the correct git log on hover. Solution: Temporarily remove the git vars from the environment.
2026-04-16test(pack): increase retry timeout for startup test #39125Evgeni Chasnovski1
Problem: Unreliable test on Windows which sometimes fails with too many failed retries. Solution: Increase timeout in hopes that it will be enough to make it pass more frequently. This should not affect fast and already working platforms.
2026-04-14test(pack): use n.rmdir() to delete directories #39046Evgeni Chasnovski1
Problem: using `vim.fs.rm(dir_path, { force = true, recursive = true })` can result in an error on Windows if the process has a handle to it. Solution: Use `n.rmdir()` helper in cases when its possible side effects (like changing working directory) does not matter.
2026-03-29test: fix s390x failuresJustin M. Keyes1
Problem: failures in s390x CI. Solution: - runtime/lua/man.lua: parse_path() can return nil but 3 callers didn't handle it. - skip some tests on s390x. TODO: - TODO: why "build/bin/xxd is not executable" on s390x? - TODO: other failures, not addressed (see below). OTHER FAILURES: FAILED test/functional/treesitter/fold_spec.lua @ 87: treesitter foldexpr recomputes fold levels after lines are added/removed test/functional/treesitter/fold_spec.lua:95: Expected objects to be the same. Passed in: (table: 0x4013c18940) { [1] = '0' [2] = '0' [3] = '0' *[4] = '0' [5] = '0' ... Expected: (table: 0x4005acf900) { [1] = '0' [2] = '0' [3] = '>1' *[4] = '1' [5] = '1' ... stack traceback: (tail call): ? test/functional/treesitter/fold_spec.lua:95: in function <test/functional/treesitter/fold_spec.lua:87> FAILED test/functional/treesitter/select_spec.lua @ 52: treesitter incremental-selection works test/functional/treesitter/select_spec.lua:63: Expected objects to be the same. Passed in: (string) 'bar(2)' Expected: (string) 'foo(1)' stack traceback: (tail call): ? test/functional/treesitter/select_spec.lua:63: in function <test/functional/treesitter/select_spec.lua:52> FAILED test/functional/treesitter/select_spec.lua @ 69: treesitter incremental-selection repeat test/functional/treesitter/select_spec.lua:82: Expected objects to be the same. Passed in: (string) '2' Expected: (string) '4' stack traceback: (tail call): ? test/functional/treesitter/select_spec.lua:82: in function <test/functional/treesitter/select_spec.lua:69> FAILED test/functional/treesitter/select_spec.lua @ 98: treesitter incremental-selection history test/functional/treesitter/select_spec.lua:111: Expected objects to be the same. Passed in: (string) 'bar(2)' Expected: (string) 'foo(1)' stack traceback: (tail call): ? test/functional/treesitter/select_spec.lua:111: in function <test/functional/treesitter/select_spec.lua:98> FAILED test/functional/treesitter/select_spec.lua @ 186: treesitter incremental-selection with injections works test/functional/treesitter/select_spec.lua:201: Expected objects to be the same. Passed in: (string) 'lua' Expected: (string) 'foo' stack traceback: (tail call): ? test/functional/treesitter/select_spec.lua:201: in function <test/functional/treesitter/select_spec.lua:186> FAILED test/functional/treesitter/select_spec.lua @ 216: treesitter incremental-selection with injections ignores overlapping nodes test/functional/treesitter/select_spec.lua:231: Expected objects to be the same. Passed in: (string) ' )' Expected: (string) ' foo(' stack traceback: (tail call): ? test/functional/treesitter/select_spec.lua:231: in function <test/functional/treesitter/select_spec.lua:216> FAILED test/functional/treesitter/select_spec.lua @ 307: treesitter incremental-selection with injections handles disjointed trees test/functional/treesitter/select_spec.lua:337: Expected objects to be the same. Passed in: (string) 'int' Expected: (string) '1}' stack traceback: (tail call): ? test/functional/treesitter/select_spec.lua:337: in function <test/functional/treesitter/select_spec.lua:307> ERROR test/functional/treesitter/parser_spec.lua @ 562: treesitter parser API can run async parses with string parsers test/functional/treesitter/parser_spec.lua:565: attempt to index a nil value stack traceback: test/functional/testnvim/exec_lua.lua:124: in function <test/functional/testnvim/exec_lua.lua:105> (tail call): ? (tail call): ? test/functional/treesitter/parser_spec.lua:563: in function <test/functional/treesitter/parser_spec.lua:562> FAILED test/functional/core/job_spec.lua @ 1157: jobs jobstop() kills entire process tree #6530 test/functional/core/job_spec.lua:1244: retry() attempts: 94 test/functional/core/job_spec.lua:1246: Expected objects to be the same. Passed in: (table: 0x401dd74b30) { [name] = 'sleep <defunct>' [pid] = 33579 [ppid] = 1 } Expected: (userdata) 'vim.NIL' stack traceback: test/testutil.lua:89: in function 'retry' test/functional/core/job_spec.lua:1244: in function <test/functional/core/job_spec.lua:1157>
2026-03-27fix(pack): set `source` in progress report #38511Evgeni Chasnovski1
Problem: Progress reports via `nvim_echo()` gained an ability to set `source` and `vim.pack` doesn't currently set one. Solution: Set `source` to 'vim.pack'. Ideally, the title then can be something else more informative (like "update", "download", etc.), but it is used when showing progress messages. So it has to be "vim.pack" in this case.
2026-03-24fix(pack): add trailing newline to lockfile #38469Justin Mayhew1
Problem: JSON files should end with a trailing newline so that Unix tools work as expected, Git doesn't report "No newline at end of file" and to avoid noise in diffs from editors and other tools adding the missing newline. Solution: Add trailing newline.
2026-03-03fix(pack): ensure `data` spec is passed in events during lockfile sync #38139Evgeni Chasnovski1
Problem: During initial "bootstrap" via lockfile synchronization, the whole plugin specification is reconstructed from the lockfile data, ignoring potential user changes added in the first `vim.pack.add()`. This is enough in most situations since it is the only data needed for actual installation. However, this affects specification passed to `PackChanged[Pre]` events. In particular, `data` field is missing which can be a problem if there is a `PackChanged kind=install` hook that uses that field (like with some kind of `build` method used during install). And there might be different `version` set in `vim.pack.add()`. Solution: Pass the `specs` input of the first `vim.pack.add()` down to lockfile synchronization and use it to reconstruct plugin specification for the to-be-installed plugin. If present among the user's `specs`, it is used but with forced `src` from the lockfile (as it is the one used during installation). Note that this still has a caveat when using separate `vim.pack.add()`, as only the specs from the first input (when the lockfile synchronization happens) is taken into account.
2026-02-20test: start test runners in Xtest_xdg dir (#37964)zeertzjq1
This is a better way to prevent parallel tests from interfering with each other, as there are many ways files can be created and deleted in tests, so enforcing different file names is hard. Using $TMPDIR can also work in most cases, but 'backipskip' etc. have special defaults for $TMPDIR. Symlink runtime/, src/, test/ and README.md to Xtest_xdg dir to make tests more convenient (and symlinking test/ is required for busted). Also, use README.md instead of test/README.md in the Ex mode inccommand test, as test/README.md no longer contains 'N' char.
2026-02-14fix(pack): explicitly close confirmation window #37861Evgeni Chasnovski1
Problem: Executing `nvim_buf_delete()` does not guarantee that the window which shows the buffer is going to close after `:write` or `:quit`. In particular, if there is no listed buffer present. Solution: Explicitly close the window that was created for confirmation buffer. Use `pcall` to catch cases when the window was already closed or when it is the last window.
2026-02-08fix(pack): close confirmation buffer, not tabpage #37756Evgeni Chasnovski1
2026-01-29test(pack): cover that events work when installing from the lockfileEvgeni Chasnovski1
2026-01-16test(pack): use a helper to `vim.pack.add` inside `testnvim` instanceEvgeni Chasnovski1
Problem: A lot of test cases require executing `vim.pack.add()` inside `testnvim` instance to install and/or add plugins. This requires access to the information about tested repo sources (stored in a Lua variable inside a runner instance), so it needs to go `exec_lua(function() vim.pack.add({...}) end)` route, which in formatted form adds two more lines. Since it is very frequent, it pads test files with (mostly avoidable and unncessary) lines. Solution: Add `vim_pack_add()` helper specifically to execute `vim.pack.add` inside `testnvim` instance. Use it where possible: usually when `vim.pack.add` is not a subject of the test. It is not quite doable for some cases, though: - When error in `vim.pack.add` is tested. - When specification includes `vim.version.range` (since `exec_lua` seems to not transfer necessary class metatables). This removes ~100 lines from the file at the cost of sometimes using `vim_pack_add` and sometimes having to reside to `vim.pack.add`. Which seems like a positive trade-off. This also allows making a more compact `exec_lua('vim.pack.update()')` instructions since `vim.pack.update` doesn't depend on the upvalues stored in test runner instance.
2026-01-16test(pack): make various small cleanupsEvgeni Chasnovski1
Problem: `vim.pack` tests might benefit from a cleanup Solution: Do the cleanup: - Use `t.retry` instead of full `vim.uv.sleep` to make tests quicker. - Use `pack_assert_content` helper for a common task of checking the content of a special Lua file inside an installed plugin. This is used as a proxy of installed plugin's state on the disk. - Use `([[...]]):format()` approach (instead of array of strings) in one more place. Should improve readability.
2026-01-12fix(pack): actually checkout proper version of submodulesEvgeni Chasnovski1
Problem: Installing plugin with submodules doesn't check out their state (due to `git clone --no-checkout` to not end up with default branch code in case of invalid `version`). Updating a plugin with submodules doesn't update their state. Solution: Update `git_checkout` helper to account for submodules. Another approach would be `git checkout --recurse-submodules ...`, but that doesn't seem to allow `--filter=blob:none` for submodules, which is nice to have. Also make `git_clone` wrapper simpler since `--no-checkout` makes `--recurse-submodules` and `--also-filter-submodules` do nothing.
2026-01-12test(pack): adjust `add` startup tests to just sleep with longer timeoutEvgeni Chasnovski1
Problem: The `add` startup tests mock startup process which requires *some* amount of time to be done. Previous solution with `vim.wait` to wait just enough time to register that startup script has finished doesn't seem to work as intended (it just wait full time). Its timeout is also seems to be barely enough to pass on Windows CI. Which will be a problem with more `git` actions done on startup in the future/next commit. Solution: Just sleep predetermined amount of time and explicitly check if startup script finished executing.
2026-01-12fix(pack): skip `git stash` during installEvgeni Chasnovski1
Problem: Installing plugin is done via `git clone --no-checkout ...` (to not end up with default branch code in case of invalid `version`). This leaves cloned repo in a state that `git stash` will actually add an entry to the stash list. Although not critical, better to not have that if possible. Solution: explicitly skip `git stash` step in checkout during install.
2025-12-30Merge #37113 from echasnovski/pack-better-revertJustin M. Keyes1
2025-12-28feat(pack)!: suggest "delete" code action only for not active pluginsEvgeni Chasnovski1
Problem: Deleting active plugins can lead to a situation when it is reinstalled after restart. Solution: Suggest "delete" code action only for not active plugins. Whether a plugin is not active is visible by a hint in its header.
2025-12-28feat(pack): hint in confirmation buffer that plugin is not activeEvgeni Chasnovski1
Problem: After `vim.pack.update()` it is not clear if plugin is active or not. This can be useful to detect cases when plugin was removed from 'init.lua' but there was no `vim.pack.del()`. Solution: Add ` (not active)` suffix with distinctive highlighting to header of plugins that are not active. It will also be shown in in-process LSP document symbols to have quick reference about which plugins are not active.
2025-12-28feat(pack)!: make `del()` only remove non-active plugins by defaultEvgeni Chasnovski1
Problem: Using `vim.pack.del()` to delete active plugin can lead to a situation when this plugin is reinstalled after restart. Removing plugin from 'init.lua' is documented, but can be missed. Solution: Make `del()` only remove non-active plugins by default and throw an informative error if there is an active plugin. Add a way to force delete any plugin by adding `opts.force`. This also makes `del()` signature be the same as other functions, which is nice.
2025-12-26feat(pack): allow running `update()` without Internet connectionEvgeni Chasnovski1
Problem: There is now way to run `update()` without Internet connection while there are some workflows that do not require it. Like "switch plugin version" and "revert latest update". Solution: Add `opts.offline` to `update()`. This also allows now to treat `vim.pack.update(nil, { offline = true })` as a way to interactively explore currently installed plugins.
2025-12-26feat(pack): allow choosing update target in `update()`Evgeni Chasnovski1
Problem: There are two fairly common workflows that involve lockfile and can be made more straightforward in `vim.pack`: 1. Revert latest update. Like if it introduced unwanted behavior. 2. Update to a revision in the lockfile. Like if already updated on another machine, verified that everything works, `git add` + `git commit` + `git push` the config, and want to have the same plugin states on current machine. Solution: Make `update` allow `opts.target`. By default it uses `version` from a plugin specification (like a regular "get new changes from source" workflow). But it also allows `"lockfile"` value to indicate that target revision after update should be taken from the current lockfile verbatim. With this, the workflows are: 1. Revert (somehow) to the lockfile before the update, restart, and `vim.pack.update({ 'plugin' }, { target = 'lockfile' })`. If Git tracked, revert with `git checkout HEAD -- nvim-pack-lock.json`. For non-VCS tracked lockfile, the revisions can be taken from the log file. It would be nicer if `update()` would backup a lockfile before doing an update, but that might require discussions. 2. `git pull` + `:restart` + `vim.pack.update(nil, { target = 'lockfile' })`. The only caveats are for new and deleted plugins: - New plugins (not present locally but present in the lockfile) will be installed at lockfile revision during restart. - Deleted plugins (present locally but not present in the lockfile) will still be present: both locally *and* in the lockfile. They can be located by `git diff -- nvim-pack-lock.json` and require manual `vim.pack.del({ 'old-plugin1', 'old-plugin2' })`.
2025-12-25feat(pack)!: change `src` of installed plugin inside `update()` in placeEvgeni Chasnovski1
Problem: Changing `src` of already installed plugin currently takes effect immediately inside `vim.pack.add()` and acts as "delete and later fresh install". Although more robust, this might lead to unintentional data loss (since plugin is deleted) if the plugin was manually modified or the new source is not valid. Also this introduces unnecessary differentiation between "change `version`" and "change `src`" of already installed plugin. Solution: Require an explicit `vim.pack.update()` to change plugin's source. It is done by conditionally changing `origin` remote of the Git repo. The effect does not require update confirmation in order to have new changes fetched from the new `src` right away. If in the future there are more types of plugins supported (i.e. not only Git repos), also do extra work (like delete + install) during `vim.pack.update()`.
2025-11-17fix(pack): rename confirmation buffer to again use `nvim-pack://` schemeEvgeni Chasnovski1
Problem: `nvim://` scheme feels more like a generalized interface that may be requested externally, and it acts like CLI args (roughly). This is how `vscode://` works. Anything that behaves like an "app" or a "protocol" deserves its own scheme. For such Nvim-owned things they will be called `nvim-xx://`. Solution: Use `nvim-pack://confirm#<bufnr>` template for confirmation buffer name instead of `nvim://pack-confirm#<bufnr>`.
2025-11-17feat(pack)!: synchronize lockfile with installed plugins when reading itEvgeni Chasnovski1
Problem: Lockfile can become out of sync with what is actually installed on disk when user performs (somewhat reasonable) manual actions like: - Delete lockfile and expect it to regenerate. - Delete plugin directory without `vim.pack.del()`. - Manually edit lock data in a bad way. Solution: Synchronize lockfile data with installed plugins on every lockfile read. In particular: 1. Install immediately all missing plugins with valid lock data. This helps with "manually delete plugin directory" case by prompting user to figure out how to properly delete a plugin. 2. Repair lock data for properly installed plugins. This helps with "manually deleted lockfile", "manually edited lockfile in an unexpected way", "installation terminated due to timeout" cases. 3. Remove unrepairable corrupted lock data and their plugins. This includes bad lock data for missing plugins and any lock data for corrupted plugins (right now this only means that plugin path is not a directory, but can be built upon). Step 1 also improves usability in case there are lazy loaded plugins that are rarely loaded (like on `FileType` event, for example): - Previously starting with config+lockfile on a new machine only installs rare `vim.pack.add()` plugin after it is called (while an entry in lockfile would still be present). This could be problematic if there is no Internet connection, for example. - Now all plugins from the lockfile are installed before actually executing the first `vim.pack.add()` call in 'init.lua'. And later they are only loaded on a rare `vim.pack.add()` call. --- Synchronizing lockfile on its every read makes it work more robustly if other `vim.pack` functions are called without any `vim.pack.add()`. --- Performance for a regular startup (good lockfile, everything is installed) is not affected and usually even increased. The bottleneck in this area is figuring out which plugins need to be installed. Previously the check was done by `vim.uv.fs_stat()` for every plugin in `vim.pack.add()`. Now it is replaced with a single `vim.fs.dir()` traversal during lockfile sync while later using lockfile data to figure out if plugin needs to be installed. The single `vim.fs.dir` approach scales better than `vim.uv.fs_stat`, but might be less performant if there are many plugins that will be not loaded via `vim.pack.add()` during startup. Rough estimate of how long the same steps (read lockfile and normalize plugin array) take with a single `vim.pack.add()` filled with 43 plugins benchmarking: - Before commit: ~700 ms - After commit: ~550 ms
2025-11-17fix(pack)!: ensure plugin is fully absent if not fully installedEvgeni Chasnovski1
Problem: Currently it is possible to have plugin in a "partial install" state when `git clone` was successfull but `git checkout` was not. This was done to not checkout default branch by default in these situations (for security reasons). The problem is that it adds complexity when both dealing with lockfile (plugin's `rev` might be `nil`) and in how `src` and `version` are treated (wrong `src` - no plugin on disk; wrong `version` - "partial" plugin on disk). Solution: Treat plugin as "installed" if both `git clone` and `git checkout` are successful, while ensuring that not installed plugins are not on disk and in lockfile. This also means that if in 'init.lua' there is a `vim.pack.add()` with bad `version`, for first install there will be an informative error about it BUT next session will also try to install it. The solution is the same - adjust `version` beforehand.
2025-11-17fix(pack)!: adjust install confirm (no error on "No", show names)Evgeni Chasnovski1
Problem: Installation confirmation has several usability issues: - Choosing "No" results in a `vim.pack.add()` error. This was by design to ensure that all later code that *might* reference presumably installed plugin will not get executed. However, this is often too restrictive since there might be no such code (like if plugin's effects are automated in its 'plugin/' directory). Instead the potential code using not installed plugin will throw an error. No error on "No" will also be useful for planned lockfile repair. - List of soon-to-be-installed plugins doesn't mention plugin names. This might be confusing if plugins are installed under different name. Solution: Silently drop installation step if user chose "No" and show plugin names in confirmation text (together with their pretty aligned sources).
2025-11-16feat(pack): update `add()` to handle source change for installed pluginEvgeni Chasnovski1
Problem: Changing `src` of an existing plugin cleanly requires manual `vim.pack.del()` prior to executing `vim.pack.add()` with a new `src`. Solution: Autodetect `src` change for an existing plugin (by comparing against lockfile data). If different - properly delete immediately and treat this as new plugin installation. Alternative solution might be to update `origin` remote in the installed plugin after calling `vim.pack.update()`. Although, doable, this 1) requires more code; and 2) works only for Git plugins (which might be not the only type of plugins in the future). Automatic "delete and clean install" feels more robust.
2025-11-16fix(pack)!: make default `opts.load` in `add()` to work inside 'plugin/'Evgeni Chasnovski1
Problem: Plain `vim.pack.add()` calls (with default `opts.load`) does not fully work if called inside 'plugin/' runtime directory. In particular, 'plugin/' files of newly added plugins are not sourced. This is because `opts.load` is `false` during the whole startup, which means `:packadd!` is used (modify 'runtimepath' but not force source newly added 'plugin/' files). This use case is common due to users organizing their config as separate files in '~/.config/nvim/plugin/'. Solution: Use newly added `v:vim_did_init` to decide default `opts.load` value instead of `v:vim_did_enter`.
2025-11-06fix(pack): use more correct URI for confirmation buffer nameEvgeni Chasnovski1
Problem: Confirmation buffer is named with `nvim-pack://` as scheme prefix and uses buffer id (needed for in-process LSP) as one an entry in the "hierarchical part". Solution: Use `nvim://pack-confirm#<buf>` format with a more ubiquitous `nvim://` prefix and buffer id at the end as the optional fragment.
2025-11-06fix(pack): consistently use "revision" instead of "state"Evgeni Chasnovski1
Problem: In some areas plugin's revision is named "state". This might be confusing for the users. Solution: Consistently use "revision" to indicate "plugin's state on disk".
2025-11-06fix(pack): use full hashes in lockfile and revision descriptionEvgeni Chasnovski1
Problem: Using abbreviated version of commit hashes might be unreliable in the long term (although highly unlikely). Solution: Use full hashes in lockfile and revision description (in confirmation buffer and log). Keep abbreviated hashes when displaying update changes (for brevity).
2025-10-23fix(pack)!: do not trigger `PackChanged[Pre] kind=update` during installEvgeni Chasnovski1
Problem: `PackChanged[Pre]` events with `kind=update` are triggered both during plugin's initial installation and after already installed plugin was updated. It was a deliberate decision to allow writing only a single update hook to act as a dedicated "build" entry point (like execute `make` or `cargo build —release`). This mimics how other plugin managers have a single "build" command. This was a result of 'mini.deps' experience with the different approach: "update" hooks are not run during install. This proved to be confusing as it requires to write two hooks. But also the reason might be that 'mini.deps' names it "checkout" hook instead of "update". However, the `vim.pack` event approach makes it lower cost to handle separate "update" and "install" events. Something like `if ev.data.kind == 'install' or ev.data.kind == 'update' then` instead of two autocommands. Plus this makes clearer separation of events. Solution: do not trigger `PackChanged[Pre] kind=update` event during install.
2025-10-23feat(pack): add `active` field to `PackChanged` event dataEvgeni Chasnovski1
Problem: Inside `PackChanged[Pre]` callbacks it might be useful to tell if the affected plugin is active or not. It is already possible via extra `vim.pack.get({ 'plug-name' })[1].active`, but it is not quite user-friendly for something that might be needed frequently in real world use cases. Solution: Supply extra `active` event data field.
2025-10-23test(pack): refactor 'pack_spec.lua' with better codeEvgeni Chasnovski1
Problem: 'pack_spec.lua' test file's code can be improved. Solution: Refactor some aspects. In particular: - Rewrite `find_in_log` for finding event data in event log into a generator function that from log list returns a finder function. This allows it to take less arguments and be more concise. - Consistently use `local function f()` instead of `local f = function()`. - Prefer to use `fn.readblob()` instead of `fn.readfile()` to assert text from a file. - Use `([[...]]):format()` approach to testing file content (instead of array of strings). Should improve readability. - Universally prefer using "assert" to mean "check if certain expectation about the process holds up" (instead of occasional "validate").
2025-10-12test(plugin/pack_spec): handle pcall path truncation #36143Vlad1
Problem In `vim.pack` the source of any errors is included in the `update` buffer from lua's `pcall` method. Since the full path is known it is replaced in the unit test by the string `VIM_PACK_RUNTIME`. The issue is that `pcall` does not necessarily include the full path, it instead uses the `lua_Debug` `short_src` value which can be truncated. This means depending on where you've cloned the repo locally the test can fail. Solution Change the replacement pattern for the traceback to be more generic and handle any path prefix, not just the value of `vim.env.VIMRUNTIME`.
2025-10-09fix(vim.pack): skip checkout on bad `version` #36038Mickaël RAYBAUD-ROIG1
Refs: #36037
2025-10-07fix(pack): handle lockfile in case of install errors #36064Evgeni Chasnovski1
Problem: If plugin was intended to install but there were errors (like if there is a typo in `src`), lockfile still includes its entry. This leads to all source of problems (like not correct `get()` output, not working `update()`, etc.). Solution: Explicitly account for plugins that were not installed. Alternative solution might be to write a safe `lock_set(plug, field, value)` wrapper (which sets field for a correct `plugins` entry in the lockfile Lua table) and use it after install to detect the change in `version`. However, this always requires an extra pass through plugins on every startup, which is suboptimal. Optimizing for the "happy path" should be a priority in `add()`.
2025-10-05feat(pack): add code actions in confirmation bufferEvgeni Chasnovski1
Problem: No way to granularly operate on plugins when inside confirmation buffer. Solution: Implement code actions for in-process LSP that act on "plugin at cursor": - Update (if has updates). - Skip updating (if has updates). - Delete. Activate via default `gra` or `vim.lsp.buf.code_action()`.
2025-10-05feat(pack): add `[[` and `]]` mappings in confirmation bufferEvgeni Chasnovski1
Problem: No easy/robust way to jump between plugin sections. Solution: Add `[[` and `]]` mappings.
2025-10-04feat(pack): prefer using revision from lockfile during installEvgeni Chasnovski1
Problem: Installing plugin always pulls latest `version` changes (usually from the default branch or "latest version tag"). It is more robust to prefer initial installation to use the latest recorded (i.e. "working") revision. Solution: Prefer using revision from the lockfile (if present) during install. The extra `update()` will pull the latest changes.
2025-10-04feat(pack)!: make `update()` include not active plugins by defaultEvgeni Chasnovski1
Problem: Running `update()` by default doesn't include not active plugins, because there was no way to get relevant `version` to get updates from. This might be a problem in presence of lazy loaded plugins, i.e. ones that can be "not *yet* active" but still needed to be updated. Solution: Include not active plugins by default since their `version` is tracked via lockfile.
2025-10-04feat(pack)!: update `get()` to return revision regardless of `opts.info`Evgeni Chasnovski1
Problem: The revision data is returned behind `opts.info` flag because it required extra Git calls. With lockfile it is not the case. Solution: Use lockfile to always set `rev` field in output of `get()`.
2025-10-04fix(pack)!: use lockfile in `get()` for data about non-active pluginsEvgeni Chasnovski1
Problem: `get()` doesn't return `spec.version` about not-yet-active plugins (because there was no way to know that without `add()`). Solution: Use lockfile data to set `spec.version` of non-active plugins.
2025-10-04feat(pack): add initial lockfile trackingEvgeni Chasnovski1
Problem: Some use cases require or benefit from persistent on disk storage of plugin data (a.k.a. "lockfile"): 1. Allow `update()` to act on not-yet-active plugins. Currently if `add()` is not yet called, then plugin's version is unknown and `update()` can't decide where to look for changes. 2. Efficiently know plugin's dependencies without having to read 'pkg.json' files on every load for every plugin. This is for the future, after there is `packspec` support (or other declaration of dependencies on plugin's side). 3. Allow initial install to check out the exact latest "working" state for a reproducible setup. Currently it pulls the latest available `version.` 4. Ensure that all declared plugins are installed, even if lazy loaded. So that later `add()` does not trigger auto-install (when there might be no Internet connection, for example) and there is no issues with knowing which plugins are used in the config (so even never loaded rare plugins are still installed and can be updated). 5. Allow `add()` to detect if plugin's spec has changed between Nvim sessions and act accordingly. I.e. either set new `src` as origin or enforce `version.` This is not critical and can be done during `update()`, but it might be nice to have. Solution: Add lockfile in JSON format that tracks (adds, updtes, removes) necessary data for described use cases. Here are the required data that enables each point: 1. `name` -> `version` map. 2. `name` -> `dependencies` map. 3. `name` -> `rev` map. Probably also requires `name` -> `src` map to ensure that commit comes from correct origin. 4. `name` -> `src` map. It would be good to also track the order, but that might be too many complications and redundant together with point 2. 5. Map from `name` to all relevant spec fields. I.e. `name` -> `src` and `name` -> `version` for now. Storing data might be too much, but can be discussed, of course. This commit only adds lockfile tracking without implementing actual use cases. It is stored in user's config directory and is suggested to be tracked via version control. Example of a lockfile: ```json { # Extra nesting to more future proof. "plugins": { "plug-a": { "ref": "abcdef1" "src": "https://github.com/user/plug-a", # No `version` means it was `nil` (infer default branch later) }, "plug-b": { "dependencies": ["plugin-a", "plug-c"], "src": "https://github.com/user/plug-b", "ref": "bcdefg2", # Enclose string `version` in quotes "version": "'dev'" }, "plug-c": { "src": "https://github.com/user/plug-c", "ref": "cdefgh3", # Store `vim.version.Range` via its `tostring()` output "version": ">=0.0.0", } } } ```
2025-09-13feat(pack): confirm "Always" to install all plugins #35733Evgeni Chasnovski1
Problem: First clean start with config containing multiple `vim.pack.add()` calls requires to explicitly confirm each one. Although usually a rare occurrence, it still might be tedious. Solution: Add a third choice during installation confirmation that approves current and all next installs within current session. It is reset after session restart.
2025-09-11fix(runtime): add "Added", "Changed", "Removed" to "vim" colorscheme (#35704)zeertzjq1
These were added to syncolor.vim in Vim patch 9.1.0016.
2025-09-07fix(pack): handle Git environment variables #35626Evgeni Chasnovski1
Problem: Some environment variables which are useful when working inside a bare repository can affect any Git operation. Solution: Explicitly unset problematic environment variables.
2025-09-07feat(pack): vim.pack.get() gets VCS info #35631Evgeni Chasnovski1
Problem: Force resolve `spec.version` overrides the information about whether a user supplied `version` or not. Knowing it might be useful in some use cases (like comparing to previously set `spec` to detect if it has changed). Solution: Do not resolve `spec.version`. This also improves speed when triggering events and calling `get()`. - Place default branch first when listing all branches. - Use correct terminology in `get_hash` helper. - Do not return `{ '' }` if there are no tags. Problem: There is no way to get more information about installed plugins, like current revision or default branch (necessary if resolving default `spec.version` manually). As computing Git data migth take some time, also allow `get()` to limit output to only necessary set of plugins. Solution: - introduce arguments to `get(names, opts)`, which follows other `vim.pack` functions. Plugin extra info is returned by default and should be opt-out via `opts.info = false`. - Examples: - Get current revision: `get({ 'plug-name' })[1].rev` - Get default branch: `get({ 'plug_name' })[1].branches[1]` - `update()` and `del()` act on plugins in the same order their names are supplied. This is less surprising. - default `opts.info` to `true` since this simplifies logic for the common user, while still leaving the door open for a faster `get()` if needed.