diff --git a/modules/performance.nix b/modules/performance.nix index 9709b3f0..59233583 100644 --- a/modules/performance.nix +++ b/modules/performance.nix @@ -10,7 +10,8 @@ in Whether to enable EXPERIMENTAL option to combine all plugins into a single plugin pack. It can significantly reduce startup time, but all your plugins must have unique filenames and doc tags. - Any collision will result in a build failure. + Any collision will result in a build failure. To avoid collisions + you can add your plugin to the `standalonePlugins` option. Only standard neovim runtime directories are linked to the combined plugin. If some of your plugins contain important files outside of standard directories, add these paths to `pathsToLink` option. @@ -22,6 +23,12 @@ in example = [ "/data" ]; description = "List of paths to link into a combined plugin pack."; }; + standalonePlugins = lib.mkOption { + type = with types; listOf (either str package); + default = [ ]; + example = [ "nvim-treesitter" ]; + description = "List of plugins (names or packages) to exclude from plugin pack."; + }; }; }; diff --git a/modules/top-level/output.nix b/modules/top-level/output.nix index b3bd121e..024e348c 100644 --- a/modules/top-level/output.nix +++ b/modules/top-level/output.nix @@ -98,13 +98,26 @@ in in lib.unique (builtins.concatMap pluginWithItsDeps normalizedPlugins); + # Remove dependencies from all plugins in a list + removeDependencies = ps: map (p: p // { plugin = removeAttrs p.plugin [ "dependencies" ]; }) ps; + # Separated start and opt plugins partitionedPlugins = builtins.partition (p: p.optional) allPlugins; startPlugins = partitionedPlugins.wrong; # Remove opt plugin dependencies since they are already available in start plugins - optPlugins = map ( - p: p // { plugin = builtins.removeAttrs p.plugin [ "dependencies" ]; } - ) partitionedPlugins.right; + optPlugins = removeDependencies partitionedPlugins.right; + + # Test if plugin shouldn't be included in plugin pack + isStandalone = + p: + builtins.elem p.plugin config.performance.combinePlugins.standalonePlugins + || builtins.elem (lib.getName p.plugin) config.performance.combinePlugins.standalonePlugins; + + # Separated standalone and combined start plugins + partitionedStartPlugins = builtins.partition isStandalone startPlugins; + toCombinePlugins = partitionedStartPlugins.wrong; + # Remove standalone plugin dependencies since they are already available in start plugins + standaloneStartPlugins = removeDependencies partitionedStartPlugins.right; # Combine start plugins into a single pack pluginPack = @@ -121,12 +134,12 @@ in ] ); }) - ) startPlugins; + ) toCombinePlugins; # Python3 dependencies python3Dependencies = let - deps = map (p: p.plugin.python3Dependencies or (_: [ ])) startPlugins; + deps = map (p: p.plugin.python3Dependencies or (_: [ ])) toCombinePlugins; in ps: builtins.concatMap (f: f ps) deps; @@ -149,7 +162,7 @@ in # Combined plugin configs combinedConfig = builtins.concatStringsSep "\n" ( - builtins.concatMap (x: lib.optional (x.config != null && x.config != "") x.config) startPlugins + builtins.concatMap (x: lib.optional (x.config != null && x.config != "") x.config) toCombinePlugins ); in normalize { @@ -158,7 +171,7 @@ in }; # Combined plugins - combinedPlugins = [ pluginPack ] ++ optPlugins; + combinedPlugins = [ pluginPack ] ++ standaloneStartPlugins ++ optPlugins; # Plugins to use in finalPackage plugins = if config.performance.combinePlugins.enable then combinedPlugins else normalizedPlugins; diff --git a/tests/test-sources/modules/performance/combine-plugins.nix b/tests/test-sources/modules/performance/combine-plugins.nix index b102827f..e2289ee3 100644 --- a/tests/test-sources/modules/performance/combine-plugins.nix +++ b/tests/test-sources/modules/performance/combine-plugins.nix @@ -233,4 +233,70 @@ in } ]; }; + + # Test that standalonePlugins option works + standalone-plugins.module = + { config, ... }: + { + performance.combinePlugins = { + enable = true; + standalonePlugins = with pkgs.vimPlugins; [ + # By plugin name + "nvim-treesitter" + # By package itself + nvim-lspconfig + # Its dependency, plenary-nvim, not in this list, so will be combined + telescope-nvim + # Dependency of other plugin + "nvim-cmp" + ]; + }; + extraPlugins = with pkgs.vimPlugins; [ + nvim-treesitter + nvim-lspconfig + telescope-nvim + # Only its dependency (nvim-cmp) won't be combined, but not the plugin itself + cmp-dictionary + # More plugins + gitsigns-nvim + luasnip + ]; + extraConfigLuaPost = '' + -- Plugins are loadable + require("nvim-treesitter") + require("lspconfig") + require("telescope") + require("plenary") + require("cmp_dictionary") + require("cmp") + require("gitsigns") + require("luasnip") + + -- Verify if plugin is standalone or combined + local function is_standalone(name, dirname) + local paths = vim.api.nvim_get_runtime_file("lua/" .. name, true); + assert(#paths == 1, "more than one copy of " .. name .. " in runtime") + return paths[1]:match("^(.+)/lua/"):find(dirname or name, 1, true) ~= nil + end + + -- Standalone plugins + assert(is_standalone("nvim-treesitter"), "nvim-treesitter is combined, expected standalone") + assert(is_standalone("lspconfig"), "nvim-lspconfig is combined, expected standalone") + assert(is_standalone("telescope"), "telescope-nvim is combined, expected standalone") + -- Add trailing slash to ensure that it doesn't match cmp_dictionary + assert(is_standalone("cmp/", "nvim-cmp"), "nvim-cmp is combined, expected standalone") + -- Combined plugins + assert(not is_standalone("plenary"), "plenary-nvim is standalone, expected combined") + assert(not is_standalone("cmp_dictionary", "cmp-dictionary"), "cmp-dictionary is standalone, expected combined") + assert(not is_standalone("gitsigns"), "gitsigns-nvim is standalone, expected combined") + assert(not is_standalone("luasnip"), "luasnip is standalone, expected combined") + ''; + assertions = [ + { + # plugin-pack, nvim-treesitter, nvim-lspconfig, telescope-nvim, nvim-cmp + assertion = pluginCount config.finalPackage "start" == 5; + message = "Wrong number of plugins in packpathDirs"; + } + ]; + }; }