From c89092c6667e0dfa741e721949571dd8d1c3d3e5 Mon Sep 17 00:00:00 2001 From: Wil Thomason Date: Sun, 4 Dec 2022 20:39:26 -0600 Subject: [PATCH] feat: create singleton events/commands/keymaps for load handlers Implementing the idea @lewis6991 proposed for handling multiple loads on the same command/keymap/event added by separate calls to `add`. Changes the loaders to keep a table of plugins associated with a given load trigger and only create new events/keymaps/commands for new triggers, otherwise simply appending to the set of plugins to be loaded on the given trigger. One note: we may need to make loaders delete plugins from these lists after load, to handle cases like: - add(event, plugins_1) - event happens, plugins_1 loaded - add(event, plugins_2) - event happens, without deleting plugins_1 gets loaded again I don't believe that this is a potential bug introduced by this change---it could've happened with the more naive handlers as well --- lua/packer/handlers/cmd.lua | 22 +++++++++++----------- lua/packer/handlers/event.lua | 25 +++++++++++-------------- lua/packer/handlers/ft.lua | 31 ++++++++++++++----------------- lua/packer/handlers/keys.lua | 22 ++++++++++++---------- lua/packer/handlers/util.lua | 21 --------------------- lua/packer/loader.lua | 5 ----- lua/packer/plugin.lua | 10 ---------- teal/packer/handlers/cmd.tl | 22 +++++++++++----------- teal/packer/handlers/event.tl | 25 +++++++++++-------------- teal/packer/handlers/ft.tl | 31 ++++++++++++++----------------- teal/packer/handlers/keys.tl | 22 ++++++++++++---------- teal/packer/handlers/util.tl | 21 --------------------- teal/packer/loader.tl | 5 ----- teal/packer/plugin.tl | 10 ---------- 14 files changed, 96 insertions(+), 176 deletions(-) delete mode 100644 lua/packer/handlers/util.lua delete mode 100644 teal/packer/handlers/util.tl diff --git a/lua/packer/handlers/cmd.lua b/lua/packer/handlers/cmd.lua index 37b9189fa..989d6226c 100644 --- a/lua/packer/handlers/cmd.lua +++ b/lua/packer/handlers/cmd.lua @@ -1,26 +1,26 @@ -local util = require('packer.handlers.util') +local command_plugins = {} return function(plugins, loader) - local commands = {} + local new_commands = {} for _, plugin in pairs(plugins) do if plugin.cmd then for _, cmd in ipairs(plugin.cmd) do - commands[cmd] = commands[cmd] or {} - table.insert(commands[cmd], plugin) + if not command_plugins[cmd] then + command_plugins[cmd] = {} + new_commands[#new_commands + 1] = cmd + end + + table.insert(command_plugins[cmd], plugin) end end end - for cmd, cplugins in pairs(commands) do - - util.register_destructor(cplugins, function() - vim.api.nvim_del_user_command(cmd) - end) + for _, cmd in ipairs(new_commands) do vim.api.nvim_create_user_command(cmd, function(args) - loader(cplugins) - + vim.api.nvim_del_user_command(cmd) + loader(command_plugins[cmd]) local lines = args.line1 == args.line2 and '' or (args.line1 .. ',' .. args.line2) vim.cmd(string.format( '%s %s%s%s %s', diff --git a/lua/packer/handlers/event.lua b/lua/packer/handlers/event.lua index f142f08ee..3dbf73e6a 100644 --- a/lua/packer/handlers/event.lua +++ b/lua/packer/handlers/event.lua @@ -1,33 +1,30 @@ -local util = require('packer.handlers.util') +local event_plugins = {} return function(plugins, loader) - local events = {} - + local new_events = {} for _, plugin in pairs(plugins) do if plugin.event then for _, event in ipairs(plugin.event) do - events[event] = events[event] or {} - table.insert(events[event], plugin) + if not event_plugins[event] then + event_plugins[event] = {} + new_events[#new_events + 1] = event + end + + table.insert(event_plugins[event], plugin) end end end - for event, eplugins in pairs(events) do + for _, event in ipairs(new_events) do local ev, pattern = unpack(vim.split(event, '%s+')) - - local id = vim.api.nvim_create_autocmd(ev, { + vim.api.nvim_create_autocmd(ev, { pattern = pattern, once = true, callback = function() - loader(eplugins) + loader(event_plugins[event]) vim.api.nvim_exec_autocmds(event, { modeline = false }) end, }) - - util.register_destructor(eplugins, function() - pcall(vim.api.nvim_del_autocmd, id) - end) - end end \ No newline at end of file diff --git a/lua/packer/handlers/ft.lua b/lua/packer/handlers/ft.lua index 6b0236ef1..e868f5d77 100644 --- a/lua/packer/handlers/ft.lua +++ b/lua/packer/handlers/ft.lua @@ -1,12 +1,10 @@ -local join_paths = require('packer.util').join_paths - -local util = require('packer.handlers.util') +local util = require('packer.util') local function detect_ftdetect(plugin_path) local source_paths = {} for _, parts in ipairs({ { 'ftdetect' }, { 'after', 'ftdetect' } }) do parts[#parts + 1] = [[**/*.\(vim\|lua\)]] - local path = join_paths(plugin_path, unpack(parts)) + local path = util.join_paths(plugin_path, unpack(parts)) local ok, files = pcall(vim.fn.glob, path, false, true) if not ok then if string.find(files, 'E77') then @@ -22,38 +20,37 @@ local function detect_ftdetect(plugin_path) return source_paths end -return function(plugins, loader) - local fts = {} +local ft_plugins = {} +return function(plugins, loader) + local new_fts = {} local ftdetect_paths = {} - for _, plugin in pairs(plugins) do if plugin.ft then for _, ft in ipairs(plugin.ft) do - fts[ft] = fts[ft] or {} - table.insert(fts[ft], plugin) + if not ft_plugins[ft] then + ft_plugins[ft] = {} + new_fts[#new_fts + 1] = ft + end + + table.insert(ft_plugins[ft], plugin) end vim.list_extend(ftdetect_paths, detect_ftdetect(plugin.install_path)) end end - for ft, fplugins in pairs(fts) do - local id = vim.api.nvim_create_autocmd('FileType', { + for _, ft in ipairs(new_fts) do + vim.api.nvim_create_autocmd('FileType', { pattern = ft, once = true, callback = function() - loader(fplugins) + loader(ft_plugins[ft]) for _, group in ipairs({ 'filetypeplugin', 'filetypeindent', 'syntaxset' }) do vim.api.nvim_exec_autocmds('FileType', { group = group, pattern = ft, modeline = false }) end end, }) - - util.register_destructor(fplugins, function() - pcall(vim.api.nvim_del_autocmd, id) - end) - end if #ftdetect_paths > 0 then diff --git a/lua/packer/handlers/keys.lua b/lua/packer/handlers/keys.lua index 23485a9de..8f4d614f5 100644 --- a/lua/packer/handlers/keys.lua +++ b/lua/packer/handlers/keys.lua @@ -1,30 +1,32 @@ -local util = require('packer.handlers.util') +local keymap_plugins = {} return function(plugins, loader) - local keymaps = {} + local new_keymaps = {} for _, plugin in pairs(plugins) do if plugin.keys then for _, keymap in ipairs(plugin.keys) do - keymaps[keymap] = keymaps[keymap] or {} - table.insert(keymaps[keymap], plugin) + if not keymap_plugins[keymap] then + keymap_plugins[keymap] = {} + new_keymaps[#new_keymaps + 1] = keymap + end + + table.insert(keymap_plugins[keymap], plugin) end end end - for keymap, kplugins in pairs(keymaps) do + for _, keymap in ipairs(new_keymaps) do + local kplugins = keymap_plugins[keymap] local names = vim.tbl_map(function(e) return e.name end, kplugins) - util.register_destructor(kplugins, function() - vim.keymap.del(keymap[1], keymap[2]) - end) - vim.keymap.set(keymap[1], keymap[2], function() + vim.keymap.del(keymap[1], keymap[2]) loader(kplugins) vim.api.nvim_feedkeys(keymap[2], keymap[1], false) end, { - desc = 'Packer lazy load: ' .. table.concat(names, ', '), + desc = 'packer.nvim lazy load: ' .. table.concat(names, ', '), silent = true, }) end diff --git a/lua/packer/handlers/util.lua b/lua/packer/handlers/util.lua deleted file mode 100644 index 16f4b97a8..000000000 --- a/lua/packer/handlers/util.lua +++ /dev/null @@ -1,21 +0,0 @@ -local M = {} - - -local destructors = {} - -function M.register_destructor(plugins, f) - local id = #destructors + 1 - - destructors[id] = function() - if destructors[id] then - f() - destructors[id] = nil - end - end - - for _, p in ipairs(plugins) do - table.insert(p.destructors, destructors[id]) - end -end - -return M \ No newline at end of file diff --git a/lua/packer/loader.lua b/lua/packer/loader.lua index e4471acac..d141aef10 100644 --- a/lua/packer/loader.lua +++ b/lua/packer/loader.lua @@ -66,11 +66,6 @@ function M.load_plugin(plugin) log.debug('Running loader for ' .. plugin.name) - - for _, d in pairs(plugin.destructors) do - d() - end - apply_config(plugin, true) if not plugin.start then diff --git a/lua/packer/plugin.lua b/lua/packer/plugin.lua index a4e60f0e5..8d4ddc635 100644 --- a/lua/packer/plugin.lua +++ b/lua/packer/plugin.lua @@ -75,11 +75,6 @@ local M = {UserSpec = {}, Plugin = {}, } - - - - -local destructor_mt = { __mode = 'kv' } M.plugins = {} @@ -232,11 +227,6 @@ function M.process_spec( config_pre = spec.config_pre, config = spec.config, revs = {}, - - - - - destructors = setmetatable({}, destructor_mt), } if required_by then diff --git a/teal/packer/handlers/cmd.tl b/teal/packer/handlers/cmd.tl index caada7ad3..643ffe264 100644 --- a/teal/packer/handlers/cmd.tl +++ b/teal/packer/handlers/cmd.tl @@ -1,26 +1,26 @@ -local util = require 'packer.handlers.util' +local command_plugins : {string: {Plugin}} = {} return function(plugins: {string:Plugin}, loader: function({Plugin})) - local commands: {string:{Plugin}} = {} + local new_commands: {string} = {} for _, plugin in pairs(plugins) do if plugin.cmd then for _, cmd in ipairs(plugin.cmd) do - commands[cmd] = commands[cmd] or {} - table.insert(commands[cmd], plugin) + if not command_plugins[cmd] then + command_plugins[cmd] = {} + new_commands[#new_commands + 1] = cmd + end + + table.insert(command_plugins[cmd], plugin) end end end - for cmd, cplugins in pairs(commands) do - - util.register_destructor(cplugins, function() - vim.api.nvim_del_user_command(cmd) - end) + for _, cmd in ipairs(new_commands) do vim.api.nvim_create_user_command(cmd, function(args: vim.api.UserCmdParams) - loader(cplugins) - + vim.api.nvim_del_user_command(cmd) + loader(command_plugins[cmd]) local lines = args.line1 == args.line2 and '' or (args.line1 .. ',' .. args.line2) vim.cmd(string.format( '%s %s%s%s %s', diff --git a/teal/packer/handlers/event.tl b/teal/packer/handlers/event.tl index 797384979..39bea8a98 100644 --- a/teal/packer/handlers/event.tl +++ b/teal/packer/handlers/event.tl @@ -1,33 +1,30 @@ -local util = require 'packer.handlers.util' +local event_plugins: {string:{Plugin}} = {} return function(plugins: {string:Plugin}, loader: function({Plugin})) - local events: {string:{Plugin}} = {} - + local new_events: {string} = {} for _, plugin in pairs(plugins) do if plugin.event then for _, event in ipairs(plugin.event) do - events[event] = events[event] or {} - table.insert(events[event], plugin) + if not event_plugins[event] then + event_plugins[event] = {} + new_events[#new_events + 1] = event + end + + table.insert(event_plugins[event], plugin) end end end - for event, eplugins in pairs(events) do + for _, event in ipairs(new_events) do -- Handle 'User Foo' events local ev, pattern = unpack(vim.split(event, '%s+')) - - local id = vim.api.nvim_create_autocmd(ev, { + vim.api.nvim_create_autocmd(ev, { pattern = pattern, once = true, callback = function() - loader(eplugins) + loader(event_plugins[event]) vim.api.nvim_exec_autocmds(event, { modeline = false }) end }) - - util.register_destructor(eplugins, function() - pcall(vim.api.nvim_del_autocmd, id) - end) - end end diff --git a/teal/packer/handlers/ft.tl b/teal/packer/handlers/ft.tl index f57ae10fc..e9398ff9c 100644 --- a/teal/packer/handlers/ft.tl +++ b/teal/packer/handlers/ft.tl @@ -1,12 +1,10 @@ -local join_paths = require 'packer.util'.join_paths - -local util = require 'packer.handlers.util' +local util = require 'packer.util' local function detect_ftdetect(plugin_path: string): {string} local source_paths = {} for _, parts in ipairs{ { 'ftdetect' }, { 'after', 'ftdetect' } } do parts[#parts+1] = [[**/*.\(vim\|lua\)]] - local path = join_paths(plugin_path, unpack(parts)) + local path = util.join_paths(plugin_path, unpack(parts)) local ok, files = pcall(vim.fn.glob, path, false, true) if not ok then if string.find(files as string, 'E77') then @@ -22,38 +20,37 @@ local function detect_ftdetect(plugin_path: string): {string} return source_paths end -return function(plugins: {string:Plugin}, loader: function({Plugin})) - local fts: {string:{Plugin}} = {} +local ft_plugins: {string:{Plugin}} = {} +return function(plugins: {string:Plugin}, loader: function({Plugin})) + local new_fts: {string} = {} local ftdetect_paths = {} - for _, plugin in pairs(plugins) do if plugin.ft then for _, ft in ipairs(plugin.ft) do - fts[ft] = fts[ft] or {} - table.insert(fts[ft], plugin) + if not ft_plugins[ft] then + ft_plugins[ft] = {} + new_fts[#new_fts + 1] = ft + end + + table.insert(ft_plugins[ft], plugin) end vim.list_extend(ftdetect_paths, detect_ftdetect(plugin.install_path)) end end - for ft, fplugins in pairs(fts) do - local id = vim.api.nvim_create_autocmd('FileType', { + for _, ft in ipairs(new_fts) do + vim.api.nvim_create_autocmd('FileType', { pattern = ft, once = true, callback = function() - loader(fplugins) + loader(ft_plugins[ft]) for _, group in ipairs{'filetypeplugin', 'filetypeindent', 'syntaxset'} do vim.api.nvim_exec_autocmds('FileType', { group = group, pattern = ft, modeline = false }) end end }) - - util.register_destructor(fplugins, function() - pcall(vim.api.nvim_del_autocmd, id) - end) - end if #ftdetect_paths > 0 then diff --git a/teal/packer/handlers/keys.tl b/teal/packer/handlers/keys.tl index fe0cfb1c9..03b5b5bdc 100644 --- a/teal/packer/handlers/keys.tl +++ b/teal/packer/handlers/keys.tl @@ -1,30 +1,32 @@ -local util = require 'packer.handlers.util' +local keymap_plugins: {{string,string}:{Plugin}} = {} return function(plugins: {string:Plugin}, loader: function({Plugin})) - local keymaps: {{string,string}:{Plugin}} = {} + local new_keymaps: {{string, string}} = {} for _, plugin in pairs(plugins) do if plugin.keys then for _, keymap in ipairs(plugin.keys) do - keymaps[keymap] = keymaps[keymap] or {} - table.insert(keymaps[keymap], plugin) + if not keymap_plugins[keymap] then + keymap_plugins[keymap] = {} + new_keymaps[#new_keymaps + 1] = keymap + end + + table.insert(keymap_plugins[keymap], plugin) end end end - for keymap, kplugins in pairs(keymaps) do + for _, keymap in ipairs(new_keymaps) do + local kplugins = keymap_plugins[keymap] local names = vim.tbl_map(function(e: Plugin): string return e.name end, kplugins) - util.register_destructor(kplugins, function() - vim.keymap.del(keymap[1], keymap[2]) - end) - vim.keymap.set(keymap[1], keymap[2], function() + vim.keymap.del(keymap[1], keymap[2]) loader(kplugins) vim.api.nvim_feedkeys(keymap[2], keymap[1], false) end, { - desc = 'Packer lazy load: '..table.concat(names, ', '), + desc = 'packer.nvim lazy load: '..table.concat(names, ', '), silent = true }) end diff --git a/teal/packer/handlers/util.tl b/teal/packer/handlers/util.tl deleted file mode 100644 index fa361d9bb..000000000 --- a/teal/packer/handlers/util.tl +++ /dev/null @@ -1,21 +0,0 @@ -local M = {} - --- 'strong' table to keep track of destructors -local destructors: {function()} = {} - -function M.register_destructor(plugins: {Plugin}, f: function()) - local id = #destructors+1 - - destructors[id] = function() - if destructors[id] then - f() - destructors[id] = nil - end - end - - for _, p in ipairs(plugins) do - table.insert(p.destructors, destructors[id]) - end -end - -return M diff --git a/teal/packer/loader.tl b/teal/packer/loader.tl index 2b5fdd73b..93ce5da0c 100644 --- a/teal/packer/loader.tl +++ b/teal/packer/loader.tl @@ -66,11 +66,6 @@ function M.load_plugin(plugin: Plugin) log.debug('Running loader for '..plugin.name) - -- Tidy up any lazy-loading state - for _, d in pairs(plugin.destructors) do - d() - end - apply_config(plugin, true) -- spec.config_pre() if not plugin.start then diff --git a/teal/packer/plugin.tl b/teal/packer/plugin.tl index 1c26cbb06..697f00894 100644 --- a/teal/packer/plugin.tl +++ b/teal/packer/plugin.tl @@ -69,9 +69,6 @@ local record M -- Built from a simple plugin spec (a string) simple: boolean - -- Callbacks to destroy unused lazy-load handlers when a plugin is loaded - destructors : {integer:function()} - messages: {string} err: {string} end @@ -79,8 +76,6 @@ local record M plugins: {string:Plugin} end -local destructor_mt = { __mode = 'kv' } - M.plugins = {} local function guess_plugin_type(path: string): string, M.Plugin.Type @@ -232,11 +227,6 @@ function M.process_spec( config_pre = spec.config_pre, config = spec.config, revs = {}, - - -- Because a destructor can be shared across plugins, store the callbacks - -- as weak references. When they are removed from the handlers internal - -- table, they will be garbage collected. - destructors = setmetatable({}, destructor_mt) } if required_by then