diff options
Diffstat (limited to 'src/nvim/lua')
-rw-r--r-- | src/nvim/lua/converter.c | 8 | ||||
-rw-r--r-- | src/nvim/lua/converter.h | 1 | ||||
-rw-r--r-- | src/nvim/lua/executor.c | 281 | ||||
-rw-r--r-- | src/nvim/lua/executor.h | 2 | ||||
-rw-r--r-- | src/nvim/lua/treesitter.c | 17 | ||||
-rw-r--r-- | src/nvim/lua/vim.lua | 198 |
6 files changed, 397 insertions, 110 deletions
diff --git a/src/nvim/lua/converter.c b/src/nvim/lua/converter.c index 030df69caa..ce8c9b0d06 100644 --- a/src/nvim/lua/converter.c +++ b/src/nvim/lua/converter.c @@ -245,7 +245,7 @@ bool nlua_pop_typval(lua_State *lstate, typval_T *ret_tv) } else { dictitem_T *const di = tv_dict_item_alloc_len(s, len); if (tv_dict_add(cur.tv->vval.v_dict, di) == FAIL) { - assert(false); + abort(); } kv_push(stack, cur); cur = (TVPopStackItem) { &di->di_tv, false, false, 0 }; @@ -391,7 +391,7 @@ bool nlua_pop_typval(lua_State *lstate, typval_T *ret_tv) break; } default: { - assert(false); + abort(); } } nlua_pop_typval_table_processing_end: @@ -400,7 +400,6 @@ nlua_pop_typval_table_processing_end: case LUA_TFUNCTION: { LuaCFunctionState *state = xmalloc(sizeof(LuaCFunctionState)); state->lua_callable.func_ref = nlua_ref(lstate, -1); - state->lua_callable.table_ref = LUA_NOREF; char_u *name = register_cfunc( &nlua_CFunction_func_call, @@ -412,6 +411,7 @@ nlua_pop_typval_table_processing_end: break; } case LUA_TUSERDATA: { + // TODO(bfredl): check mt.__call and convert to function? nlua_pushref(lstate, nlua_nil_ref); bool is_nil = lua_rawequal(lstate, -2, -1); lua_pop(lstate, 1); @@ -1200,7 +1200,7 @@ Object nlua_pop_Object(lua_State *const lstate, bool ref, Error *const err) break; } default: { - assert(false); + abort(); } } break; diff --git a/src/nvim/lua/converter.h b/src/nvim/lua/converter.h index 8601a32418..43a7e06019 100644 --- a/src/nvim/lua/converter.h +++ b/src/nvim/lua/converter.h @@ -11,7 +11,6 @@ typedef struct { LuaRef func_ref; - LuaRef table_ref; } LuaCallable; typedef struct { diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c index 3219c02068..f99a2dd0fe 100644 --- a/src/nvim/lua/executor.c +++ b/src/nvim/lua/executor.c @@ -5,6 +5,7 @@ #include <lualib.h> #include <lauxlib.h> +#include "nvim/assert.h" #include "nvim/version.h" #include "nvim/misc1.h" #include "nvim/getchar.h" @@ -16,8 +17,10 @@ #include "nvim/api/vim.h" #include "nvim/msgpack_rpc/channel.h" #include "nvim/vim.h" +#include "nvim/extmark.h" #include "nvim/ex_getln.h" #include "nvim/ex_cmds2.h" +#include "nvim/map.h" #include "nvim/message.h" #include "nvim/memline.h" #include "nvim/buffer_defs.h" @@ -32,9 +35,7 @@ #include "nvim/event/time.h" #include "nvim/event/loop.h" -#ifdef WIN32 #include "nvim/os/os.h" -#endif #include "nvim/lua/converter.h" #include "nvim/lua/executor.h" @@ -63,6 +64,11 @@ typedef struct { } \ } +#if __has_feature(address_sanitizer) + PMap(handle_T) *nlua_ref_markers = NULL; +# define NLUA_TRACK_REFS +#endif + /// Convert lua error into a Vim error message /// /// @param lstate Lua interpreter state. @@ -547,6 +553,13 @@ static int nlua_state_init(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL static lua_State *nlua_init(void) FUNC_ATTR_NONNULL_RET FUNC_ATTR_WARN_UNUSED_RESULT { +#ifdef NLUA_TRACK_REFS + const char *env = os_getenv("NVIM_LUA_NOTRACK"); + if (!env || !*env) { + nlua_ref_markers = pmap_new(handle_T)(); + } +#endif + lua_State *lstate = luaL_newstate(); if (lstate == NULL) { EMSG(_("E970: Failed to initialize lua interpreter")); @@ -554,9 +567,13 @@ static lua_State *nlua_init(void) } luaL_openlibs(lstate); nlua_state_init(lstate); + return lstate; } +// only to be used by nlua_enter and nlua_free_all_mem! +static lua_State *global_lstate = NULL; + /// Enter lua interpreter /// /// Calls nlua_init() if needed. Is responsible for pre-lua call initalization @@ -567,26 +584,39 @@ static lua_State *nlua_init(void) static lua_State *nlua_enter(void) FUNC_ATTR_NONNULL_RET FUNC_ATTR_WARN_UNUSED_RESULT { - static lua_State *global_lstate = NULL; if (global_lstate == NULL) { global_lstate = nlua_init(); } lua_State *const lstate = global_lstate; - // Last used p_rtp value. Must not be dereferenced because value pointed to - // may already be freed. Used to check whether &runtimepath option value - // changed. - static const void *last_p_rtp = NULL; - if (last_p_rtp != (const void *)p_rtp) { - // stack: (empty) - lua_getglobal(lstate, "vim"); - // stack: vim - lua_pop(lstate, 1); - // stack: (empty) - last_p_rtp = (const void *)p_rtp; - } return 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_ref_markers) { + // in case there are leaked luarefs, leak the associated memory + // to get LeakSanitizer stacktraces on exit + pmap_free(handle_T)(nlua_ref_markers); + } +#endif + + nlua_refcount = 0; + lua_close(lstate); +} + static void nlua_print_event(void **argv) { char *str = argv[0]; @@ -866,17 +896,35 @@ static int nlua_getenv(lua_State *lstate) } #endif + /// add the value to the registry LuaRef nlua_ref(lua_State *lstate, int index) { lua_pushvalue(lstate, index); - return luaL_ref(lstate, LUA_REGISTRYINDEX); + LuaRef ref = luaL_ref(lstate, LUA_REGISTRYINDEX); + if (ref > 0) { + nlua_refcount++; +#ifdef NLUA_TRACK_REFS + if (nlua_ref_markers) { + // 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_ref_markers) { + xfree(pmap_get(handle_T)(nlua_ref_markers, ref)); + } +#endif luaL_unref(lstate, LUA_REGISTRYINDEX, ref); } } @@ -893,19 +941,11 @@ 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 nlua_newref(lua_State *lstate, LuaRef original_ref) -{ - nlua_pushref(lstate, original_ref); - LuaRef new_ref = nlua_ref(lstate, -1); - lua_pop(lstate, 1); - - return new_ref; -} - LuaRef api_new_luaref(LuaRef original_ref) { if (original_ref == LUA_NOREF) { @@ -913,7 +953,10 @@ LuaRef api_new_luaref(LuaRef original_ref) } lua_State *const lstate = nlua_enter(); - return nlua_newref(lstate, original_ref); + nlua_pushref(lstate, original_ref); + LuaRef new_ref = nlua_ref(lstate, -1); + lua_pop(lstate, 1); + return new_ref; } @@ -1023,25 +1066,13 @@ int typval_exec_lua_callable( typval_T *rettv ) { - int offset = 0; LuaRef cb = lua_cb.func_ref; - if (cb == LUA_NOREF) { - // This shouldn't happen. - luaL_error(lstate, "Invalid function passed to VimL"); - return ERROR_OTHER; - } - nlua_pushref(lstate, cb); - if (lua_cb.table_ref != LUA_NOREF) { - offset += 1; - nlua_pushref(lstate, lua_cb.table_ref); - } - PUSH_ALL_TYPVALS(lstate, argvars, argcount, false); - if (lua_pcall(lstate, argcount + offset, 1, 0)) { + if (lua_pcall(lstate, argcount, 1, 0)) { nlua_print(lstate); return ERROR_OTHER; } @@ -1213,13 +1244,16 @@ void ex_luado(exarg_T *const eap) break; } lua_pushvalue(lstate, -1); - lua_pushstring(lstate, (const char *)ml_get_buf(curbuf, l, false)); + const char *old_line = (const char *)ml_get_buf(curbuf, l, false); + lua_pushstring(lstate, old_line); lua_pushnumber(lstate, (lua_Number)l); if (lua_pcall(lstate, 2, 1, 0)) { 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); @@ -1229,7 +1263,7 @@ void ex_luado(exarg_T *const eap) } } ml_replace(l, (char_u *)new_line_transformed, false); - changed_bytes(l, 0); + inserted_bytes(l, 0, (int)old_line_len, (int)new_line_len); } lua_pop(lstate, 1); } @@ -1272,6 +1306,12 @@ bool nlua_exec_file(const char *path) 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); @@ -1288,8 +1328,85 @@ static void nlua_add_treesitter(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL lua_pushcfunction(lstate, tslua_inspect_lang); lua_setfield(lstate, -2, "_ts_inspect_language"); - lua_pushcfunction(lstate, ts_lua_parse_query); + 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 = nlua_enter(); + 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._log_keystroke, buf ] + lua_pushlstring(lstate, (const char *)pat, STRLEN(pat)); + + if (lua_pcall(lstate, 1, 2, 0) != 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; } static int nlua_regex(lua_State *lstate) @@ -1425,6 +1542,8 @@ static int regex_match_line(lua_State *lstate) return nret; } +// Required functions for lua c functions as VimL callbacks + int nlua_CFunction_func_call( int argcount, typval_T *argvars, @@ -1434,53 +1553,40 @@ int nlua_CFunction_func_call( lua_State *const lstate = nlua_enter(); LuaCFunctionState *funcstate = (LuaCFunctionState *)state; - return typval_exec_lua_callable( - lstate, - funcstate->lua_callable, - argcount, - argvars, - rettv); + return typval_exec_lua_callable(lstate, funcstate->lua_callable, + argcount, argvars, rettv); } -/// Required functions for lua c functions as VimL callbacks + void nlua_CFunction_func_free(void *state) { lua_State *const lstate = nlua_enter(); LuaCFunctionState *funcstate = (LuaCFunctionState *)state; nlua_unref(lstate, funcstate->lua_callable.func_ref); - nlua_unref(lstate, funcstate->lua_callable.table_ref); xfree(funcstate); } bool nlua_is_table_from_lua(typval_T *const arg) { - if (arg->v_type != VAR_DICT && arg->v_type != VAR_LIST) { - return false; - } - if (arg->v_type == VAR_DICT) { - return arg->vval.v_dict->lua_table_ref > 0 - && arg->vval.v_dict->lua_table_ref != LUA_NOREF; + 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 > 0 - && arg->vval.v_list->lua_table_ref != LUA_NOREF; + return arg->vval.v_list->lua_table_ref != LUA_NOREF; + } else { + return false; } - - return false; } char_u *nlua_register_table_as_callable(typval_T *const arg) { - if (!nlua_is_table_from_lua(arg)) { - return NULL; - } - - LuaRef table_ref; + 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; - } else { + } + + if (table_ref == LUA_NOREF) { return NULL; } @@ -1490,55 +1596,34 @@ char_u *nlua_register_table_as_callable(typval_T *const arg) int top = lua_gettop(lstate); #endif - nlua_pushref(lstate, table_ref); + 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"); + 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; } - - LuaRef new_table_ref = nlua_newref(lstate, table_ref); + lua_pop(lstate, 2); // [table] LuaCFunctionState *state = xmalloc(sizeof(LuaCFunctionState)); state->lua_callable.func_ref = nlua_ref(lstate, -1); - state->lua_callable.table_ref = new_table_ref; - char_u *name = register_cfunc( - &nlua_CFunction_func_call, - &nlua_CFunction_func_free, - state); + char_u *name = register_cfunc(&nlua_CFunction_func_call, + &nlua_CFunction_func_free, state); - lua_pop(lstate, 3); + lua_pop(lstate, 1); // [] assert(top == lua_gettop(lstate)); return name; } -/// Helper function to free a list_T -void nlua_free_typval_list(list_T *const l) -{ - if (l->lua_table_ref != LUA_NOREF && l->lua_table_ref > 0) { - lua_State *const lstate = nlua_enter(); - nlua_unref(lstate, l->lua_table_ref); - l->lua_table_ref = LUA_NOREF; - } -} - - -/// Helper function to free a dict_T -void nlua_free_typval_dict(dict_T *const d) -{ - if (d->lua_table_ref != LUA_NOREF && d->lua_table_ref > 0) { - lua_State *const lstate = nlua_enter(); - nlua_unref(lstate, d->lua_table_ref); - d->lua_table_ref = LUA_NOREF; - } -} - void nlua_execute_log_keystroke(int c) { char_u buf[NUMBUFLEN]; diff --git a/src/nvim/lua/executor.h b/src/nvim/lua/executor.h index 1d7a15d9aa..ea774ac2e3 100644 --- a/src/nvim/lua/executor.h +++ b/src/nvim/lua/executor.h @@ -16,6 +16,8 @@ void nlua_add_api_functions(lua_State *lstate) REAL_FATTR_NONNULL_ALL; EXTERN LuaRef nlua_nil_ref INIT(= LUA_NOREF); EXTERN LuaRef nlua_empty_dict_ref INIT(= LUA_NOREF); +EXTERN int nlua_refcount INIT(= 0); + #define set_api_error(s, err) \ do { \ Error *err_ = (err); \ diff --git a/src/nvim/lua/treesitter.c b/src/nvim/lua/treesitter.c index a640b97d3b..c186928ae2 100644 --- a/src/nvim/lua/treesitter.c +++ b/src/nvim/lua/treesitter.c @@ -171,7 +171,7 @@ int tslua_add_language(lua_State *L) TSLanguage *lang = lang_parser(); if (lang == NULL) { - return luaL_error(L, "Failed to load parser: internal error"); + return luaL_error(L, "Failed to load parser %s: internal error", path); } uint32_t lang_version = ts_language_version(lang); @@ -179,7 +179,8 @@ int tslua_add_language(lua_State *L) || lang_version > TREE_SITTER_LANGUAGE_VERSION) { return luaL_error( L, - "ABI version mismatch : supported between %d and %d, found %d", + "ABI version mismatch for %s: supported between %d and %d, found %d", + path, TREE_SITTER_MIN_COMPATIBLE_LANGUAGE_VERSION, TREE_SITTER_LANGUAGE_VERSION, lang_version); } @@ -221,13 +222,19 @@ int tslua_inspect_lang(lua_State *L) lua_setfield(L, -2, "symbols"); // [retval] size_t nfields = (size_t)ts_language_field_count(lang); - lua_createtable(L, nfields-1, 1); // [retval, fields] - for (size_t i = 0; i < nfields; i++) { + lua_createtable(L, nfields, 1); // [retval, fields] + // Field IDs go from 1 to nfields inclusive (extra index 0 maps to NULL) + for (size_t i = 1; i <= nfields; i++) { lua_pushstring(L, ts_language_field_name_for_id(lang, i)); lua_rawseti(L, -2, i); // [retval, fields] } lua_setfield(L, -2, "fields"); // [retval] + + uint32_t lang_version = ts_language_version(lang); + lua_pushinteger(L, lang_version); // [retval, version] + lua_setfield(L, -2, "_abi_version"); + return 1; } @@ -1108,7 +1115,7 @@ static int querycursor_gc(lua_State *L) // Query methods -int ts_lua_parse_query(lua_State *L) +int tslua_parse_query(lua_State *L) { if (lua_gettop(L) < 2 || !lua_isstring(L, 1) || !lua_isstring(L, 2)) { return luaL_error(L, "string expected"); diff --git a/src/nvim/lua/vim.lua b/src/nvim/lua/vim.lua index 80b311de2c..eb54ff28ee 100644 --- a/src/nvim/lua/vim.lua +++ b/src/nvim/lua/vim.lua @@ -39,6 +39,16 @@ assert(vim) vim.inspect = package.loaded['vim.inspect'] assert(vim.inspect) +vim.log = { + levels = { + TRACE = 0; + DEBUG = 1; + INFO = 2; + WARN = 3; + ERROR = 4; + } +} + -- Internal-only until comments in #8107 are addressed. -- Returns: -- {errcode}, {output} @@ -253,8 +263,15 @@ end -- vim.fn.{func}(...) vim.fn = setmetatable({}, { __index = function(t, key) - local function _fn(...) - return vim.call(key, ...) + local _fn + if vim.api[key] ~= nil then + _fn = function() + error(string.format("Tried to call API function with vim.fn: use vim.api.%s instead", key)) + end + else + _fn = function(...) + return vim.call(key, ...) + end end t[key] = _fn return _fn @@ -478,6 +495,23 @@ function vim.defer_fn(fn, timeout) return timer end + +--- Notification provider +--- without a runtime, writes to :Messages +-- see :help nvim_notify +--@param msg Content of the notification to show to the user +--@param log_level Optional log level +--@param opts Dictionary with optional options (timeout, etc) +function vim.notify(msg, log_level, _opts) + + if log_level == vim.log.levels.ERROR then + vim.api.nvim_err_writeln(msg) + else + vim.api.nvim_echo({{msg}}, true, {}) + end +end + + local on_keystroke_callbacks = {} --- Register a lua {fn} with an {id} to be run after every keystroke. @@ -534,4 +568,164 @@ function vim._log_keystroke(char) end end +--- Generate a list of possible completions for the string. +--- String starts with ^ and then has the pattern. +--- +--- 1. Can we get it to just return things in the global namespace with that name prefix +--- 2. Can we get it to return things from global namespace even with `print(` in front. +function vim._expand_pat(pat, env) + env = env or _G + + pat = string.sub(pat, 2, #pat) + + if pat == '' then + local result = vim.tbl_keys(env) + table.sort(result) + return result, 0 + end + + -- TODO: We can handle spaces in [] ONLY. + -- We should probably do that at some point, just for cooler completion. + -- TODO: We can suggest the variable names to go in [] + -- This would be difficult as well. + -- Probably just need to do a smarter match than just `:match` + + -- Get the last part of the pattern + local last_part = pat:match("[%w.:_%[%]'\"]+$") + if not last_part then return {}, 0 end + + local parts, search_index = vim._expand_pat_get_parts(last_part) + + local match_part = string.sub(last_part, search_index, #last_part) + local prefix_match_pat = string.sub(pat, 1, #pat - #match_part) or '' + + local final_env = env + + for _, part in ipairs(parts) do + if type(final_env) ~= 'table' then + return {}, 0 + end + local key + + -- Normally, we just have a string + -- Just attempt to get the string directly from the environment + if type(part) == "string" then + key = part + else + -- However, sometimes you want to use a variable, and complete on it + -- With this, you have the power. + + -- MY_VAR = "api" + -- vim[MY_VAR] + -- -> _G[MY_VAR] -> "api" + local result_key = part[1] + if not result_key then + return {}, 0 + end + + local result = rawget(env, result_key) + + if result == nil then + return {}, 0 + end + + key = result + end + local field = rawget(final_env, key) + if field == nil then + local mt = getmetatable(final_env) + if mt and type(mt.__index) == "table" then + field = rawget(mt.__index, key) + end + end + final_env = field + + if not final_env then + return {}, 0 + end + end + + local keys = {} + local function insert_keys(obj) + for k,_ in pairs(obj) do + if type(k) == "string" and string.sub(k,1,string.len(match_part)) == match_part then + table.insert(keys,k) + end + end + end + + if type(final_env) == "table" then + insert_keys(final_env) + end + local mt = getmetatable(final_env) + if mt and type(mt.__index) == "table" then + insert_keys(mt.__index) + end + + table.sort(keys) + + return keys, #prefix_match_pat +end + +vim._expand_pat_get_parts = function(lua_string) + local parts = {} + + local accumulator, search_index = '', 1 + local in_brackets, bracket_end = false, -1 + local string_char = nil + for idx = 1, #lua_string do + local s = lua_string:sub(idx, idx) + + if not in_brackets and (s == "." or s == ":") then + table.insert(parts, accumulator) + accumulator = '' + + search_index = idx + 1 + elseif s == "[" then + in_brackets = true + + table.insert(parts, accumulator) + accumulator = '' + + search_index = idx + 1 + elseif in_brackets then + if idx == bracket_end then + in_brackets = false + search_index = idx + 1 + + if string_char == "VAR" then + table.insert(parts, { accumulator }) + accumulator = '' + + string_char = nil + end + elseif not string_char then + bracket_end = string.find(lua_string, ']', idx, true) + + if s == '"' or s == "'" then + string_char = s + elseif s ~= ' ' then + string_char = "VAR" + accumulator = s + end + elseif string_char then + if string_char ~= s then + accumulator = accumulator .. s + else + table.insert(parts, accumulator) + accumulator = '' + + string_char = nil + end + end + else + accumulator = accumulator .. s + end + end + + parts = vim.tbl_filter(function(val) return #val > 0 end, parts) + + return parts, search_index +end + return module |