|
|
@ -4,118 +4,221 @@ local package_manager = require('plugin/vim_plug') |
|
|
|
local file_exists = require('lib/file_exists') |
|
|
|
local directory_exists = require('lib/directory_exists') |
|
|
|
local has_command = require('lib/has_command') |
|
|
|
local merge = require('lib/merge') |
|
|
|
|
|
|
|
function M:initialize() |
|
|
|
self.path = self.config.path or "~/.config/nvim/plugins/" |
|
|
|
self.auto_install = self.config.auto_install or true |
|
|
|
self.auto_cleanup = self.config.auto_cleanup or true |
|
|
|
self.need_install = false |
|
|
|
self.plugins = {} |
|
|
|
self.keymap = nukevim.modules:get('keymap') |
|
|
|
end |
|
|
|
|
|
|
|
-- Add a new plugin to the plugin system |
|
|
|
function M:add(name, opts, config, keys, enable) |
|
|
|
if (enable == nil) then enable = true end |
|
|
|
plugin = { initialized = false, enable = enable, health = { }, name = name, opts = opts or {}, config = config or {}, keys = keys or {} } |
|
|
|
table.insert(self.plugins, plugin) |
|
|
|
return plugin |
|
|
|
end |
|
|
|
|
|
|
|
-- Check whether a plugin is registered and available in the plugin system |
|
|
|
function M:has(name) |
|
|
|
for idx, plugin in pairs(self.plugins) do |
|
|
|
if (plugin.name == name) then |
|
|
|
return plugin.initialized |
|
|
|
end |
|
|
|
end |
|
|
|
return false |
|
|
|
end |
|
|
|
|
|
|
|
|
|
|
|
-- Register all plugins from the config with ourself |
|
|
|
function M:register() |
|
|
|
for idx, plugin in pairs(self.config.plugins) do |
|
|
|
reg_plugin = self:add(plugin.name, plugin.config or nil, plugin.keys or nil, plugin.requires or nil) |
|
|
|
reg_plugin.enable = plugin.enable |
|
|
|
self:add(plugin.name, plugin.opts, plugin.config, plugin.keys, plugin.enable) |
|
|
|
end |
|
|
|
end |
|
|
|
|
|
|
|
-- Actually initialize all the plugins |
|
|
|
function M:commit() |
|
|
|
package_manager:initialize(self.path) |
|
|
|
package_manager:enter() |
|
|
|
for idx, plugin in pairs(self.plugins) do |
|
|
|
-- Check that this plugin is enabled, dependencies are satisfied, etc |
|
|
|
should_initialize = self:shouldInitializePlugin(plugin) |
|
|
|
-- HACK: Should have a better way to detect this |
|
|
|
if (plugin.enable == false and plugin.enable_diag_message == 'not installed' and self.auto_install) then |
|
|
|
should_initialize = true |
|
|
|
end |
|
|
|
if (should_initialize) then |
|
|
|
-- Add to the plugin manager |
|
|
|
package_manager:add(plugin.name, plugin.config or nil) |
|
|
|
|
|
|
|
-- Register any key bindings it specifies |
|
|
|
if (plugin.keys ~= nil) then |
|
|
|
for idx, key in ipairs(plugin.keys) do |
|
|
|
self.keymap:add(key) |
|
|
|
end |
|
|
|
package_manager:enter(); |
|
|
|
|
|
|
|
-- build out our temporary array of plugins |
|
|
|
-- instead of building out a proper dependency tree, we just iterate through |
|
|
|
-- removing plugins as they're instantiated or determined to not be instantiable (lol) |
|
|
|
-- and we have nothing left or don't change the size of the list |
|
|
|
plugins = {} |
|
|
|
for idx,plugin in pairs(self.plugins) do |
|
|
|
table.insert(plugins, plugin) |
|
|
|
end |
|
|
|
|
|
|
|
last_size = #plugins |
|
|
|
while true do |
|
|
|
|
|
|
|
for idx=#plugins,1,-1 do |
|
|
|
plugin = plugins[idx] |
|
|
|
|
|
|
|
plugin_enabled, plugin_enabled_messages = self:isPluginEnabled(plugin) |
|
|
|
plugin_installed, plugin_installed_messages = self:isPluginInstalled(plugin) |
|
|
|
plugin_required_providers, plugin_providers_messages = self:checkPluginRequiredProviders(plugin) |
|
|
|
plugin_required_binaries, plugin_binaries_messages = self:checkPluginRequiredBinaries(plugin) |
|
|
|
plugin_required_plugins, plugin_plugins_messages = self:checkPluginRequiredPlugins(plugin) |
|
|
|
|
|
|
|
if (plugin_enabled) then |
|
|
|
plugin.health = merge({plugin_enabled_messages, plugin_installed_messages, plugin_providers_messages, plugin_binaries_messages, plugin_plugins_messages}) |
|
|
|
else |
|
|
|
plugin.health = plugin_enabled_messages |
|
|
|
end |
|
|
|
|
|
|
|
-- If the plugin has a lua module in nukevim, load and register that |
|
|
|
if (file_exists(vim.env.HOME .. '/.config/nvim/lua/plugin/' .. plugin.name:gsub('%.', '-') .. '.lua')) then |
|
|
|
local plugin_module = require('plugin/' .. plugin.name:gsub('%.', '-')):new() |
|
|
|
plugin_module:configure(plugin.config) |
|
|
|
nukevim.modules:addInstance(plugin.name, plugin_module) |
|
|
|
-- If everything passed, then we're good |
|
|
|
-- If the only thing failing is that the plugin isn't installed _and_ |
|
|
|
-- we have auto_install enabled, then register it and mark needs_install |
|
|
|
-- so we can just install it after. |
|
|
|
if (plugin_enabled and (plugin_installed or self.auto_install) and plugin_required_providers and plugin_required_binaries and plugin_required_plugins) then |
|
|
|
self:enablePlugin(plugin) |
|
|
|
if (not plugin_installed) then self.needs_install = true end |
|
|
|
table.remove(plugins, idx) |
|
|
|
|
|
|
|
-- Otherwise if any of these conditions occur then we know the plugin |
|
|
|
-- will never be enabled so we can explicitly disable and remove it. |
|
|
|
elseif ( |
|
|
|
(not plugin_enabled) or |
|
|
|
(not plugin_installed) or |
|
|
|
(not plugin_required_providers) or |
|
|
|
(not plugin_required_binaries) |
|
|
|
) then |
|
|
|
plugin.enable = false |
|
|
|
table.remove(plugins, idx) |
|
|
|
|
|
|
|
else |
|
|
|
-- Otherwise, the only thing that could have failed is a required |
|
|
|
-- plugin, so leave it in for next pass to see if the dependency |
|
|
|
-- is now satisfied. |
|
|
|
|
|
|
|
end |
|
|
|
|
|
|
|
end |
|
|
|
|
|
|
|
if (#plugins == last_size) then |
|
|
|
-- we didn't do anything this pass, give up |
|
|
|
break |
|
|
|
end |
|
|
|
last_size = #plugins |
|
|
|
end |
|
|
|
|
|
|
|
package_manager:exit() |
|
|
|
|
|
|
|
end |
|
|
|
|
|
|
|
function M:shouldInitializePlugin(plugin) |
|
|
|
if (plugin.enable ~= nil and plugin.enable ~= true) then |
|
|
|
plugin.enable = false |
|
|
|
plugin.enable_diag_class = 'warn' |
|
|
|
plugin.enable_diag_message = 'explicitly disabled' |
|
|
|
return false |
|
|
|
end |
|
|
|
-- Once we determine a plugin should be loaded, this actually does it |
|
|
|
function M:enablePlugin(plugin) |
|
|
|
package_manager:add(plugin.name, plugin.opts) |
|
|
|
|
|
|
|
if (plugin.requires ~= nil) then |
|
|
|
for i=1,#plugin.requires do |
|
|
|
if (not has_command(plugin.requires[i])) then |
|
|
|
plugin.enable = false |
|
|
|
plugin.enable_diag_class = 'error' |
|
|
|
plugin.enable_diag_message = ('missing dependency: %s'):format(plugin.requires[i]) |
|
|
|
return false |
|
|
|
end |
|
|
|
-- Register key bindings |
|
|
|
if (plugin.keys ~= nil) then |
|
|
|
for idx, key in ipairs(plugin.keys) do |
|
|
|
self.keymap:add(key) |
|
|
|
end |
|
|
|
end |
|
|
|
|
|
|
|
name = plugin.name:match('/(.*)') |
|
|
|
if (not directory_exists(self.path .. '/' .. name)) then |
|
|
|
plugin.enable = false |
|
|
|
plugin.enable_diag_class = 'error' |
|
|
|
plugin.enable_diag_message = 'not installed' |
|
|
|
plugin.needs_install = true |
|
|
|
return false |
|
|
|
-- If the plugin has a lua module in nukevim, load and register that |
|
|
|
if (file_exists(vim.env.HOME .. '/.config/nvim/lua/plugin/' .. plugin.name:gsub('%.', '-') .. '.lua')) then |
|
|
|
local plugin_module = require('plugin/' .. plugin.name:gsub('%.', '-')):new() |
|
|
|
plugin_module:configure(plugin.config) |
|
|
|
nukevim.modules:addInstance(plugin.name, plugin_module) |
|
|
|
end |
|
|
|
|
|
|
|
plugin.enable = true |
|
|
|
plugin.enable_diag_class = 'ok' |
|
|
|
plugin.enable_diag_message = 'all checks passed'; |
|
|
|
return true |
|
|
|
plugin.initialized = true |
|
|
|
end |
|
|
|
|
|
|
|
function M:boot() |
|
|
|
if (self.auto_install and package_manager:hasPendingPackages()) then |
|
|
|
package_manager:install() |
|
|
|
-- Check whether a plugin is supposed to be enabled |
|
|
|
function M:isPluginEnabled(plugin) |
|
|
|
if (plugin.enable == nil or plugin.enable == true) then |
|
|
|
return true, { } |
|
|
|
else |
|
|
|
for idx, plugin in pairs(self.plugins) do |
|
|
|
if (plugin.needs_install == true) then |
|
|
|
package_manager:install() |
|
|
|
break |
|
|
|
end |
|
|
|
end |
|
|
|
return false, { { 'info', 'explicitly disabled' } } |
|
|
|
end |
|
|
|
end |
|
|
|
|
|
|
|
if (self.auto_cleanup) then |
|
|
|
package_manager:cleanup() |
|
|
|
-- Check whether a plugin is installed |
|
|
|
function M:isPluginInstalled(plugin) |
|
|
|
if (package_manager:installed(plugin.name)) then |
|
|
|
return true, { } |
|
|
|
else |
|
|
|
return false, { { 'error', 'not installed' } } |
|
|
|
end |
|
|
|
end |
|
|
|
|
|
|
|
function M:add(name, config, keys, requires) |
|
|
|
plugin = { name = name, config = config, keys = keys, requires = requires } |
|
|
|
table.insert(self.plugins, plugin) |
|
|
|
return plugin |
|
|
|
-- Check whether all of a plugin's declared providers are available |
|
|
|
function M:checkPluginRequiredProviders(plugin) |
|
|
|
if (plugin.opts.requires == nil or plugin.opts.requires.provider == nil) then |
|
|
|
return true, { } |
|
|
|
end |
|
|
|
messages = {} |
|
|
|
for idx,provider in ipairs(plugin.opts.requires.provider) do |
|
|
|
if (vim.fn.has(provider) == 0) then |
|
|
|
table.insert(messages, { 'error', ('missing provider: %s'):format(provider) }) |
|
|
|
end |
|
|
|
end |
|
|
|
if (#messages == 0) then |
|
|
|
return true, { } |
|
|
|
else |
|
|
|
return false, messages |
|
|
|
end |
|
|
|
end |
|
|
|
|
|
|
|
function M:has(name) |
|
|
|
for idx, plugin in pairs(self.plugins) do |
|
|
|
if (plugin.name == name) then |
|
|
|
return plugin.enable == nil or plugin.enable == true |
|
|
|
-- Check whether all of a plugin's declared system binaries are available |
|
|
|
function M:checkPluginRequiredBinaries(plugin) |
|
|
|
if (plugin.opts.requires == nil or plugin.opts.requires.binary == nil) then |
|
|
|
return true, { } |
|
|
|
end |
|
|
|
messages = {} |
|
|
|
for idx,binary in ipairs(plugin.opts.requires.binary) do |
|
|
|
if (not has_command(binary)) then |
|
|
|
table.insert(messages, { 'error', ('missing binary: %s'):format(binary) }) |
|
|
|
end |
|
|
|
end |
|
|
|
return false |
|
|
|
if (#messages == 0) then |
|
|
|
return true, { } |
|
|
|
else |
|
|
|
return false, messages |
|
|
|
end |
|
|
|
end |
|
|
|
|
|
|
|
-- Check whether all of a plugin's dependencies on other plugins are satisfied |
|
|
|
function M:checkPluginRequiredPlugins(plugin) |
|
|
|
if (plugin.opts.requires == nil or plugin.opts.requires.plugin == nil) then |
|
|
|
return true, { } |
|
|
|
end |
|
|
|
messages = {} |
|
|
|
for idx, plugin in ipairs(plugin.opts.requires.plugin) do |
|
|
|
if (not self:has(plugin)) then |
|
|
|
table.insert(messages, { 'error', ('missing required plugin: %s'):format(plugin) }) |
|
|
|
end |
|
|
|
for i, p in pairs(self.plugins) do |
|
|
|
if (p.name == plugin and p.enable == false) then |
|
|
|
table.insert(messages, { 'error', ('missing required plugin: %s'):format(plugin) }) |
|
|
|
end |
|
|
|
end |
|
|
|
end |
|
|
|
if (#messages == 0) then |
|
|
|
return true, { } |
|
|
|
else |
|
|
|
return false, messages |
|
|
|
end |
|
|
|
end |
|
|
|
|
|
|
|
function M:boot() |
|
|
|
if (self.auto_install and (package_manager:hasPendingPackages() or self.needs_install)) then |
|
|
|
self:install() |
|
|
|
end |
|
|
|
|
|
|
|
if (self.auto_cleanup) then |
|
|
|
self:cleanup() |
|
|
|
end |
|
|
|
end |
|
|
|
|
|
|
|
function M:install() |
|
|
@ -130,7 +233,13 @@ function M:health() |
|
|
|
messages = {} |
|
|
|
|
|
|
|
for idx, plugin in pairs(self.plugins) do |
|
|
|
table.insert(messages, { plugin.enable_diag_class, ('%s: %s'):format(plugin.name, plugin.enable_diag_message) }) |
|
|
|
if (#plugin.health == 0) then |
|
|
|
table.insert(messages, { 'ok', ('%s: %s'):format(plugin.name, 'all checks passed') }) |
|
|
|
else |
|
|
|
for i, msg in pairs(plugin.health) do |
|
|
|
table.insert(messages, { msg[1], ('%s: %s'):format(plugin.name, msg[2]) }) |
|
|
|
end |
|
|
|
end |
|
|
|
end |
|
|
|
|
|
|
|
return { |
|
|
|