nix-community.nixvim/tests/test-sources/modules/performance/combine-plugins.nix
Stanislav Asunkin 65d35db5ca modules/performance: fix specifying combinePlugin.standalonePlugins as packages when byte compilation enabled
Previously, specifying plugins as packages in the
`performance.combinePlugins.standalonePlugins` option did not work when
the `performance.byteCompileLua` option was also enabled. This issue was
due to several package transformations performed by the
`byteCompileLua` which broke package comparison.

There are at least three methods to fix the issue:

- Change transformation order: combine plugins first, then byte-compile
  them.
- Compare every possible transformation when determining if plugins are
  standalone.
- Get the name of the package and use it for comparison.

The first method did not work because the current `byteCompileLuaDrv`
implementation does not support symlinks to directories. The second
method appears too fragile. This commit implements the third method, as
it requires minimal code changes and is straightforward. The downside is
that it might exclude multiple packages with the same name, although
this should be rare.
2025-06-02 18:15:32 +03:00

327 lines
10 KiB
Nix

{ lib, pkgs, ... }:
let
pluginStubs = pkgs.callPackage ../../../utils/plugin-stubs.nix { };
# Assertion for a number of plugins of given type defined in nvimPackage.packpathDirs
expectNPlugins =
config: type: n:
let
# 'build.extraFiles' must not be combined, so exclude it from counting
plugins = builtins.filter (
p: lib.getName p != lib.getName config.build.extraFiles
) config.build.nvimPackage.packpathDirs.myNeovimPackages.${type};
numPlugins = builtins.length plugins;
in
{
assertion = numPlugins == n;
message = "Expected ${toString n} '${type}' plugins defined in 'nvimPackage.packpathDirs', got ${toString numPlugins}: ${
lib.concatMapStringsSep ", " lib.getName plugins
}.";
};
# Assertion that exactly one start plugin is defined in nvimPackage.packpathDirs
expectOneStartPlugin = config: expectNPlugins config "start" 1;
in
{
# Test basic functionality
default =
{ config, ... }:
{
performance.combinePlugins.enable = true;
extraPlugins = pluginStubs.pluginPack;
extraConfigLuaPost = ''
${pluginStubs.pluginChecks}
-- No separate plugin entry in vim.api.nvim_list_runtime_paths()
${lib.concatMapStrings (
name: # lua
''
assert(not vim.iter(vim.api.nvim_list_runtime_paths()):any(function(entry)
return entry:find("${name}", 1, true)
end), "plugin '${name}' found in runtime, expected to be combined")
'') pluginStubs.pluginNames}
'';
assertions = [
(expectOneStartPlugin config)
];
};
# Test disabled option
disabled =
{ config, ... }:
{
performance.combinePlugins.enable = false;
extraPlugins = pluginStubs.pluginPack;
extraConfigLuaPost = lib.concatMapStringsSep "\n" (
name:
# lua
''
-- Separate plugin entry in vim.api.nvim_list_runtime_paths()
assert(vim.iter(vim.api.nvim_list_runtime_paths()):any(function(entry)
return entry:find("${name}", 1, true)
end), "plugin '${name}' isn't found in runtime as a separate entry, expected not to be combined")
'') pluginStubs.pluginNames;
assertions = [
(expectNPlugins config "start" (builtins.length pluginStubs.pluginPack))
];
};
# Test that pathsToLink option works
paths-to-link =
{ config, ... }:
{
performance.combinePlugins = {
enable = true;
pathsToLink = [ "/_extra" ];
};
extraPlugins = [
# A plugin with extra directory
(pluginStubs.mkPlugin "extra" {
postInstall = ''
mkdir $out/_extra
touch $out/_extra/test
'';
})
];
extraConfigLuaPost = ''
${pluginStubs.pluginChecksFor [ "extra" ]}
-- Test file is in runtime
assert(
vim.api.nvim_get_runtime_file("_extra/test", false)[1],
"'_extra/test' file isn't found in runtime, expected to be found"
)
'';
assertions = [
(expectOneStartPlugin config)
];
};
# Test that optional plugins are handled
optional-plugins =
{ config, ... }:
{
performance.combinePlugins.enable = true;
extraPlugins = with pluginStubs; [
# Start plugins
plugin1
plugin3
# Optional plugin
{
plugin = plugin2;
optional = true;
}
# Optional plugin with dependencies on plugin3 and plugin4
# Dependencies should not be duplicated
{
plugin = pluginWithDep4;
optional = true;
}
];
extraConfigLuaPost = ''
-- Start plugins are working. Dependencies of the optional plugins are also available.
${pluginStubs.pluginChecksFor [
"plugin1"
"plugin3"
"plugin4" # Dependency of the optional plugin
]}
-- Lua libraries are available. Libs of the optional plugins are also available.
${pluginStubs.libChecksFor [
"lib1"
"lib2" # Dependency of the optional plugin
"lib3"
]}
${lib.concatMapStrings
(
name: # lua
''
-- Optional plugin is not loadable
local ok = pcall(require, "${name}")
assert(not ok, "${name} is loadable, expected it to be an opt plugin")
-- Load plugin
vim.cmd.packadd("${name}")
-- Now opt plugin is working
${pluginStubs.pluginChecksFor [ name ]}
'')
[
"plugin2"
"plugin_with_dep4"
]
}
-- Only one copy of dependent plugin should be available
${lib.concatMapStrings
(
name: # lua
''
local num_plugins = #vim.api.nvim_get_runtime_file("lua/${name}/init.lua", true)
assert(num_plugins == 1, "expected 1 copy of ${name}, got " .. num_plugins)
'')
[
"plugin3"
"plugin4"
]
}
'';
assertions = [
(expectOneStartPlugin config)
# plugin2 plugin_with_dep4
(expectNPlugins config "opt" 2)
];
};
# Test that plugin configs are handled
configs =
{ config, ... }:
{
performance.combinePlugins.enable = true;
extraPlugins = with pluginStubs; [
# A plugin without config
plugin1
# A plugin with config
{
plugin = plugin2;
config = "let g:plugin2_var = 1";
}
# Optional plugin with config
{
plugin = plugin3;
optional = true;
config = "let g:plugin3_var = 1";
}
];
extraConfigLuaPost = ''
-- Configs are evaluated
assert(vim.g.plugin2_var == 1, "plugin2's config isn't evaluated")
assert(vim.g.plugin3_var == 1, "plugin3's config isn't evaluated")
'';
assertions = [
(expectOneStartPlugin config)
];
};
# Test if plenary.filetype is working
plenary-nvim = {
performance.combinePlugins.enable = true;
extraPlugins = [ pkgs.vimPlugins.plenary-nvim ];
extraConfigLuaPost = ''
-- Plenary filetype detection is usable
assert(require("plenary.filetype").detect(".bashrc") == "sh", "plenary.filetype is not working")
'';
};
}
//
# Test that config.build.extraFiles is not combined
# with or without byteCompileLua.enable = true
lib.genAttrs [ "files-plugin" "files-plugin-byte-compiled" ] (
name:
{ config, ... }:
{
performance = {
combinePlugins.enable = true;
byteCompileLua = lib.optionalAttrs (lib.hasSuffix "byte-compiled" name) {
enable = true;
plugins = true;
};
};
extraPlugins = with pluginStubs; [
plugin1
plugin2
];
# Ensure that build.extraFiles is added to extraPlugins
wrapRc = true;
# Extra user files colliding with plugins
extraFiles = {
"lua/plugin1/init.lua".text = "return 1";
};
# Another form of user files
files = {
"lua/plugin2/init.lua" = {
extraConfigLua = "return 1";
};
};
extraConfigLuaPost = ''
for _, file in ipairs({"lua/plugin1/init.lua", "lua/plugin2/init.lua"}) do
local paths_found = vim.api.nvim_get_runtime_file(file, true)
local num_found = #paths_found
-- Both plugin and user version are available
assert(num_found == 2, "expected exactly 2 versions of '" .. file .. "', got " .. num_found)
-- First found file is from build.extraFiles
assert(
paths_found[1]:find("${lib.getName config.build.extraFiles}", 1, true),
"expected first found '" .. file .. "' to be from build.extraFiles, got " .. paths_found[1]
)
end
'';
assertions = [
(expectOneStartPlugin config)
];
}
)
//
# Test that standalonePlugins option works
# with or without byteCompileLua.enable = true
lib.genAttrs [ "standalone-plugins" "standalone-plugins-byte-compiled" ] (
name:
{ config, ... }:
let
standalonePlugins = [
# By plugin name
"plugin1"
# By package itself. Its dependency, plugin4, not in this list, so will be combined
pluginStubs.pluginWithDep4
# Dependency of other plugin
"plugin5"
# Both dependency and top-level plugin
"plugin3"
];
in
{
performance = {
combinePlugins = {
enable = true;
inherit standalonePlugins;
};
byteCompileLua = lib.optionalAttrs (lib.hasSuffix "byte-compiled" name) {
enable = true;
plugins = true;
};
};
extraPlugins = pluginStubs.pluginPack;
extraConfigLuaPost = ''
${pluginStubs.pluginChecks}
${lib.concatMapStringsSep "\n" (
name:
let
isStandalone = builtins.elem name (
map (x: if builtins.isString x then x else lib.getName x) standalonePlugins
);
expectedText = if isStandalone then "standalone" else "combined";
in
# lua
''
-- Check that ${name} plugin is ${expectedText}
local paths = vim.api.nvim_get_runtime_file("lua/${name}", true)
-- Plugins shouldn't be duplicated
assert(#paths == 1, "expected exactly 1 copy of '${name}' in runtime, got ", #paths)
-- Test if plugin is standalone. This matches directory name before '/lua/'.
local is_standalone = paths[1]:match("^(.+)/lua/"):find("${name}", 1, true) ~= nil
assert(
is_standalone == ${lib.nixvim.toLuaObject isStandalone},
"expected '${name}' to be ${expectedText}, found path: " .. paths[1]
)
''
) pluginStubs.pluginNames}
'';
assertions = [
# plugin-pack and 'standalonePlugins'
(expectNPlugins config "start" (builtins.length standalonePlugins + 1))
];
}
)