aboutsummaryrefslogtreecommitdiff
path: root/src/nvim/lua
diff options
context:
space:
mode:
Diffstat (limited to 'src/nvim/lua')
-rw-r--r--src/nvim/lua/converter.c16
-rw-r--r--src/nvim/lua/executor.c443
-rw-r--r--src/nvim/lua/stdlib.c524
-rw-r--r--src/nvim/lua/stdlib.h10
-rw-r--r--src/nvim/lua/treesitter.c5
-rw-r--r--src/nvim/lua/vim.lua13
-rw-r--r--src/nvim/lua/xdiff.c6
7 files changed, 582 insertions, 435 deletions
diff --git a/src/nvim/lua/converter.c b/src/nvim/lua/converter.c
index ca3c4f604a..9f2372f831 100644
--- a/src/nvim/lua/converter.c
+++ b/src/nvim/lua/converter.c
@@ -62,7 +62,7 @@ static LuaTableProps nlua_traverse_table(lua_State *const lstate)
LuaTableProps ret;
memset(&ret, 0, sizeof(ret));
if (!lua_checkstack(lstate, lua_gettop(lstate) + 3)) {
- emsgf(_("E1502: Lua failed to grow stack to %i"), lua_gettop(lstate) + 2);
+ semsg(_("E1502: Lua failed to grow stack to %i"), lua_gettop(lstate) + 2);
ret.type = kObjectTypeNil;
return ret;
}
@@ -198,7 +198,7 @@ bool nlua_pop_typval(lua_State *lstate, typval_T *ret_tv)
kvi_push(stack, ((TVPopStackItem) { ret_tv, false, false, 0 }));
while (ret && kv_size(stack)) {
if (!lua_checkstack(lstate, lua_gettop(lstate) + 3)) {
- emsgf(_("E1502: Lua failed to grow stack to %i"), lua_gettop(lstate) + 3);
+ semsg(_("E1502: Lua failed to grow stack to %i"), lua_gettop(lstate) + 3);
ret = false;
break;
}
@@ -376,7 +376,7 @@ bool nlua_pop_typval(lua_State *lstate, typval_T *ret_tv)
cur.tv->vval.v_float = (float_T)table_props.val;
break;
case kObjectTypeNil:
- EMSG(_("E5100: Cannot convert given lua table: table "
+ emsg(_("E5100: Cannot convert given lua table: table "
"should either have a sequence of positive integer keys "
"or contain only string keys"));
ret = false;
@@ -408,13 +408,13 @@ nlua_pop_typval_table_processing_end:
cur.tv->v_type = VAR_SPECIAL;
cur.tv->vval.v_special = kSpecialVarNull;
} else {
- EMSG(_("E5101: Cannot convert given lua type"));
+ emsg(_("E5101: Cannot convert given lua type"));
ret = false;
}
break;
}
default:
- EMSG(_("E5101: Cannot convert given lua type"));
+ emsg(_("E5101: Cannot convert given lua type"));
ret = false;
break;
}
@@ -503,7 +503,7 @@ static bool typval_conv_special = false;
#define TYPVAL_ENCODE_CONV_LIST_START(tv, len) \
do { \
if (!lua_checkstack(lstate, lua_gettop(lstate) + 3)) { \
- emsgf(_("E5102: Lua failed to grow stack to %i"), \
+ semsg(_("E5102: Lua failed to grow stack to %i"), \
lua_gettop(lstate) + 3); \
return false; \
} \
@@ -526,7 +526,7 @@ static bool typval_conv_special = false;
#define TYPVAL_ENCODE_CONV_DICT_START(tv, dict, len) \
do { \
if (!lua_checkstack(lstate, lua_gettop(lstate) + 3)) { \
- emsgf(_("E5102: Lua failed to grow stack to %i"), \
+ semsg(_("E5102: Lua failed to grow stack to %i"), \
lua_gettop(lstate) + 3); \
return false; \
} \
@@ -614,7 +614,7 @@ bool nlua_push_typval(lua_State *lstate, typval_T *const tv, bool special)
const int initial_size = lua_gettop(lstate);
if (!lua_checkstack(lstate, initial_size + 2)) {
- emsgf(_("E1502: Lua failed to grow stack to %i"), initial_size + 4);
+ semsg(_("E1502: Lua failed to grow stack to %i"), initial_size + 4);
return false;
}
if (encode_vim_to_lua(lstate, tv, "nlua_push_typval argument") == FAIL) {
diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c
index 59ebafd9c8..6120deb77a 100644
--- a/src/nvim/lua/executor.c
+++ b/src/nvim/lua/executor.c
@@ -5,9 +5,7 @@
#include <lua.h>
#include <lualib.h>
-#include "cjson/lua_cjson.h"
#include "luv/luv.h"
-#include "mpack/lmpack.h"
#include "nvim/api/private/defs.h"
#include "nvim/api/private/helpers.h"
#include "nvim/api/vim.h"
@@ -27,8 +25,8 @@
#include "nvim/getchar.h"
#include "nvim/lua/converter.h"
#include "nvim/lua/executor.h"
+#include "nvim/lua/stdlib.h"
#include "nvim/lua/treesitter.h"
-#include "nvim/lua/xdiff.h"
#include "nvim/macros.h"
#include "nvim/map.h"
#include "nvim/memline.h"
@@ -36,7 +34,6 @@
#include "nvim/misc1.h"
#include "nvim/msgpack_rpc/channel.h"
#include "nvim/os/os.h"
-#include "nvim/regexp.h"
#include "nvim/screen.h"
#include "nvim/undo.h"
#include "nvim/version.h"
@@ -83,7 +80,7 @@ static void nlua_error(lua_State *const lstate, const char *const msg)
const char *const str = lua_tolstring(lstate, -1, &len);
msg_ext_set_kind("lua_error");
- emsgf_multiline(msg, (int)len, str);
+ semsg_multiline(msg, (int)len, str);
lua_pop(lstate, 1);
}
@@ -99,118 +96,12 @@ static int nlua_nvim_version(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL
return 1;
}
-/// Compare two strings, ignoring case
-///
-/// Expects two values on the stack: compared strings. Returns one of the
-/// following numbers: 0, -1 or 1.
-///
-/// Does no error handling: never call it with non-string or with some arguments
-/// omitted.
-static int nlua_stricmp(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL
-{
- size_t s1_len;
- size_t s2_len;
- const char *s1 = luaL_checklstring(lstate, 1, &s1_len);
- const char *s2 = luaL_checklstring(lstate, 2, &s2_len);
- char *nul1;
- char *nul2;
- int ret = 0;
- assert(s1[s1_len] == NUL);
- assert(s2[s2_len] == NUL);
- do {
- nul1 = memchr(s1, NUL, s1_len);
- nul2 = memchr(s2, NUL, s2_len);
- ret = STRICMP(s1, s2);
- if (ret == 0) {
- // Compare "a\0" greater then "a".
- if ((nul1 == NULL) != (nul2 == NULL)) {
- ret = ((nul1 != NULL) - (nul2 != NULL));
- break;
- }
- if (nul1 != NULL) {
- assert(nul2 != NULL);
- // Can't shift both strings by the same amount of bytes: lowercase
- // letter may have different byte-length than uppercase.
- s1_len -= (size_t)(nul1 - s1) + 1;
- s2_len -= (size_t)(nul2 - s2) + 1;
- s1 = nul1 + 1;
- s2 = nul2 + 1;
- } else {
- break;
- }
- } else {
- break;
- }
- } while (true);
- lua_pop(lstate, 2);
- lua_pushnumber(lstate, (lua_Number)((ret > 0) - (ret < 0)));
- return 1;
-}
-
-/// convert byte index to UTF-32 and UTF-16 indices
-///
-/// Expects a string and an optional index. If no index is supplied, the length
-/// of the string is returned.
-///
-/// Returns two values: the UTF-32 and UTF-16 indices.
-static int nlua_str_utfindex(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL
-{
- size_t s1_len;
- const char *s1 = luaL_checklstring(lstate, 1, &s1_len);
- intptr_t idx;
- if (lua_gettop(lstate) >= 2) {
- idx = luaL_checkinteger(lstate, 2);
- if (idx < 0 || idx > (intptr_t)s1_len) {
- return luaL_error(lstate, "index out of range");
- }
- } else {
- idx = (intptr_t)s1_len;
- }
-
- size_t codepoints = 0, codeunits = 0;
- mb_utflen((const char_u *)s1, (size_t)idx, &codepoints, &codeunits);
-
- lua_pushinteger(lstate, (long)codepoints);
- lua_pushinteger(lstate, (long)codeunits);
-
- return 2;
-}
-
-/// convert UTF-32 or UTF-16 indices to byte index.
-///
-/// Expects up to three args: string, index and use_utf16.
-/// If use_utf16 is not supplied it defaults to false (use UTF-32)
-///
-/// Returns the byte index.
-static int nlua_str_byteindex(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL
-{
- size_t s1_len;
- const char *s1 = luaL_checklstring(lstate, 1, &s1_len);
- intptr_t idx = luaL_checkinteger(lstate, 2);
- if (idx < 0) {
- return luaL_error(lstate, "index out of range");
- }
- bool use_utf16 = false;
- if (lua_gettop(lstate) >= 3) {
- use_utf16 = lua_toboolean(lstate, 3);
- }
-
- ssize_t byteidx = mb_utf_index_to_bytes((const char_u *)s1, s1_len,
- (size_t)idx, use_utf16);
- if (byteidx == -1) {
- return luaL_error(lstate, "index out of range");
- }
-
- lua_pushinteger(lstate, (long)byteidx);
-
- return 1;
-}
static void nlua_luv_error_event(void **argv)
{
char *error = (char *)argv[0];
msg_ext_set_kind("lua_error");
- emsgf_multiline("Error executing luv callback:\n%s", error);
+ semsg_multiline("Error executing luv callback:\n%s", error);
xfree(error);
}
@@ -278,14 +169,6 @@ static int nlua_schedule(lua_State *const lstate)
return 0;
}
-static struct luaL_Reg regex_meta[] = {
- { "__gc", regex_gc },
- { "__tostring", regex_tostring },
- { "match_str", regex_match_str },
- { "match_line", regex_match_line },
- { NULL, NULL }
-};
-
// Dummy timer callback. Used by f_wait().
static void dummy_timer_due_cb(TimeWatcher *tw, void *data)
{
@@ -422,39 +305,28 @@ static int nlua_state_init(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL
// vim
lua_newtable(lstate);
+
// vim.api
nlua_add_api_functions(lstate);
+
// vim.types, vim.type_idx, vim.val_idx
nlua_init_types(lstate);
- // stricmp
- lua_pushcfunction(lstate, &nlua_stricmp);
- lua_setfield(lstate, -2, "stricmp");
- // str_utfindex
- lua_pushcfunction(lstate, &nlua_str_utfindex);
- lua_setfield(lstate, -2, "str_utfindex");
- // str_byteindex
- lua_pushcfunction(lstate, &nlua_str_byteindex);
- lua_setfield(lstate, -2, "str_byteindex");
+
// neovim version
lua_pushcfunction(lstate, &nlua_nvim_version);
lua_setfield(lstate, -2, "version");
+
// schedule
lua_pushcfunction(lstate, &nlua_schedule);
lua_setfield(lstate, -2, "schedule");
+
// in_fast_event
lua_pushcfunction(lstate, &nlua_in_fast_event);
lua_setfield(lstate, -2, "in_fast_event");
+
// call
lua_pushcfunction(lstate, &nlua_call);
lua_setfield(lstate, -2, "call");
- // regex
- lua_pushcfunction(lstate, &nlua_regex);
- lua_setfield(lstate, -2, "regex");
- luaL_newmetatable(lstate, "nvim_regex");
- luaL_register(lstate, NULL, regex_meta);
- lua_pushvalue(lstate, -1); // [meta, meta]
- lua_setfield(lstate, -2, "__index"); // [meta]
- lua_pop(lstate, 1); // don't use metatable now
// rpcrequest
lua_pushcfunction(lstate, &nlua_rpcrequest);
@@ -468,30 +340,6 @@ static int nlua_state_init(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL
lua_pushcfunction(lstate, &nlua_wait);
lua_setfield(lstate, -2, "wait");
- // _getvar
- lua_pushcfunction(lstate, &nlua_getvar);
- lua_setfield(lstate, -2, "_getvar");
-
- // _setvar
- lua_pushcfunction(lstate, &nlua_setvar);
- lua_setfield(lstate, -2, "_setvar");
-
-
- // vim.loop
- luv_set_loop(lstate, &main_loop.uv);
- luv_set_callback(lstate, nlua_luv_cfpcall);
- luaopen_luv(lstate);
- lua_pushvalue(lstate, -1);
- lua_setfield(lstate, -3, "loop");
-
- // package.loaded.luv = vim.loop
- // otherwise luv will be reinitialized when require'luv'
- lua_getglobal(lstate, "package");
- lua_getfield(lstate, -1, "loaded");
- lua_pushvalue(lstate, -3);
- lua_setfield(lstate, -2, "luv");
- lua_pop(lstate, 3);
-
// vim.NIL
lua_newuserdata(lstate, 0);
lua_createtable(lstate, 0, 0);
@@ -512,28 +360,25 @@ static int nlua_state_init(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL
lua_setfield(lstate, LUA_REGISTRYINDEX, "mpack.empty_dict");
lua_setfield(lstate, -2, "_empty_dict_mt");
- // vim.mpack
- luaopen_mpack(lstate);
+ // internal vim._treesitter... API
+ nlua_add_treesitter(lstate);
+
+ // vim.loop
+ luv_set_loop(lstate, &main_loop.uv);
+ luv_set_callback(lstate, nlua_luv_cfpcall);
+ luaopen_luv(lstate);
lua_pushvalue(lstate, -1);
- lua_setfield(lstate, -3, "mpack");
+ lua_setfield(lstate, -3, "loop");
- // package.loaded.mpack = vim.mpack
- // otherwise luv will be reinitialized when require'mpack'
+ // package.loaded.luv = vim.loop
+ // otherwise luv will be reinitialized when require'luv'
lua_getglobal(lstate, "package");
lua_getfield(lstate, -1, "loaded");
lua_pushvalue(lstate, -3);
- lua_setfield(lstate, -2, "mpack");
+ lua_setfield(lstate, -2, "luv");
lua_pop(lstate, 3);
- // internal vim._treesitter... API
- nlua_add_treesitter(lstate);
-
- // vim.diff
- lua_pushcfunction(lstate, &nlua_xdl_diff);
- lua_setfield(lstate, -2, "diff");
-
- lua_cjson_new(lstate);
- lua_setfield(lstate, -2, "json");
+ nlua_state_add_stdlib(lstate);
lua_setglobal(lstate, "vim");
@@ -613,7 +458,7 @@ void nlua_init(void)
lua_State *lstate = luaL_newstate();
if (lstate == NULL) {
- EMSG(_("E970: Failed to initialize lua interpreter"));
+ emsg(_("E970: Failed to initialize lua interpreter"));
preserve_exit();
}
luaL_openlibs(lstate);
@@ -674,10 +519,10 @@ static void nlua_print_event(void **argv)
}
break;
}
- msg((char_u *)str + start);
+ msg(str + start);
}
if (len && str[len - 1] == NUL) { // Last was newline
- msg((char_u *)"");
+ msg("");
}
xfree(str);
}
@@ -743,7 +588,7 @@ nlua_print_error:
/// debug.debug: interaction with user while debugging.
///
/// @param lstate Lua interpreter state.
-int nlua_debug(lua_State *lstate)
+static int nlua_debug(lua_State *lstate)
FUNC_ATTR_NONNULL_ALL
{
const typval_T input_args[] = {
@@ -898,109 +743,6 @@ check_err:
return request ? 1 : 0;
}
-static dict_T *nlua_get_var_scope(lua_State *lstate) {
- const char *scope = luaL_checkstring(lstate, 1);
- handle_T handle = (handle_T)luaL_checkinteger(lstate, 2);
- dict_T *dict = NULL;
- Error err = ERROR_INIT;
- if (strequal(scope, "g")) {
- dict = &globvardict;
- } else if (strequal(scope, "v")) {
- dict = &vimvardict;
- } else if (strequal(scope, "b")) {
- buf_T *buf = find_buffer_by_handle(handle, &err);
- if (buf) {
- dict = buf->b_vars;
- }
- } else if (strequal(scope, "w")) {
- win_T *win = find_window_by_handle(handle, &err);
- if (win) {
- dict = win->w_vars;
- }
- } else if (strequal(scope, "t")) {
- tabpage_T *tabpage = find_tab_by_handle(handle, &err);
- if (tabpage) {
- dict = tabpage->tp_vars;
- }
- } else {
- luaL_error(lstate, "invalid scope", err.msg);
- return NULL;
- }
-
- if (ERROR_SET(&err)) {
- luaL_error(lstate, "FAIL: %s", err.msg);
- return NULL;
- }
- return dict;
-}
-
-
-static int nlua_getvar(lua_State *lstate)
-{
- // non-local return if not found
- dict_T *dict = nlua_get_var_scope(lstate);
- size_t len;
- const char *name = luaL_checklstring(lstate, 3, &len);
-
- dictitem_T *di = tv_dict_find(dict, name, (ptrdiff_t)len);
- if (di == NULL) {
- return 0; // nil
- }
- nlua_push_typval(lstate, &di->di_tv, false);
- return 1;
-}
-
-static int nlua_setvar(lua_State *lstate)
-{
- // non-local return if not found
- dict_T *dict = nlua_get_var_scope(lstate);
- String key;
- key.data = (char *)luaL_checklstring(lstate, 3, &key.size);
-
- bool del = (lua_gettop(lstate) < 4) || lua_isnil(lstate, 4);
-
- Error err = ERROR_INIT;
- dictitem_T *di = dict_check_writable(dict, key, del, &err);
- if (ERROR_SET(&err)) {
- return 0;
- }
-
- if (del) {
- // Delete the key
- if (di == NULL) {
- // Doesn't exist, nothing to do
- return 0;
- } else {
- // Delete the entry
- tv_dict_item_remove(dict, di);
- }
- } else {
- // Update the key
- typval_T tv;
-
- // Convert the lua value to a vimscript type in the temporary variable
- lua_pushvalue(lstate, 4);
- if (!nlua_pop_typval(lstate, &tv)) {
- return luaL_error(lstate, "Couldn't convert lua value");
- }
-
- if (di == NULL) {
- // Need to create an entry
- di = tv_dict_item_alloc_len(key.data, key.size);
- tv_dict_add(dict, di);
- } else {
- // Clear the old value
- tv_clear(&di->di_tv);
- }
-
- // Update the value
- tv_copy(&tv, &di->di_tv);
- // Clear the temporary variable
- tv_clear(&tv);
- }
- return 0;
-}
-
static int nlua_nil_tostring(lua_State *lstate)
{
lua_pushstring(lstate, "vim.NIL");
@@ -1349,7 +1091,7 @@ void ex_luado(exarg_T *const eap)
FUNC_ATTR_NONNULL_ALL
{
if (u_save(eap->line1 - 1, eap->line2 + 1) == FAIL) {
- EMSG(_("cannot save undo information"));
+ emsg(_("cannot save undo information"));
return;
}
const char *const cmd = (const char *)eap->arg;
@@ -1553,139 +1295,6 @@ cleanup:
return ret;
}
-static int nlua_regex(lua_State *lstate)
-{
- Error err = ERROR_INIT;
- const char *text = luaL_checkstring(lstate, 1);
- regprog_T *prog = NULL;
-
- TRY_WRAP({
- try_start();
- prog = vim_regcomp((char_u *)text, RE_AUTO | RE_MAGIC | RE_STRICT);
- try_end(&err);
- });
-
- if (ERROR_SET(&err)) {
- return luaL_error(lstate, "couldn't parse regex: %s", err.msg);
- }
- assert(prog);
-
- regprog_T **p = lua_newuserdata(lstate, sizeof(regprog_T *));
- *p = prog;
-
- lua_getfield(lstate, LUA_REGISTRYINDEX, "nvim_regex"); // [udata, meta]
- lua_setmetatable(lstate, -2); // [udata]
- return 1;
-}
-
-static regprog_T **regex_check(lua_State *L)
-{
- return luaL_checkudata(L, 1, "nvim_regex");
-}
-
-
-static int regex_gc(lua_State *lstate)
-{
- regprog_T **prog = regex_check(lstate);
- vim_regfree(*prog);
- return 0;
-}
-
-static int regex_tostring(lua_State *lstate)
-{
- lua_pushstring(lstate, "<regex>");
- return 1;
-}
-
-static int regex_match(lua_State *lstate, regprog_T **prog, char_u *str)
-{
- regmatch_T rm;
- rm.regprog = *prog;
- rm.rm_ic = false;
- bool match = vim_regexec(&rm, str, 0);
- *prog = rm.regprog;
-
- if (match) {
- lua_pushinteger(lstate, (lua_Integer)(rm.startp[0]-str));
- lua_pushinteger(lstate, (lua_Integer)(rm.endp[0]-str));
- return 2;
- }
- return 0;
-}
-
-static int regex_match_str(lua_State *lstate)
-{
- regprog_T **prog = regex_check(lstate);
- const char *str = luaL_checkstring(lstate, 2);
- int nret = regex_match(lstate, prog, (char_u *)str);
-
- if (!*prog) {
- return luaL_error(lstate, "regex: internal error");
- }
-
- return nret;
-}
-
-static int regex_match_line(lua_State *lstate)
-{
- regprog_T **prog = regex_check(lstate);
-
- int narg = lua_gettop(lstate);
- if (narg < 3) {
- return luaL_error(lstate, "not enough args");
- }
-
- long bufnr = luaL_checkinteger(lstate, 2);
- long rownr = luaL_checkinteger(lstate, 3);
- long start = 0, end = -1;
- if (narg >= 4) {
- start = luaL_checkinteger(lstate, 4);
- }
- if (narg >= 5) {
- end = luaL_checkinteger(lstate, 5);
- if (end < 0) {
- return luaL_error(lstate, "invalid end");
- }
- }
-
- buf_T *buf = bufnr ? handle_get_buffer((int)bufnr) : curbuf;
- if (!buf || buf->b_ml.ml_mfp == NULL) {
- return luaL_error(lstate, "invalid buffer");
- }
-
- if (rownr >= buf->b_ml.ml_line_count) {
- return luaL_error(lstate, "invalid row");
- }
-
- char_u *line = ml_get_buf(buf, rownr+1, false);
- size_t len = STRLEN(line);
-
- if (start < 0 || (size_t)start > len) {
- return luaL_error(lstate, "invalid start");
- }
-
- char_u save = NUL;
- if (end >= 0) {
- if ((size_t)end > len || end < start) {
- return luaL_error(lstate, "invalid end");
- }
- save = line[end];
- line[end] = NUL;
- }
-
- int nret = regex_match(lstate, prog, line+start);
-
- if (end >= 0) {
- line[end] = save;
- }
-
- if (!*prog) {
- return luaL_error(lstate, "regex: internal error");
- }
-
- return nret;
-}
-
// Required functions for lua c functions as VimL callbacks
int nlua_CFunction_func_call(int argcount, typval_T *argvars, typval_T *rettv, void *state)
diff --git a/src/nvim/lua/stdlib.c b/src/nvim/lua/stdlib.c
new file mode 100644
index 0000000000..db79e9e7e9
--- /dev/null
+++ b/src/nvim/lua/stdlib.c
@@ -0,0 +1,524 @@
+// 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 <lauxlib.h>
+#include <lua.h>
+#include <lualib.h>
+
+#include "cjson/lua_cjson.h"
+#include "luv/luv.h"
+#include "mpack/lmpack.h"
+#include "nvim/api/private/defs.h"
+#include "nvim/api/private/helpers.h"
+#include "nvim/api/vim.h"
+#include "nvim/ascii.h"
+#include "nvim/assert.h"
+#include "nvim/buffer_defs.h"
+#include "nvim/change.h"
+#include "nvim/cursor.h"
+#include "nvim/eval/userfunc.h"
+#include "nvim/event/loop.h"
+#include "nvim/event/time.h"
+#include "nvim/ex_cmds2.h"
+#include "nvim/ex_getln.h"
+#include "nvim/extmark.h"
+#include "nvim/func_attr.h"
+#include "nvim/garray.h"
+#include "nvim/getchar.h"
+#include "nvim/lua/converter.h"
+#include "nvim/lua/executor.h"
+#include "nvim/lua/stdlib.h"
+#include "nvim/lua/treesitter.h"
+#include "nvim/lua/xdiff.h"
+#include "nvim/macros.h"
+#include "nvim/map.h"
+#include "nvim/memline.h"
+#include "nvim/message.h"
+#include "nvim/misc1.h"
+#include "nvim/msgpack_rpc/channel.h"
+#include "nvim/os/os.h"
+#include "nvim/regexp.h"
+#include "nvim/regexp_defs.h"
+#include "nvim/screen.h"
+#include "nvim/types.h"
+#include "nvim/undo.h"
+#include "nvim/version.h"
+#include "nvim/vim.h"
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "lua/stdlib.c.generated.h"
+#endif
+
+static int regex_match(lua_State *lstate, regprog_T **prog, char_u *str)
+{
+ regmatch_T rm;
+ rm.regprog = *prog;
+ rm.rm_ic = false;
+ bool match = vim_regexec(&rm, str, 0);
+ *prog = rm.regprog;
+
+ if (match) {
+ lua_pushinteger(lstate, (lua_Integer)(rm.startp[0]-str));
+ lua_pushinteger(lstate, (lua_Integer)(rm.endp[0]-str));
+ return 2;
+ }
+ return 0;
+}
+
+static int regex_match_str(lua_State *lstate)
+{
+ regprog_T **prog = regex_check(lstate);
+ const char *str = luaL_checkstring(lstate, 2);
+ int nret = regex_match(lstate, prog, (char_u *)str);
+
+ if (!*prog) {
+ return luaL_error(lstate, "regex: internal error");
+ }
+
+ return nret;
+}
+
+static int regex_match_line(lua_State *lstate)
+{
+ regprog_T **prog = regex_check(lstate);
+
+ int narg = lua_gettop(lstate);
+ if (narg < 3) {
+ return luaL_error(lstate, "not enough args");
+ }
+
+ long bufnr = luaL_checkinteger(lstate, 2);
+ long rownr = luaL_checkinteger(lstate, 3);
+ long start = 0, end = -1;
+ if (narg >= 4) {
+ start = luaL_checkinteger(lstate, 4);
+ }
+ if (narg >= 5) {
+ end = luaL_checkinteger(lstate, 5);
+ if (end < 0) {
+ return luaL_error(lstate, "invalid end");
+ }
+ }
+
+ buf_T *buf = bufnr ? handle_get_buffer((int)bufnr) : curbuf;
+ if (!buf || buf->b_ml.ml_mfp == NULL) {
+ return luaL_error(lstate, "invalid buffer");
+ }
+
+ if (rownr >= buf->b_ml.ml_line_count) {
+ return luaL_error(lstate, "invalid row");
+ }
+
+ char_u *line = ml_get_buf(buf, rownr+1, false);
+ size_t len = STRLEN(line);
+
+ if (start < 0 || (size_t)start > len) {
+ return luaL_error(lstate, "invalid start");
+ }
+
+ char_u save = NUL;
+ if (end >= 0) {
+ if ((size_t)end > len || end < start) {
+ return luaL_error(lstate, "invalid end");
+ }
+ save = line[end];
+ line[end] = NUL;
+ }
+
+ int nret = regex_match(lstate, prog, line+start);
+
+ if (end >= 0) {
+ line[end] = save;
+ }
+
+ if (!*prog) {
+ return luaL_error(lstate, "regex: internal error");
+ }
+
+ return nret;
+}
+
+static regprog_T **regex_check(lua_State *L)
+{
+ return luaL_checkudata(L, 1, "nvim_regex");
+}
+
+static int regex_gc(lua_State *lstate)
+{
+ regprog_T **prog = regex_check(lstate);
+ vim_regfree(*prog);
+ return 0;
+}
+
+static int regex_tostring(lua_State *lstate)
+{
+ lua_pushstring(lstate, "<regex>");
+ return 1;
+}
+
+static struct luaL_Reg regex_meta[] = {
+ { "__gc", regex_gc },
+ { "__tostring", regex_tostring },
+ { "match_str", regex_match_str },
+ { "match_line", regex_match_line },
+ { NULL, NULL }
+};
+
+/// convert byte index to UTF-32 and UTF-16 indices
+///
+/// Expects a string and an optional index. If no index is supplied, the length
+/// of the string is returned.
+///
+/// Returns two values: the UTF-32 and UTF-16 indices.
+int nlua_str_utfindex(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL
+{
+ size_t s1_len;
+ const char *s1 = luaL_checklstring(lstate, 1, &s1_len);
+ intptr_t idx;
+ if (lua_gettop(lstate) >= 2) {
+ idx = luaL_checkinteger(lstate, 2);
+ if (idx < 0 || idx > (intptr_t)s1_len) {
+ return luaL_error(lstate, "index out of range");
+ }
+ } else {
+ idx = (intptr_t)s1_len;
+ }
+
+ size_t codepoints = 0, codeunits = 0;
+ mb_utflen((const char_u *)s1, (size_t)idx, &codepoints, &codeunits);
+
+ lua_pushinteger(lstate, (long)codepoints);
+ lua_pushinteger(lstate, (long)codeunits);
+
+ return 2;
+}
+
+/// return byte indices of codepoints in a string (only supports utf-8 currently).
+///
+/// Expects a string.
+///
+/// Returns a list of codepoints.
+static int nlua_str_utf_pos(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL
+{
+ size_t s1_len;
+ const char *s1 = luaL_checklstring(lstate, 1, &s1_len);
+ lua_newtable(lstate);
+
+ size_t idx = 1;
+ size_t clen;
+ for (size_t i = 0; i < s1_len && s1[i] != NUL; i += clen) {
+ clen = (size_t)utf_ptr2len_len((const char_u *)(s1)+i, (int)(s1_len-i));
+ lua_pushinteger(lstate, (long)i + 1);
+ lua_rawseti(lstate, -2, (int)idx);
+ idx++;
+ }
+
+ return 1;
+}
+
+/// Return the offset from the 1-indexed byte position to the first byte of the
+/// current character.
+///
+/// Expects a string and an int.
+///
+/// Returns the byte offset to the first byte of the current character
+/// pointed into by the offset.
+static int nlua_str_utf_start(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL
+{
+ size_t s1_len;
+ const char *s1 = luaL_checklstring(lstate, 1, &s1_len);
+ long offset = luaL_checkinteger(lstate, 2);
+ if (offset < 0 || offset > (intptr_t)s1_len) {
+ return luaL_error(lstate, "index out of range");
+ }
+ int tail_offset = mb_head_off((char_u *)s1, (char_u *)s1 + (char_u)offset - 1);
+ lua_pushinteger(lstate, tail_offset);
+ return 1;
+}
+
+/// Return the offset from the 1-indexed byte position to the last
+/// byte of the current character.
+///
+/// Expects a string and an int.
+///
+/// Returns the byte offset to the last byte of the current character
+/// pointed into by the offset.
+static int nlua_str_utf_end(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL
+{
+ size_t s1_len;
+ const char *s1 = luaL_checklstring(lstate, 1, &s1_len);
+ long offset = luaL_checkinteger(lstate, 2);
+ if (offset < 0 || offset > (intptr_t)s1_len) {
+ return luaL_error(lstate, "index out of range");
+ }
+ int tail_offset = mb_tail_off((char_u *)s1, (char_u *)s1 + (char_u)offset - 1);
+ lua_pushinteger(lstate, tail_offset);
+ return 1;
+}
+
+/// convert UTF-32 or UTF-16 indices to byte index.
+///
+/// Expects up to three args: string, index and use_utf16.
+/// If use_utf16 is not supplied it defaults to false (use UTF-32)
+///
+/// Returns the byte index.
+int nlua_str_byteindex(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL
+{
+ size_t s1_len;
+ const char *s1 = luaL_checklstring(lstate, 1, &s1_len);
+ intptr_t idx = luaL_checkinteger(lstate, 2);
+ if (idx < 0) {
+ return luaL_error(lstate, "index out of range");
+ }
+ bool use_utf16 = false;
+ if (lua_gettop(lstate) >= 3) {
+ use_utf16 = lua_toboolean(lstate, 3);
+ }
+
+ ssize_t byteidx = mb_utf_index_to_bytes((const char_u *)s1, s1_len,
+ (size_t)idx, use_utf16);
+ if (byteidx == -1) {
+ return luaL_error(lstate, "index out of range");
+ }
+
+ lua_pushinteger(lstate, (long)byteidx);
+
+ return 1;
+}
+
+int nlua_regex(lua_State *lstate)
+{
+ Error err = ERROR_INIT;
+ const char *text = luaL_checkstring(lstate, 1);
+ regprog_T *prog = NULL;
+
+ TRY_WRAP({
+ try_start();
+ prog = vim_regcomp((char_u *)text, RE_AUTO | RE_MAGIC | RE_STRICT);
+ try_end(&err);
+ });
+
+ if (ERROR_SET(&err)) {
+ return luaL_error(lstate, "couldn't parse regex: %s", err.msg);
+ }
+ assert(prog);
+
+ regprog_T **p = lua_newuserdata(lstate, sizeof(regprog_T *));
+ *p = prog;
+
+ lua_getfield(lstate, LUA_REGISTRYINDEX, "nvim_regex"); // [udata, meta]
+ lua_setmetatable(lstate, -2); // [udata]
+ return 1;
+}
+
+static dict_T *nlua_get_var_scope(lua_State *lstate)
+{
+ const char *scope = luaL_checkstring(lstate, 1);
+ handle_T handle = (handle_T)luaL_checkinteger(lstate, 2);
+ dict_T *dict = NULL;
+ Error err = ERROR_INIT;
+ if (strequal(scope, "g")) {
+ dict = &globvardict;
+ } else if (strequal(scope, "v")) {
+ dict = &vimvardict;
+ } else if (strequal(scope, "b")) {
+ buf_T *buf = find_buffer_by_handle(handle, &err);
+ if (buf) {
+ dict = buf->b_vars;
+ }
+ } else if (strequal(scope, "w")) {
+ win_T *win = find_window_by_handle(handle, &err);
+ if (win) {
+ dict = win->w_vars;
+ }
+ } else if (strequal(scope, "t")) {
+ tabpage_T *tabpage = find_tab_by_handle(handle, &err);
+ if (tabpage) {
+ dict = tabpage->tp_vars;
+ }
+ } else {
+ luaL_error(lstate, "invalid scope", err.msg);
+ return NULL;
+ }
+
+ if (ERROR_SET(&err)) {
+ luaL_error(lstate, "FAIL: %s", err.msg);
+ return NULL;
+ }
+ return dict;
+}
+
+
+int nlua_setvar(lua_State *lstate)
+{
+ // non-local return if not found
+ dict_T *dict = nlua_get_var_scope(lstate);
+ String key;
+ key.data = (char *)luaL_checklstring(lstate, 3, &key.size);
+
+ bool del = (lua_gettop(lstate) < 4) || lua_isnil(lstate, 4);
+
+ Error err = ERROR_INIT;
+ dictitem_T *di = dict_check_writable(dict, key, del, &err);
+ if (ERROR_SET(&err)) {
+ return 0;
+ }
+
+ if (del) {
+ // Delete the key
+ if (di == NULL) {
+ // Doesn't exist, nothing to do
+ return 0;
+ } else {
+ // Delete the entry
+ tv_dict_item_remove(dict, di);
+ }
+ } else {
+ // Update the key
+ typval_T tv;
+
+ // Convert the lua value to a vimscript type in the temporary variable
+ lua_pushvalue(lstate, 4);
+ if (!nlua_pop_typval(lstate, &tv)) {
+ return luaL_error(lstate, "Couldn't convert lua value");
+ }
+
+ if (di == NULL) {
+ // Need to create an entry
+ di = tv_dict_item_alloc_len(key.data, key.size);
+ tv_dict_add(dict, di);
+ } else {
+ // Clear the old value
+ tv_clear(&di->di_tv);
+ }
+
+ // Update the value
+ tv_copy(&tv, &di->di_tv);
+ // Clear the temporary variable
+ tv_clear(&tv);
+ }
+ return 0;
+}
+
+int nlua_getvar(lua_State *lstate)
+{
+ // non-local return if not found
+ dict_T *dict = nlua_get_var_scope(lstate);
+ size_t len;
+ const char *name = luaL_checklstring(lstate, 3, &len);
+
+ dictitem_T *di = tv_dict_find(dict, name, (ptrdiff_t)len);
+ if (di == NULL) {
+ return 0; // nil
+ }
+ nlua_push_typval(lstate, &di->di_tv, false);
+ return 1;
+}
+
+/// Compare two strings, ignoring case
+///
+/// Expects two values on the stack: compared strings. Returns one of the
+/// following numbers: 0, -1 or 1.
+///
+/// Does no error handling: never call it with non-string or with some arguments
+/// omitted.
+static int nlua_stricmp(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL
+{
+ size_t s1_len;
+ size_t s2_len;
+ const char *s1 = luaL_checklstring(lstate, 1, &s1_len);
+ const char *s2 = luaL_checklstring(lstate, 2, &s2_len);
+ char *nul1;
+ char *nul2;
+ int ret = 0;
+ assert(s1[s1_len] == NUL);
+ assert(s2[s2_len] == NUL);
+ do {
+ nul1 = memchr(s1, NUL, s1_len);
+ nul2 = memchr(s2, NUL, s2_len);
+ ret = STRICMP(s1, s2);
+ if (ret == 0) {
+ // Compare "a\0" greater then "a".
+ if ((nul1 == NULL) != (nul2 == NULL)) {
+ ret = ((nul1 != NULL) - (nul2 != NULL));
+ break;
+ }
+ if (nul1 != NULL) {
+ assert(nul2 != NULL);
+ // Can't shift both strings by the same amount of bytes: lowercase
+ // letter may have different byte-length than uppercase.
+ s1_len -= (size_t)(nul1 - s1) + 1;
+ s2_len -= (size_t)(nul2 - s2) + 1;
+ s1 = nul1 + 1;
+ s2 = nul2 + 1;
+ } else {
+ break;
+ }
+ } else {
+ break;
+ }
+ } while (true);
+ lua_pop(lstate, 2);
+ lua_pushnumber(lstate, (lua_Number)((ret > 0) - (ret < 0)));
+ return 1;
+}
+
+
+void nlua_state_add_stdlib(lua_State *const lstate)
+{
+ // stricmp
+ lua_pushcfunction(lstate, &nlua_stricmp);
+ lua_setfield(lstate, -2, "stricmp");
+ // str_utfindex
+ lua_pushcfunction(lstate, &nlua_str_utfindex);
+ lua_setfield(lstate, -2, "str_utfindex");
+ // str_byteindex
+ lua_pushcfunction(lstate, &nlua_str_byteindex);
+ lua_setfield(lstate, -2, "str_byteindex");
+ // str_utf_pos
+ lua_pushcfunction(lstate, &nlua_str_utf_pos);
+ lua_setfield(lstate, -2, "str_utf_pos");
+ // str_utf_start
+ lua_pushcfunction(lstate, &nlua_str_utf_start);
+ lua_setfield(lstate, -2, "str_utf_start");
+ // str_utf_end
+ lua_pushcfunction(lstate, &nlua_str_utf_end);
+ lua_setfield(lstate, -2, "str_utf_end");
+ // regex
+ lua_pushcfunction(lstate, &nlua_regex);
+ lua_setfield(lstate, -2, "regex");
+ luaL_newmetatable(lstate, "nvim_regex");
+ luaL_register(lstate, NULL, regex_meta);
+
+ lua_pushvalue(lstate, -1); // [meta, meta]
+ lua_setfield(lstate, -2, "__index"); // [meta]
+ lua_pop(lstate, 1); // don't use metatable now
+
+ // _getvar
+ lua_pushcfunction(lstate, &nlua_getvar);
+ lua_setfield(lstate, -2, "_getvar");
+
+ // _setvar
+ lua_pushcfunction(lstate, &nlua_setvar);
+ lua_setfield(lstate, -2, "_setvar");
+
+ // vim.mpack
+ luaopen_mpack(lstate);
+ lua_pushvalue(lstate, -1);
+ lua_setfield(lstate, -3, "mpack");
+
+ // package.loaded.mpack = vim.mpack
+ // otherwise luv will be reinitialized when require'mpack'
+ lua_getglobal(lstate, "package");
+ lua_getfield(lstate, -1, "loaded");
+ lua_pushvalue(lstate, -3);
+ lua_setfield(lstate, -2, "mpack");
+ lua_pop(lstate, 3);
+
+ // vim.diff
+ lua_pushcfunction(lstate, &nlua_xdl_diff);
+ lua_setfield(lstate, -2, "diff");
+
+ lua_cjson_new(lstate);
+ lua_setfield(lstate, -2, "json");
+}
diff --git a/src/nvim/lua/stdlib.h b/src/nvim/lua/stdlib.h
new file mode 100644
index 0000000000..17aec6714d
--- /dev/null
+++ b/src/nvim/lua/stdlib.h
@@ -0,0 +1,10 @@
+#ifndef NVIM_LUA_STDLIB_H
+#define NVIM_LUA_STDLIB_H
+
+#include <lua.h>
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "lua/stdlib.h.generated.h"
+#endif
+
+#endif // NVIM_LUA_STDLIB_H
diff --git a/src/nvim/lua/treesitter.c b/src/nvim/lua/treesitter.c
index bd978cc8ab..60a000843f 100644
--- a/src/nvim/lua/treesitter.c
+++ b/src/nvim/lua/treesitter.c
@@ -934,7 +934,7 @@ push:
ts_tree_cursor_current_node(ud),
lua_upvalueindex(2)); // [node]
- const char * field = ts_tree_cursor_current_field_name(ud);
+ const char *field = ts_tree_cursor_current_field_name(ud);
if (field != NULL) {
lua_pushstring(L, ts_tree_cursor_current_field_name(ud));
@@ -1195,7 +1195,8 @@ int tslua_parse_query(lua_State *L)
}
-static const char *query_err_string(TSQueryError err) {
+static const char *query_err_string(TSQueryError err)
+{
switch (err) {
case TSQueryErrorSyntax:
return "invalid syntax";
diff --git a/src/nvim/lua/vim.lua b/src/nvim/lua/vim.lua
index 51b7430957..30c7034209 100644
--- a/src/nvim/lua/vim.lua
+++ b/src/nvim/lua/vim.lua
@@ -323,22 +323,25 @@ end
do
local validate = vim.validate
- local function make_dict_accessor(scope)
+ local function make_dict_accessor(scope, handle)
validate {
scope = {scope, 's'};
}
local mt = {}
function mt:__newindex(k, v)
- return vim._setvar(scope, 0, k, v)
+ return vim._setvar(scope, handle or 0, k, v)
end
function mt:__index(k)
- return vim._getvar(scope, 0, k)
+ if handle == nil and type(k) == 'number' then
+ return make_dict_accessor(scope, k)
+ end
+ return vim._getvar(scope, handle or 0, k)
end
return setmetatable({}, mt)
end
- vim.g = make_dict_accessor('g')
- vim.v = make_dict_accessor('v')
+ vim.g = make_dict_accessor('g', false)
+ vim.v = make_dict_accessor('v', false)
vim.b = make_dict_accessor('b')
vim.w = make_dict_accessor('w')
vim.t = make_dict_accessor('t')
diff --git a/src/nvim/lua/xdiff.c b/src/nvim/lua/xdiff.c
index 3955fbe72c..7eda9b6270 100644
--- a/src/nvim/lua/xdiff.c
+++ b/src/nvim/lua/xdiff.c
@@ -62,7 +62,7 @@ static int hunk_locations_cb(long start_a, long count_a, long start_b, long coun
start_b += 1;
}
- lua_State * lstate = (lua_State *)cb_data;
+ lua_State *lstate = (lua_State *)cb_data;
lua_createtable(lstate, 0, 0);
lua_pushinteger(lstate, start_a);
@@ -93,7 +93,7 @@ static int call_on_hunk_cb(long start_a, long count_a, long start_b, long count_
}
hunkpriv_t *priv = (hunkpriv_t *)cb_data;
- lua_State * lstate = priv->lstate;
+ lua_State *lstate = priv->lstate;
Error *err = priv->err;
const int fidx = lua_gettop(lstate);
lua_pushvalue(lstate, fidx);
@@ -133,7 +133,7 @@ static mmfile_t get_string_arg(lua_State *lstate, int idx)
static bool check_xdiff_opt(ObjectType actType, ObjectType expType, const char *name, Error *err)
{
if (actType != expType) {
- const char * type_str =
+ const char *type_str =
expType == kObjectTypeString ? "string" :
expType == kObjectTypeInteger ? "integer" :
expType == kObjectTypeBoolean ? "boolean" :