diff options
Diffstat (limited to 'src/nvim/lua')
-rw-r--r-- | src/nvim/lua/converter.c | 15 | ||||
-rw-r--r-- | src/nvim/lua/executor.c | 107 | ||||
-rw-r--r-- | src/nvim/lua/executor.h | 1 | ||||
-rw-r--r-- | src/nvim/lua/spell.c | 101 | ||||
-rw-r--r-- | src/nvim/lua/spell.h | 12 | ||||
-rw-r--r-- | src/nvim/lua/stdlib.c | 5 | ||||
-rw-r--r-- | src/nvim/lua/treesitter.c | 4 | ||||
-rw-r--r-- | src/nvim/lua/vim.lua | 63 |
8 files changed, 287 insertions, 21 deletions
diff --git a/src/nvim/lua/converter.c b/src/nvim/lua/converter.c index b6792a5a97..0fbd56ed53 100644 --- a/src/nvim/lua/converter.c +++ b/src/nvim/lua/converter.c @@ -617,6 +617,14 @@ bool nlua_push_typval(lua_State *lstate, typval_T *const tv, bool special) semsg(_("E1502: Lua failed to grow stack to %i"), initial_size + 4); return false; } + if (tv->v_type == VAR_FUNC) { + ufunc_T *fp = find_func(tv->vval.v_string); + assert(fp != NULL); + if (fp->uf_cb == nlua_CFunction_func_call) { + nlua_pushref(lstate, ((LuaCFunctionState *)fp->uf_cb_state)->lua_callable.func_ref); + return true; + } + } if (encode_vim_to_lua(lstate, tv, "nlua_push_typval argument") == FAIL) { return false; } @@ -1242,7 +1250,12 @@ LuaRef nlua_pop_LuaRef(lua_State *const lstate, Error *err) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT \ { \ type ret; \ - ret = (type)lua_tonumber(lstate, -1); \ + if (lua_type(lstate, -1) != LUA_TNUMBER) { \ + api_set_error(err, kErrorTypeValidation, "Expected Lua number"); \ + ret = (type)-1; \ + } else { \ + ret = (type)lua_tonumber(lstate, -1); \ + } \ lua_pop(lstate, 1); \ return ret; \ } diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c index b09d133495..cfdbe7b344 100644 --- a/src/nvim/lua/executor.c +++ b/src/nvim/lua/executor.c @@ -18,6 +18,7 @@ #include "nvim/event/loop.h" #include "nvim/event/time.h" #include "nvim/ex_cmds2.h" +#include "nvim/ex_docmd.h" #include "nvim/ex_getln.h" #include "nvim/extmark.h" #include "nvim/func_attr.h" @@ -404,9 +405,9 @@ static int nlua_state_init(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL { 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_error(lstate, _("E5106: Error while creating shared module: %.*s")); + nlua_error(lstate, _("E5106: Error while creating shared module: %.*s\n")); return 1; } } @@ -416,31 +417,40 @@ static int nlua_state_init(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL lua_getfield(lstate, -1, "loaded"); // [package, loaded] 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_error(lstate, _("E5106: Error while creating inspect module: %.*s")); + nlua_error(lstate, _("E5106: Error while creating inspect module: %.*s\n")); return 1; } // [package, loaded, inspect] lua_setfield(lstate, -2, "vim.inspect"); // [package, loaded] 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_error(lstate, _("E5106: Error while creating vim.F module: %.*s")); + nlua_error(lstate, _("E5106: Error while creating vim.F module: %.*s\n")); return 1; } // [package, loaded, module] lua_setfield(lstate, -2, "vim.F"); // [package, loaded] + code = (char *)&lua_filetype_module[0]; + if (luaL_loadbuffer(lstate, code, sizeof(lua_filetype_module) - 1, "@vim/filetype.lua") + || nlua_pcall(lstate, 0, 1)) { + nlua_error(lstate, _("E5106: Error while creating vim.filetype module: %.*s")); + return 1; + } + // [package, loaded, module] + lua_setfield(lstate, -2, "vim.filetype"); // [package, loaded] + lua_pop(lstate, 2); // [] } { 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_error(lstate, _("E5106: Error while creating vim module: %.*s")); + nlua_error(lstate, _("E5106: Error while creating vim module: %.*s\n")); return 1; } } @@ -450,9 +460,9 @@ static int nlua_state_init(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL lua_getfield(lstate, -1, "loaded"); // [package, loaded] 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_error(lstate, _("E5106: Error while creating vim._meta module: %.*s")); + nlua_error(lstate, _("E5106: Error while creating vim._meta module: %.*s\n")); return 1; } // [package, loaded, module] @@ -914,6 +924,24 @@ void nlua_typval_call(const char *str, size_t len, typval_T *const args, int arg } } +void nlua_call_user_expand_func(expand_T *xp, typval_T *ret_tv) + FUNC_ATTR_NONNULL_ALL +{ + lua_State *const lstate = global_lstate; + + nlua_pushref(lstate, xp->xp_luaref); + lua_pushstring(lstate, (char *)xp->xp_pattern); + lua_pushstring(lstate, (char *)xp->xp_line); + lua_pushinteger(lstate, xp->xp_col); + + if (nlua_pcall(lstate, 3, 1)) { + nlua_error(lstate, _("E5108: Error executing Lua function: %.*s")); + return; + } + + nlua_pop_typval(lstate, ret_tv); +} + static void nlua_typval_exec(const char *lcmd, size_t lcmd_len, const char *name, typval_T *const args, int argcount, bool special, typval_T *ret_tv) { @@ -1096,11 +1124,23 @@ void ex_lua(exarg_T *const eap) FUNC_ATTR_NONNULL_ALL { size_t len; - char *const code = script_get(eap, &len); + char *code = script_get(eap, &len); if (eap->skip) { xfree(code); return; } + // When =expr is used transform it to print(vim.inspect(expr)) + if (code[0] == '=') { + len += sizeof("vim.pretty_print()") - sizeof("="); + // code_buf needs to be 1 char larger then len for null byte in the end. + // lua nlua_typval_exec doesn't expect null terminated string so len + // needs to end before null byte. + char *code_buf = xmallocz(len); + vim_snprintf(code_buf, len+1, "vim.pretty_print(%s)", code+1); + xfree(code); + code = code_buf; + } + nlua_typval_exec(code, len, ":lua", NULL, 0, false, NULL); xfree(code); @@ -1432,3 +1472,48 @@ void nlua_execute_on_key(int c) #endif } +void nlua_do_ucmd(ucmd_T *cmd, exarg_T *eap) +{ + lua_State *const lstate = global_lstate; + + nlua_pushref(lstate, cmd->uc_luaref); + + lua_newtable(lstate); + lua_pushboolean(lstate, eap->forceit == 1); + lua_setfield(lstate, -2, "bang"); + + lua_pushinteger(lstate, eap->line1); + lua_setfield(lstate, -2, "line1"); + + lua_pushinteger(lstate, eap->line2); + lua_setfield(lstate, -2, "line2"); + + lua_pushstring(lstate, (const char *)eap->arg); + lua_setfield(lstate, -2, "args"); + + lua_pushstring(lstate, (const char *)&eap->regname); + lua_setfield(lstate, -2, "reg"); + + lua_pushinteger(lstate, eap->addr_count); + lua_setfield(lstate, -2, "range"); + + if (eap->addr_count > 0) { + lua_pushinteger(lstate, eap->line2); + } else { + lua_pushinteger(lstate, cmd->uc_def); + } + lua_setfield(lstate, -2, "count"); + + // The size of this buffer is chosen empirically to be large enough to hold + // every possible modifier (with room to spare). If the list of possible + // modifiers grows this may need to be updated. + char buf[200] = { 0 }; + (void)uc_mods(buf); + lua_pushstring(lstate, buf); + lua_setfield(lstate, -2, "mods"); + + if (nlua_pcall(lstate, 1, 0)) { + nlua_error(lstate, _("Error executing Lua callback: %.*s")); + } +} + diff --git a/src/nvim/lua/executor.h b/src/nvim/lua/executor.h index a1f66bd02b..bf78f7ec5e 100644 --- a/src/nvim/lua/executor.h +++ b/src/nvim/lua/executor.h @@ -7,6 +7,7 @@ #include "nvim/api/private/defs.h" #include "nvim/eval/typval.h" #include "nvim/ex_cmds_defs.h" +#include "nvim/ex_docmd.h" #include "nvim/func_attr.h" #include "nvim/lua/converter.h" diff --git a/src/nvim/lua/spell.c b/src/nvim/lua/spell.c new file mode 100644 index 0000000000..3a63f61200 --- /dev/null +++ b/src/nvim/lua/spell.c @@ -0,0 +1,101 @@ +// This is an open source non-commercial project. Dear PVS-Studio, please check +// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com + +#include <lua.h> +#include <lauxlib.h> + +#include "nvim/spell.h" +#include "nvim/vim.h" +#include "nvim/lua/spell.h" + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "lua/spell.c.generated.h" +#endif + +int nlua_spell_check(lua_State *lstate) +{ + if (lua_gettop(lstate) < 1) { + return luaL_error(lstate, "Expected 1 argument"); + } + + if (lua_type(lstate, 1) != LUA_TSTRING) { + luaL_argerror(lstate, 1, "expected string"); + } + + const char *str = lua_tolstring(lstate, 1, NULL); + + // spell.c requires that 'spell' is enabled, so we need to temporarily enable + // it before we can call spell functions. + const int wo_spell_save = curwin->w_p_spell; + + if (!curwin->w_p_spell) { + did_set_spelllang(curwin); + curwin->w_p_spell = true; + } + + // Check 'spelllang' + if (*curwin->w_s->b_p_spl == NUL) { + emsg(_(e_no_spell)); + curwin->w_p_spell = wo_spell_save; + return 0; + } + + hlf_T attr = HLF_COUNT; + size_t len = 0; + size_t pos = 0; + int capcol = -1; + int no_res = 0; + const char * result; + + lua_createtable(lstate, 0, 0); + + while (*str != NUL) { + attr = HLF_COUNT; + len = spell_check(curwin, (char_u *)str, &attr, &capcol, false); + assert(len <= INT_MAX); + + if (attr != HLF_COUNT) { + lua_createtable(lstate, 3, 0); + + lua_pushlstring(lstate, str, len); + lua_rawseti(lstate, -2, 1); + + result = attr == HLF_SPB ? "bad" : + attr == HLF_SPR ? "rare" : + attr == HLF_SPL ? "local" : + attr == HLF_SPC ? "caps" : + NULL; + + assert(result != NULL); + + lua_pushstring(lstate, result); + lua_rawseti(lstate, -2, 2); + + // +1 for 1-indexing + lua_pushinteger(lstate, (long)pos + 1); + lua_rawseti(lstate, -2, 3); + + lua_rawseti(lstate, -2, ++no_res); + } + + str += len; + pos += len; + capcol -= (int)len; + } + + // Restore 'spell' + curwin->w_p_spell = wo_spell_save; + return 1; +} + +static const luaL_Reg spell_functions[] = { + { "check", nlua_spell_check }, + { NULL , NULL } +}; + +int luaopen_spell(lua_State *L) +{ + lua_newtable(L); + luaL_register(L, NULL, spell_functions); + return 1; +} diff --git a/src/nvim/lua/spell.h b/src/nvim/lua/spell.h new file mode 100644 index 0000000000..8f798a5191 --- /dev/null +++ b/src/nvim/lua/spell.h @@ -0,0 +1,12 @@ +#ifndef NVIM_LUA_SPELL_H +#define NVIM_LUA_SPELL_H + +#include <lauxlib.h> +#include <lua.h> +#include <lualib.h> + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "lua/spell.h.generated.h" +#endif + +#endif // NVIM_LUA_SPELL_H diff --git a/src/nvim/lua/stdlib.c b/src/nvim/lua/stdlib.c index 0d6789317c..18a579ed0f 100644 --- a/src/nvim/lua/stdlib.c +++ b/src/nvim/lua/stdlib.c @@ -30,6 +30,7 @@ #include "nvim/lua/stdlib.h" #include "nvim/lua/treesitter.h" #include "nvim/lua/xdiff.h" +#include "nvim/lua/spell.h" #include "nvim/macros.h" #include "nvim/map.h" #include "nvim/memline.h" @@ -518,6 +519,10 @@ void nlua_state_add_stdlib(lua_State *const lstate) lua_pushcfunction(lstate, &nlua_xdl_diff); lua_setfield(lstate, -2, "diff"); + // vim.spell + luaopen_spell(lstate); + lua_setfield(lstate, -2, "spell"); + lua_cjson_new(lstate); lua_setfield(lstate, -2, "json"); } diff --git a/src/nvim/lua/treesitter.c b/src/nvim/lua/treesitter.c index 60a000843f..f4067ad02f 100644 --- a/src/nvim/lua/treesitter.c +++ b/src/nvim/lua/treesitter.c @@ -129,6 +129,10 @@ void tslua_init(lua_State *L) build_meta(L, TS_META_QUERY, query_meta); build_meta(L, TS_META_QUERYCURSOR, querycursor_meta); build_meta(L, TS_META_TREECURSOR, treecursor_meta); + +#ifdef NVIM_TS_HAS_SET_ALLOCATOR + ts_set_allocator(xmalloc, xcalloc, xrealloc, xfree); +#endif } int tslua_has_language(lua_State *L) diff --git a/src/nvim/lua/vim.lua b/src/nvim/lua/vim.lua index c1a1e7f162..731e7d8d36 100644 --- a/src/nvim/lua/vim.lua +++ b/src/nvim/lua/vim.lua @@ -40,6 +40,9 @@ assert(vim) vim.inspect = package.loaded['vim.inspect'] assert(vim.inspect) +vim.filetype = package.loaded['vim.filetype'] +assert(vim.filetype) + local pathtrails = {} vim._so_trails = {} for s in (package.cpath..';'):gmatch('[^;]*;') do @@ -112,6 +115,9 @@ setmetatable(vim, { elseif key == 'ui' then t.ui = require('vim.ui') return t.ui + elseif key == 'keymap' then + t.keymap = require('vim.keymap') + return t.keymap end end }) @@ -419,23 +425,43 @@ function vim.defer_fn(fn, timeout) end ---- Notification provider +--- Display a notification to the user. +--- +--- This function can be overridden by plugins to display notifications using a +--- custom provider (such as the system notification provider). By default, +--- writes to |:messages|. --- ---- Without a runtime, writes to :Messages ----@see :help nvim_notify ----@param msg string Content of the notification to show to the user ----@param log_level number|nil enum from vim.log.levels ----@param opts table|nil additional options (timeout, etc) -function vim.notify(msg, log_level, opts) -- luacheck: no unused - if log_level == vim.log.levels.ERROR then +---@param msg string Content of the notification to show to the user. +---@param level number|nil One of the values from |vim.log.levels|. +---@param opts table|nil Optional parameters. Unused by default. +function vim.notify(msg, level, opts) -- luacheck: no unused args + if level == vim.log.levels.ERROR then vim.api.nvim_err_writeln(msg) - elseif log_level == vim.log.levels.WARN then + elseif level == vim.log.levels.WARN then vim.api.nvim_echo({{msg, 'WarningMsg'}}, true, {}) else vim.api.nvim_echo({{msg}}, true, {}) end end +do + local notified = {} + + --- Display a notification only one time. + --- + --- Like |vim.notify()|, but subsequent calls with the same message will not + --- display a notification. + --- + ---@param msg string Content of the notification to show to the user. + ---@param level number|nil One of the values from |vim.log.levels|. + ---@param opts table|nil Optional parameters. Unused by default. + function vim.notify_once(msg, level, opts) -- luacheck: no unused args + if not notified[msg] then + vim.notify(msg, level, opts) + notified[msg] = true + end + end +end ---@private function vim.register_keystroke_callback() @@ -663,4 +689,23 @@ vim._expand_pat_get_parts = function(lua_string) return parts, search_index end +---Prints given arguments in human-readable format. +---Example: +---<pre> +--- -- Print highlight group Normal and store it's contents in a variable. +--- local hl_normal = vim.pretty_print(vim.api.nvim_get_hl_by_name("Normal", true)) +---</pre> +---@see |vim.inspect()| +---@return given arguments. +function vim.pretty_print(...) + local objects = {} + for i = 1, select('#', ...) do + local v = select(i, ...) + table.insert(objects, vim.inspect(v)) + end + + print(table.concat(objects, ' ')) + return ... +end + return module |