modules/output: refactor config generation

The motivation for this change was to avoid generating empty
config sections like

    vim.cmd([[

    ]])

To make a config generation cleaner several helper functions introduced:

* `hasContent` have been moved to helpers
* `concatNonEmptyLines` joins strings (which has content) separated with
  newlines
* `wrapVimscriptForLua` wraps a lua string for using in Vimscript, but
  only if the string has content, otherwise empty string is returned
* `wrapLuaForVimscript` wraps Vimscript for using in lua, but only if
  the string has content, otherwise empty string is returned

Added tests:

* testing that all possible config sections are present in the final
  generated config
* testing that the config files generated by empty `files` definitions
  don't have any content in it
This commit is contained in:
Stanislav Asunkin 2024-07-21 11:55:52 +03:00 committed by Gaétan Lepage
parent 6dc0bda459
commit 299d0406bb
4 changed files with 149 additions and 37 deletions

View file

@ -4,7 +4,14 @@
_nixvimTests, _nixvimTests,
}: }:
with lib; with lib;
{ rec {
# Whether a string contains something other than whitespaces
hasContent = str: builtins.match "[[:space:]]*" str == null;
# Concatenate a list of strings, adding a newline at the end of each one,
# but skipping strings containing only whitespace characters
concatNonEmptyLines = lines: concatLines (builtins.filter hasContent lines);
listToUnkeyedAttrs = listToUnkeyedAttrs =
list: list:
builtins.listToAttrs (lib.lists.imap0 (idx: lib.nameValuePair "__unkeyed-${toString idx}") list); builtins.listToAttrs (lib.lists.imap0 (idx: lib.nameValuePair "__unkeyed-${toString idx}") list);
@ -118,4 +125,26 @@ with lib;
${string} ${string}
end end
''; '';
# Wrap Vimscript for using in lua,
# but only if the string contains something other than whitespaces
# TODO: account for a possible ']]' in the string
wrapVimscriptForLua =
string:
optionalString (hasContent string) ''
vim.cmd([[
${string}
]])
'';
# Wrap lua script for using in Vimscript,
# but only if the string contains something other than whitespaces
# TODO: account for a possible 'EOF' if the string
wrapLuaForVimscript =
string:
optionalString (hasContent string) ''
lua << EOF
${string}
EOF
'';
} }

View file

@ -1,4 +1,9 @@
{ lib, config, ... }: {
lib,
config,
helpers,
...
}:
with lib; with lib;
let let
pluginWithConfigType = types.submodule { pluginWithConfigType = types.submodule {
@ -97,29 +102,27 @@ in
}; };
}; };
config = config = {
let content =
contentLua = '' if config.type == "lua" then
${config.extraConfigLuaPre} # Lua
vim.cmd([[ helpers.concatNonEmptyLines [
${config.extraConfigVim} config.extraConfigLuaPre
]]) (helpers.wrapVimscriptForLua config.extraConfigVim)
${config.extraConfigLua} config.extraConfigLua
${config.extraConfigLuaPost} config.extraConfigLuaPost
''; ]
else
contentVim = '' # Vimscript
lua << EOF helpers.concatNonEmptyLines [
${config.extraConfigLuaPre} (helpers.wrapLuaForVimscript config.extraConfigLuaPre)
EOF config.extraConfigVim
${config.extraConfigVim} (helpers.wrapLuaForVimscript (
lua << EOF helpers.concatNonEmptyLines [
${config.extraConfigLua} config.extraConfigLua
${config.extraConfigLuaPost} config.extraConfigLuaPost
EOF ]
''; ))
in ];
{ };
content = if config.type == "lua" then contentLua else contentVim;
};
} }

View file

@ -107,16 +107,10 @@ with lib;
} }
); );
customRC = customRC = helpers.concatNonEmptyLines [
let (helpers.wrapVimscriptForLua neovimConfig.neovimRcContent)
hasContent = str: (builtins.match "[[:space:]]*" str) == null; config.content
in ];
(optionalString (hasContent neovimConfig.neovimRcContent) ''
vim.cmd([[
${neovimConfig.neovimRcContent}
]])
'')
+ config.content;
init = helpers.writeLua "init.lua" customRC; init = helpers.writeLua "init.lua" customRC;

View file

@ -4,4 +4,90 @@
# Make sure jsregexp is in LUA_PATH # Make sure jsregexp is in LUA_PATH
extraConfigLua = ''require("jsregexp")''; extraConfigLua = ''require("jsregexp")'';
}; };
# Test that all extraConfigs are present in output
all-configs.module =
{
config,
pkgs,
lib,
...
}:
let
configs = {
extraConfigLuaPre = "string.format('extraConfigLuaPre1')";
extraConfigLua = "string.format('extraConfigLua2')";
extraConfigLuaPost = "string.format('extraConfigLuaPost3')";
extraConfigVim = "let g:var = 'extraConfigVim4'";
};
mkConfigAssertions = name: value: [
{
assertion = lib.hasInfix "extraConfigLuaPre1" value;
message = "Configuration file ${name} does not contain extraConfigLuaPre.";
}
{
assertion = lib.hasInfix "extraConfigLua2" value;
message = "Configuration file ${name} does not contain extraConfigLua.";
}
{
assertion = lib.hasInfix "extraConfigLuaPost3" value;
message = "Configuration file ${name} does not contain extraConfigLuaPost.";
}
{
assertion = lib.hasInfix "extraConfigVim4" value;
message = "Configuration file ${name} does not contain extraConfigVim.";
}
];
in
configs
// {
files = {
"test.lua" = configs;
"test.vim" = configs;
};
# Plugin configs
extraPlugins = [
{
plugin = pkgs.emptyDirectory;
config = "let g:var = 'neovimRcContent5'";
}
];
assertions =
mkConfigAssertions "init.lua" config.content
++ mkConfigAssertions "test.lua" config.files."test.lua".content
++ mkConfigAssertions "test.vim" config.files."test.vim".content
# Check the final generated init.lua too
++ mkConfigAssertions "initPath" (builtins.readFile config.initPath)
++ [
# Only init.lua contains configuration from plugin definitions
{
assertion = lib.hasInfix "neovimRcContent5" (builtins.readFile config.initPath);
message = "Configuration file init.lua does not contain plugin configs";
}
];
};
files-default-empty.module =
{ config, helpers, ... }:
{
files = {
# lua type
"test.lua" = { };
# vim type
"test.vim" = { };
};
assertions = [
{
assertion = !helpers.hasContent config.files."test.lua".content;
message = "Default content of test.lua file is expected to be empty.";
}
{
assertion = !helpers.hasContent config.files."test.vim".content;
message = "Default content of test.vim file is expected to be empty.";
}
];
};
} }