modules/performance: add an option to combine plugins to a single plugin pack

This commit is contained in:
Stanislav Asunkin 2024-07-07 20:22:39 +03:00 committed by traxys
parent 119f12db27
commit fdb3950c59
4 changed files with 202 additions and 3 deletions

View file

@ -19,6 +19,7 @@
./lua-loader.nix
./opts.nix
./output.nix
./performance.nix
./plugins.nix
];
}

54
modules/performance.nix Normal file
View file

@ -0,0 +1,54 @@
{ lib, ... }:
let
inherit (lib) types;
in
{
options.performance = {
combinePlugins = {
enable = lib.mkEnableOption "combinePlugins" // {
description = ''
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.
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.
'';
};
pathsToLink = lib.mkOption {
type = with types; listOf str;
default = [ ];
example = [ "/data" ];
description = "List of paths to link into a combined plugin pack.";
};
};
};
config.performance = {
# Set option value with default priority so that values are appended by default
combinePlugins.pathsToLink = [
# :h rtp
"/autoload"
"/colors"
"/compiler"
"/doc"
"/ftplugin"
"/indent"
"/keymap"
"/lang"
"/lua"
"/pack"
"/parser"
"/plugin"
"/queries"
"/rplugin"
"/spell"
"/syntax"
"/tutor"
"/after"
# ftdetect
"/ftdetect"
];
};
}

View file

@ -74,15 +74,54 @@ in
config =
let
# Plugin list extended with dependencies
allPlugins =
let
pluginWithItsDeps = p: [ p ] ++ builtins.concatMap pluginWithItsDeps p.dependencies or [ ];
in
lib.unique (builtins.concatMap pluginWithItsDeps config.extraPlugins);
# All plugins with doc tags removed
allPluginsOverridden = map (
plugin:
plugin.overrideAttrs (prev: {
nativeBuildInputs = lib.remove pkgs.vimUtils.vimGenDocHook prev.nativeBuildInputs or [ ];
configurePhase = builtins.concatStringsSep "\n" (
builtins.filter (s: s != ":") [
prev.configurePhase or ":"
"rm -vf doc/tags"
]
);
})
) allPlugins;
# Combine all plugins into a single pack
pluginPack = pkgs.vimUtils.toVimPlugin (
pkgs.buildEnv {
name = "plugin-pack";
paths = allPluginsOverridden;
inherit (config.performance.combinePlugins) pathsToLink;
# Remove empty directories and activate vimGenDocHook
postBuild = ''
find $out -type d -empty -delete
runHook preFixup
'';
}
);
# Combined plugins
combinedPlugins = [ pluginPack ];
# Plugins to use in finalPackage
plugins = if config.performance.combinePlugins.enable then combinedPlugins else config.extraPlugins;
defaultPlugin = {
plugin = null;
config = "";
optional = false;
};
normalizedPlugins = map (
x: defaultPlugin // (if x ? plugin then x else { plugin = x; })
) config.extraPlugins;
normalizedPlugins = map (x: defaultPlugin // (if x ? plugin then x else { plugin = x; })) plugins;
neovimConfig = pkgs.neovimUtils.makeNeovimConfig (
{

View file

@ -0,0 +1,105 @@
{ pkgs, ... }:
{
# Test basic functionality
default.module =
{ config, ... }:
{
performance.combinePlugins.enable = true;
extraPlugins = with pkgs.vimPlugins; [
nvim-lspconfig
nvim-treesitter
];
extraConfigLuaPost = ''
-- Plugins are loadable
require("lspconfig")
require("nvim-treesitter")
-- No separate plugin entries in nvim_list_runtime_paths
assert(not vim.iter(vim.api.nvim_list_runtime_paths()):any(function(entry)
return entry:find("treesitter") or entry:find("lspconfig")
end), "separate plugins are found in runtime")
-- Help tags are generated
assert(vim.fn.getcompletion("lspconfig", "help")[1], "no help tags for nvim-lspconfig")
assert(vim.fn.getcompletion("nvim-treesitter", "help")[1], "no help tags for nvim-treesitter")
'';
assertions = [
{
assertion = builtins.length config.finalPackage.packpathDirs.myNeovimPackages.start == 1;
message = "More than one plugin is defined in packpathDirs, expected one plugin pack.";
}
];
};
# Test disabled option
disabled.module =
{ config, ... }:
{
performance.combinePlugins.enable = false;
extraPlugins = with pkgs.vimPlugins; [
nvim-lspconfig
nvim-treesitter
];
assertions = [
{
assertion = builtins.length config.finalPackage.packpathDirs.myNeovimPackages.start >= 2;
message = "Only one plugin is defined in packpathDirs, expected at least two.";
}
];
};
# Test that plugin dependencies are handled
dependencies.module =
{ config, ... }:
{
performance.combinePlugins.enable = true;
extraPlugins = with pkgs.vimPlugins; [
# Depends on nvim-cmp
cmp-dictionary
# Depends on telescope-nvim which itself depends on plenary-nvim
telescope-undo-nvim
];
extraConfigLuaPost = ''
-- Plugins and its dependencies are loadable
require("cmp_dictionary")
require("cmp")
require("telescope-undo")
require("telescope")
require("plenary")
'';
assertions = [
{
assertion = builtins.length config.finalPackage.packpathDirs.myNeovimPackages.start == 1;
message = "More than one plugin is defined in packpathDirs.";
}
];
};
# Test that pathsToLink option works
paths-to-link.module =
{ config, ... }:
{
performance.combinePlugins = {
enable = true;
# fzf native library is in build directory
pathsToLink = [ "/build" ];
};
extraPlugins = [ pkgs.vimPlugins.telescope-fzf-native-nvim ];
extraConfigLuaPost = ''
-- Native library is in runtimepath
assert(
vim.api.nvim_get_runtime_file("build/libfzf.so", false)[1],
"build/libfzf.so is not found in runtimepath"
)
-- Native library is loadable
require("fzf_lib")
'';
assertions = [
{
assertion = builtins.length config.finalPackage.packpathDirs.myNeovimPackages.start == 1;
message = "More than one plugin is defined in packpathDirs.";
}
];
};
}