commit c9965491d01f3da26c227053f9051011b43942f1
parent 8c28507fcf6fa5e39adba2a7f9dc07442aa3bbeb
Author: Evgeni Chasnovski <evgeni.chasnovski@gmail.com>
Date: Fri, 26 Dec 2025 20:05:12 +0200
feat(pack): allow running `update()` without Internet connection
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.
Diffstat:
4 files changed, 49 insertions(+), 8 deletions(-)
diff --git a/runtime/doc/pack.txt b/runtime/doc/pack.txt
@@ -288,12 +288,18 @@ Another approach is to utilize Git's `insteadOf` configuration:
These sources will be used verbatim in |vim.pack-lockfile|, so reusing the
config on different machine will require the same Git configuration.
+Explore installed plugins ~
+• `vim.pack.update(nil, { offline = true })`
+• Navigate between plugins with `[[` and `]]`. List them with `gO`
+ (|vim.lsp.buf.document_symbol()|).
+
Switch plugin's version and/or source ~
• Update 'init.lua' for plugin to have desired `version` and/or `src`. Let's
say, the switch is for plugin named 'plugin1'.
• |:restart|. The plugin's state on disk (revision and/or tracked source) is
not yet changed. Only plugin's `version` in |vim.pack-lockfile| is updated.
-• Execute `vim.pack.update({ 'plugin1' })`. The plugin's source is updated.
+• Execute `vim.pack.update({ 'plugin1' })`. The plugin's source is updated. If
+ only switching version, use `{ offline = true }` option table.
• Review changes and either confirm or discard them. If discarded, revert
`version` change in 'init.lua' as well or you will be prompted again next
time you run |vim.pack.update()|.
@@ -315,7 +321,8 @@ Revert plugin after an update ~
locate the revisions before the latest update, and (carefully) adjust
current lockfile to have those revisions.
• |:restart|.
-• `vim.pack.update({ 'plugin' }, { target = 'lockfile' })`. Read and confirm.
+• `vim.pack.update({ 'plugin' }, { offline = true, target = 'lockfile' })`.
+ Read and confirm.
Synchronize config across machines ~
• On main machine:
@@ -491,6 +498,8 @@ update({names}, {opts}) *vim.pack.update()*
• {opts} (`table?`) A table with the following fields:
• {force}? (`boolean`) Whether to skip confirmation and make
updates immediately. Default `false`.
+ • {offline}? (`boolean`) Whether to skip downloading new
+ updates. Default: `false`.
• {target}? (`string`) How to compute a new plugin revision.
One of:
• "version" (default) - use latest revision matching
diff --git a/runtime/lua/vim/pack.lua b/runtime/lua/vim/pack.lua
@@ -88,6 +88,12 @@
--- These sources will be used verbatim in |vim.pack-lockfile|, so reusing
--- the config on different machine will require the same Git configuration.
---
+---Explore installed plugins ~
+---
+---- `vim.pack.update(nil, { offline = true })`
+---- Navigate between plugins with `[[` and `]]`. List them with `gO`
+--- (|vim.lsp.buf.document_symbol()|).
+---
---Switch plugin's version and/or source ~
---
---- Update 'init.lua' for plugin to have desired `version` and/or `src`.
@@ -95,6 +101,7 @@
---- |:restart|. The plugin's state on disk (revision and/or tracked source)
--- is not yet changed. Only plugin's `version` in |vim.pack-lockfile| is updated.
---- Execute `vim.pack.update({ 'plugin1' })`. The plugin's source is updated.
+--- If only switching version, use `{ offline = true }` option table.
---- Review changes and either confirm or discard them. If discarded, revert
--- `version` change in 'init.lua' as well or you will be prompted again next time
--- you run |vim.pack.update()|.
@@ -119,7 +126,8 @@
--- locate the revisions before the latest update, and (carefully) adjust
--- current lockfile to have those revisions.
---- |:restart|.
----- `vim.pack.update({ 'plugin' }, { target = 'lockfile' })`. Read and confirm.
+---- `vim.pack.update({ 'plugin' }, { offline = true, target = 'lockfile' })`.
+--- Read and confirm.
---
---Synchronize config across machines ~
---
@@ -1152,6 +1160,8 @@ end
--- @inlinedoc
--- @field force? boolean Whether to skip confirmation and make updates immediately. Default `false`.
---
+--- @field offline? boolean Whether to skip downloading new updates. Default: `false`.
+---
--- How to compute a new plugin revision. One of:
--- - "version" (default) - use latest revision matching `version` from plugin specification.
--- - "lockfile" - use revision from the lockfile. Useful for reverting or performing controlled
@@ -1191,7 +1201,7 @@ end
--- @param opts? vim.pack.keyset.update
function M.update(names, opts)
vim.validate('names', names, vim.islist, true, 'list')
- opts = vim.tbl_extend('force', { force = false, target = 'version' }, opts or {})
+ opts = vim.tbl_extend('force', { force = false, offline = false, target = 'version' }, opts or {})
local plug_list = plug_list_from_names(names)
if #plug_list == 0 then
@@ -1217,7 +1227,7 @@ function M.update(names, opts)
end
-- Fetch
- if not opts._offline then
+ if not opts.offline then
-- Using '--tags --force' means conflicting tags will be synced with remote
local args = { 'fetch', '--quiet', '--tags', '--force', '--recurse-submodules=yes', 'origin' }
git_cmd(args, p.path)
@@ -1237,8 +1247,8 @@ function M.update(names, opts)
trigger_event(p, 'PackChanged', 'update')
end
end
- local progress_title = opts.force and (opts._offline and 'Applying updates' or 'Updating')
- or 'Downloading updates'
+ local progress_title = opts.force and (opts.offline and 'Applying updates' or 'Updating')
+ or (opts.offline and 'Computing updates' or 'Downloading updates')
run_list(plug_list, do_update, progress_title)
if needs_lock_write then
diff --git a/runtime/lua/vim/pack/_lsp.lua b/runtime/lua/vim/pack/_lsp.lua
@@ -157,7 +157,7 @@ end
local commands = {
update_plugin = function(plug_data)
- vim.pack.update({ plug_data.name }, { force = true, _offline = true })
+ vim.pack.update({ plug_data.name }, { force = true, offline = true })
end,
skip_update_plugin = function(_) end,
delete_plugin = function(plug_data)
diff --git a/test/functional/plugin/pack_spec.lua b/test/functional/plugin/pack_spec.lua
@@ -1765,6 +1765,28 @@ describe('vim.pack', function()
assert_origin(basic_src)
end)
+ it('can do offline update', function()
+ local defbranch_path = pack_get_plug_path('defbranch')
+ local defbranch_lua_file = vim.fs.joinpath(defbranch_path, 'lua', 'defbranch.lua')
+
+ n.exec_lua(function()
+ vim.pack.add({ { src = repos_src.defbranch, version = 'main' } })
+ end)
+
+ track_nvim_echo()
+
+ eq('return "defbranch dev"', fn.readblob(defbranch_lua_file))
+ n.exec_lua(function()
+ vim.pack.update({ 'defbranch' }, { offline = true })
+ end)
+
+ -- There should be no progress report about downloading updates
+ assert_progress_report('Computing updates', { 'defbranch' })
+
+ n.exec('write')
+ eq('return "defbranch main"', fn.readblob(defbranch_lua_file))
+ end)
+
it('shows progress report', function()
track_nvim_echo()
exec_lua(function()