Last active
November 10, 2021 20:42
-
-
Save dmitmel/0b852399b6ef6f69741cb5179757c8ce to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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 "luv/luv.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/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/screen.h" | |
#include "nvim/undo.h" | |
#include "nvim/version.h" | |
#include "nvim/vim.h" | |
static int in_fast_callback = 0; | |
// Initialized in nlua_init(). | |
static lua_State *global_lstate = NULL; | |
typedef struct { | |
Error err; | |
String lua_err_str; | |
} LuaError; | |
#ifdef INCLUDE_GENERATED_DECLARATIONS | |
# include "lua/executor.c.generated.h" | |
# include "lua/vim_module.generated.h" | |
#endif | |
#define PUSH_ALL_TYPVALS(lstate, args, argcount, special) \ | |
for (int i = 0; i < argcount; i++) { \ | |
if (args[i].v_type == VAR_UNKNOWN) { \ | |
lua_pushnil(lstate); \ | |
} else { \ | |
nlua_push_typval(lstate, &args[i], special); \ | |
} \ | |
} | |
#if __has_feature(address_sanitizer) | |
static PMap(handle_T) nlua_ref_markers = MAP_INIT; | |
static bool nlua_track_refs = false; | |
# define NLUA_TRACK_REFS | |
#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"); | |
semsg_multiline(msg, (int)len, str); | |
lua_pop(lstate, 1); | |
} | |
/// Like lua_pcall, but use debug.traceback as errfunc. | |
/// | |
/// @param lstate Lua interpreter state | |
/// @param[in] nargs Number of arguments expected by the function being called. | |
/// @param[in] nresults Number of results the function returns. | |
static int nlua_pcall(lua_State *lstate, int nargs, int nresults) | |
{ | |
lua_getglobal(lstate, "debug"); | |
lua_getfield(lstate, -1, "traceback"); | |
lua_remove(lstate, -2); | |
lua_insert(lstate, -2 - nargs); | |
int status = lua_pcall(lstate, nargs, nresults, -2 - nargs); | |
if (status) { | |
lua_remove(lstate, -2); | |
} else { | |
lua_remove(lstate, -1 - nresults); | |
} | |
return status; | |
} | |
/// Gets the version of the current Nvim build. | |
/// | |
/// @param lstate Lua interpreter state. | |
static int nlua_nvim_version(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL | |
{ | |
Dictionary version = version_dict(); | |
nlua_push_Dictionary(lstate, version, true); | |
api_free_dictionary(version); | |
return 1; | |
} | |
static void nlua_luv_error_event(void **argv) | |
{ | |
char *error = (char *)argv[0]; | |
msg_ext_set_kind("lua_error"); | |
semsg_multiline("Error executing luv callback:\n%s", error); | |
xfree(error); | |
} | |
static int nlua_luv_cfpcall(lua_State *lstate, int nargs, int nresult, int flags) | |
FUNC_ATTR_NONNULL_ALL | |
{ | |
int retval; | |
// luv callbacks might be executed at any os_breakcheck/line_breakcheck | |
// call, so using the API directly here is not safe. | |
in_fast_callback++; | |
int top = lua_gettop(lstate); | |
int status = nlua_pcall(lstate, nargs, nresult); | |
if (status) { | |
if (status == LUA_ERRMEM && !(flags & LUVF_CALLBACK_NOEXIT)) { | |
// consider out of memory errors unrecoverable, just like xmalloc() | |
mch_errmsg(e_outofmem); | |
mch_errmsg("\n"); | |
preserve_exit(); | |
} | |
const char *error = lua_tostring(lstate, -1); | |
multiqueue_put(main_loop.events, nlua_luv_error_event, | |
1, xstrdup(error)); | |
lua_pop(lstate, 1); // error message | |
retval = -status; | |
} else { // LUA_OK | |
if (nresult == LUA_MULTRET) { | |
nresult = lua_gettop(lstate) - top + nargs + 1; | |
} | |
retval = nresult; | |
} | |
in_fast_callback--; | |
return retval; | |
} | |
static void nlua_schedule_event(void **argv) | |
{ | |
LuaRef cb = (LuaRef)(ptrdiff_t)argv[0]; | |
lua_State *const lstate = global_lstate; | |
nlua_pushref(lstate, cb); | |
nlua_unref(lstate, cb); | |
if (nlua_pcall(lstate, 0, 0)) { | |
nlua_error(lstate, _("Error executing vim.schedule lua callback: %.*s")); | |
} | |
} | |
/// Schedule Lua callback on main loop's event queue | |
/// | |
/// @param lstate Lua interpreter state. | |
static int nlua_schedule(lua_State *const lstate) | |
FUNC_ATTR_NONNULL_ALL | |
{ | |
if (lua_type(lstate, 1) != LUA_TFUNCTION) { | |
lua_pushliteral(lstate, "vim.schedule: expected function"); | |
return lua_error(lstate); | |
} | |
LuaRef cb = nlua_ref(lstate, 1); | |
multiqueue_put(main_loop.events, nlua_schedule_event, | |
1, (void *)(ptrdiff_t)cb); | |
return 0; | |
} | |
// Dummy timer callback. Used by f_wait(). | |
static void dummy_timer_due_cb(TimeWatcher *tw, void *data) | |
{ | |
} | |
// Dummy timer close callback. Used by f_wait(). | |
static void dummy_timer_close_cb(TimeWatcher *tw, void *data) | |
{ | |
xfree(tw); | |
} | |
static bool nlua_wait_condition(lua_State *lstate, int *status, bool *callback_result) | |
{ | |
lua_pushvalue(lstate, 2); | |
*status = nlua_pcall(lstate, 0, 1); | |
if (*status) { | |
return true; // break on error, but keep error on stack | |
} | |
*callback_result = lua_toboolean(lstate, -1); | |
lua_pop(lstate, 1); | |
return *callback_result; // break if true | |
} | |
/// "vim.wait(timeout, condition[, interval])" function | |
static int nlua_wait(lua_State *lstate) | |
FUNC_ATTR_NONNULL_ALL | |
{ | |
intptr_t timeout = luaL_checkinteger(lstate, 1); | |
if (timeout < 0) { | |
return luaL_error(lstate, "timeout must be > 0"); | |
} | |
int lua_top = lua_gettop(lstate); | |
// Check if condition can be called. | |
bool is_function = false; | |
if (lua_top >= 2 && !lua_isnil(lstate, 2)) { | |
is_function = (lua_type(lstate, 2) == LUA_TFUNCTION); | |
// Check if condition is callable table | |
if (!is_function && luaL_getmetafield(lstate, 2, "__call") != 0) { | |
is_function = (lua_type(lstate, -1) == LUA_TFUNCTION); | |
lua_pop(lstate, 1); | |
} | |
if (!is_function) { | |
lua_pushliteral(lstate, | |
"vim.wait: if passed, condition must be a function"); | |
return lua_error(lstate); | |
} | |
} | |
intptr_t interval = 200; | |
if (lua_top >= 3 && !lua_isnil(lstate, 3)) { | |
interval = luaL_checkinteger(lstate, 3); | |
if (interval < 0) { | |
return luaL_error(lstate, "interval must be > 0"); | |
} | |
} | |
bool fast_only = false; | |
if (lua_top >= 4) { | |
fast_only = lua_toboolean(lstate, 4); | |
} | |
MultiQueue *loop_events = fast_only || in_fast_callback > 0 | |
? main_loop.fast_events : main_loop.events; | |
TimeWatcher *tw = xmalloc(sizeof(TimeWatcher)); | |
// Start dummy timer. | |
time_watcher_init(&main_loop, tw, NULL); | |
tw->events = loop_events; | |
tw->blockable = true; | |
time_watcher_start(tw, | |
dummy_timer_due_cb, | |
(uint64_t)interval, | |
(uint64_t)interval); | |
int pcall_status = 0; | |
bool callback_result = false; | |
LOOP_PROCESS_EVENTS_UNTIL(&main_loop, | |
loop_events, | |
(int)timeout, | |
is_function ? nlua_wait_condition(lstate, | |
&pcall_status, | |
&callback_result) : false || got_int); | |
// Stop dummy timer | |
time_watcher_stop(tw); | |
time_watcher_close(tw, dummy_timer_close_cb); | |
if (pcall_status) { | |
return lua_error(lstate); | |
} else if (callback_result) { | |
lua_pushboolean(lstate, 1); | |
lua_pushnil(lstate); | |
} else if (got_int) { | |
got_int = false; | |
vgetc(); | |
lua_pushboolean(lstate, 0); | |
lua_pushinteger(lstate, -2); | |
} else { | |
lua_pushboolean(lstate, 0); | |
lua_pushinteger(lstate, -1); | |
} | |
return 2; | |
} | |
/// Initialize lua interpreter state | |
/// | |
/// Called by lua interpreter itself to initialize state. | |
static int nlua_state_init(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL | |
{ | |
lua_pushcfunction(lstate, &nlua_print); | |
lua_setglobal(lstate, "print"); | |
// debug.debug | |
lua_getglobal(lstate, "debug"); | |
lua_pushcfunction(lstate, &nlua_debug); | |
lua_setfield(lstate, -2, "debug"); | |
lua_pop(lstate, 1); | |
#ifdef WIN32 | |
// os.getenv | |
lua_getglobal(lstate, "os"); | |
lua_pushcfunction(lstate, &nlua_getenv); | |
lua_setfield(lstate, -2, "getenv"); | |
lua_pop(lstate, 1); | |
#endif | |
// vim | |
lua_newtable(lstate); | |
// vim.api | |
nlua_add_api_functions(lstate); | |
// vim.types, vim.type_idx, vim.val_idx | |
nlua_init_types(lstate); | |
// 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"); | |
// rpcrequest | |
lua_pushcfunction(lstate, &nlua_rpcrequest); | |
lua_setfield(lstate, -2, "rpcrequest"); | |
// rpcnotify | |
lua_pushcfunction(lstate, &nlua_rpcnotify); | |
lua_setfield(lstate, -2, "rpcnotify"); | |
// wait | |
lua_pushcfunction(lstate, &nlua_wait); | |
lua_setfield(lstate, -2, "wait"); | |
// vim.NIL | |
lua_newuserdata(lstate, 0); | |
lua_createtable(lstate, 0, 0); | |
lua_pushcfunction(lstate, &nlua_nil_tostring); | |
lua_setfield(lstate, -2, "__tostring"); | |
lua_setmetatable(lstate, -2); | |
nlua_nil_ref = nlua_ref(lstate, -1); | |
lua_pushvalue(lstate, -1); | |
lua_setfield(lstate, LUA_REGISTRYINDEX, "mpack.NIL"); | |
lua_setfield(lstate, -2, "NIL"); | |
// vim._empty_dict_mt | |
lua_createtable(lstate, 0, 0); | |
lua_pushcfunction(lstate, &nlua_empty_dict_tostring); | |
lua_setfield(lstate, -2, "__tostring"); | |
nlua_empty_dict_ref = nlua_ref(lstate, -1); | |
lua_pushvalue(lstate, -1); | |
lua_setfield(lstate, LUA_REGISTRYINDEX, "mpack.empty_dict"); | |
lua_setfield(lstate, -2, "_empty_dict_mt"); | |
// 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, "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); | |
nlua_state_add_stdlib(lstate); | |
lua_setglobal(lstate, "vim"); | |
{ | |
const char *code = (char *)&shared_module[0]; | |
if (luaL_loadbuffer(lstate, code, strlen(code), "@vim/shared.lua") | |
|| nlua_pcall(lstate, 0, 0)) { | |
nlua_error(lstate, _("E5106: Error while creating shared module: %.*s")); | |
return 1; | |
} | |
} | |
{ | |
lua_getglobal(lstate, "package"); // [package] | |
lua_getfield(lstate, -1, "loaded"); // [package, loaded] | |
const char *code = (char *)&inspect_module[0]; | |
if (luaL_loadbuffer(lstate, code, strlen(code), "@vim/inspect.lua") | |
|| nlua_pcall(lstate, 0, 1)) { | |
nlua_error(lstate, _("E5106: Error while creating inspect module: %.*s")); | |
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") | |
|| nlua_pcall(lstate, 0, 1)) { | |
nlua_error(lstate, _("E5106: Error while creating vim.F module: %.*s")); | |
return 1; | |
} | |
// [package, loaded, module] | |
lua_setfield(lstate, -2, "vim.F"); // [package, loaded] | |
lua_pop(lstate, 2); // [] | |
} | |
{ | |
const char *code = (char *)&vim_module[0]; | |
if (luaL_loadbuffer(lstate, code, strlen(code), "@vim.lua") | |
|| nlua_pcall(lstate, 0, 0)) { | |
nlua_error(lstate, _("E5106: Error while creating vim module: %.*s")); | |
return 1; | |
} | |
} | |
{ | |
lua_getglobal(lstate, "package"); // [package] | |
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") | |
|| nlua_pcall(lstate, 0, 1)) { | |
nlua_error(lstate, _("E5106: Error while creating vim._meta module: %.*s")); | |
return 1; | |
} | |
// [package, loaded, module] | |
lua_setfield(lstate, -2, "vim._meta"); // [package, loaded] | |
lua_pop(lstate, 2); // [] | |
} | |
return 0; | |
} | |
/// Initialize global lua interpreter | |
/// | |
/// Crashes Nvim if initialization fails. | |
void nlua_init(void) | |
{ | |
#ifdef NLUA_TRACK_REFS | |
const char *env = os_getenv("NVIM_LUA_NOTRACK"); | |
if (!env || !*env) { | |
nlua_track_refs = true; | |
} | |
#endif | |
lua_State *lstate = luaL_newstate(); | |
if (lstate == NULL) { | |
emsg(_("E970: Failed to initialize lua interpreter")); | |
preserve_exit(); | |
} | |
luaL_openlibs(lstate); | |
nlua_state_init(lstate); | |
global_lstate = lstate; | |
} | |
void nlua_free_all_mem(void) | |
{ | |
if (!global_lstate) { | |
return; | |
} | |
lua_State *lstate = global_lstate; | |
nlua_unref(lstate, nlua_nil_ref); | |
nlua_unref(lstate, nlua_empty_dict_ref); | |
#ifdef NLUA_TRACK_REFS | |
if (nlua_refcount) { | |
fprintf(stderr, "%d lua references were leaked!", nlua_refcount); | |
} | |
if (nlua_track_refs) { | |
// in case there are leaked luarefs, leak the associated memory | |
// to get LeakSanitizer stacktraces on exit | |
pmap_destroy(handle_T)(&nlua_ref_markers); | |
} | |
#endif | |
nlua_refcount = 0; | |
lua_close(lstate); | |
} | |
static void nlua_print_event(void **argv) | |
{ | |
char *str = argv[0]; | |
const size_t len = (size_t)(intptr_t)argv[1]-1; // exclude final NUL | |
for (size_t i = 0; i < len;) { | |
const size_t start = i; | |
while (i < len) { | |
switch (str[i]) { | |
case NUL: | |
str[i] = NL; | |
i++; | |
continue; | |
case NL: | |
// TODO(bfredl): use proper multiline msg? Probably should implement | |
// print() in lua in terms of nvim_message(), when it is available. | |
str[i] = NUL; | |
i++; | |
break; | |
default: | |
i++; | |
continue; | |
} | |
break; | |
} | |
msg(str + start); | |
} | |
if (len && str[len - 1] == NUL) { // Last was newline | |
msg(""); | |
} | |
xfree(str); | |
} | |
/// Print as a Vim message | |
/// | |
/// @param lstate Lua interpreter state. | |
static int nlua_print(lua_State *const lstate) | |
FUNC_ATTR_NONNULL_ALL | |
{ | |
#define PRINT_ERROR(msg) \ | |
do { \ | |
errmsg = msg; \ | |
errmsg_len = sizeof(msg) - 1; \ | |
goto nlua_print_error; \ | |
} while (0) | |
const int nargs = lua_gettop(lstate); | |
lua_getglobal(lstate, "tostring"); | |
const char *errmsg = NULL; | |
size_t errmsg_len = 0; | |
garray_T msg_ga; | |
ga_init(&msg_ga, 1, 80); | |
int curargidx = 1; | |
for (; curargidx <= nargs; curargidx++) { | |
lua_pushvalue(lstate, -1); // tostring | |
lua_pushvalue(lstate, curargidx); // arg | |
// Do not use nlua_pcall here to avoid duplicate stack trace information | |
if (lua_pcall(lstate, 1, 1, 0)) { | |
errmsg = lua_tolstring(lstate, -1, &errmsg_len); | |
goto nlua_print_error; | |
} | |
size_t len; | |
const char *const s = lua_tolstring(lstate, -1, &len); | |
if (s == NULL) { | |
PRINT_ERROR("<Unknown error: lua_tolstring returned NULL for tostring result>"); | |
} | |
ga_concat_len(&msg_ga, s, len); | |
if (curargidx < nargs) { | |
ga_append(&msg_ga, ' '); | |
} | |
lua_pop(lstate, 1); | |
} | |
#undef PRINT_ERROR | |
ga_append(&msg_ga, NUL); | |
if (in_fast_callback) { | |
multiqueue_put(main_loop.events, nlua_print_event, | |
2, msg_ga.ga_data, msg_ga.ga_len); | |
} else { | |
nlua_print_event((void *[]){ msg_ga.ga_data, | |
(void *)(intptr_t)msg_ga.ga_len }); | |
} | |
return 0; | |
nlua_print_error: | |
ga_clear(&msg_ga); | |
const char *fmt = _("E5114: Error while converting print argument #%i: %.*s"); | |
size_t len = (size_t)vim_snprintf((char *)IObuff, IOSIZE, fmt, curargidx, | |
(int)errmsg_len, errmsg); | |
lua_pushlstring(lstate, (char *)IObuff, len); | |
return lua_error(lstate); | |
} | |
/// debug.debug: interaction with user while debugging. | |
/// | |
/// @param lstate Lua interpreter state. | |
static int nlua_debug(lua_State *lstate) | |
FUNC_ATTR_NONNULL_ALL | |
{ | |
const typval_T input_args[] = { | |
{ | |
.v_lock = VAR_FIXED, | |
.v_type = VAR_STRING, | |
.vval.v_string = (char_u *)"lua_debug> ", | |
}, | |
{ | |
.v_type = VAR_UNKNOWN, | |
}, | |
}; | |
for (;;) { | |
lua_settop(lstate, 0); | |
typval_T input; | |
get_user_input(input_args, &input, false, false); | |
msg_putchar('\n'); // Avoid outputting on input line. | |
if (input.v_type != VAR_STRING | |
|| input.vval.v_string == NULL | |
|| *input.vval.v_string == NUL | |
|| STRCMP(input.vval.v_string, "cont") == 0) { | |
tv_clear(&input); | |
return 0; | |
} | |
if (luaL_loadbuffer(lstate, (const char *)input.vval.v_string, | |
STRLEN(input.vval.v_string), "=(debug command)")) { | |
nlua_error(lstate, _("E5115: Error while loading debug string: %.*s")); | |
} else if (nlua_pcall(lstate, 0, 0)) { | |
nlua_error(lstate, _("E5116: Error while calling debug string: %.*s")); | |
} | |
tv_clear(&input); | |
} | |
return 0; | |
} | |
int nlua_in_fast_event(lua_State *lstate) | |
{ | |
lua_pushboolean(lstate, in_fast_callback > 0); | |
return 1; | |
} | |
int nlua_call(lua_State *lstate) | |
{ | |
Error err = ERROR_INIT; | |
size_t name_len; | |
const char_u *name = (const char_u *)luaL_checklstring(lstate, 1, &name_len); | |
if (!nlua_is_deferred_safe()) { | |
return luaL_error(lstate, e_luv_api_disabled, "vimL function"); | |
} | |
int nargs = lua_gettop(lstate)-1; | |
if (nargs > MAX_FUNC_ARGS) { | |
return luaL_error(lstate, "Function called with too many arguments"); | |
} | |
typval_T vim_args[MAX_FUNC_ARGS + 1]; | |
int i = 0; // also used for freeing the variables | |
for (; i < nargs; i++) { | |
lua_pushvalue(lstate, (int)i+2); | |
if (!nlua_pop_typval(lstate, &vim_args[i])) { | |
api_set_error(&err, kErrorTypeException, | |
"error converting argument %d", i+1); | |
goto free_vim_args; | |
} | |
} | |
TRY_WRAP({ | |
// TODO(bfredl): this should be simplified in error handling refactor | |
force_abort = false; | |
suppress_errthrow = false; | |
current_exception = NULL; | |
did_emsg = false; | |
try_start(); | |
typval_T rettv; | |
funcexe_T funcexe = FUNCEXE_INIT; | |
funcexe.firstline = curwin->w_cursor.lnum; | |
funcexe.lastline = curwin->w_cursor.lnum; | |
funcexe.evaluate = true; | |
// call_func() retval is deceptive, ignore it. Instead we set `msg_list` | |
// (TRY_WRAP) to capture abort-causing non-exception errors. | |
(void)call_func(name, (int)name_len, &rettv, nargs, vim_args, &funcexe); | |
if (!try_end(&err)) { | |
nlua_push_typval(lstate, &rettv, false); | |
} | |
tv_clear(&rettv); | |
}); | |
free_vim_args: | |
while (i > 0) { | |
tv_clear(&vim_args[--i]); | |
} | |
if (ERROR_SET(&err)) { | |
lua_pushstring(lstate, err.msg); | |
api_clear_error(&err); | |
return lua_error(lstate); | |
} | |
return 1; | |
} | |
static int nlua_rpcrequest(lua_State *lstate) | |
{ | |
if (!nlua_is_deferred_safe()) { | |
return luaL_error(lstate, e_luv_api_disabled, "rpcrequest"); | |
} | |
return nlua_rpc(lstate, true); | |
} | |
static int nlua_rpcnotify(lua_State *lstate) | |
{ | |
return nlua_rpc(lstate, false); | |
} | |
static int nlua_rpc(lua_State *lstate, bool request) | |
{ | |
size_t name_len; | |
uint64_t chan_id = (uint64_t)luaL_checkinteger(lstate, 1); | |
const char *name = luaL_checklstring(lstate, 2, &name_len); | |
int nargs = lua_gettop(lstate)-2; | |
Error err = ERROR_INIT; | |
Array args = ARRAY_DICT_INIT; | |
for (int i = 0; i < nargs; i++) { | |
lua_pushvalue(lstate, (int)i+3); | |
ADD(args, nlua_pop_Object(lstate, false, &err)); | |
if (ERROR_SET(&err)) { | |
api_free_array(args); | |
goto check_err; | |
} | |
} | |
if (request) { | |
Object result = rpc_send_call(chan_id, name, args, &err); | |
if (!ERROR_SET(&err)) { | |
nlua_push_Object(lstate, result, false); | |
api_free_object(result); | |
} | |
} else { | |
if (!rpc_send_event(chan_id, name, args)) { | |
api_set_error(&err, kErrorTypeValidation, | |
"Invalid channel: %" PRIu64, chan_id); | |
} | |
} | |
check_err: | |
if (ERROR_SET(&err)) { | |
lua_pushstring(lstate, err.msg); | |
api_clear_error(&err); | |
return lua_error(lstate); | |
} | |
return request ? 1 : 0; | |
} | |
static int nlua_nil_tostring(lua_State *lstate) | |
{ | |
lua_pushstring(lstate, "vim.NIL"); | |
return 1; | |
} | |
static int nlua_empty_dict_tostring(lua_State *lstate) | |
{ | |
lua_pushstring(lstate, "vim.empty_dict()"); | |
return 1; | |
} | |
#ifdef WIN32 | |
/// os.getenv: override os.getenv to maintain coherency. #9681 | |
/// | |
/// uv_os_setenv uses SetEnvironmentVariableW which does not update _environ. | |
/// | |
/// @param lstate Lua interpreter state. | |
static int nlua_getenv(lua_State *lstate) | |
{ | |
lua_pushstring(lstate, os_getenv(luaL_checkstring(lstate, 1))); | |
return 1; | |
} | |
#endif | |
/// add the value to the registry | |
LuaRef nlua_ref(lua_State *lstate, int index) | |
{ | |
lua_pushvalue(lstate, index); | |
LuaRef ref = luaL_ref(lstate, LUA_REGISTRYINDEX); | |
if (ref > 0) { | |
nlua_refcount++; | |
#ifdef NLUA_TRACK_REFS | |
if (nlua_track_refs) { | |
// dummy allocation to make LeakSanitizer track our luarefs | |
pmap_put(handle_T)(&nlua_ref_markers, ref, xmalloc(3)); | |
} | |
#endif | |
} | |
return ref; | |
} | |
/// remove the value from the registry | |
void nlua_unref(lua_State *lstate, LuaRef ref) | |
{ | |
if (ref > 0) { | |
nlua_refcount--; | |
#ifdef NLUA_TRACK_REFS | |
// NB: don't remove entry from map to track double-unref | |
if (nlua_track_refs) { | |
xfree(pmap_get(handle_T)(&nlua_ref_markers, ref)); | |
} | |
#endif | |
luaL_unref(lstate, LUA_REGISTRYINDEX, ref); | |
} | |
} | |
void api_free_luaref(LuaRef ref) | |
{ | |
nlua_unref(global_lstate, ref); | |
} | |
/// push a value referenced in the registry | |
void nlua_pushref(lua_State *lstate, LuaRef ref) | |
{ | |
lua_rawgeti(lstate, LUA_REGISTRYINDEX, ref); | |
} | |
/// Gets a new reference to an object stored at original_ref | |
/// | |
/// NOTE: It does not copy the value, it creates a new ref to the lua object. | |
/// Leaves the stack unchanged. | |
LuaRef api_new_luaref(LuaRef original_ref) | |
{ | |
if (original_ref == LUA_NOREF) { | |
return LUA_NOREF; | |
} | |
lua_State *const lstate = global_lstate; | |
nlua_pushref(lstate, original_ref); | |
LuaRef new_ref = nlua_ref(lstate, -1); | |
lua_pop(lstate, 1); | |
return new_ref; | |
} | |
/// Evaluate lua string | |
/// | |
/// Used for luaeval(). | |
/// | |
/// @param[in] str String to execute. | |
/// @param[in] arg Second argument to `luaeval()`. | |
/// @param[out] ret_tv Location where result will be saved. | |
/// | |
/// @return Result of the execution. | |
void nlua_typval_eval(const String str, typval_T *const arg, typval_T *const ret_tv) | |
FUNC_ATTR_NONNULL_ALL | |
{ | |
#define EVALHEADER "local _A=select(1,...) return (" | |
const size_t lcmd_len = sizeof(EVALHEADER) - 1 + str.size + 1; | |
char *lcmd; | |
if (lcmd_len < IOSIZE) { | |
lcmd = (char *)IObuff; | |
} else { | |
lcmd = xmalloc(lcmd_len); | |
} | |
memcpy(lcmd, EVALHEADER, sizeof(EVALHEADER) - 1); | |
memcpy(lcmd + sizeof(EVALHEADER) - 1, str.data, str.size); | |
lcmd[lcmd_len - 1] = ')'; | |
#undef EVALHEADER | |
nlua_typval_exec(lcmd, lcmd_len, "luaeval()", arg, 1, true, ret_tv); | |
if (lcmd != (char *)IObuff) { | |
xfree(lcmd); | |
} | |
} | |
void nlua_typval_call(const char *str, size_t len, typval_T *const args, int argcount, | |
typval_T *ret_tv) | |
FUNC_ATTR_NONNULL_ALL | |
{ | |
#define CALLHEADER "return " | |
#define CALLSUFFIX "(...)" | |
const size_t lcmd_len = sizeof(CALLHEADER) - 1 + len + sizeof(CALLSUFFIX) - 1; | |
char *lcmd; | |
if (lcmd_len < IOSIZE) { | |
lcmd = (char *)IObuff; | |
} else { | |
lcmd = xmalloc(lcmd_len); | |
} | |
memcpy(lcmd, CALLHEADER, sizeof(CALLHEADER) - 1); | |
memcpy(lcmd + sizeof(CALLHEADER) - 1, str, len); | |
memcpy(lcmd + sizeof(CALLHEADER) - 1 + len, CALLSUFFIX, | |
sizeof(CALLSUFFIX) - 1); | |
#undef CALLHEADER | |
#undef CALLSUFFIX | |
nlua_typval_exec(lcmd, lcmd_len, "v:lua", args, argcount, false, ret_tv); | |
if (lcmd != (char *)IObuff) { | |
xfree(lcmd); | |
} | |
} | |
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) | |
{ | |
if (check_secure()) { | |
if (ret_tv) { | |
ret_tv->v_type = VAR_NUMBER; | |
ret_tv->vval.v_number = 0; | |
} | |
return; | |
} | |
lua_State *const lstate = global_lstate; | |
if (luaL_loadbuffer(lstate, lcmd, lcmd_len, name)) { | |
nlua_error(lstate, _("E5107: Error loading lua %.*s")); | |
return; | |
} | |
PUSH_ALL_TYPVALS(lstate, args, argcount, special); | |
if (nlua_pcall(lstate, argcount, ret_tv ? 1 : 0)) { | |
nlua_error(lstate, _("E5108: Error executing lua %.*s")); | |
return; | |
} | |
if (ret_tv) { | |
nlua_pop_typval(lstate, ret_tv); | |
} | |
} | |
int nlua_source_using_linegetter(LineGetter fgetline, void *cookie, char *name) | |
{ | |
const linenr_T save_sourcing_lnum = sourcing_lnum; | |
const sctx_T save_current_sctx = current_sctx; | |
current_sctx.sc_sid = SID_STR; | |
current_sctx.sc_seq = 0; | |
current_sctx.sc_lnum = 0; | |
sourcing_lnum = 0; | |
garray_T ga; | |
char_u *line = NULL; | |
ga_init(&ga, (int)sizeof(char_u *), 10); | |
while ((line = fgetline(0, cookie, 0, false)) != NULL) { | |
GA_APPEND(char_u *, &ga, line); | |
} | |
char *code = (char *)ga_concat_strings_sep(&ga, "\n"); | |
size_t len = strlen(code); | |
nlua_typval_exec(code, len, name, NULL, 0, false, NULL); | |
sourcing_lnum = save_sourcing_lnum; | |
current_sctx = save_current_sctx; | |
ga_clear_strings(&ga); | |
xfree(code); | |
return OK; | |
} | |
/// Call a LuaCallable given some typvals | |
/// | |
/// Used to call any lua callable passed from Lua into VimL | |
/// | |
/// @param[in] lstate Lua State | |
/// @param[in] lua_cb Lua Callable | |
/// @param[in] argcount Count of typval arguments | |
/// @param[in] argvars Typval Arguments | |
/// @param[out] rettv The return value from the called function. | |
int typval_exec_lua_callable(lua_State *lstate, LuaCallable lua_cb, int argcount, typval_T *argvars, | |
typval_T *rettv) | |
{ | |
LuaRef cb = lua_cb.func_ref; | |
nlua_pushref(lstate, cb); | |
PUSH_ALL_TYPVALS(lstate, argvars, argcount, false); | |
if (nlua_pcall(lstate, argcount, 1)) { | |
nlua_print(lstate); | |
return ERROR_OTHER; | |
} | |
nlua_pop_typval(lstate, rettv); | |
return ERROR_NONE; | |
} | |
/// Execute Lua string | |
/// | |
/// Used for nvim_exec_lua() and internally to execute a lua string. | |
/// | |
/// @param[in] str String to execute. | |
/// @param[in] args array of ... args | |
/// @param[out] err Location where error will be saved. | |
/// | |
/// @return Return value of the execution. | |
Object nlua_exec(const String str, const Array args, Error *err) | |
{ | |
lua_State *const lstate = global_lstate; | |
if (luaL_loadbuffer(lstate, str.data, str.size, "<nvim>")) { | |
size_t len; | |
const char *errstr = lua_tolstring(lstate, -1, &len); | |
api_set_error(err, kErrorTypeValidation, | |
"Error loading lua: %.*s", (int)len, errstr); | |
return NIL; | |
} | |
for (size_t i = 0; i < args.size; i++) { | |
nlua_push_Object(lstate, args.items[i], false); | |
} | |
if (nlua_pcall(lstate, (int)args.size, 1)) { | |
size_t len; | |
const char *errstr = lua_tolstring(lstate, -1, &len); | |
api_set_error(err, kErrorTypeException, | |
"Error executing lua: %.*s", (int)len, errstr); | |
return NIL; | |
} | |
return nlua_pop_Object(lstate, false, err); | |
} | |
/// call a LuaRef as a function (or table with __call metamethod) | |
/// | |
/// @param ref the reference to call (not consumed) | |
/// @param name if non-NULL, sent to callback as first arg | |
/// if NULL, only args are used | |
/// @param retval if true, convert return value to Object | |
/// if false, discard return value | |
/// @param err Error details, if any (if NULL, errors are echoed) | |
/// @return Return value of function, if retval was set. Otherwise NIL. | |
Object nlua_call_ref(LuaRef ref, const char *name, Array args, bool retval, Error *err) | |
{ | |
lua_State *const lstate = global_lstate; | |
nlua_pushref(lstate, ref); | |
int nargs = (int)args.size; | |
if (name != NULL) { | |
lua_pushstring(lstate, name); | |
nargs++; | |
} | |
for (size_t i = 0; i < args.size; i++) { | |
nlua_push_Object(lstate, args.items[i], false); | |
} | |
if (nlua_pcall(lstate, nargs, retval ? 1 : 0)) { | |
// if err is passed, the caller will deal with the error. | |
if (err) { | |
size_t len; | |
const char *errstr = lua_tolstring(lstate, -1, &len); | |
api_set_error(err, kErrorTypeException, | |
"Error executing lua: %.*s", (int)len, errstr); | |
} else { | |
nlua_error(lstate, _("Error executing lua callback: %.*s")); | |
} | |
return NIL; | |
} | |
if (retval) { | |
Error dummy = ERROR_INIT; | |
if (err == NULL) { | |
err = &dummy; | |
} | |
return nlua_pop_Object(lstate, false, err); | |
} else { | |
return NIL; | |
} | |
} | |
/// check if the current execution context is safe for calling deferred API | |
/// methods. Luv callbacks are unsafe as they are called inside the uv loop. | |
bool nlua_is_deferred_safe(void) | |
{ | |
return in_fast_callback == 0; | |
} | |
/// Run lua string | |
/// | |
/// Used for :lua. | |
/// | |
/// @param eap VimL command being run. | |
void ex_lua(exarg_T *const eap) | |
FUNC_ATTR_NONNULL_ALL | |
{ | |
size_t len; | |
char *const code = script_get(eap, &len); | |
if (eap->skip) { | |
xfree(code); | |
return; | |
} | |
nlua_typval_exec(code, len, ":lua", NULL, 0, false, NULL); | |
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 (u_save(eap->line1 - 1, eap->line2 + 1) == FAIL) { | |
emsg(_("cannot save undo information")); | |
return; | |
} | |
const char *const cmd = (const char *)eap->arg; | |
const size_t cmd_len = strlen(cmd); | |
lua_State *const lstate = global_lstate; | |
#define DOSTART "return function(line, linenr) " | |
#define DOEND " end" | |
const size_t lcmd_len = (cmd_len | |
+ (sizeof(DOSTART) - 1) | |
+ (sizeof(DOEND) - 1)); | |
char *lcmd; | |
if (lcmd_len < IOSIZE) { | |
lcmd = (char *)IObuff; | |
} else { | |
lcmd = xmalloc(lcmd_len + 1); | |
} | |
memcpy(lcmd, DOSTART, sizeof(DOSTART) - 1); | |
memcpy(lcmd + sizeof(DOSTART) - 1, cmd, cmd_len); | |
memcpy(lcmd + sizeof(DOSTART) - 1 + cmd_len, DOEND, sizeof(DOEND) - 1); | |
#undef DOSTART | |
#undef DOEND | |
if (luaL_loadbuffer(lstate, lcmd, lcmd_len, ":luado")) { | |
nlua_error(lstate, _("E5109: Error loading lua: %.*s")); | |
if (lcmd_len >= IOSIZE) { | |
xfree(lcmd); | |
} | |
return; | |
} | |
if (lcmd_len >= IOSIZE) { | |
xfree(lcmd); | |
} | |
if (nlua_pcall(lstate, 0, 1)) { | |
nlua_error(lstate, _("E5110: Error executing lua: %.*s")); | |
return; | |
} | |
for (linenr_T l = eap->line1; l <= eap->line2; l++) { | |
if (l > curbuf->b_ml.ml_line_count) { | |
break; | |
} | |
lua_pushvalue(lstate, -1); | |
const char *old_line = (const char *)ml_get_buf(curbuf, l, false); | |
lua_pushstring(lstate, old_line); | |
lua_pushnumber(lstate, (lua_Number)l); | |
if (nlua_pcall(lstate, 2, 1)) { | |
nlua_error(lstate, _("E5111: Error calling lua: %.*s")); | |
break; | |
} | |
if (lua_isstring(lstate, -1)) { | |
size_t old_line_len = STRLEN(old_line); | |
size_t new_line_len; | |
const char *const new_line = lua_tolstring(lstate, -1, &new_line_len); | |
char *const new_line_transformed = xmemdupz(new_line, new_line_len); | |
for (size_t i = 0; i < new_line_len; i++) { | |
if (new_line_transformed[i] == NUL) { | |
new_line_transformed[i] = '\n'; | |
} | |
} | |
ml_replace(l, (char_u *)new_line_transformed, false); | |
inserted_bytes(l, 0, (int)old_line_len, (int)new_line_len); | |
} | |
lua_pop(lstate, 1); | |
} | |
lua_pop(lstate, 1); | |
check_cursor(); | |
update_screen(NOT_VALID); | |
} | |
/// Run lua file | |
/// | |
/// Used for :luafile. | |
/// | |
/// @param eap VimL command being run. | |
void ex_luafile(exarg_T *const eap) | |
FUNC_ATTR_NONNULL_ALL | |
{ | |
nlua_exec_file((const char *)eap->arg); | |
} | |
/// execute lua code from a file. | |
/// | |
/// @param path path of the file | |
/// | |
/// @return true if everything ok, false if there was an error (echoed) | |
bool nlua_exec_file(const char *path) | |
FUNC_ATTR_NONNULL_ALL | |
{ | |
lua_State *const lstate = global_lstate; | |
if (luaL_loadfile(lstate, path)) { | |
nlua_error(lstate, _("E5112: Error while creating lua chunk: %.*s")); | |
return false; | |
} | |
if (nlua_pcall(lstate, 0, 0)) { | |
nlua_error(lstate, _("E5113: Error while calling lua chunk: %.*s")); | |
return false; | |
} | |
return true; | |
} | |
int tslua_get_language_version(lua_State *L) | |
{ | |
lua_pushnumber(L, TREE_SITTER_LANGUAGE_VERSION); | |
return 1; | |
} | |
static void nlua_add_treesitter(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL | |
{ | |
tslua_init(lstate); | |
lua_pushcfunction(lstate, tslua_push_parser); | |
lua_setfield(lstate, -2, "_create_ts_parser"); | |
lua_pushcfunction(lstate, tslua_add_language); | |
lua_setfield(lstate, -2, "_ts_add_language"); | |
lua_pushcfunction(lstate, tslua_has_language); | |
lua_setfield(lstate, -2, "_ts_has_language"); | |
lua_pushcfunction(lstate, tslua_inspect_lang); | |
lua_setfield(lstate, -2, "_ts_inspect_language"); | |
lua_pushcfunction(lstate, tslua_parse_query); | |
lua_setfield(lstate, -2, "_ts_parse_query"); | |
lua_pushcfunction(lstate, tslua_get_language_version); | |
lua_setfield(lstate, -2, "_ts_get_language_version"); | |
} | |
int nlua_expand_pat(expand_T *xp, char_u *pat, int *num_results, char_u ***results) | |
{ | |
lua_State *const lstate = global_lstate; | |
int ret = OK; | |
// [ vim ] | |
lua_getglobal(lstate, "vim"); | |
// [ vim, vim._expand_pat ] | |
lua_getfield(lstate, -1, "_expand_pat"); | |
luaL_checktype(lstate, -1, LUA_TFUNCTION); | |
// [ vim, vim._on_key, buf ] | |
lua_pushlstring(lstate, (const char *)pat, STRLEN(pat)); | |
if (nlua_pcall(lstate, 1, 2) != 0) { | |
nlua_error(lstate, | |
_("Error executing vim._expand_pat: %.*s")); | |
return FAIL; | |
} | |
Error err = ERROR_INIT; | |
*num_results = 0; | |
*results = NULL; | |
int prefix_len = (int)nlua_pop_Integer(lstate, &err); | |
if (ERROR_SET(&err)) { | |
ret = FAIL; | |
goto cleanup; | |
} | |
Array completions = nlua_pop_Array(lstate, &err); | |
if (ERROR_SET(&err)) { | |
ret = FAIL; | |
goto cleanup_array; | |
} | |
garray_T result_array; | |
ga_init(&result_array, (int)sizeof(char *), 80); | |
for (size_t i = 0; i < completions.size; i++) { | |
Object v = completions.items[i]; | |
if (v.type != kObjectTypeString) { | |
ret = FAIL; | |
goto cleanup_array; | |
} | |
GA_APPEND(char_u *, | |
&result_array, | |
vim_strsave((char_u *)v.data.string.data)); | |
} | |
xp->xp_pattern += prefix_len; | |
*results = result_array.ga_data; | |
*num_results = result_array.ga_len; | |
cleanup_array: | |
api_free_array(completions); | |
cleanup: | |
if (ret == FAIL) { | |
ga_clear(&result_array); | |
} | |
return ret; | |
} | |
// Required functions for lua c functions as VimL callbacks | |
int nlua_CFunction_func_call(int argcount, typval_T *argvars, typval_T *rettv, void *state) | |
{ | |
lua_State *const lstate = global_lstate; | |
LuaCFunctionState *funcstate = (LuaCFunctionState *)state; | |
return typval_exec_lua_callable(lstate, funcstate->lua_callable, | |
argcount, argvars, rettv); | |
} | |
void nlua_CFunction_func_free(void *state) | |
{ | |
lua_State *const lstate = global_lstate; | |
LuaCFunctionState *funcstate = (LuaCFunctionState *)state; | |
nlua_unref(lstate, funcstate->lua_callable.func_ref); | |
xfree(funcstate); | |
} | |
bool nlua_is_table_from_lua(typval_T *const arg) | |
{ | |
if (arg->v_type == VAR_DICT) { | |
return arg->vval.v_dict->lua_table_ref != LUA_NOREF; | |
} else if (arg->v_type == VAR_LIST) { | |
return arg->vval.v_list->lua_table_ref != LUA_NOREF; | |
} else { | |
return false; | |
} | |
} | |
char_u *nlua_register_table_as_callable(typval_T *const arg) | |
{ | |
LuaRef table_ref = LUA_NOREF; | |
if (arg->v_type == VAR_DICT) { | |
table_ref = arg->vval.v_dict->lua_table_ref; | |
} else if (arg->v_type == VAR_LIST) { | |
table_ref = arg->vval.v_list->lua_table_ref; | |
} | |
if (table_ref == LUA_NOREF) { | |
return NULL; | |
} | |
lua_State *const lstate = global_lstate; | |
#ifndef NDEBUG | |
int top = lua_gettop(lstate); | |
#endif | |
nlua_pushref(lstate, table_ref); // [table] | |
if (!lua_getmetatable(lstate, -1)) { | |
lua_pop(lstate, 1); | |
assert(top == lua_gettop(lstate)); | |
return NULL; | |
} // [table, mt] | |
lua_getfield(lstate, -1, "__call"); // [table, mt, mt.__call] | |
if (!lua_isfunction(lstate, -1)) { | |
lua_pop(lstate, 3); | |
assert(top == lua_gettop(lstate)); | |
return NULL; | |
} | |
lua_pop(lstate, 2); // [table] | |
LuaCFunctionState *state = xmalloc(sizeof(LuaCFunctionState)); | |
state->lua_callable.func_ref = nlua_ref(lstate, -1); | |
char_u *name = register_cfunc(&nlua_CFunction_func_call, | |
&nlua_CFunction_func_free, state); | |
lua_pop(lstate, 1); // [] | |
assert(top == lua_gettop(lstate)); | |
return name; | |
} | |
void nlua_execute_on_key(int c) | |
{ | |
char_u buf[NUMBUFLEN]; | |
size_t buf_len = special_to_buf(c, mod_mask, false, buf); | |
lua_State *const lstate = global_lstate; | |
#ifndef NDEBUG | |
int top = lua_gettop(lstate); | |
#endif | |
// [ vim ] | |
lua_getglobal(lstate, "vim"); | |
// [ vim, vim._on_key] | |
lua_getfield(lstate, -1, "_on_key"); | |
luaL_checktype(lstate, -1, LUA_TFUNCTION); | |
// [ vim, vim._on_key, buf ] | |
lua_pushlstring(lstate, (const char *)buf, buf_len); | |
if (nlua_pcall(lstate, 1, 0)) { | |
nlua_error(lstate, | |
_("Error executing vim.on_key Lua callback: %.*s")); | |
} | |
// [ vim ] | |
lua_pop(lstate, 1); | |
#ifndef NDEBUG | |
// [ ] | |
assert(top == lua_gettop(lstate)); | |
#endif | |
} | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
[ | |
{ | |
"range": { | |
"end": { | |
"character": 21, | |
"line": 39 | |
}, | |
"start": { | |
"character": 0, | |
"line": 3 | |
} | |
}, | |
"newText": "#include \"nvim/lua/executor.h\"\n\n#include <lauxlib.h>\n#include <lua.h>\n#include <lualib.h>\n\n#include \"luv/luv.h\"\n#include \"nvim/api/private/defs.h\"\n#include \"nvim/api/private/helpers.h\"\n#include \"nvim/api/vim.h\"\n#include \"nvim/ascii.h\"\n#include \"nvim/assert.h\"\n#include \"nvim/buffer_defs.h\"\n#include \"nvim/change.h\"\n#include \"nvim/cursor.h\"\n#include \"nvim/eval/userfunc.h\"\n#include \"nvim/event/loop.h\"\n#include \"nvim/event/time.h\"\n#include \"nvim/ex_cmds2.h\"\n#include \"nvim/ex_getln.h\"\n#include \"nvim/extmark.h\"\n#include \"nvim/func_attr.h\"\n#include \"nvim/garray.h\"\n#include \"nvim/getchar.h\"\n#include \"nvim/lua/converter.h\"\n#include \"nvim/lua/stdlib.h\"\n#include \"nvim/lua/treesitter.h\"\n#include \"nvim/macros.h\"\n#include \"nvim/map.h\"\n#include \"nvim/memline.h\"\n#include \"nvim/message.h\"\n#include \"nvim/misc1.h\"\n#include \"nvim/msgpack_rpc/channel.h\"\n#include \"nvim/os/os.h\"\n#include \"nvim/screen.h\"\n#include \"nvim/undo.h\"\n#include \"nvim/version.h\"\n#include \"nvim/vim.h\"" | |
}, | |
{ | |
"range": { | |
"end": { | |
"character": 2, | |
"line": 52 | |
}, | |
"start": { | |
"character": 1, | |
"line": 52 | |
} | |
}, | |
"newText": "" | |
}, | |
{ | |
"range": { | |
"end": { | |
"character": 2, | |
"line": 53 | |
}, | |
"start": { | |
"character": 1, | |
"line": 53 | |
} | |
}, | |
"newText": "" | |
}, | |
{ | |
"range": { | |
"end": { | |
"character": 2, | |
"line": 57 | |
}, | |
"start": { | |
"character": 57, | |
"line": 56 | |
} | |
}, | |
"newText": " \\\n " | |
}, | |
{ | |
"range": { | |
"end": { | |
"character": 4, | |
"line": 58 | |
}, | |
"start": { | |
"character": 38, | |
"line": 57 | |
} | |
}, | |
"newText": " \\\n " | |
}, | |
{ | |
"range": { | |
"end": { | |
"character": 6, | |
"line": 59 | |
}, | |
"start": { | |
"character": 40, | |
"line": 58 | |
} | |
}, | |
"newText": " \\\n " | |
}, | |
{ | |
"range": { | |
"end": { | |
"character": 4, | |
"line": 60 | |
}, | |
"start": { | |
"character": 26, | |
"line": 59 | |
} | |
}, | |
"newText": " \\\n " | |
}, | |
{ | |
"range": { | |
"end": { | |
"character": 6, | |
"line": 61 | |
}, | |
"start": { | |
"character": 12, | |
"line": 60 | |
} | |
}, | |
"newText": " \\\n " | |
}, | |
{ | |
"range": { | |
"end": { | |
"character": 4, | |
"line": 62 | |
}, | |
"start": { | |
"character": 50, | |
"line": 61 | |
} | |
}, | |
"newText": " \\\n " | |
}, | |
{ | |
"range": { | |
"end": { | |
"character": 2, | |
"line": 63 | |
}, | |
"start": { | |
"character": 5, | |
"line": 62 | |
} | |
}, | |
"newText": " \\\n " | |
}, | |
{ | |
"range": { | |
"end": { | |
"character": 2, | |
"line": 68 | |
}, | |
"start": { | |
"character": 1, | |
"line": 68 | |
} | |
}, | |
"newText": "" | |
}, | |
{ | |
"range": { | |
"end": { | |
"character": 2, | |
"line": 76 | |
}, | |
"start": { | |
"character": 70, | |
"line": 75 | |
} | |
}, | |
"newText": " " | |
}, | |
{ | |
"range": { | |
"end": { | |
"character": 0, | |
"line": 108 | |
}, | |
"start": { | |
"character": 1, | |
"line": 105 | |
} | |
}, | |
"newText": "\n\n" | |
}, | |
{ | |
"range": { | |
"end": { | |
"character": 0, | |
"line": 120 | |
}, | |
"start": { | |
"character": 1, | |
"line": 117 | |
} | |
}, | |
"newText": "\n\n" | |
}, | |
{ | |
"range": { | |
"end": { | |
"character": 2, | |
"line": 129 | |
}, | |
"start": { | |
"character": 81, | |
"line": 128 | |
} | |
}, | |
"newText": "\n " | |
}, | |
{ | |
"range": { | |
"end": { | |
"character": 19, | |
"line": 149 | |
}, | |
"start": { | |
"character": 58, | |
"line": 148 | |
} | |
}, | |
"newText": " " | |
}, | |
{ | |
"range": { | |
"end": { | |
"character": 2, | |
"line": 178 | |
}, | |
"start": { | |
"character": 49, | |
"line": 177 | |
} | |
}, | |
"newText": " " | |
}, | |
{ | |
"range": { | |
"end": { | |
"character": 17, | |
"line": 188 | |
}, | |
"start": { | |
"character": 55, | |
"line": 187 | |
} | |
}, | |
"newText": " " | |
}, | |
{ | |
"range": { | |
"end": { | |
"character": 2, | |
"line": 217 | |
}, | |
"start": { | |
"character": 39, | |
"line": 216 | |
} | |
}, | |
"newText": " " | |
}, | |
{ | |
"range": { | |
"end": { | |
"character": 22, | |
"line": 239 | |
}, | |
"start": { | |
"character": 29, | |
"line": 238 | |
} | |
}, | |
"newText": " " | |
}, | |
{ | |
"range": { | |
"end": { | |
"character": 17, | |
"line": 254 | |
}, | |
"start": { | |
"character": 15, | |
"line": 254 | |
} | |
}, | |
"newText": " " | |
}, | |
{ | |
"range": { | |
"end": { | |
"character": 26, | |
"line": 257 | |
}, | |
"start": { | |
"character": 25, | |
"line": 257 | |
} | |
}, | |
"newText": "\n " | |
}, | |
{ | |
"range": { | |
"end": { | |
"character": 4, | |
"line": 258 | |
}, | |
"start": { | |
"character": 61, | |
"line": 257 | |
} | |
}, | |
"newText": " " | |
}, | |
{ | |
"range": { | |
"end": { | |
"character": 21, | |
"line": 267 | |
}, | |
"start": { | |
"character": 24, | |
"line": 266 | |
} | |
}, | |
"newText": " " | |
}, | |
{ | |
"range": { | |
"end": { | |
"character": 21, | |
"line": 268 | |
}, | |
"start": { | |
"character": 40, | |
"line": 267 | |
} | |
}, | |
"newText": " " | |
}, | |
{ | |
"range": { | |
"end": { | |
"character": 21, | |
"line": 269 | |
}, | |
"start": { | |
"character": 40, | |
"line": 268 | |
} | |
}, | |
"newText": " " | |
}, | |
{ | |
"range": { | |
"end": { | |
"character": 28, | |
"line": 275 | |
}, | |
"start": { | |
"character": 39, | |
"line": 274 | |
} | |
}, | |
"newText": " " | |
}, | |
{ | |
"range": { | |
"end": { | |
"character": 28, | |
"line": 276 | |
}, | |
"start": { | |
"character": 40, | |
"line": 275 | |
} | |
}, | |
"newText": " " | |
}, | |
{ | |
"range": { | |
"end": { | |
"character": 40, | |
"line": 277 | |
}, | |
"start": { | |
"character": 39, | |
"line": 277 | |
} | |
}, | |
"newText": "\n " | |
}, | |
{ | |
"range": { | |
"end": { | |
"character": 62, | |
"line": 278 | |
}, | |
"start": { | |
"character": 69, | |
"line": 277 | |
} | |
}, | |
"newText": " " | |
}, | |
{ | |
"range": { | |
"end": { | |
"character": 62, | |
"line": 279 | |
}, | |
"start": { | |
"character": 76, | |
"line": 278 | |
} | |
}, | |
"newText": " " | |
}, | |
{ | |
"range": { | |
"end": { | |
"character": 80, | |
"line": 279 | |
}, | |
"start": { | |
"character": 79, | |
"line": 279 | |
} | |
}, | |
"newText": "\n " | |
}, | |
{ | |
"range": { | |
"end": { | |
"character": 39, | |
"line": 415 | |
}, | |
"start": { | |
"character": 37, | |
"line": 415 | |
} | |
}, | |
"newText": " " | |
}, | |
{ | |
"range": { | |
"end": { | |
"character": 8, | |
"line": 429 | |
}, | |
"start": { | |
"character": 65, | |
"line": 428 | |
} | |
}, | |
"newText": " " | |
}, | |
{ | |
"range": { | |
"end": { | |
"character": 8, | |
"line": 442 | |
}, | |
"start": { | |
"character": 63, | |
"line": 441 | |
} | |
}, | |
"newText": " " | |
}, | |
{ | |
"range": { | |
"end": { | |
"character": 39, | |
"line": 449 | |
}, | |
"start": { | |
"character": 37, | |
"line": 449 | |
} | |
}, | |
"newText": " " | |
}, | |
{ | |
"range": { | |
"end": { | |
"character": 8, | |
"line": 454 | |
}, | |
"start": { | |
"character": 69, | |
"line": 453 | |
} | |
}, | |
"newText": " " | |
}, | |
{ | |
"range": { | |
"end": { | |
"character": 0, | |
"line": 491 | |
}, | |
"start": { | |
"character": 1, | |
"line": 488 | |
} | |
}, | |
"newText": "\n\n" | |
}, | |
{ | |
"range": { | |
"end": { | |
"character": 46, | |
"line": 520 | |
}, | |
"start": { | |
"character": 46, | |
"line": 520 | |
} | |
}, | |
"newText": " " | |
}, | |
{ | |
"range": { | |
"end": { | |
"character": 47, | |
"line": 520 | |
}, | |
"start": { | |
"character": 47, | |
"line": 520 | |
} | |
}, | |
"newText": " " | |
}, | |
{ | |
"range": { | |
"end": { | |
"character": 6, | |
"line": 526 | |
}, | |
"start": { | |
"character": 23, | |
"line": 525 | |
} | |
}, | |
"newText": "\n " | |
}, | |
{ | |
"range": { | |
"end": { | |
"character": 8, | |
"line": 527 | |
}, | |
"start": { | |
"character": 15, | |
"line": 526 | |
} | |
}, | |
"newText": "\n " | |
}, | |
{ | |
"range": { | |
"end": { | |
"character": 8, | |
"line": 528 | |
}, | |
"start": { | |
"character": 20, | |
"line": 527 | |
} | |
}, | |
"newText": "\n " | |
}, | |
{ | |
"range": { | |
"end": { | |
"character": 8, | |
"line": 529 | |
}, | |
"start": { | |
"character": 12, | |
"line": 528 | |
} | |
}, | |
"newText": "\n " | |
}, | |
{ | |
"range": { | |
"end": { | |
"character": 6, | |
"line": 530 | |
}, | |
"start": { | |
"character": 17, | |
"line": 529 | |
} | |
}, | |
"newText": "\n " | |
}, | |
{ | |
"range": { | |
"end": { | |
"character": 8, | |
"line": 531 | |
}, | |
"start": { | |
"character": 14, | |
"line": 530 | |
} | |
}, | |
"newText": "\n " | |
}, | |
{ | |
"range": { | |
"end": { | |
"character": 8, | |
"line": 532 | |
}, | |
"start": { | |
"character": 76, | |
"line": 531 | |
} | |
}, | |
"newText": "\n " | |
}, | |
{ | |
"range": { | |
"end": { | |
"character": 8, | |
"line": 533 | |
}, | |
"start": { | |
"character": 75, | |
"line": 532 | |
} | |
}, | |
"newText": "\n " | |
}, | |
{ | |
"range": { | |
"end": { | |
"character": 8, | |
"line": 534 | |
}, | |
"start": { | |
"character": 21, | |
"line": 533 | |
} | |
}, | |
"newText": "\n " | |
}, | |
{ | |
"range": { | |
"end": { | |
"character": 8, | |
"line": 535 | |
}, | |
"start": { | |
"character": 12, | |
"line": 534 | |
} | |
}, | |
"newText": "\n " | |
}, | |
{ | |
"range": { | |
"end": { | |
"character": 6, | |
"line": 536 | |
}, | |
"start": { | |
"character": 14, | |
"line": 535 | |
} | |
}, | |
"newText": "\n " | |
}, | |
{ | |
"range": { | |
"end": { | |
"character": 8, | |
"line": 537 | |
}, | |
"start": { | |
"character": 14, | |
"line": 536 | |
} | |
}, | |
"newText": "\n " | |
}, | |
{ | |
"range": { | |
"end": { | |
"character": 8, | |
"line": 538 | |
}, | |
"start": { | |
"character": 12, | |
"line": 537 | |
} | |
}, | |
"newText": "\n " | |
}, | |
{ | |
"range": { | |
"end": { | |
"character": 2, | |
"line": 554 | |
}, | |
"start": { | |
"character": 46, | |
"line": 553 | |
} | |
}, | |
"newText": " " | |
}, | |
{ | |
"range": { | |
"end": { | |
"character": 2, | |
"line": 557 | |
}, | |
"start": { | |
"character": 24, | |
"line": 556 | |
} | |
}, | |
"newText": " \\\n " | |
}, | |
{ | |
"range": { | |
"end": { | |
"character": 4, | |
"line": 558 | |
}, | |
"start": { | |
"character": 6, | |
"line": 557 | |
} | |
}, | |
"newText": " \\\n " | |
}, | |
{ | |
"range": { | |
"end": { | |
"character": 4, | |
"line": 559 | |
}, | |
"start": { | |
"character": 17, | |
"line": 558 | |
} | |
}, | |
"newText": " \\\n " | |
}, | |
{ | |
"range": { | |
"end": { | |
"character": 4, | |
"line": 560 | |
}, | |
"start": { | |
"character": 33, | |
"line": 559 | |
} | |
}, | |
"newText": " \\\n " | |
}, | |
{ | |
"range": { | |
"end": { | |
"character": 2, | |
"line": 561 | |
}, | |
"start": { | |
"character": 26, | |
"line": 560 | |
} | |
}, | |
"newText": " \\\n " | |
}, | |
{ | |
"range": { | |
"end": { | |
"character": 32, | |
"line": 570 | |
}, | |
"start": { | |
"character": 30, | |
"line": 570 | |
} | |
}, | |
"newText": " " | |
}, | |
{ | |
"range": { | |
"end": { | |
"character": 19, | |
"line": 593 | |
}, | |
"start": { | |
"character": 54, | |
"line": 592 | |
} | |
}, | |
"newText": " " | |
}, | |
{ | |
"range": { | |
"end": { | |
"character": 33, | |
"line": 595 | |
}, | |
"start": { | |
"character": 32, | |
"line": 595 | |
} | |
}, | |
"newText": "" | |
}, | |
{ | |
"range": { | |
"end": { | |
"character": 33, | |
"line": 596 | |
}, | |
"start": { | |
"character": 48, | |
"line": 595 | |
} | |
}, | |
"newText": " " | |
}, | |
{ | |
"range": { | |
"end": { | |
"character": 65, | |
"line": 596 | |
}, | |
"start": { | |
"character": 64, | |
"line": 596 | |
} | |
}, | |
"newText": "" | |
}, | |
{ | |
"range": { | |
"end": { | |
"character": 13, | |
"line": 603 | |
}, | |
"start": { | |
"character": 12, | |
"line": 603 | |
} | |
}, | |
"newText": "\n " | |
}, | |
{ | |
"range": { | |
"end": { | |
"character": 36, | |
"line": 604 | |
}, | |
"start": { | |
"character": 75, | |
"line": 603 | |
} | |
}, | |
"newText": " " | |
}, | |
{ | |
"range": { | |
"end": { | |
"character": 2, | |
"line": 613 | |
}, | |
"start": { | |
"character": 40, | |
"line": 612 | |
} | |
}, | |
"newText": " " | |
}, | |
{ | |
"range": { | |
"end": { | |
"character": 4, | |
"line": 616 | |
}, | |
"start": { | |
"character": 33, | |
"line": 615 | |
} | |
}, | |
"newText": "\n " | |
}, | |
{ | |
"range": { | |
"end": { | |
"character": 6, | |
"line": 617 | |
}, | |
"start": { | |
"character": 5, | |
"line": 616 | |
} | |
}, | |
"newText": "\n " | |
}, | |
{ | |
"range": { | |
"end": { | |
"character": 6, | |
"line": 618 | |
}, | |
"start": { | |
"character": 26, | |
"line": 617 | |
} | |
}, | |
"newText": "\n " | |
}, | |
{ | |
"range": { | |
"end": { | |
"character": 6, | |
"line": 619 | |
}, | |
"start": { | |
"character": 27, | |
"line": 618 | |
} | |
}, | |
"newText": "\n " | |
}, | |
{ | |
"range": { | |
"end": { | |
"character": 4, | |
"line": 620 | |
}, | |
"start": { | |
"character": 47, | |
"line": 619 | |
} | |
}, | |
"newText": "\n " | |
}, | |
{ | |
"range": { | |
"end": { | |
"character": 4, | |
"line": 621 | |
}, | |
"start": { | |
"character": 6, | |
"line": 620 | |
} | |
}, | |
"newText": "\n " | |
}, | |
{ | |
"range": { | |
"end": { | |
"character": 6, | |
"line": 622 | |
}, | |
"start": { | |
"character": 5, | |
"line": 621 | |
} | |
}, | |
"newText": "\n " | |
}, | |
{ | |
"range": { | |
"end": { | |
"character": 4, | |
"line": 623 | |
}, | |
"start": { | |
"character": 28, | |
"line": 622 | |
} | |
}, | |
"newText": "\n " | |
}, | |
{ | |
"range": { | |
"end": { | |
"character": 8, | |
"line": 631 | |
}, | |
"start": { | |
"character": 34, | |
"line": 630 | |
} | |
}, | |
"newText": " " | |
}, | |
{ | |
"range": { | |
"end": { | |
"character": 8, | |
"line": 632 | |
}, | |
"start": { | |
"character": 38, | |
"line": 631 | |
} | |
}, | |
"newText": " " | |
}, | |
{ | |
"range": { | |
"end": { | |
"character": 24, | |
"line": 638 | |
}, | |
"start": { | |
"character": 66, | |
"line": 637 | |
} | |
}, | |
"newText": " " | |
}, | |
{ | |
"range": { | |
"end": { | |
"character": 53, | |
"line": 638 | |
}, | |
"start": { | |
"character": 52, | |
"line": 638 | |
} | |
}, | |
"newText": "\n " | |
}, | |
{ | |
"range": { | |
"end": { | |
"character": 32, | |
"line": 663 | |
}, | |
"start": { | |
"character": 32, | |
"line": 663 | |
} | |
}, | |
"newText": " " | |
}, | |
{ | |
"range": { | |
"end": { | |
"character": 33, | |
"line": 663 | |
}, | |
"start": { | |
"character": 33, | |
"line": 663 | |
} | |
}, | |
"newText": " " | |
}, | |
{ | |
"range": { | |
"end": { | |
"character": 32, | |
"line": 671 | |
}, | |
"start": { | |
"character": 32, | |
"line": 671 | |
} | |
}, | |
"newText": " " | |
}, | |
{ | |
"range": { | |
"end": { | |
"character": 33, | |
"line": 671 | |
}, | |
"start": { | |
"character": 33, | |
"line": 671 | |
} | |
}, | |
"newText": " " | |
}, | |
{ | |
"range": { | |
"end": { | |
"character": 20, | |
"line": 674 | |
}, | |
"start": { | |
"character": 46, | |
"line": 673 | |
} | |
}, | |
"newText": " " | |
}, | |
{ | |
"range": { | |
"end": { | |
"character": 53, | |
"line": 674 | |
}, | |
"start": { | |
"character": 53, | |
"line": 674 | |
} | |
}, | |
"newText": " " | |
}, | |
{ | |
"range": { | |
"end": { | |
"character": 54, | |
"line": 674 | |
}, | |
"start": { | |
"character": 54, | |
"line": 674 | |
} | |
}, | |
"newText": " " | |
}, | |
{ | |
"range": { | |
"end": { | |
"character": 32, | |
"line": 731 | |
}, | |
"start": { | |
"character": 32, | |
"line": 731 | |
} | |
}, | |
"newText": " " | |
}, | |
{ | |
"range": { | |
"end": { | |
"character": 33, | |
"line": 731 | |
}, | |
"start": { | |
"character": 33, | |
"line": 731 | |
} | |
}, | |
"newText": " " | |
}, | |
{ | |
"range": { | |
"end": { | |
"character": 32, | |
"line": 736 | |
}, | |
"start": { | |
"character": 32, | |
"line": 736 | |
} | |
}, | |
"newText": " " | |
}, | |
{ | |
"range": { | |
"end": { | |
"character": 33, | |
"line": 736 | |
}, | |
"start": { | |
"character": 33, | |
"line": 736 | |
} | |
}, | |
"newText": " " | |
}, | |
{ | |
"range": { | |
"end": { | |
"character": 20, | |
"line": 753 | |
}, | |
"start": { | |
"character": 47, | |
"line": 752 | |
} | |
}, | |
"newText": " " | |
}, | |
{ | |
"range": { | |
"end": { | |
"character": 0, | |
"line": 780 | |
}, | |
"start": { | |
"character": 1, | |
"line": 777 | |
} | |
}, | |
"newText": "\n\n" | |
}, | |
{ | |
"range": { | |
"end": { | |
"character": 0, | |
"line": 794 | |
}, | |
"start": { | |
"character": 6, | |
"line": 791 | |
} | |
}, | |
"newText": "\n\n" | |
}, | |
{ | |
"range": { | |
"end": { | |
"character": 0, | |
"line": 838 | |
}, | |
"start": { | |
"character": 1, | |
"line": 835 | |
} | |
}, | |
"newText": "\n\n" | |
}, | |
{ | |
"range": { | |
"end": { | |
"character": 0, | |
"line": 856 | |
}, | |
"start": { | |
"character": 1, | |
"line": 853 | |
} | |
}, | |
"newText": "\n\n" | |
}, | |
{ | |
"range": { | |
"end": { | |
"character": 40, | |
"line": 865 | |
}, | |
"start": { | |
"character": 39, | |
"line": 865 | |
} | |
}, | |
"newText": "\n " | |
}, | |
{ | |
"range": { | |
"end": { | |
"character": 61, | |
"line": 865 | |
}, | |
"start": { | |
"character": 60, | |
"line": 865 | |
} | |
}, | |
"newText": "\n " | |
}, | |
{ | |
"range": { | |
"end": { | |
"character": 2, | |
"line": 866 | |
}, | |
"start": { | |
"character": 84, | |
"line": 865 | |
} | |
}, | |
"newText": " " | |
}, | |
{ | |
"range": { | |
"end": { | |
"character": 39, | |
"line": 887 | |
}, | |
"start": { | |
"character": 38, | |
"line": 887 | |
} | |
}, | |
"newText": "\n " | |
}, | |
{ | |
"range": { | |
"end": { | |
"character": 51, | |
"line": 887 | |
}, | |
"start": { | |
"character": 50, | |
"line": 887 | |
} | |
}, | |
"newText": "\n " | |
}, | |
{ | |
"range": { | |
"end": { | |
"character": 73, | |
"line": 887 | |
}, | |
"start": { | |
"character": 72, | |
"line": 887 | |
} | |
}, | |
"newText": "\n " | |
}, | |
{ | |
"range": { | |
"end": { | |
"character": 2, | |
"line": 889 | |
}, | |
"start": { | |
"character": 39, | |
"line": 888 | |
} | |
}, | |
"newText": " " | |
}, | |
{ | |
"range": { | |
"end": { | |
"character": 9, | |
"line": 903 | |
}, | |
"start": { | |
"character": 57, | |
"line": 902 | |
} | |
}, | |
"newText": " " | |
}, | |
{ | |
"range": { | |
"end": { | |
"character": 47, | |
"line": 914 | |
}, | |
"start": { | |
"character": 46, | |
"line": 914 | |
} | |
}, | |
"newText": "\n " | |
}, | |
{ | |
"range": { | |
"end": { | |
"character": 64, | |
"line": 914 | |
}, | |
"start": { | |
"character": 63, | |
"line": 914 | |
} | |
}, | |
"newText": "\n " | |
}, | |
{ | |
"range": { | |
"end": { | |
"character": 51, | |
"line": 915 | |
}, | |
"start": { | |
"character": 50, | |
"line": 915 | |
} | |
}, | |
"newText": "\n " | |
}, | |
{ | |
"range": { | |
"end": { | |
"character": 65, | |
"line": 915 | |
}, | |
"start": { | |
"character": 64, | |
"line": 915 | |
} | |
}, | |
"newText": "\n " | |
}, | |
{ | |
"range": { | |
"end": { | |
"character": 79, | |
"line": 915 | |
}, | |
"start": { | |
"character": 78, | |
"line": 915 | |
} | |
}, | |
"newText": "\n " | |
}, | |
{ | |
"range": { | |
"end": { | |
"character": 48, | |
"line": 979 | |
}, | |
"start": { | |
"character": 47, | |
"line": 979 | |
} | |
}, | |
"newText": "\n " | |
}, | |
{ | |
"range": { | |
"end": { | |
"character": 68, | |
"line": 979 | |
}, | |
"start": { | |
"character": 67, | |
"line": 979 | |
} | |
}, | |
"newText": "\n " | |
}, | |
{ | |
"range": { | |
"end": { | |
"character": 82, | |
"line": 979 | |
}, | |
"start": { | |
"character": 81, | |
"line": 979 | |
} | |
}, | |
"newText": "\n " | |
}, | |
{ | |
"range": { | |
"end": { | |
"character": 18, | |
"line": 1015 | |
}, | |
"start": { | |
"character": 44, | |
"line": 1014 | |
} | |
}, | |
"newText": " " | |
}, | |
{ | |
"range": { | |
"end": { | |
"character": 18, | |
"line": 1027 | |
}, | |
"start": { | |
"character": 43, | |
"line": 1026 | |
} | |
}, | |
"newText": " " | |
}, | |
{ | |
"range": { | |
"end": { | |
"character": 20, | |
"line": 1062 | |
}, | |
"start": { | |
"character": 45, | |
"line": 1061 | |
} | |
}, | |
"newText": " " | |
}, | |
{ | |
"range": { | |
"end": { | |
"character": 2, | |
"line": 1093 | |
}, | |
"start": { | |
"character": 31, | |
"line": 1092 | |
} | |
}, | |
"newText": " " | |
}, | |
{ | |
"range": { | |
"end": { | |
"character": 2, | |
"line": 1112 | |
}, | |
"start": { | |
"character": 33, | |
"line": 1111 | |
} | |
}, | |
"newText": " " | |
}, | |
{ | |
"range": { | |
"end": { | |
"character": 27, | |
"line": 1126 | |
}, | |
"start": { | |
"character": 34, | |
"line": 1125 | |
} | |
}, | |
"newText": " " | |
}, | |
{ | |
"range": { | |
"end": { | |
"character": 27, | |
"line": 1127 | |
}, | |
"start": { | |
"character": 50, | |
"line": 1126 | |
} | |
}, | |
"newText": " " | |
}, | |
{ | |
"range": { | |
"end": { | |
"character": 2, | |
"line": 1193 | |
}, | |
"start": { | |
"character": 35, | |
"line": 1192 | |
} | |
}, | |
"newText": " " | |
}, | |
{ | |
"range": { | |
"end": { | |
"character": 2, | |
"line": 1204 | |
}, | |
"start": { | |
"character": 37, | |
"line": 1203 | |
} | |
}, | |
"newText": " " | |
}, | |
{ | |
"range": { | |
"end": { | |
"character": 15, | |
"line": 1267 | |
}, | |
"start": { | |
"character": 22, | |
"line": 1266 | |
} | |
}, | |
"newText": " " | |
}, | |
{ | |
"range": { | |
"end": { | |
"character": 14, | |
"line": 1299 | |
}, | |
"start": { | |
"character": 23, | |
"line": 1298 | |
} | |
}, | |
"newText": " " | |
}, | |
{ | |
"range": { | |
"end": { | |
"character": 14, | |
"line": 1300 | |
}, | |
"start": { | |
"character": 28, | |
"line": 1299 | |
} | |
}, | |
"newText": " " | |
}, | |
{ | |
"range": { | |
"end": { | |
"character": 34, | |
"line": 1327 | |
}, | |
"start": { | |
"character": 66, | |
"line": 1326 | |
} | |
}, | |
"newText": " " | |
}, | |
{ | |
"range": { | |
"end": { | |
"character": 32, | |
"line": 1388 | |
}, | |
"start": { | |
"character": 58, | |
"line": 1387 | |
} | |
}, | |
"newText": " " | |
}, | |
{ | |
"range": { | |
"end": { | |
"character": 2, | |
"line": 1391 | |
}, | |
"start": { | |
"character": 66, | |
"line": 1388 | |
} | |
}, | |
"newText": "\n\n " | |
}, | |
{ | |
"range": { | |
"end": { | |
"character": 15, | |
"line": 1420 | |
}, | |
"start": { | |
"character": 22, | |
"line": 1419 | |
} | |
}, | |
"newText": " " | |
}, | |
{ | |
"range": { | |
"end": { | |
"character": 0, | |
"line": 1432 | |
}, | |
"start": { | |
"character": 1, | |
"line": 1430 | |
} | |
}, | |
"newText": "\n" | |
} | |
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
set rtp+=~/.config/nvim/plugged/nvim-lspconfig | |
lua <<EOF | |
require('lspconfig')['ccls'].setup({}) | |
function _G.UndoDestroyer_save(dump_path) | |
dump_path = dump_path or 'UndoDestroyer.json' | |
local lsp = require('vim.lsp') | |
local bufnr = vim.api.nvim_get_current_buf() | |
local clients = lsp.buf_get_clients(bufnr) | |
assert(#clients == 1) | |
clients[1].request( | |
'textDocument/formatting', | |
lsp.util.make_formatting_params(), | |
function(_, result) | |
vim.fn.writefile({vim.fn.json_encode(result)}, dump_path) | |
end, | |
bufnr | |
) | |
end | |
function _G.UndoDestroyer_load(dump_path) | |
dump_path = dump_path or 'UndoDestroyer.json' | |
local lsp = require('vim.lsp') | |
local bufnr = vim.api.nvim_get_current_buf() | |
local result = vim.fn.json_decode(vim.fn.readfile(dump_path)) | |
lsp.util.apply_text_edits(result, bufnr) | |
end | |
EOF | |
command! -nargs=? UndoDestroyerSave call v:lua.UndoDestroyer_save(<f-args>) | |
command! -nargs=? UndoDestroyerLoad call v:lua.UndoDestroyer_load(<f-args>) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment