mirror of
https://github.com/neovim/neovim.git
synced 2025-02-25 18:55:25 -06:00
Merge pull request #16125 from dundargoc/refactor/lua/split-executor
refactor: split lua/executor.c into two files
This commit is contained in:
commit
a5d6f36fc7
@ -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"
|
||||
@ -99,135 +96,6 @@ 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;
|
||||
}
|
||||
|
||||
/// 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;
|
||||
}
|
||||
|
||||
/// 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)
|
||||
{
|
||||
@ -301,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)
|
||||
{
|
||||
@ -445,42 +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");
|
||||
// str_utf_pos
|
||||
lua_pushcfunction(lstate, &nlua_str_utf_pos);
|
||||
lua_setfield(lstate, -2, "str_utf_pos");
|
||||
|
||||
// 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);
|
||||
@ -494,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);
|
||||
@ -538,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);
|
||||
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);
|
||||
|
||||
// internal vim._treesitter... API
|
||||
nlua_add_treesitter(lstate);
|
||||
|
||||
// vim.diff
|
||||
lua_pushcfunction(lstate, &nlua_xdl_diff);
|
||||
lua_setfield(lstate, -2, "diff");
|
||||
// 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");
|
||||
|
||||
lua_cjson_new(lstate);
|
||||
lua_setfield(lstate, -2, "json");
|
||||
// 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);
|
||||
|
||||
nlua_state_add_stdlib(lstate);
|
||||
|
||||
lua_setglobal(lstate, "vim");
|
||||
|
||||
@ -769,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[] = {
|
||||
@ -924,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");
|
||||
@ -1579,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)
|
||||
|
479
src/nvim/lua/stdlib.c
Normal file
479
src/nvim/lua/stdlib.c
Normal file
@ -0,0 +1,479 @@
|
||||
// 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;
|
||||
}
|
||||
|
||||
|
||||
/// 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");
|
||||
// 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");
|
||||
}
|
10
src/nvim/lua/stdlib.h
Normal file
10
src/nvim/lua/stdlib.h
Normal file
@ -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
|
Loading…
Reference in New Issue
Block a user