From 17e89049927e72b055e082464c1eabc8983791ac Mon Sep 17 00:00:00 2001 From: Stanislav Asunkin <1353637+stasjok@users.noreply.github.com> Date: Thu, 25 Jul 2024 13:25:13 +0300 Subject: [PATCH 1/4] modules/performance: add `performance.byteCompileLua` option * add `performance.byteCompileLua.enable` toggle to enable or disable all byte compiling features * add `performance.byteCompileLua.initLua` toggle to enable or disable byte compiling of init.lua * add `writeByteCompiledLua` helper for saving byte compiled lua source code to the nix store * `nixvim-print-init` utility is always pointed to uncompiled init.lua * add tests --- lib/builders.nix | 22 +++ modules/performance.nix | 10 ++ modules/top-level/output.nix | 14 +- .../modules/performance/byte-compile-lua.nix | 139 ++++++++++++++++++ 4 files changed, 183 insertions(+), 2 deletions(-) create mode 100644 tests/test-sources/modules/performance/byte-compile-lua.nix diff --git a/lib/builders.nix b/lib/builders.nix index dcd2713d1d..2079309e56 100644 --- a/lib/builders.nix +++ b/lib/builders.nix @@ -26,4 +26,26 @@ --indent-width 4 \ "$out" ''; + + /* + Write a byte compiled lua file to the nix store. + + # Type + + ``` + writeByteCompiledLua :: String -> String -> Derivation + ``` + + # Arguments + + - [name] The name of the derivation + - [text] The content of the lua file + */ + writeByteCompiledLua = + name: text: + pkgs.runCommandLocal name { inherit text; } '' + echo -n "$text" > "$out" + + ${lib.getExe' pkgs.luajit "luajit"} -bd -- "$out" "$out" + ''; } diff --git a/modules/performance.nix b/modules/performance.nix index db0953d452..98bc70eb3f 100644 --- a/modules/performance.nix +++ b/modules/performance.nix @@ -4,6 +4,16 @@ let in { options.performance = { + byteCompileLua = { + enable = lib.mkEnableOption "byte compiling of lua files"; + initLua = lib.mkOption { + description = "Whether to byte compile init.lua."; + type = types.bool; + default = true; + example = false; + }; + }; + combinePlugins = { enable = lib.mkEnableOption "combinePlugins" // { description = '' diff --git a/modules/top-level/output.nix b/modules/top-level/output.nix index 6f70876099..351965997d 100644 --- a/modules/top-level/output.nix +++ b/modules/top-level/output.nix @@ -206,7 +206,17 @@ in config.content ]; - init = helpers.writeLua "init.lua" customRC; + textInit = helpers.writeLua "init.lua" customRC; + byteCompiledInit = helpers.writeByteCompiledLua "init.lua" customRC; + init = + if + config.type == "lua" + && config.performance.byteCompileLua.enable + && config.performance.byteCompileLua.initLua + then + byteCompiledInit + else + textInit; extraWrapperArgs = builtins.concatStringsSep " " ( (optional ( @@ -232,7 +242,7 @@ in name = "nixvim-print-init"; runtimeInputs = [ pkgs.bat ]; text = '' - bat --language=lua "${init}" + bat --language=lua "${textInit}" ''; }; diff --git a/tests/test-sources/modules/performance/byte-compile-lua.nix b/tests/test-sources/modules/performance/byte-compile-lua.nix new file mode 100644 index 0000000000..15a7330ca2 --- /dev/null +++ b/tests/test-sources/modules/performance/byte-compile-lua.nix @@ -0,0 +1,139 @@ +{ pkgs, helpers, ... }: +let + isByteCompiledFun = '' + local function is_byte_compiled(filename) + local f = assert(io.open(filename, "rb")) + local data = assert(f:read("*a")) + -- Assume that file is binary if it contains null bytes + for i = 1, #data do + if data:byte(i) == 0 then + return true + end + end + return false + end + + local function test_rtp_file(name, is_compiled) + local file = assert(vim.api.nvim_get_runtime_file(name, false)[1], "file " .. name .. " not found in runtime") + if is_compiled then + assert(is_byte_compiled(file), name .. " is expected to be byte compiled, but it's not") + else + assert(not is_byte_compiled(file), name .. " is not expected to be byte compiled, but it is") + end + end + ''; +in +{ + default.module = + { config, ... }: + { + performance.byteCompileLua.enable = true; + + extraFiles = { + "plugin/file_text.lua".text = "vim.opt.tabstop = 2"; + "plugin/file_source.lua".source = helpers.writeLua "file_source.lua" "vim.opt.tabstop = 2"; + "plugin/test.vim".text = "set tabstop=2"; + "plugin/test.json".text = builtins.toJSON { a = 1; }; + }; + + files = { + "plugin/file.lua" = { + opts.tabstop = 2; + }; + "plugin/file.vim" = { + opts.tabstop = 2; + }; + }; + + extraPlugins = [ pkgs.vimPlugins.nvim-lspconfig ]; + + extraConfigLua = '' + -- The test will search for this string in nixvim-print-init output: VALIDATING_STRING. + -- Since this is the comment, it won't appear in byte compiled file. + ''; + + # Using plugin for the test code to avoid infinite recursion + extraFiles."plugin/test.lua".text = '' + ${isByteCompiledFun} + + -- vimrc is byte compiled + local init = vim.env.MYVIMRC or vim.fn.getscriptinfo({name = "init.lua"})[1].name + assert(is_byte_compiled(init), "MYVIMRC is expected to be byte compiled, but it's not") + + -- nixvim-print-init prints text + local init_content = vim.fn.system("${config.printInitPackage}/bin/nixvim-print-init") + assert(init_content:find("VALIDATING_STRING"), "nixvim-print-init's output is byte compiled") + + -- extraFiles + test_rtp_file("plugin/file_text.lua", false) + test_rtp_file("plugin/file_source.lua", false) + test_rtp_file("plugin/test.vim", false) + test_rtp_file("plugin/test.json", false) + + -- files + test_rtp_file("plugin/file.lua", false) + test_rtp_file("plugin/file.vim", false) + + -- Plugins and neovim runtime aren't byte compiled by default + test_rtp_file("lua/vim/lsp.lua", false) + test_rtp_file("lua/lspconfig.lua", false) + ''; + + }; + + disabled.module = + { config, ... }: + { + performance.byteCompileLua.enable = false; + + extraFiles."plugin/test1.lua".text = "vim.opt.tabstop = 2"; + + files."plugin/test2.lua".opts.tabstop = 2; + + extraPlugins = [ pkgs.vimPlugins.nvim-lspconfig ]; + + extraConfigLua = '' + -- The test will search for this string in nixvim-print-init output: VALIDATING_STRING. + -- Since this is the comment, it won't appear in byte compiled file. + ''; + + # Using plugin for the test code to avoid infinite recursion + extraFiles."plugin/test.lua".text = '' + ${isByteCompiledFun} + + -- vimrc + local init = vim.env.MYVIMRC or vim.fn.getscriptinfo({name = "init.lua"})[1].name + assert(not is_byte_compiled(init), "MYVIMRC is not expected to be byte compiled, but it is") + + -- nixvim-print-init prints text + local init_content = vim.fn.system("${config.printInitPackage}/bin/nixvim-print-init") + assert(init_content:find("VALIDATING_STRING"), "nixvim-print-init's output is byte compiled") + + -- Nothing is byte compiled + -- extraFiles + test_rtp_file("plugin/test1.lua", false) + -- files + test_rtp_file("plugin/test2.lua", false) + -- Plugins + test_rtp_file("lua/lspconfig.lua", false) + -- Neovim runtime + test_rtp_file("lua/vim/lsp.lua", false) + ''; + + }; + + init-lua-disabled = { + performance.byteCompileLua = { + enable = true; + initLua = false; + }; + + extraConfigLuaPost = '' + ${isByteCompiledFun} + + -- vimrc is not byte compiled + local init = vim.env.MYVIMRC or vim.fn.getscriptinfo({name = "init.lua"})[1].name + assert(not is_byte_compiled(init), "MYVIMRC is not expected to be byte compiled, but it is") + ''; + }; +} From 44849233e00811f319976a6c88bc76612067c8a1 Mon Sep 17 00:00:00 2001 From: Stanislav Asunkin <1353637+stasjok@users.noreply.github.com> Date: Thu, 25 Jul 2024 18:09:16 +0300 Subject: [PATCH 2/4] modules/performance: add ability to byte compile lua configuration files This commit adds support for byte compiling lua configuration files. It's enabled by default (if byte compiling is enabled at all) and can be disabled with `performance.byteCompileLua.configs` toggle. To implement this feature `extraFiles..finalSource` internal read-only option is introduced. `source` option cannot be used because it's user configurable. In order to access the values of the `performance.byteCompileLua` options, parent config is added to specialArgs of extraFiles submodule. Then the usages of `source` option changed to `finalSource` in all relevant places (filesPlugin and wrappers). Added more helpers for various cases of byte compiling: * `byteCompileLuaFile` byte compiles lua file * `byteCompileLuaHook` is a setup hook that byte compiles all lua files * `byteCompileLuaDrv` overrides derivation by adding byteCompileLuaHook to it Added tests to validate that extraFiles specified by various methods are handled correctly. Added a separate home-manager test, that is intended to validate that extraFiles propagated to wrapper modules are correctly byte compiled. --- flake-modules/wrappers.nix | 7 ++ lib/builders.nix | 86 ++++++++++++++- modules/files.nix | 36 +++++- modules/performance.nix | 6 + modules/top-level/files/default.nix | 4 +- tests/fetch-tests.nix | 2 +- .../modules/hm-extra-files-byte-compiling.nix | 104 ++++++++++++++++++ .../modules/performance/byte-compile-lua.nix | 56 +++++++++- .../modules/performance/files/file.lua | 1 + wrappers/_shared.nix | 4 +- 10 files changed, 292 insertions(+), 14 deletions(-) create mode 100644 tests/modules/hm-extra-files-byte-compiling.nix create mode 100644 tests/test-sources/modules/performance/files/file.lua diff --git a/flake-modules/wrappers.nix b/flake-modules/wrappers.nix index 1323e2bb3a..30fa85be2a 100644 --- a/flake-modules/wrappers.nix +++ b/flake-modules/wrappers.nix @@ -15,6 +15,13 @@ inherit (inputs) home-manager; nixvim = self; }).activationPackage; + home-manager-extra-files-byte-compiling = + import ../tests/modules/hm-extra-files-byte-compiling.nix + { + inherit pkgs; + inherit (inputs) home-manager; + nixvim = self; + }; } // pkgs.lib.optionalAttrs (!pkgs.stdenv.isDarwin) { nixos-module = diff --git a/lib/builders.nix b/lib/builders.nix index 2079309e56..ec7443af2c 100644 --- a/lib/builders.nix +++ b/lib/builders.nix @@ -1,5 +1,5 @@ { lib, pkgs }: -{ +rec { /* Write a lua file to the nix store, formatted using stylua. @@ -48,4 +48,88 @@ ${lib.getExe' pkgs.luajit "luajit"} -bd -- "$out" "$out" ''; + + /* + Get a source lua file and write a byte compiled copy of it + to the nix store. + + # Type + + ``` + byteCompileLuaFile :: String -> String -> Derivation + ``` + + # Arguments + + - [name] The name of the derivation + - [src] The path to the source lua file + */ + byteCompileLuaFile = + name: src: + pkgs.runCommandLocal name { inherit src; } '' + ${lib.getExe' pkgs.luajit "luajit"} -bd -- "$src" "$out" + ''; + + # Setup hook to byte compile all lua files in the output directory. + # Invalid lua files are ignored. + byteCompileLuaHook = pkgs.makeSetupHook { name = "byte-compile-lua-hook"; } ( + let + luajit = lib.getExe' pkgs.luajit "luajit"; + in + pkgs.writeText "byte-compile-lua-hook.sh" # bash + '' + byteCompileLuaPostFixup() { + # Target is a single file + if [[ -f $out ]]; then + if [[ $out = *.lua ]]; then + tmp=$(mktemp) + ${luajit} -bd -- "$out" "$tmp" + mv "$tmp" "$out" + fi + return + fi + + # Target is a directory + while IFS= read -r -d "" file; do + tmp=$(mktemp -u "$file.XXXX") + # Ignore invalid lua files + if ${luajit} -bd -- "$file" "$tmp"; then + mv "$tmp" "$file" + else + echo "WARNING: Ignoring byte compiling error for '$file' lua file" >&2 + fi + done < <(find "$out" -type f,l -name "*.lua" -print0) + } + + postFixupHooks+=(byteCompileLuaPostFixup) + '' + ); + + /* + Returns an overridden derivation with all lua files byte compiled. + + # Type + + ``` + byteCompileLuaDrv :: Derivation -> Derivation + ``` + + # Arguments + + - [drv] Input derivation + */ + byteCompileLuaDrv = + drv: + drv.overrideAttrs ( + prev: + { + nativeBuildInputs = prev.nativeBuildInputs or [ ] ++ [ byteCompileLuaHook ]; + } + // lib.optionalAttrs (prev ? buildCommand) { + buildCommand = '' + ${prev.buildCommand} + runHook postFixup + ''; + } + ); } diff --git a/modules/files.nix b/modules/files.nix index 68606af002..6b5b3def03 100644 --- a/modules/files.nix +++ b/modules/files.nix @@ -2,14 +2,16 @@ lib, helpers, pkgs, + config, ... }: let - fileType = lib.types.submodule ( + fileTypeModule = { name, config, options, + topConfig, ... }: { @@ -41,6 +43,14 @@ let type = lib.types.path; description = "Path of the source file."; }; + + finalSource = lib.mkOption { + type = lib.types.path; + description = "Path to the final source file."; + readOnly = true; + visible = false; + internal = true; + }; }; config = @@ -54,9 +64,29 @@ let # This means our `source` definition has the same priority as `text`. lib.mkDerivedConfig options.text (pkgs.writeText derivationName) ); + finalSource = + # Byte compile lua files if performance.byteCompileLua option is enabled + if + lib.hasSuffix ".lua" config.target + && topConfig.performance.byteCompileLua.enable + && topConfig.performance.byteCompileLua.configs + then + if lib.isDerivation config.source then + # Source is a derivation + helpers.byteCompileLuaDrv config.source + else + # Source is a path or string + helpers.byteCompileLuaFile derivationName config.source + else + config.source; }; - } - ); + }; + + fileType = lib.types.submoduleWith { + shorthandOnlyDefinesConfig = true; + modules = [ fileTypeModule ]; + specialArgs.topConfig = config; + }; # TODO: Added 2024-07-07, remove after 24.11 # Before we had a fileType, we used types.str. diff --git a/modules/performance.nix b/modules/performance.nix index 98bc70eb3f..359f02dcd9 100644 --- a/modules/performance.nix +++ b/modules/performance.nix @@ -12,6 +12,12 @@ in default = true; example = false; }; + configs = lib.mkOption { + description = "Whether to byte compile lua configuration files."; + type = types.bool; + default = true; + example = false; + }; }; combinePlugins = { diff --git a/modules/top-level/files/default.nix b/modules/top-level/files/default.nix index c9ec06f828..d23c2726cb 100644 --- a/modules/top-level/files/default.nix +++ b/modules/top-level/files/default.nix @@ -84,11 +84,11 @@ in mkdir -p "$out" ${lib.concatMapStringsSep "\n" ( - { target, source, ... }: + { target, finalSource, ... }: lib.escapeShellArgs [ "makeEntry" # Force local source paths to be added to the store - "${source}" + "${finalSource}" target ] ) extraFiles} diff --git a/tests/fetch-tests.nix b/tests/fetch-tests.nix index c454655d01..ea37a6698d 100644 --- a/tests/fetch-tests.nix +++ b/tests/fetch-tests.nix @@ -14,7 +14,7 @@ let file = "${root}/${relativePath}/${name}"; in if type == "regular" then - [ + lib.optional (lib.hasSuffix ".nix" name) [ { namespace = namespace ++ [ (lib.strings.removeSuffix ".nix" name) ]; cases = import file; diff --git a/tests/modules/hm-extra-files-byte-compiling.nix b/tests/modules/hm-extra-files-byte-compiling.nix new file mode 100644 index 0000000000..3cb6b5b389 --- /dev/null +++ b/tests/modules/hm-extra-files-byte-compiling.nix @@ -0,0 +1,104 @@ +{ + nixvim, + pkgs, + home-manager, +}: +let + config = { + home = { + username = "nixvim"; + homeDirectory = "/invalid/dir"; + stateVersion = "24.05"; + }; + + programs.nixvim = { + enable = true; + + performance.byteCompileLua.enable = true; + + extraFiles = { + "extra-files/test1.lua".text = "vim.opt.tabstop = 2"; + "extra-files/test2.lua".source = builtins.toFile "file_source.lua" "vim.opt.tabstop = 2"; + "extra-files/test3.lua".source = pkgs.writeText "test3.lua" "vim.opt.tabstop = 2"; + "extra-files/test.vim".text = "set tabstop=2"; + "extra-files/test.json".text = builtins.toJSON { a = 1; }; + }; + + files = { + "files/test.lua".opts.tabstop = 2; + "files/test.vim".opts.tabstop = 2; + }; + }; + }; + + homeFilesByteCompilingEnabled = + (home-manager.lib.homeManagerConfiguration { + inherit pkgs; + + modules = [ + nixvim.homeManagerModules.nixvim + config + { programs.nixvim.performance.byteCompileLua.configs = true; } + ]; + }).config.home-files; + + homeFilesByteCompilingDisabled = + (home-manager.lib.homeManagerConfiguration { + inherit pkgs; + + modules = [ + nixvim.homeManagerModules.nixvim + config + { programs.nixvim.performance.byteCompileLua.configs = false; } + ]; + }).config.home-files; +in +pkgs.runCommand "home-manager-extra-files-byte-compiling" { } '' + is_binary() { + ! grep -qI . "$1" + } + test_byte_compiled() { + if ! is_binary "$home_files/.config/nvim/$1"; then + echo "File $1 is expected to be byte compiled, but it's not" + exit 1 + fi + } + test_not_byte_compiled() { + if is_binary "$home_files/.config/nvim/$1"; then + echo "File $1 is not expected to be byte compiled, but it is" + exit 1 + fi + } + + # Test directory with extraFiles byte compiling enabled + home_files="${homeFilesByteCompilingEnabled}" + + echo "Testing home-files with extraFiles byte compiling enabled" + + # extraFiles + test_byte_compiled extra-files/test1.lua + test_byte_compiled extra-files/test2.lua + test_byte_compiled extra-files/test3.lua + test_not_byte_compiled extra-files/test.vim + test_not_byte_compiled extra-files/test.json + # files + test_byte_compiled files/test.lua + test_not_byte_compiled files/test.vim + + # Test directory with extraFiles byte compiling disabled + home_files="${homeFilesByteCompilingDisabled}" + + echo "Testing home-files with extraFiles byte compiling disabled" + + # extraFiles + test_not_byte_compiled extra-files/test1.lua + test_not_byte_compiled extra-files/test2.lua + test_not_byte_compiled extra-files/test3.lua + test_not_byte_compiled extra-files/test.vim + test_not_byte_compiled extra-files/test.json + # files + test_not_byte_compiled files/test.lua + test_not_byte_compiled files/test.vim + + touch $out +'' diff --git a/tests/test-sources/modules/performance/byte-compile-lua.nix b/tests/test-sources/modules/performance/byte-compile-lua.nix index 15a7330ca2..a0f2edd783 100644 --- a/tests/test-sources/modules/performance/byte-compile-lua.nix +++ b/tests/test-sources/modules/performance/byte-compile-lua.nix @@ -30,10 +30,31 @@ in performance.byteCompileLua.enable = true; extraFiles = { + # By text "plugin/file_text.lua".text = "vim.opt.tabstop = 2"; + # By simple source derivation using buildCommand "plugin/file_source.lua".source = helpers.writeLua "file_source.lua" "vim.opt.tabstop = 2"; + # By standard derivation, it needs to execute fixupPhase + "plugin/file_drv.lua".source = pkgs.stdenvNoCC.mkDerivation { + name = "file_drv.lua"; + src = pkgs.emptyDirectory; + buildPhase = '' + echo "vim.opt.tabstop = 2" > $out + ''; + }; + # By path + "plugin/file_path.lua".source = ./files/file.lua; + # By string + "plugin/file_string.lua".source = builtins.toFile "file_path.lua" "vim.opt.tabstop = 2"; + # By derivation converted to string + "plugin/file_drv_string.lua".source = toString ( + helpers.writeLua "file_drv_string.lua" "vim.opt.tabstop = 2" + ); + # Non-lua files "plugin/test.vim".text = "set tabstop=2"; "plugin/test.json".text = builtins.toJSON { a = 1; }; + # Lua file with txt extension won't be byte compiled + "test.txt".source = helpers.writeLua "test.txt" "vim.opt.tabstop = 2"; }; files = { @@ -64,14 +85,19 @@ in local init_content = vim.fn.system("${config.printInitPackage}/bin/nixvim-print-init") assert(init_content:find("VALIDATING_STRING"), "nixvim-print-init's output is byte compiled") - -- extraFiles - test_rtp_file("plugin/file_text.lua", false) - test_rtp_file("plugin/file_source.lua", false) + -- lua extraFiles are byte compiled + test_rtp_file("plugin/file_text.lua", true) + test_rtp_file("plugin/file_source.lua", true) + test_rtp_file("plugin/file_drv.lua", true) + test_rtp_file("plugin/file_path.lua", true) + test_rtp_file("plugin/file_string.lua", true) + test_rtp_file("plugin/file_drv_string.lua", true) test_rtp_file("plugin/test.vim", false) test_rtp_file("plugin/test.json", false) + test_rtp_file("test.txt", false) - -- files - test_rtp_file("plugin/file.lua", false) + -- lua files are byte compiled + test_rtp_file("plugin/file.lua", true) test_rtp_file("plugin/file.vim", false) -- Plugins and neovim runtime aren't byte compiled by default @@ -136,4 +162,24 @@ in assert(not is_byte_compiled(init), "MYVIMRC is not expected to be byte compiled, but it is") ''; }; + + configs-disabled = { + performance.byteCompileLua = { + enable = true; + configs = false; + }; + + extraFiles."plugin/test1.lua".text = "vim.opt.tabstop = 2"; + + files."plugin/test2.lua".opts.tabstop = 2; + + extraConfigLuaPost = '' + ${isByteCompiledFun} + + -- extraFiles + test_rtp_file("plugin/test1.lua", false) + -- files + test_rtp_file("plugin/test2.lua", false) + ''; + }; } diff --git a/tests/test-sources/modules/performance/files/file.lua b/tests/test-sources/modules/performance/files/file.lua new file mode 100644 index 0000000000..0ba6a7d950 --- /dev/null +++ b/tests/test-sources/modules/performance/files/file.lua @@ -0,0 +1 @@ +vim.opt.tabstop = 2 diff --git a/wrappers/_shared.nix b/wrappers/_shared.nix index af9848b5f3..a707e3ee5a 100644 --- a/wrappers/_shared.nix +++ b/wrappers/_shared.nix @@ -53,11 +53,11 @@ in setAttrByPath filesOpt ( listToAttrs ( map ( - { target, source, ... }: + { target, finalSource, ... }: { name = filesPrefix + target; value = { - inherit source; + source = finalSource; }; } ) extraFiles From 55ca9d235b114353af7921076aa3fb7d0acd37f5 Mon Sep 17 00:00:00 2001 From: Stanislav Asunkin <1353637+stasjok@users.noreply.github.com> Date: Wed, 17 Jul 2024 21:06:30 +0300 Subject: [PATCH 3/4] modules/performance: add ability to byte compile lua plugins This commit adds `performance.byteCompileLua.plugins` toggle that, if enabled, byte compiles all lua files in plugins --- modules/performance.nix | 3 + modules/top-level/output.nix | 23 ++++++- .../modules/performance/byte-compile-lua.nix | 68 +++++++++++++++++++ 3 files changed, 92 insertions(+), 2 deletions(-) diff --git a/modules/performance.nix b/modules/performance.nix index 359f02dcd9..9714b6101c 100644 --- a/modules/performance.nix +++ b/modules/performance.nix @@ -18,6 +18,9 @@ in default = true; example = false; }; + plugins = lib.mkEnableOption "plugins" // { + description = "Whether to byte compile lua plugins."; + }; }; combinePlugins = { diff --git a/modules/top-level/output.nix b/modules/top-level/output.nix index 351965997d..4472ecfc87 100644 --- a/modules/top-level/output.nix +++ b/modules/top-level/output.nix @@ -87,8 +87,27 @@ in defaultPlugin // (if p ? plugin then p else { plugin = p; }); normalizePluginList = plugins: map normalize plugins; - # Normalized plugin list - normalizedPlugins = normalizePluginList config.extraPlugins; + # Byte compiling of normalized plugin list + byteCompilePlugins = + plugins: + let + byteCompile = + p: + (helpers.byteCompileLuaDrv p).overrideAttrs ( + prev: lib.optionalAttrs (prev ? dependencies) { dependencies = map byteCompile prev.dependencies; } + ); + in + map (p: p // { plugin = byteCompile p.plugin; }) plugins; + + # Normalized and optionally byte compiled plugin list + normalizedPlugins = + let + normalized = normalizePluginList config.extraPlugins; + in + if config.performance.byteCompileLua.enable && config.performance.byteCompileLua.plugins then + byteCompilePlugins normalized + else + normalized; # Plugin list extended with dependencies allPlugins = diff --git a/tests/test-sources/modules/performance/byte-compile-lua.nix b/tests/test-sources/modules/performance/byte-compile-lua.nix index a0f2edd783..e906c59623 100644 --- a/tests/test-sources/modules/performance/byte-compile-lua.nix +++ b/tests/test-sources/modules/performance/byte-compile-lua.nix @@ -183,3 +183,71 @@ in ''; }; } +// + # Two equal tests, one with combinePlugins.enable = true + pkgs.lib.genAttrs + [ + "plugins" + "plugins-combined" + ] + (name: { + performance = { + byteCompileLua = { + enable = true; + plugins = true; + }; + + combinePlugins.enable = pkgs.lib.hasSuffix "combined" name; + }; + + extraPlugins = with pkgs.vimPlugins; [ + nvim-lspconfig + # Depends on plenary-nvim + telescope-nvim + # buildCommand plugin with python3 dependency + ((pkgs.writeTextDir "/plugin/test.lua" "vim.opt.tabstop = 2").overrideAttrs { + passthru.python3Dependencies = ps: [ ps.pyyaml ]; + }) + # Plugin with invalid lua file tests/indent/lua/cond.lua (should be ignored) + nvim-treesitter + ]; + + extraConfigLuaPost = '' + ${isByteCompiledFun} + + -- Plugins are loadable + require("lspconfig") + require("telescope") + require("plenary") + require("nvim-treesitter") + + -- Python modules are importable + vim.cmd.py3("import yaml") + + -- nvim-lspconfig + test_rtp_file("lua/lspconfig.lua", true) + test_rtp_file("lua/lspconfig/server_configurations/nixd.lua", true) + test_rtp_file("plugin/lspconfig.lua", true) + test_rtp_file("doc/lspconfig.txt", false) + + -- telescope-nvim + test_rtp_file("lua/telescope/init.lua", true) + test_rtp_file("lua/telescope/builtin/init.lua", true) + test_rtp_file("plugin/telescope.lua", true) + test_rtp_file("autoload/health/telescope.vim", false) + test_rtp_file("doc/telescope.txt", false) + + -- Dependency of telescope-nvim (plenary-nvim) + test_rtp_file("lua/plenary/init.lua", true) + test_rtp_file("plugin/plenary.vim", false) + + -- Test plugin + test_rtp_file("plugin/test.lua", true) + + -- nvim-treesitter + test_rtp_file("lua/nvim-treesitter/health.lua", true) + test_rtp_file("lua/nvim-treesitter/install.lua", true) + test_rtp_file("plugin/nvim-treesitter.lua", true) + test_rtp_file("queries/nix/highlights.scm", false) + ''; + }) From 9314cd46f0f1ff756bae1ae1718c116be894930d Mon Sep 17 00:00:00 2001 From: Stanislav Asunkin <1353637+stasjok@users.noreply.github.com> Date: Wed, 17 Jul 2024 22:26:06 +0300 Subject: [PATCH 4/4] modules/performance: add ability to byte compile nvim runtime directory This commit adds `performance.byteCompileLua.nvimRuntime` toggle that, if enabled, byte compiles all lua files in Nvim runtime directory. --- modules/performance.nix | 3 ++ modules/top-level/output.nix | 23 ++++++++++++- .../modules/performance/byte-compile-lua.nix | 34 +++++++++++++++++++ 3 files changed, 59 insertions(+), 1 deletion(-) diff --git a/modules/performance.nix b/modules/performance.nix index 9714b6101c..2d08615866 100644 --- a/modules/performance.nix +++ b/modules/performance.nix @@ -21,6 +21,9 @@ in plugins = lib.mkEnableOption "plugins" // { description = "Whether to byte compile lua plugins."; }; + nvimRuntime = lib.mkEnableOption "nvimRuntime" // { + description = "Whether to byte compile lua files in Nvim runtime."; + }; }; combinePlugins = { diff --git a/modules/top-level/output.nix b/modules/top-level/output.nix index 4472ecfc87..d828cd080f 100644 --- a/modules/top-level/output.nix +++ b/modules/top-level/output.nix @@ -244,7 +244,28 @@ in ++ (optional config.wrapRc ''--add-flags -u --add-flags "${init}"'') ); - wrappedNeovim = pkgs.wrapNeovimUnstable config.package ( + package = + if config.performance.byteCompileLua.enable && config.performance.byteCompileLua.nvimRuntime then + # Using symlinkJoin to avoid rebuilding neovim + pkgs.symlinkJoin { + name = "neovim-byte-compiled-${lib.getVersion config.package}"; + paths = [ config.package ]; + # Required attributes from original neovim package + inherit (config.package) lua; + nativeBuildInputs = [ helpers.byteCompileLuaHook ]; + postBuild = '' + # Replace Nvim's binary symlink with a regular file, + # or Nvim will use original runtime directory + rm $out/bin/nvim + cp ${config.package}/bin/nvim $out/bin/nvim + + runHook postFixup + ''; + } + else + config.package; + + wrappedNeovim = pkgs.wrapNeovimUnstable package ( neovimConfig // { wrapperArgs = lib.escapeShellArgs neovimConfig.wrapperArgs + " " + extraWrapperArgs; diff --git a/tests/test-sources/modules/performance/byte-compile-lua.nix b/tests/test-sources/modules/performance/byte-compile-lua.nix index e906c59623..3ed2945047 100644 --- a/tests/test-sources/modules/performance/byte-compile-lua.nix +++ b/tests/test-sources/modules/performance/byte-compile-lua.nix @@ -182,6 +182,40 @@ in test_rtp_file("plugin/test2.lua", false) ''; }; + + nvim-runtime = { + performance.byteCompileLua = { + enable = true; + nvimRuntime = true; + }; + + extraPlugins = [ + # Python 3 dependencies + (pkgs.vimPlugins.nvim-lspconfig.overrideAttrs { passthru.python3Dependencies = ps: [ ps.pyyaml ]; }) + ]; + + extraConfigLuaPost = '' + ${isByteCompiledFun} + + -- vim namespace is working + vim.opt.tabstop = 2 + vim.api.nvim_get_runtime_file("init.lua", false) + vim.lsp.get_clients() + vim.treesitter.language.get_filetypes("nix") + vim.iter({}) + + test_rtp_file("lua/vim/lsp.lua", true) + test_rtp_file("lua/vim/iter.lua", true) + test_rtp_file("lua/vim/treesitter/query.lua", true) + test_rtp_file("lua/vim/lsp/buf.lua", true) + test_rtp_file("plugin/editorconfig.lua", true) + test_rtp_file("plugin/tutor.vim", false) + test_rtp_file("ftplugin/vim.vim", false) + + -- Python3 packages are importable + vim.cmd.py3("import yaml") + ''; + }; } // # Two equal tests, one with combinePlugins.enable = true