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") + ''; + }; +}