diff options
author | ZyX <kp-pav@yandex.ru> | 2017-01-29 18:40:39 +0300 |
---|---|---|
committer | ZyX <kp-pav@yandex.ru> | 2017-03-27 00:12:42 +0300 |
commit | 9114d9be778b07f4a49edc078f1c159aa51320d8 (patch) | |
tree | c89808b3a62862e7893c8127d7f1b373f8916067 | |
parent | b4e2860c69a4e96fa305b535ce0dbdde37632fe1 (diff) | |
download | rneovim-9114d9be778b07f4a49edc078f1c159aa51320d8.tar.gz rneovim-9114d9be778b07f4a49edc078f1c159aa51320d8.tar.bz2 rneovim-9114d9be778b07f4a49edc078f1c159aa51320d8.zip |
executor: Add :luado command
-rw-r--r-- | src/nvim/ex_cmds.lua | 2 | ||||
-rw-r--r-- | src/nvim/macros.h | 9 | ||||
-rw-r--r-- | src/nvim/viml/executor/executor.c | 121 | ||||
-rw-r--r-- | test/functional/lua/luaeval_spec.lua | 2 |
4 files changed, 129 insertions, 5 deletions
diff --git a/src/nvim/ex_cmds.lua b/src/nvim/ex_cmds.lua index b34975f00e..25e74ebf9b 100644 --- a/src/nvim/ex_cmds.lua +++ b/src/nvim/ex_cmds.lua @@ -1548,7 +1548,7 @@ return { command='luado', flags=bit.bor(RANGE, DFLALL, EXTRA, NEEDARG, CMDWIN), addr_type=ADDR_LINES, - func='ex_ni', + func='ex_luado', }, { command='luafile', diff --git a/src/nvim/macros.h b/src/nvim/macros.h index 650bf76156..5042663041 100644 --- a/src/nvim/macros.h +++ b/src/nvim/macros.h @@ -19,6 +19,15 @@ # define MAX(X, Y) ((X) > (Y) ? (X) : (Y)) #endif +/// String with length +/// +/// For use in functions which accept (char *s, size_t len) pair in arguments. +/// +/// @param[in] s Static string. +/// +/// @return `s, sizeof(s) - 1` +#define S_LEN(s) (s), (sizeof(s) - 1) + /* * Position comparisons */ diff --git a/src/nvim/viml/executor/executor.c b/src/nvim/viml/executor/executor.c index fb39716f5c..80f2651afc 100644 --- a/src/nvim/viml/executor/executor.c +++ b/src/nvim/viml/executor/executor.c @@ -12,6 +12,13 @@ #include "nvim/vim.h" #include "nvim/ex_getln.h" #include "nvim/message.h" +#include "nvim/memline.h" +#include "nvim/buffer_defs.h" +#include "nvim/macros.h" +#include "nvim/screen.h" +#include "nvim/cursor.h" +#include "nvim/undo.h" +#include "nvim/ascii.h" #include "nvim/viml/executor/executor.h" #include "nvim/viml/executor/converter.h" @@ -38,6 +45,17 @@ typedef struct { lua_pushcfunction(lstate, &function); \ lua_call(lstate, 0, numret); \ } while (0) +/// Call C function which expects one argument +/// +/// @param function Called function +/// @param numret Number of returned arguments +/// @param a… Supplied argument (should be a void* pointer) +#define NLUA_CALL_C_FUNCTION_1(lstate, function, numret, a1) \ + do { \ + lua_pushcfunction(lstate, &function); \ + lua_pushlightuserdata(lstate, a1); \ + lua_call(lstate, 1, numret); \ + } while (0) /// Call C function which expects two arguments /// /// @param function Called function @@ -117,7 +135,7 @@ static int nlua_stricmp(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL /// of view). static int nlua_exec_lua_string(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL { - const String *const str = (String *)lua_touserdata(lstate, 1); + const String *const str = (const String *)lua_touserdata(lstate, 1); typval_T *const ret_tv = (typval_T *)lua_touserdata(lstate, 2); lua_pop(lstate, 2); @@ -135,6 +153,79 @@ static int nlua_exec_lua_string(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL return 0; } +/// Evaluate lua string for each line in range +/// +/// Expects two values on the stack: string to evaluate and pointer to integer +/// array with line range. Always returns nothing (from the lua point of view). +static int nlua_exec_luado_string(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL +{ + const String *const str = (const String *)lua_touserdata(lstate, 1); + const linenr_T *const range = (const linenr_T *)lua_touserdata(lstate, 1); + lua_pop(lstate, 1); + +#define DOSTART "return function(line, linenr) " +#define DOEND " end" + const size_t lcmd_len = str->size + (sizeof(DOSTART) - 1) + (sizeof(DOEND) - 1); + char *lcmd; + if (lcmd_len < IOSIZE) { + lcmd = (char *)IObuff; + } else { + lcmd = xmalloc(lcmd_len); + } + memcpy(lcmd, S_LEN(DOSTART)); + memcpy(lcmd + sizeof(DOSTART) - 1, str->data, str->size); + memcpy(lcmd + sizeof(DOSTART) - 1 + str->size, S_LEN(DOEND)); +#undef DOSTART +#undef DOEND + + if (luaL_loadbuffer(lstate, lcmd, lcmd_len, NLUA_EVAL_NAME)) { + nlua_error(lstate, _("E5109: Error while creating lua chunk: %.*s")); + return 0; + } + if (lua_pcall(lstate, 0, 1, 0)) { + nlua_error(lstate, _("E5110: Error while creating lua function: %.*s")); + return 0; + } + for (linenr_T l = range[0]; l < range[1]; l++) { + if (l > curbuf->b_ml.ml_line_count) { + break; + } + lua_pushvalue(lstate, -1); + lua_pushstring(lstate, (const char *)ml_get_buf(curbuf, l, false)); + lua_pushnumber(lstate, (lua_Number)l); + if (lua_pcall(lstate, 2, 1, 0)) { + nlua_error(lstate, _("E5111: Error while calling lua function: %.*s")); + break; + } + if (lua_isstring(lstate, -1)) { + if (sandbox) { + EMSG(_("E5112: Not allowed in sandbox")); + lua_pop(lstate, 1); + break; + } + size_t new_line_len; + const char *new_line = lua_tolstring(lstate, -1, &new_line_len); + char *const new_line_transformed = ( + new_line_len < IOSIZE + ? memcpy(IObuff, new_line, new_line_len) + : xmemdupz(new_line, new_line_len)); + new_line_transformed[new_line_len] = NUL; + for (size_t i = 0; i < new_line_len; i++) { + if (new_line_transformed[new_line_len] == NUL) { + new_line_transformed[new_line_len] = '\n'; + } + } + ml_replace(l, (char_u *)new_line_transformed, true); + changed_bytes(l, 0); + } + lua_pop(lstate, 1); + } + lua_pop(lstate, 1); + check_cursor(); + update_screen(NOT_VALID); + return 0; +} + /// Initialize lua interpreter state /// /// Called by lua interpreter itself to initialize state. @@ -200,7 +291,7 @@ void executor_exec_lua(const String str, typval_T *const ret_tv) static int nlua_eval_lua_string(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL { - const String *const str = (String *)lua_touserdata(lstate, 1); + const String *const str = (const String *)lua_touserdata(lstate, 1); typval_T *const arg = (typval_T *)lua_touserdata(lstate, 2); typval_T *const ret_tv = (typval_T *)lua_touserdata(lstate, 3); lua_pop(lstate, 3); @@ -215,7 +306,7 @@ static int nlua_eval_lua_string(lua_State *const lstate) } else { lcmd = xmalloc(lcmd_len); } - memcpy(lcmd, EVALHEADER, sizeof(EVALHEADER) - 1); + memcpy(lcmd, S_LEN(EVALHEADER)); memcpy(lcmd + sizeof(EVALHEADER) - 1, str->data, str->size); lcmd[lcmd_len - 1] = ')'; #undef EVALHEADER @@ -288,3 +379,27 @@ void ex_lua(exarg_T *const eap) clear_tv(&tv); xfree(code); } + +/// Run lua string for each line in range +/// +/// Used for :luado. +/// +/// @param eap VimL command being run. +void ex_luado(exarg_T *const eap) + FUNC_ATTR_NONNULL_ALL +{ + if (global_lstate == NULL) { + global_lstate = init_lua(); + } + if (u_save(eap->line1 - 1, eap->line2 + 1) == FAIL) { + EMSG(_("cannot save undo information")); + return; + } + const String cmd = { + .size = STRLEN(eap->arg), + .data = (char *)eap->arg, + }; + const linenr_T range[] = { eap->line1, eap->line2 }; + NLUA_CALL_C_FUNCTION_2(global_lstate, nlua_exec_luado_string, 0, + (void *)&cmd, (void *)range); +} diff --git a/test/functional/lua/luaeval_spec.lua b/test/functional/lua/luaeval_spec.lua index 6132d44bee..5d0180b212 100644 --- a/test/functional/lua/luaeval_spec.lua +++ b/test/functional/lua/luaeval_spec.lua @@ -36,7 +36,7 @@ describe('luaeval()', function() it('is successfully received', function() local t = {t=true, f=false, --[[n=NIL,]] d={l={'string', 42, 0.42}}} eq(t, funcs.luaeval("_A", t)) - -- Not tested: nil, funcrefs, returned object identity: behaviour will + -- Not tested: nil, funcrefs, returned object identity: behaviour will -- most likely change. end) end) |