// 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 <lualib.h>
#include <lauxlib.h>
#include "nvim/misc1.h"
#include "nvim/getchar.h"
#include "nvim/garray.h"
#include "nvim/func_attr.h"
#include "nvim/api/private/defs.h"
#include "nvim/api/private/helpers.h"
#include "nvim/api/private/handle.h"
#include "nvim/api/vim.h"
#include "nvim/msgpack_rpc/channel.h"
#include "nvim/vim.h"
#include "nvim/ex_getln.h"
#include "nvim/ex_cmds2.h"
#include "nvim/message.h"
#include "nvim/memline.h"
#include "nvim/buffer_defs.h"
#include "nvim/regexp.h"
#include "nvim/macros.h"
#include "nvim/screen.h"
#include "nvim/cursor.h"
#include "nvim/undo.h"
#include "nvim/ascii.h"
#include "nvim/change.h"
#include "nvim/eval/userfunc.h"
#include "nvim/event/time.h"
#include "nvim/event/loop.h"
#ifdef WIN32
#include "nvim/os/os.h"
#endif
#include "nvim/lua/executor.h"
#include "nvim/lua/converter.h"
#include "nvim/lua/treesitter.h"
#include "luv/luv.h"
static int in_fast_callback = 0;
typedef struct {
Error err;
String lua_err_str;
} LuaError;
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "lua/vim_module.generated.h"
# include "lua/executor.c.generated.h"
#endif
/// Convert lua error into a Vim error message
///
/// @param lstate Lua interpreter state.
/// @param[in] msg Message base, must contain one `%s`.
static void nlua_error(lua_State *const lstate, const char *const msg)
FUNC_ATTR_NONNULL_ALL
{
size_t len;
const char *const str = lua_tolstring(lstate, -1, &len);
msg_ext_set_kind("lua_error");
emsgf_multiline(msg, (int)len, str);
lua_pop(lstate, 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 indicies
///
/// 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 indicies.
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 indicies 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_c
|