mirror of
				https://github.com/neovim/neovim.git
				synced 2025-10-26 12:27:24 +00:00 
			
		
		
		
	perf: pre-compile embedded Lua source into bytecode (#16631)
The Lua modules that make up vim.lua are embedded as raw source files into the nvim binary. These sources are loaded by the Lua runtime on startuptime. We can pre-compile these sources into Lua bytecode before embedding them into the binary, which minimizes the size of the binary and improves startuptime.
This commit is contained in:
		| @@ -543,6 +543,24 @@ endif() | |||||||
|  |  | ||||||
| message(STATUS "Using Lua interpreter: ${LUA_PRG}") | message(STATUS "Using Lua interpreter: ${LUA_PRG}") | ||||||
|  |  | ||||||
|  | if(NOT WIN32) | ||||||
|  |   if(PREFER_LUA) | ||||||
|  |     foreach(CURRENT_LUAC_PRG luac5.1 luac) | ||||||
|  |       find_program(_CHECK_LUAC_PRG ${CURRENT_LUAC_PRG}) | ||||||
|  |       if(_CHECK_LUAC_PRG) | ||||||
|  |         set(LUAC_PRG "${_CHECK_LUAC_PRG} -s -o - %s" CACHE STRING "Format for compiling to Lua bytecode") | ||||||
|  |         break() | ||||||
|  |       endif() | ||||||
|  |     endforeach() | ||||||
|  |   elseif(LUA_PRG MATCHES "luajit") | ||||||
|  |     set(LUAC_PRG "${LUA_PRG} -b -s %s -" CACHE STRING "Format for compiling to Lua bytecode") | ||||||
|  |   endif() | ||||||
|  | endif() | ||||||
|  |  | ||||||
|  | if(LUAC_PRG) | ||||||
|  |   message(STATUS "Using Lua compiler: ${LUAC_PRG}") | ||||||
|  | endif() | ||||||
|  |  | ||||||
| # Setup busted. | # Setup busted. | ||||||
| find_program(BUSTED_PRG NAMES busted busted.bat) | find_program(BUSTED_PRG NAMES busted busted.bat) | ||||||
| find_program(BUSTED_LUA_PRG busted-lua) | find_program(BUSTED_LUA_PRG busted-lua) | ||||||
|   | |||||||
| @@ -326,7 +326,9 @@ add_custom_command( | |||||||
|  |  | ||||||
| add_custom_command( | add_custom_command( | ||||||
|   OUTPUT ${VIM_MODULE_FILE} |   OUTPUT ${VIM_MODULE_FILE} | ||||||
|   COMMAND ${LUA_PRG} ${CHAR_BLOB_GENERATOR} ${VIM_MODULE_FILE} |   COMMAND ${CMAKE_COMMAND} -E env | ||||||
|  |       "LUAC_PRG=${LUAC_PRG}" | ||||||
|  |       ${LUA_PRG} ${CHAR_BLOB_GENERATOR} -c ${VIM_MODULE_FILE} | ||||||
|       ${LUA_VIM_MODULE_SOURCE} vim_module |       ${LUA_VIM_MODULE_SOURCE} vim_module | ||||||
|       ${LUA_SHARED_MODULE_SOURCE} shared_module |       ${LUA_SHARED_MODULE_SOURCE} shared_module | ||||||
|       ${LUA_INSPECT_MODULE_SOURCE} inspect_module |       ${LUA_INSPECT_MODULE_SOURCE} inspect_module | ||||||
| @@ -339,6 +341,7 @@ add_custom_command( | |||||||
|     ${LUA_INSPECT_MODULE_SOURCE} |     ${LUA_INSPECT_MODULE_SOURCE} | ||||||
|     ${LUA_F_MODULE_SOURCE} |     ${LUA_F_MODULE_SOURCE} | ||||||
|     ${LUA_META_MODULE_SOURCE} |     ${LUA_META_MODULE_SOURCE} | ||||||
|  |   VERBATIM | ||||||
| ) | ) | ||||||
|  |  | ||||||
| list(APPEND NVIM_GENERATED_SOURCES | list(APPEND NVIM_GENERATED_SOURCES | ||||||
|   | |||||||
| @@ -1,12 +1,26 @@ | |||||||
| if arg[1] == '--help' then | if arg[1] == '--help' then | ||||||
|   print('Usage:') |   print('Usage:') | ||||||
|   print('  '..arg[0]..' target source varname [source varname]...') |   print('  '..arg[0]..' [-c] target source varname [source varname]...') | ||||||
|   print('') |   print('') | ||||||
|   print('Generates C file with big uint8_t blob.') |   print('Generates C file with big uint8_t blob.') | ||||||
|   print('Blob will be stored in a static const array named varname.') |   print('Blob will be stored in a static const array named varname.') | ||||||
|   os.exit() |   os.exit() | ||||||
| end | end | ||||||
|  |  | ||||||
|  | -- Recognized options: | ||||||
|  | --   -c   compile Lua bytecode | ||||||
|  | local options = {} | ||||||
|  |  | ||||||
|  | while true do | ||||||
|  |   local opt = string.match(arg[1], "^-(%w)") | ||||||
|  |   if not opt then | ||||||
|  |     break | ||||||
|  |   end | ||||||
|  |  | ||||||
|  |   options[opt] = true | ||||||
|  |   table.remove(arg, 1) | ||||||
|  | end | ||||||
|  |  | ||||||
| assert(#arg >= 3 and (#arg - 1) % 2 == 0) | assert(#arg >= 3 and (#arg - 1) % 2 == 0) | ||||||
|  |  | ||||||
| local target_file = arg[1] or error('Need a target file') | local target_file = arg[1] or error('Need a target file') | ||||||
| @@ -23,11 +37,25 @@ for argi = 2, #arg, 2 do | |||||||
|   end |   end | ||||||
|   varnames[varname] = source_file |   varnames[varname] = source_file | ||||||
|  |  | ||||||
|   local source = io.open(source_file, 'r') |  | ||||||
|       or error(string.format("source_file %q doesn't exist", source_file)) |  | ||||||
|  |  | ||||||
|   target:write(('static const uint8_t %s[] = {\n'):format(varname)) |   target:write(('static const uint8_t %s[] = {\n'):format(varname)) | ||||||
|  |  | ||||||
|  |   local output | ||||||
|  |   if options.c then | ||||||
|  |     local luac = os.getenv("LUAC_PRG") | ||||||
|  |     if luac then | ||||||
|  |       output = io.popen(luac:format(source_file), "r"):read("*a") | ||||||
|  |     else | ||||||
|  |       print("LUAC_PRG is undefined") | ||||||
|  |     end | ||||||
|  |   end | ||||||
|  |  | ||||||
|  |   if not output then | ||||||
|  |     local source = io.open(source_file, "r") | ||||||
|  |         or error(string.format("source_file %q doesn't exist", source_file)) | ||||||
|  |     output = source:read("*a") | ||||||
|  |     source:close() | ||||||
|  |   end | ||||||
|  |  | ||||||
|   local num_bytes = 0 |   local num_bytes = 0 | ||||||
|   local MAX_NUM_BYTES = 15  -- 78 / 5: maximum number of bytes on one line |   local MAX_NUM_BYTES = 15  -- 78 / 5: maximum number of bytes on one line | ||||||
|   target:write(' ') |   target:write(' ') | ||||||
| @@ -41,19 +69,13 @@ for argi = 2, #arg, 2 do | |||||||
|     end |     end | ||||||
|   end |   end | ||||||
|  |  | ||||||
|   for line in source:lines() do |   for i = 1, string.len(output) do | ||||||
|     for i = 1, string.len(line) do |     local byte = output:byte(i) | ||||||
|       local byte = line:byte(i) |  | ||||||
|       assert(byte ~= 0) |  | ||||||
|     target:write(string.format(' %3u,', byte)) |     target:write(string.format(' %3u,', byte)) | ||||||
|     increase_num_bytes() |     increase_num_bytes() | ||||||
|   end |   end | ||||||
|     target:write(string.format(' %3u,', string.byte('\n', 1))) |  | ||||||
|     increase_num_bytes() |  | ||||||
|   end |  | ||||||
|  |  | ||||||
|   target:write('  0};\n') |   target:write('  0};\n') | ||||||
|   source:close() |  | ||||||
| end | end | ||||||
|  |  | ||||||
| target:close() | target:close() | ||||||
|   | |||||||
| @@ -404,9 +404,9 @@ static int nlua_state_init(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL | |||||||
|  |  | ||||||
|   { |   { | ||||||
|     const char *code = (char *)&shared_module[0]; |     const char *code = (char *)&shared_module[0]; | ||||||
|     if (luaL_loadbuffer(lstate, code, strlen(code), "@vim/shared.lua") |     if (luaL_loadbuffer(lstate, code, sizeof(shared_module) - 1, "@vim/shared.lua") | ||||||
|         || nlua_pcall(lstate, 0, 0)) { |         || nlua_pcall(lstate, 0, 0)) { | ||||||
|       nlua_error(lstate, _("E5106: Error while creating shared module: %.*s")); |       nlua_error(lstate, _("E5106: Error while creating shared module: %.*s\n")); | ||||||
|       return 1; |       return 1; | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| @@ -416,18 +416,18 @@ static int nlua_state_init(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL | |||||||
|     lua_getfield(lstate, -1, "loaded");  // [package, loaded] |     lua_getfield(lstate, -1, "loaded");  // [package, loaded] | ||||||
|  |  | ||||||
|     const char *code = (char *)&inspect_module[0]; |     const char *code = (char *)&inspect_module[0]; | ||||||
|     if (luaL_loadbuffer(lstate, code, strlen(code), "@vim/inspect.lua") |     if (luaL_loadbuffer(lstate, code, sizeof(inspect_module) - 1, "@vim/inspect.lua") | ||||||
|         || nlua_pcall(lstate, 0, 1)) { |         || nlua_pcall(lstate, 0, 1)) { | ||||||
|       nlua_error(lstate, _("E5106: Error while creating inspect module: %.*s")); |       nlua_error(lstate, _("E5106: Error while creating inspect module: %.*s\n")); | ||||||
|       return 1; |       return 1; | ||||||
|     } |     } | ||||||
|     // [package, loaded, inspect] |     // [package, loaded, inspect] | ||||||
|     lua_setfield(lstate, -2, "vim.inspect");  // [package, loaded] |     lua_setfield(lstate, -2, "vim.inspect");  // [package, loaded] | ||||||
|  |  | ||||||
|     code = (char *)&lua_F_module[0]; |     code = (char *)&lua_F_module[0]; | ||||||
|     if (luaL_loadbuffer(lstate, code, strlen(code), "@vim/F.lua") |     if (luaL_loadbuffer(lstate, code, sizeof(lua_F_module) - 1, "@vim/F.lua") | ||||||
|         || nlua_pcall(lstate, 0, 1)) { |         || nlua_pcall(lstate, 0, 1)) { | ||||||
|       nlua_error(lstate, _("E5106: Error while creating vim.F module: %.*s")); |       nlua_error(lstate, _("E5106: Error while creating vim.F module: %.*s\n")); | ||||||
|       return 1; |       return 1; | ||||||
|     } |     } | ||||||
|     // [package, loaded, module] |     // [package, loaded, module] | ||||||
| @@ -438,9 +438,9 @@ static int nlua_state_init(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL | |||||||
|  |  | ||||||
|   { |   { | ||||||
|     const char *code = (char *)&vim_module[0]; |     const char *code = (char *)&vim_module[0]; | ||||||
|     if (luaL_loadbuffer(lstate, code, strlen(code), "@vim.lua") |     if (luaL_loadbuffer(lstate, code, sizeof(vim_module) - 1, "@vim.lua") | ||||||
|         || nlua_pcall(lstate, 0, 0)) { |         || nlua_pcall(lstate, 0, 0)) { | ||||||
|       nlua_error(lstate, _("E5106: Error while creating vim module: %.*s")); |       nlua_error(lstate, _("E5106: Error while creating vim module: %.*s\n")); | ||||||
|       return 1; |       return 1; | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| @@ -450,9 +450,9 @@ static int nlua_state_init(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL | |||||||
|     lua_getfield(lstate, -1, "loaded");  // [package, loaded] |     lua_getfield(lstate, -1, "loaded");  // [package, loaded] | ||||||
|  |  | ||||||
|     const char *code = (char *)&lua_meta_module[0]; |     const char *code = (char *)&lua_meta_module[0]; | ||||||
|     if (luaL_loadbuffer(lstate, code, strlen(code), "@vim/_meta.lua") |     if (luaL_loadbuffer(lstate, code, sizeof(lua_meta_module) - 1, "@vim/_meta.lua") | ||||||
|         || nlua_pcall(lstate, 0, 1)) { |         || nlua_pcall(lstate, 0, 1)) { | ||||||
|       nlua_error(lstate, _("E5106: Error while creating vim._meta module: %.*s")); |       nlua_error(lstate, _("E5106: Error while creating vim._meta module: %.*s\n")); | ||||||
|       return 1; |       return 1; | ||||||
|     } |     } | ||||||
|     // [package, loaded, module] |     // [package, loaded, module] | ||||||
|   | |||||||
| @@ -131,9 +131,9 @@ describe('lua stdlib', function() | |||||||
|     eq(false, funcs.luaeval('vim.startswith("123", "2")')) |     eq(false, funcs.luaeval('vim.startswith("123", "2")')) | ||||||
|     eq(false, funcs.luaeval('vim.startswith("123", "1234")')) |     eq(false, funcs.luaeval('vim.startswith("123", "1234")')) | ||||||
|  |  | ||||||
|     eq("Error executing lua: vim/shared.lua:0: prefix: expected string, got nil", |     matches("prefix: expected string, got nil", | ||||||
|       pcall_err(exec_lua, 'return vim.startswith("123", nil)')) |       pcall_err(exec_lua, 'return vim.startswith("123", nil)')) | ||||||
|     eq("Error executing lua: vim/shared.lua:0: s: expected string, got nil", |     matches("s: expected string, got nil", | ||||||
|       pcall_err(exec_lua, 'return vim.startswith(nil, "123")')) |       pcall_err(exec_lua, 'return vim.startswith(nil, "123")')) | ||||||
|   end) |   end) | ||||||
|  |  | ||||||
| @@ -147,9 +147,9 @@ describe('lua stdlib', function() | |||||||
|     eq(false, funcs.luaeval('vim.endswith("123", "2")')) |     eq(false, funcs.luaeval('vim.endswith("123", "2")')) | ||||||
|     eq(false, funcs.luaeval('vim.endswith("123", "1234")')) |     eq(false, funcs.luaeval('vim.endswith("123", "1234")')) | ||||||
|  |  | ||||||
|     eq("Error executing lua: vim/shared.lua:0: suffix: expected string, got nil", |     matches("suffix: expected string, got nil", | ||||||
|       pcall_err(exec_lua, 'return vim.endswith("123", nil)')) |       pcall_err(exec_lua, 'return vim.endswith("123", nil)')) | ||||||
|     eq("Error executing lua: vim/shared.lua:0: s: expected string, got nil", |     matches("s: expected string, got nil", | ||||||
|       pcall_err(exec_lua, 'return vim.endswith(nil, "123")')) |       pcall_err(exec_lua, 'return vim.endswith(nil, "123")')) | ||||||
|   end) |   end) | ||||||
|  |  | ||||||
| @@ -220,9 +220,9 @@ describe('lua stdlib', function() | |||||||
|     eq({"yy","xx"}, exec_lua("return test_table")) |     eq({"yy","xx"}, exec_lua("return test_table")) | ||||||
|  |  | ||||||
|     -- Validates args. |     -- Validates args. | ||||||
|     eq('Error executing lua: vim.schedule: expected function', |     matches('vim.schedule: expected function', | ||||||
|       pcall_err(exec_lua, "vim.schedule('stringly')")) |       pcall_err(exec_lua, "vim.schedule('stringly')")) | ||||||
|     eq('Error executing lua: vim.schedule: expected function', |     matches('vim.schedule: expected function', | ||||||
|       pcall_err(exec_lua, "vim.schedule()")) |       pcall_err(exec_lua, "vim.schedule()")) | ||||||
|  |  | ||||||
|     exec_lua([[ |     exec_lua([[ | ||||||
| @@ -232,7 +232,7 @@ describe('lua stdlib', function() | |||||||
|     ]]) |     ]]) | ||||||
|  |  | ||||||
|     feed("<cr>") |     feed("<cr>") | ||||||
|     eq('Error executing vim.schedule lua callback: [string "<nvim>"]:2: big failure\nvery async', remove_trace(eval("v:errmsg"))) |     matches('big failure\nvery async', remove_trace(eval("v:errmsg"))) | ||||||
|  |  | ||||||
|     local screen = Screen.new(60,5) |     local screen = Screen.new(60,5) | ||||||
|     screen:set_default_attr_ids({ |     screen:set_default_attr_ids({ | ||||||
| @@ -300,16 +300,16 @@ describe('lua stdlib', function() | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     for _, t in ipairs(loops) do |     for _, t in ipairs(loops) do | ||||||
|       eq("Error executing lua: vim/shared.lua:0: Infinite loop detected", pcall_err(split, t[1], t[2])) |       matches("Infinite loop detected", pcall_err(split, t[1], t[2])) | ||||||
|     end |     end | ||||||
|  |  | ||||||
|     -- Validates args. |     -- Validates args. | ||||||
|     eq(true, pcall(split, 'string', 'string')) |     eq(true, pcall(split, 'string', 'string')) | ||||||
|     eq('Error executing lua: vim/shared.lua:0: s: expected string, got number', |     matches('s: expected string, got number', | ||||||
|       pcall_err(split, 1, 'string')) |       pcall_err(split, 1, 'string')) | ||||||
|     eq('Error executing lua: vim/shared.lua:0: sep: expected string, got number', |     matches('sep: expected string, got number', | ||||||
|       pcall_err(split, 'string', 1)) |       pcall_err(split, 'string', 1)) | ||||||
|     eq('Error executing lua: vim/shared.lua:0: kwargs: expected table, got number', |     matches('kwargs: expected table, got number', | ||||||
|       pcall_err(split, 'string', 'string', 1)) |       pcall_err(split, 'string', 'string', 1)) | ||||||
|   end) |   end) | ||||||
|  |  | ||||||
| @@ -330,7 +330,7 @@ describe('lua stdlib', function() | |||||||
|     end |     end | ||||||
|  |  | ||||||
|     -- Validates args. |     -- Validates args. | ||||||
|     eq('Error executing lua: vim/shared.lua:0: s: expected string, got number', |     matches('s: expected string, got number', | ||||||
|       pcall_err(trim, 2)) |       pcall_err(trim, 2)) | ||||||
|   end) |   end) | ||||||
|  |  | ||||||
| @@ -410,7 +410,7 @@ describe('lua stdlib', function() | |||||||
|       return getmetatable(t2) == mt |       return getmetatable(t2) == mt | ||||||
|     ]])) |     ]])) | ||||||
|  |  | ||||||
|     eq('Error executing lua: vim/shared.lua:0: Cannot deepcopy object of type thread', |     matches('Cannot deepcopy object of type thread', | ||||||
|       pcall_err(exec_lua, [[ |       pcall_err(exec_lua, [[ | ||||||
|         local thread = coroutine.create(function () return 0 end) |         local thread = coroutine.create(function () return 0 end) | ||||||
|         local t = {thr = thread} |         local t = {thr = thread} | ||||||
| @@ -423,7 +423,7 @@ describe('lua stdlib', function() | |||||||
|     eq('foo%%%-bar', exec_lua([[return vim.pesc(vim.pesc('foo-bar'))]])) |     eq('foo%%%-bar', exec_lua([[return vim.pesc(vim.pesc('foo-bar'))]])) | ||||||
|  |  | ||||||
|     -- Validates args. |     -- Validates args. | ||||||
|     eq('Error executing lua: vim/shared.lua:0: s: expected string, got number', |     matches('s: expected string, got number', | ||||||
|       pcall_err(exec_lua, [[return vim.pesc(2)]])) |       pcall_err(exec_lua, [[return vim.pesc(2)]])) | ||||||
|   end) |   end) | ||||||
|  |  | ||||||
| @@ -548,19 +548,19 @@ describe('lua stdlib', function() | |||||||
|       return c.x.a == 1 and c.x.b == 2 and c.x.c == nil and count == 1 |       return c.x.a == 1 and c.x.b == 2 and c.x.c == nil and count == 1 | ||||||
|     ]])) |     ]])) | ||||||
|  |  | ||||||
|     eq('Error executing lua: vim/shared.lua:0: invalid "behavior": nil', |     matches('invalid "behavior": nil', | ||||||
|       pcall_err(exec_lua, [[ |       pcall_err(exec_lua, [[ | ||||||
|         return vim.tbl_extend() |         return vim.tbl_extend() | ||||||
|       ]]) |       ]]) | ||||||
|     ) |     ) | ||||||
|  |  | ||||||
|     eq('Error executing lua: vim/shared.lua:0: wrong number of arguments (given 1, expected at least 3)', |     matches('wrong number of arguments %(given 1, expected at least 3%)', | ||||||
|       pcall_err(exec_lua, [[ |       pcall_err(exec_lua, [[ | ||||||
|         return vim.tbl_extend("keep") |         return vim.tbl_extend("keep") | ||||||
|       ]]) |       ]]) | ||||||
|     ) |     ) | ||||||
|  |  | ||||||
|     eq('Error executing lua: vim/shared.lua:0: wrong number of arguments (given 2, expected at least 3)', |     matches('wrong number of arguments %(given 2, expected at least 3%)', | ||||||
|       pcall_err(exec_lua, [[ |       pcall_err(exec_lua, [[ | ||||||
|         return vim.tbl_extend("keep", {}) |         return vim.tbl_extend("keep", {}) | ||||||
|       ]]) |       ]]) | ||||||
| @@ -661,19 +661,19 @@ describe('lua stdlib', function() | |||||||
|       return vim.tbl_deep_extend("force", a, b) |       return vim.tbl_deep_extend("force", a, b) | ||||||
|     ]]), {a = 123 }) |     ]]), {a = 123 }) | ||||||
|  |  | ||||||
|     eq('Error executing lua: vim/shared.lua:0: invalid "behavior": nil', |     matches('invalid "behavior": nil', | ||||||
|       pcall_err(exec_lua, [[ |       pcall_err(exec_lua, [[ | ||||||
|         return vim.tbl_deep_extend() |         return vim.tbl_deep_extend() | ||||||
|       ]]) |       ]]) | ||||||
|     ) |     ) | ||||||
|  |  | ||||||
|     eq('Error executing lua: vim/shared.lua:0: wrong number of arguments (given 1, expected at least 3)', |     matches('wrong number of arguments %(given 1, expected at least 3%)', | ||||||
|       pcall_err(exec_lua, [[ |       pcall_err(exec_lua, [[ | ||||||
|         return vim.tbl_deep_extend("keep") |         return vim.tbl_deep_extend("keep") | ||||||
|       ]]) |       ]]) | ||||||
|     ) |     ) | ||||||
|  |  | ||||||
|     eq('Error executing lua: vim/shared.lua:0: wrong number of arguments (given 2, expected at least 3)', |     matches('wrong number of arguments %(given 2, expected at least 3%)', | ||||||
|       pcall_err(exec_lua, [[ |       pcall_err(exec_lua, [[ | ||||||
|         return vim.tbl_deep_extend("keep", {}) |         return vim.tbl_deep_extend("keep", {}) | ||||||
|       ]]) |       ]]) | ||||||
| @@ -706,7 +706,7 @@ describe('lua stdlib', function() | |||||||
|  |  | ||||||
|   it('vim.list_extend', function() |   it('vim.list_extend', function() | ||||||
|     eq({1,2,3}, exec_lua [[ return vim.list_extend({1}, {2,3}) ]]) |     eq({1,2,3}, exec_lua [[ return vim.list_extend({1}, {2,3}) ]]) | ||||||
|     eq('Error executing lua: vim/shared.lua:0: src: expected table, got nil', |     matches('src: expected table, got nil', | ||||||
|       pcall_err(exec_lua, [[ return vim.list_extend({1}, nil) ]])) |       pcall_err(exec_lua, [[ return vim.list_extend({1}, nil) ]])) | ||||||
|     eq({1,2}, exec_lua [[ return vim.list_extend({1}, {2;a=1}) ]]) |     eq({1,2}, exec_lua [[ return vim.list_extend({1}, {2;a=1}) ]]) | ||||||
|     eq(true, exec_lua [[ local a = {1} return vim.list_extend(a, {2;a=1}) == a ]]) |     eq(true, exec_lua [[ local a = {1} return vim.list_extend(a, {2;a=1}) == a ]]) | ||||||
| @@ -730,7 +730,7 @@ describe('lua stdlib', function() | |||||||
|     assert(vim.deep_equal(a, { A = 1; [1] = 'A'; })) |     assert(vim.deep_equal(a, { A = 1; [1] = 'A'; })) | ||||||
|     vim.tbl_add_reverse_lookup(a) |     vim.tbl_add_reverse_lookup(a) | ||||||
|     ]] |     ]] | ||||||
|     matches('^Error executing lua: vim/shared%.lua:0: The reverse lookup found an existing value for "[1A]" while processing key "[1A]"$', |     matches('The reverse lookup found an existing value for "[1A]" while processing key "[1A]"$', | ||||||
|       pcall_err(exec_lua, code)) |       pcall_err(exec_lua, code)) | ||||||
|   end) |   end) | ||||||
|  |  | ||||||
| @@ -771,7 +771,7 @@ describe('lua stdlib', function() | |||||||
|   end) |   end) | ||||||
|  |  | ||||||
|   it('vim.fn should error when calling API function', function() |   it('vim.fn should error when calling API function', function() | ||||||
|       eq('Error executing lua: vim.lua:0: Tried to call API function with vim.fn: use vim.api.nvim_get_current_line instead', |       matches('Tried to call API function with vim.fn: use vim.api.nvim_get_current_line instead', | ||||||
|           pcall_err(exec_lua, "vim.fn.nvim_get_current_line()")) |           pcall_err(exec_lua, "vim.fn.nvim_get_current_line()")) | ||||||
|   end) |   end) | ||||||
|  |  | ||||||
| @@ -907,37 +907,37 @@ describe('lua stdlib', function() | |||||||
|     exec_lua("vim.validate{arg1={{}, 't' }, arg2={ 'foo', 's' }}") |     exec_lua("vim.validate{arg1={{}, 't' }, arg2={ 'foo', 's' }}") | ||||||
|     exec_lua("vim.validate{arg1={2, function(a) return (a % 2) == 0  end, 'even number' }}") |     exec_lua("vim.validate{arg1={2, function(a) return (a % 2) == 0  end, 'even number' }}") | ||||||
|  |  | ||||||
|     eq('Error executing lua: [string "<nvim>"]:0: opt[1]: expected table, got number', |     matches('expected table, got number', | ||||||
|       pcall_err(exec_lua, "vim.validate{ 1, 'x' }")) |       pcall_err(exec_lua, "vim.validate{ 1, 'x' }")) | ||||||
|     eq('Error executing lua: [string "<nvim>"]:0: invalid type name: x', |     matches('invalid type name: x', | ||||||
|       pcall_err(exec_lua, "vim.validate{ arg1={ 1, 'x' }}")) |       pcall_err(exec_lua, "vim.validate{ arg1={ 1, 'x' }}")) | ||||||
|     eq('Error executing lua: [string "<nvim>"]:0: invalid type name: 1', |     matches('invalid type name: 1', | ||||||
|       pcall_err(exec_lua, "vim.validate{ arg1={ 1, 1 }}")) |       pcall_err(exec_lua, "vim.validate{ arg1={ 1, 1 }}")) | ||||||
|     eq('Error executing lua: [string "<nvim>"]:0: invalid type name: nil', |     matches('invalid type name: nil', | ||||||
|       pcall_err(exec_lua, "vim.validate{ arg1={ 1 }}")) |       pcall_err(exec_lua, "vim.validate{ arg1={ 1 }}")) | ||||||
|  |  | ||||||
|     -- Validated parameters are required by default. |     -- Validated parameters are required by default. | ||||||
|     eq('Error executing lua: [string "<nvim>"]:0: arg1: expected string, got nil', |     matches('arg1: expected string, got nil', | ||||||
|       pcall_err(exec_lua, "vim.validate{ arg1={ nil, 's' }}")) |       pcall_err(exec_lua, "vim.validate{ arg1={ nil, 's' }}")) | ||||||
|     -- Explicitly required. |     -- Explicitly required. | ||||||
|     eq('Error executing lua: [string "<nvim>"]:0: arg1: expected string, got nil', |     matches('arg1: expected string, got nil', | ||||||
|       pcall_err(exec_lua, "vim.validate{ arg1={ nil, 's', false }}")) |       pcall_err(exec_lua, "vim.validate{ arg1={ nil, 's', false }}")) | ||||||
|  |  | ||||||
|     eq('Error executing lua: [string "<nvim>"]:0: arg1: expected table, got number', |     matches('arg1: expected table, got number', | ||||||
|       pcall_err(exec_lua, "vim.validate{arg1={1, 't'}}")) |       pcall_err(exec_lua, "vim.validate{arg1={1, 't'}}")) | ||||||
|     eq('Error executing lua: [string "<nvim>"]:0: arg2: expected string, got number', |     matches('arg2: expected string, got number', | ||||||
|       pcall_err(exec_lua, "vim.validate{arg1={{}, 't'}, arg2={1, 's'}}")) |       pcall_err(exec_lua, "vim.validate{arg1={{}, 't'}, arg2={1, 's'}}")) | ||||||
|     eq('Error executing lua: [string "<nvim>"]:0: arg2: expected string, got nil', |     matches('arg2: expected string, got nil', | ||||||
|       pcall_err(exec_lua, "vim.validate{arg1={{}, 't'}, arg2={nil, 's'}}")) |       pcall_err(exec_lua, "vim.validate{arg1={{}, 't'}, arg2={nil, 's'}}")) | ||||||
|     eq('Error executing lua: [string "<nvim>"]:0: arg2: expected string, got nil', |     matches('arg2: expected string, got nil', | ||||||
|       pcall_err(exec_lua, "vim.validate{arg1={{}, 't'}, arg2={nil, 's'}}")) |       pcall_err(exec_lua, "vim.validate{arg1={{}, 't'}, arg2={nil, 's'}}")) | ||||||
|     eq('Error executing lua: [string "<nvim>"]:0: arg1: expected even number, got 3', |     matches('arg1: expected even number, got 3', | ||||||
|       pcall_err(exec_lua, "vim.validate{arg1={3, function(a) return a == 1 end, 'even number'}}")) |       pcall_err(exec_lua, "vim.validate{arg1={3, function(a) return a == 1 end, 'even number'}}")) | ||||||
|     eq('Error executing lua: [string "<nvim>"]:0: arg1: expected ?, got 3', |     matches('arg1: expected %?, got 3', | ||||||
|       pcall_err(exec_lua, "vim.validate{arg1={3, function(a) return a == 1 end}}")) |       pcall_err(exec_lua, "vim.validate{arg1={3, function(a) return a == 1 end}}")) | ||||||
|  |  | ||||||
|     -- Pass an additional message back. |     -- Pass an additional message back. | ||||||
|     eq('Error executing lua: [string "<nvim>"]:0: arg1: expected ?, got 3. Info: TEST_MSG', |     matches('arg1: expected %?, got 3. Info: TEST_MSG', | ||||||
|       pcall_err(exec_lua, "vim.validate{arg1={3, function(a) return a == 1, 'TEST_MSG' end}}")) |       pcall_err(exec_lua, "vim.validate{arg1={3, function(a) return a == 1, 'TEST_MSG' end}}")) | ||||||
|   end) |   end) | ||||||
|  |  | ||||||
| @@ -982,7 +982,7 @@ describe('lua stdlib', function() | |||||||
|     ]] |     ]] | ||||||
|     eq(NIL, funcs.luaeval "vim.g.to_delete") |     eq(NIL, funcs.luaeval "vim.g.to_delete") | ||||||
|  |  | ||||||
|     matches([[^Error executing lua: .*: attempt to index .* nil value]], |     matches([[attempt to index .* nil value]], | ||||||
|        pcall_err(exec_lua, 'return vim.g[0].testing')) |        pcall_err(exec_lua, 'return vim.g[0].testing')) | ||||||
|   end) |   end) | ||||||
|  |  | ||||||
| @@ -1009,7 +1009,7 @@ describe('lua stdlib', function() | |||||||
|       return {vim.b.nonexistant == vim.NIL, vim.b.nullvar == vim.NIL} |       return {vim.b.nonexistant == vim.NIL, vim.b.nullvar == vim.NIL} | ||||||
|     ]]) |     ]]) | ||||||
|  |  | ||||||
|     matches([[^Error executing lua: .*: attempt to index .* nil value]], |     matches([[attempt to index .* nil value]], | ||||||
|        pcall_err(exec_lua, 'return vim.b[BUF][0].testing')) |        pcall_err(exec_lua, 'return vim.b[BUF][0].testing')) | ||||||
|  |  | ||||||
|     eq({hello="world"}, funcs.luaeval "vim.b.to_delete") |     eq({hello="world"}, funcs.luaeval "vim.b.to_delete") | ||||||
| @@ -1046,7 +1046,7 @@ describe('lua stdlib', function() | |||||||
|     eq(NIL, funcs.luaeval "vim.w.nonexistant") |     eq(NIL, funcs.luaeval "vim.w.nonexistant") | ||||||
|     eq(NIL, funcs.luaeval "vim.w[WIN].nonexistant") |     eq(NIL, funcs.luaeval "vim.w[WIN].nonexistant") | ||||||
|  |  | ||||||
|     matches([[^Error executing lua: .*: attempt to index .* nil value]], |     matches([[attempt to index .* nil value]], | ||||||
|        pcall_err(exec_lua, 'return vim.w[WIN][0].testing')) |        pcall_err(exec_lua, 'return vim.w[WIN][0].testing')) | ||||||
|  |  | ||||||
|     eq({hello="world"}, funcs.luaeval "vim.w.to_delete") |     eq({hello="world"}, funcs.luaeval "vim.w.to_delete") | ||||||
| @@ -1078,7 +1078,7 @@ describe('lua stdlib', function() | |||||||
|     eq(123, funcs.luaeval "vim.t[0].other") |     eq(123, funcs.luaeval "vim.t[0].other") | ||||||
|     eq(NIL, funcs.luaeval "vim.t[0].nonexistant") |     eq(NIL, funcs.luaeval "vim.t[0].nonexistant") | ||||||
|  |  | ||||||
|     matches([[^Error executing lua: .*: attempt to index .* nil value]], |     matches([[attempt to index .* nil value]], | ||||||
|        pcall_err(exec_lua, 'return vim.t[0][0].testing')) |        pcall_err(exec_lua, 'return vim.t[0][0].testing')) | ||||||
|  |  | ||||||
|     eq({hello="world"}, funcs.luaeval "vim.t.to_delete") |     eq({hello="world"}, funcs.luaeval "vim.t.to_delete") | ||||||
| @@ -1108,7 +1108,7 @@ describe('lua stdlib', function() | |||||||
|     eq(funcs.luaeval "vim.api.nvim_get_vvar('progpath')", funcs.luaeval "vim.v.progpath") |     eq(funcs.luaeval "vim.api.nvim_get_vvar('progpath')", funcs.luaeval "vim.v.progpath") | ||||||
|     eq(false, funcs.luaeval "vim.v['false']") |     eq(false, funcs.luaeval "vim.v['false']") | ||||||
|     eq(NIL, funcs.luaeval "vim.v.null") |     eq(NIL, funcs.luaeval "vim.v.null") | ||||||
|     matches([[^Error executing lua: .*: attempt to index .* nil value]], |     matches([[attempt to index .* nil value]], | ||||||
|        pcall_err(exec_lua, 'return vim.v[0].progpath')) |        pcall_err(exec_lua, 'return vim.v[0].progpath')) | ||||||
|   end) |   end) | ||||||
|  |  | ||||||
| @@ -1128,9 +1128,9 @@ describe('lua stdlib', function() | |||||||
|     ]] |     ]] | ||||||
|     eq('', funcs.luaeval "vim.bo.filetype") |     eq('', funcs.luaeval "vim.bo.filetype") | ||||||
|     eq(true, funcs.luaeval "vim.bo[BUF].modifiable") |     eq(true, funcs.luaeval "vim.bo[BUF].modifiable") | ||||||
|     matches("^Error executing lua: .*: Invalid option name: 'nosuchopt'$", |     matches("Invalid option name: 'nosuchopt'$", | ||||||
|        pcall_err(exec_lua, 'return vim.bo.nosuchopt')) |        pcall_err(exec_lua, 'return vim.bo.nosuchopt')) | ||||||
|     matches("^Error executing lua: .*: Expected lua string$", |     matches("Expected lua string$", | ||||||
|        pcall_err(exec_lua, 'return vim.bo[0][0].autoread')) |        pcall_err(exec_lua, 'return vim.bo[0][0].autoread')) | ||||||
|   end) |   end) | ||||||
|  |  | ||||||
| @@ -1147,9 +1147,9 @@ describe('lua stdlib', function() | |||||||
|     eq(0, funcs.luaeval "vim.wo.cole") |     eq(0, funcs.luaeval "vim.wo.cole") | ||||||
|     eq(0, funcs.luaeval "vim.wo[0].cole") |     eq(0, funcs.luaeval "vim.wo[0].cole") | ||||||
|     eq(0, funcs.luaeval "vim.wo[1001].cole") |     eq(0, funcs.luaeval "vim.wo[1001].cole") | ||||||
|     matches("^Error executing lua: .*: Invalid option name: 'notanopt'$", |     matches("Invalid option name: 'notanopt'$", | ||||||
|        pcall_err(exec_lua, 'return vim.wo.notanopt')) |        pcall_err(exec_lua, 'return vim.wo.notanopt')) | ||||||
|     matches("^Error executing lua: .*: Expected lua string$", |     matches("Expected lua string$", | ||||||
|        pcall_err(exec_lua, 'return vim.wo[0][0].list')) |        pcall_err(exec_lua, 'return vim.wo[0][0].list')) | ||||||
|     eq(2, funcs.luaeval "vim.wo[1000].cole") |     eq(2, funcs.luaeval "vim.wo[1000].cole") | ||||||
|     exec_lua [[ |     exec_lua [[ | ||||||
|   | |||||||
| @@ -20,6 +20,7 @@ local nvim_prog = helpers.nvim_prog | |||||||
| local nvim_set = helpers.nvim_set | local nvim_set = helpers.nvim_set | ||||||
| local ok = helpers.ok | local ok = helpers.ok | ||||||
| local read_file = helpers.read_file | local read_file = helpers.read_file | ||||||
|  | local exec_lua = helpers.exec_lua | ||||||
|  |  | ||||||
| if helpers.pending_win32(pending) then return end | if helpers.pending_win32(pending) then return end | ||||||
|  |  | ||||||
| @@ -580,21 +581,34 @@ describe('TUI', function() | |||||||
|   end) |   end) | ||||||
|  |  | ||||||
|   it("paste: 'nomodifiable' buffer", function() |   it("paste: 'nomodifiable' buffer", function() | ||||||
|  |     local has_luajit = exec_lua('return jit ~= nil') | ||||||
|     child_session:request('nvim_command', 'set nomodifiable') |     child_session:request('nvim_command', 'set nomodifiable') | ||||||
|     child_session:request('nvim_exec_lua', [[ |     child_session:request('nvim_exec_lua', [[ | ||||||
|       -- Stack traces for this test are non-deterministic, so disable them |       -- Stack traces for this test are non-deterministic, so disable them | ||||||
|       _G.debug.traceback = function(msg) return msg end |       _G.debug.traceback = function(msg) return msg end | ||||||
|     ]], {}) |     ]], {}) | ||||||
|     feed_data('\027[200~fail 1\nfail 2\n\027[201~') |     feed_data('\027[200~fail 1\nfail 2\n\027[201~') | ||||||
|  |     if has_luajit then | ||||||
|       screen:expect{grid=[[ |       screen:expect{grid=[[ | ||||||
|                                                           | |                                                           | | ||||||
|         {4:~                                                 }| |         {4:~                                                 }| | ||||||
|         {5:                                                  }| |         {5:                                                  }| | ||||||
|       {8:paste: Error executing lua: vim.lua:243: Vim:E21: }| |         {8:paste: Error executing lua: vim.lua:0: Vim:E21: Ca}| | ||||||
|       {8:Cannot make changes, 'modifiable' is off}          | |         {8:nnot make changes, 'modifiable' is off}            | | ||||||
|         {10:Press ENTER or type command to continue}{1: }          | |         {10:Press ENTER or type command to continue}{1: }          | | ||||||
|         {3:-- TERMINAL --}                                    | |         {3:-- TERMINAL --}                                    | | ||||||
|       ]]} |       ]]} | ||||||
|  |     else | ||||||
|  |       screen:expect{grid=[[ | ||||||
|  |                                                           | | ||||||
|  |         {4:~                                                 }| | ||||||
|  |         {5:                                                  }| | ||||||
|  |         {8:paste: Error executing lua: Vim:E21: Cannot make c}| | ||||||
|  |         {8:hanges, 'modifiable' is off}                       | | ||||||
|  |         {10:Press ENTER or type command to continue}{1: }          | | ||||||
|  |         {3:-- TERMINAL --}                                    | | ||||||
|  |       ]]} | ||||||
|  |     end | ||||||
|     feed_data('\n')  -- <Enter> |     feed_data('\n')  -- <Enter> | ||||||
|     child_session:request('nvim_command', 'set modifiable') |     child_session:request('nvim_command', 'set modifiable') | ||||||
|     feed_data('\027[200~success 1\nsuccess 2\n\027[201~') |     feed_data('\027[200~success 1\nsuccess 2\n\027[201~') | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Gregory Anders
					Gregory Anders