Skip to content

Commit

Permalink
feat: support updating local plugins with fast-forward
Browse files Browse the repository at this point in the history
  • Loading branch information
lewis6991 committed Feb 4, 2024
1 parent eb654d2 commit 9ebe70f
Show file tree
Hide file tree
Showing 7 changed files with 149 additions and 79 deletions.
49 changes: 49 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,55 @@ pckr.add{
}
```

### Automatically find local plugins

This snippet can be used to automatically detect local plugins in a particular directory.

```lua
local local_plugin_dir = vim.env.HOME..'/projects/'

local function resolve(x)
if type(x) == 'string' and x:sub(1, 1) ~= '/' then
local name = vim.split(x, '/')[2]
local loc_install = vim.fs.join_paths(local_plugin_dir, name)
if name ~= '' and vim.fn.isdirectory(loc_install) == 1 then
return loc_install
end
end
end

local function try_get_local(spec)
if type(spec) == 'string' then
return resolve(spec) or spec
end

if not spec or type(spec[1]) ~= 'string' then
return spec
end

return resolve(spec[1]) or spec[1]
end

local function walk_spec(spec, field, fn)
if type(spec[field]) == 'table' then
for j in ipairs(spec[field]) do
walk_spec(spec[field], j, fn)
end
walk_spec(spec[field], 'requires', fn)
end
spec[field] = fn(spec[field])
end

local init {
'nvim-treesitter/nvim-treesitter'
-- plugins spec
}

walk_spec({init}, 1, try_get_local)

require('pckr').add(init)
```

## Debugging
`pckr.nvim` logs to `stdpath(cache)/pckr.nvim.log`. Looking at this file is usually a good start
if something isn't working as expected.
Expand Down
22 changes: 12 additions & 10 deletions lua/pckr/actions.lua
Original file line number Diff line number Diff line change
Expand Up @@ -224,22 +224,26 @@ local update_task = a.sync(function(plugin, disp, __cb)
end

local plugin_type = require('pckr.plugin_types')[plugin.type]
local actual_update = false

plugin.err = plugin_type.updater(plugin, disp)
if not plugin.err and plugin.type == 'git' then

local did_update = false
if not plugin.err then
local revs = plugin.revs
actual_update = revs[1] ~= revs[2]
if actual_update then
log.fmt_debug('Updated %s', plugin.name)
plugin.err = post_update_hook(plugin, disp)
end
did_update = revs[1] ~= revs[2]
end

if did_update then
log.fmt_debug('Updated %s', plugin.name)
plugin.err = post_update_hook(plugin, disp)
end

if plugin.err then
disp:task_failed(plugin.name, 'failed to update', plugin.err)
log.fmt_debug('Failed to update %s: %s', plugin.name, plugin.err)
elseif actual_update then
elseif not did_update then
disp:task_done(plugin.name, 'already up to date')
else
local info = {}
local ncommits = 0
if plugin.messages then
Expand All @@ -256,8 +260,6 @@ local update_task = a.sync(function(plugin, disp, __cb)
-- msg = fmt('updated: %s...%s', revs[1], revs[2])
local msg = fmt('updated: %d new commits', ncommits)
disp:task_succeeded(plugin.name, msg, info)
else
disp:task_done(plugin.name, 'already up to date')
end

return plugin.name, plugin.err
Expand Down
29 changes: 8 additions & 21 deletions lua/pckr/fsstate.lua
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,15 @@ end
--- Get installed plugins in `dir`.
--- @param dir string Directory to search
--- @return table<string,string> plugins
local function get_installed_plugins(dir)
local function get_dir_plugins(dir)
local plugins = {} --- @type table<string,string>

for name, ty in vim.fs.dir(dir) do
if ty ~= 'file' then
plugins[util.join_paths(dir, name)] = name
local path = util.join_paths(dir, name)
if vim.uv.fs_stat(path) then
plugins[path] = name
end
end
end

Expand All @@ -47,8 +50,8 @@ end
--- @param plugins table<string,Pckr.Plugin>
--- @return table<string,string>
function M.find_extra_plugins(plugins)
local opt_plugins = get_installed_plugins(config.opt_dir)
local start_plugins = get_installed_plugins(config.start_dir)
local opt_plugins = get_dir_plugins(config.opt_dir)
local start_plugins = get_dir_plugins(config.start_dir)

local extra = {} --- @type table<string,string>

Expand All @@ -66,29 +69,13 @@ function M.find_extra_plugins(plugins)
return extra
end

--- @param plugin Pckr.Plugin
--- @param opt_plugins table<string,string> Plugins installed in config.opt_dir
--- @param start_plugins table<string,string> Plugins installed in config.start_dir
--- @return boolean
local function plugin_installed(plugin, opt_plugins, start_plugins)
for path in pairs(plugin.start and start_plugins or opt_plugins) do
if cmp_paths(path, plugin.install_path) then
return true
end
end
return false
end

--- @param plugins table<string,Pckr.Plugin>
--- @return string[]
function M.find_missing_plugins(plugins)
local opt_plugins = get_installed_plugins(config.opt_dir)
local start_plugins = get_installed_plugins(config.start_dir)

local missing_plugins = {} --- @type string[]

for plugin_name, plugin in pairs(plugins) do
if not plugin_installed(plugin, opt_plugins, start_plugins) then
if not plugin.installed then
missing_plugins[#missing_plugins+1] = plugin_name
end
end
Expand Down
46 changes: 26 additions & 20 deletions lua/pckr/plugin.lua
Original file line number Diff line number Diff line change
Expand Up @@ -56,29 +56,26 @@ local config = require('pckr.config')
--- @field messages? string
--- @field err? string

--- @alias Pckr.PluginType
--- | 'git'
--- | 'local'
--- | 'unknown'
--- @alias Pckr.PluginType 'git' | 'local'

local M = {
--- @type table<string,Pckr.Plugin>
plugins = {},
}

--- @param path string
--- @param psuedo_path string
--- @return string, Pckr.PluginType
local function guess_plugin_type(path)
if vim.startswith(path, 'git://') or vim.startswith(path, 'http') or path:match('@') then
return path, 'git'
local function guess_plugin_type(psuedo_path)
if vim.startswith(psuedo_path, 'git://') or vim.startswith(psuedo_path, 'http') or psuedo_path:match('@') then
return psuedo_path, 'git'
end

if vim.fn.isdirectory(path) ~= 0 then
return path, 'local'
if vim.fn.isdirectory(psuedo_path) ~= 0 then
return psuedo_path, 'local'
end

path = table.concat(vim.split(path, '\\', { plain = true }), '/')
return config.git.default_url_format:format(path), 'git'
psuedo_path = table.concat(vim.split(psuedo_path, '\\', { plain = true }), '/')
return config.git.default_url_format:format(psuedo_path), 'git'
end

--- @param text string
Expand Down Expand Up @@ -169,7 +166,7 @@ local function process_spec_item(spec0, required_by)
return
end

local name, path = get_plugin_name(id)
local name, psuedo_path = get_plugin_name(id)

if name == '' then
log.fmt_warn('"%s" is an invalid plugin name!', id)
Expand All @@ -193,13 +190,22 @@ local function process_spec_item(spec0, required_by)
log.debug('Overriding simple plugin spec: ' .. name)
end

local install_path_dir = spec.start and config.start_dir or config.opt_dir
local install_path = util.join_paths(install_path_dir, name)
local url, plugin_type = guess_plugin_type(psuedo_path)

local url, ptype = guess_plugin_type(path)
local is_start = spec.start

if ptype == 'local' then
install_path = path
if plugin_type == 'local' and is_start then
log.fmt_warn('Ignoring start=true with local plugin', name)
is_start = false
end

local install_path --- @type string

if plugin_type == 'local' then
install_path = psuedo_path
else
local install_path_dir = is_start and config.start_dir or config.opt_dir
install_path = util.join_paths(install_path_dir, name)
end

--- @type Pckr.Plugin
Expand All @@ -209,15 +215,15 @@ local function process_spec_item(spec0, required_by)
rev = spec.rev,
tag = spec.tag,
commit = spec.commit,
start = spec.start,
start = is_start,
simple = simple,
cond = spec.cond ~= true and spec.cond or nil, -- must be function or 'false'
run = spec.run,
lock = spec.lock,
url = remove_ending_git_url(url),
install_path = install_path,
installed = vim.fn.isdirectory(install_path) ~= 0,
type = ptype,
type = plugin_type,
config_pre = normconfig(spec.config_pre),
config = normconfig(spec.config),
revs = {},
Expand Down
14 changes: 9 additions & 5 deletions lua/pckr/plugin_types.lua
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,18 @@
--- @field get_rev fun(plugin: Pckr.Plugin): string?

--- @type table<string,Pckr.PluginHandler>
local plugin_types = {}
local M = {}

return setmetatable(plugin_types, {
__index = function(_, k)
return setmetatable(M, {
--- @param t table<string,Pckr.PluginHandler>
--- @param k string
--- @return Pckr.PluginHandler?
__index = function(t, k)
if k == 'git' then
return require('pckr.plugin_types.git')
t[k] = require('pckr.plugin_types.git')
elseif k == 'local' then
return require('pckr.plugin_types.local')
t[k] = require('pckr.plugin_types.local')
end
return t[k]
end,
})
52 changes: 33 additions & 19 deletions lua/pckr/plugin_types/git.lua
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ local async = a.sync
local fmt = string.format
local uv = vim.loop

--- @class Pckr.PluginHandler
--- @class Pckr.PluginHandler.Git: Pckr.PluginHandler
local M = {}

--- @type string[]
Expand Down Expand Up @@ -82,7 +82,12 @@ end
local function git_run(args, opts)
opts = opts or {}
opts.env = opts.env or job_env
local obj = jobs.run({ config.git.cmd, unpack(args) }, opts)
local obj = jobs.run({
config.git.cmd,
'-c', 'advice.diverging=false',
'-c', 'advice.resolveConflict=false',
unpack(args)
}, opts)
local ok = obj.code == 0 and obj.signal == 0
if ok then
return true, obj.stdout
Expand Down Expand Up @@ -464,8 +469,9 @@ end

--- @param plugin Pckr.Plugin
--- @param disp Pckr.Display
--- @param ff_only? boolean
--- @return boolean, string?
local function update(plugin, disp)
local function update(plugin, disp, ff_only)
disp:task_update(plugin.name, 'checking current commit...')

plugin.revs[1] = get_head(plugin.install_path)
Expand Down Expand Up @@ -493,10 +499,19 @@ local function update(plugin, disp)

update_task('pulling updates...')

ok, out = checkout(plugin, update_task)
if ff_only then
ok, out = git_run({ 'merge', '--ff-only', '--progress'}, {
cwd = plugin.install_path,
on_stderr = function(chunk)
update_task('fast forwarding...', process_progress(chunk))
end,
})
else
ok, out = checkout(plugin, update_task)
end

if not ok then
log_err(plugin, 'failed checkout', out)
log_err(plugin, 'failed update', out)
return false, out
end

Expand All @@ -519,30 +534,29 @@ local function update(plugin, disp)
return false, out
end

plugin.messages = out

ok, out = mark_breaking_changes(plugin, disp)
local out2 --- @type string
ok, out2 = mark_breaking_changes(plugin, disp)
if not ok then
log_err(plugin, 'failed marking breaking changes', out)
return false, out
log_err(plugin, 'failed marking breaking changes', out2)
return false, out2
end
end

return true
return true, out
end

--- @param plugin Pckr.Plugin
--- @param disp Pckr.Display
--- @param ff_only? boolean
--- @return string?
M.updater = async(function(plugin, disp)
local ok, out = update(plugin, disp)
if ok then
plugin.messages = out
return
M.updater = async(function(plugin, disp, ff_only)
local ok, out = update(plugin, disp, ff_only)
if not ok then
plugin.err = out
return out
end
plugin.err = out
return out
end, 2)
plugin.messages = out
end, 3)

--- @param plugin Pckr.Plugin
--- @return string?
Expand Down
Loading

0 comments on commit 9ebe70f

Please sign in to comment.